buildSDK/bundle_build.sh
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass(['hidden','d-none']);
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass(['hidden','d-none']);
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fa
593  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594  * @cfg {String} badge text for badge
595  * @cfg {String} theme (default|glow)  
596  * @cfg {Boolean} inverse dark themed version
597  * @cfg {Boolean} toggle is it a slidy toggle button
598  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599  * @cfg {String} ontext text for on slidy toggle state
600  * @cfg {String} offtext text for off slidy toggle state
601  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
602  * @cfg {Boolean} removeClass remove the standard class..
603  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
604  * 
605  * @constructor
606  * Create a new button
607  * @param {Object} config The config object
608  */
609
610
611 Roo.bootstrap.Button = function(config){
612     Roo.bootstrap.Button.superclass.constructor.call(this, config);
613     this.weightClass = ["btn-default btn-outline-secondary", 
614                        "btn-primary", 
615                        "btn-success", 
616                        "btn-info", 
617                        "btn-warning",
618                        "btn-danger",
619                        "btn-link"
620                       ],  
621     this.addEvents({
622         // raw events
623         /**
624          * @event click
625          * When a butotn is pressed
626          * @param {Roo.bootstrap.Button} btn
627          * @param {Roo.EventObject} e
628          */
629         "click" : true,
630          /**
631          * @event toggle
632          * After the button has been toggles
633          * @param {Roo.bootstrap.Button} btn
634          * @param {Roo.EventObject} e
635          * @param {boolean} pressed (also available as button.pressed)
636          */
637         "toggle" : true
638     });
639 };
640
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
642     html: false,
643     active: false,
644     weight: '',
645     badge_weight: '',
646     outline : false,
647     size: '',
648     tag: 'button',
649     href: '',
650     disabled: false,
651     isClose: false,
652     glyphicon: '',
653     fa: '',
654     badge: '',
655     theme: 'default',
656     inverse: false,
657     
658     toggle: false,
659     ontext: 'ON',
660     offtext: 'OFF',
661     defaulton: true,
662     preventDefault: true,
663     removeClass: false,
664     name: false,
665     target: false,
666      
667     pressed : null,
668      
669     
670     getAutoCreate : function(){
671         
672         var cfg = {
673             tag : 'button',
674             cls : 'roo-button',
675             html: ''
676         };
677         
678         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
680             this.tag = 'button';
681         } else {
682             cfg.tag = this.tag;
683         }
684         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
685         
686         if (this.toggle == true) {
687             cfg={
688                 tag: 'div',
689                 cls: 'slider-frame roo-button',
690                 cn: [
691                     {
692                         tag: 'span',
693                         'data-on-text':'ON',
694                         'data-off-text':'OFF',
695                         cls: 'slider-button',
696                         html: this.offtext
697                     }
698                 ]
699             };
700             
701             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702                 cfg.cls += ' '+this.weight;
703             }
704             
705             return cfg;
706         }
707         
708         if (this.isClose) {
709             cfg.cls += ' close';
710             
711             cfg["aria-hidden"] = true;
712             
713             cfg.html = "&times;";
714             
715             return cfg;
716         }
717         
718          
719         if (this.theme==='default') {
720             cfg.cls = 'btn roo-button';
721             
722             //if (this.parentType != 'Navbar') {
723             this.weight = this.weight.length ?  this.weight : 'default';
724             //}
725             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
726                 
727                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729                 cfg.cls += ' btn-' + outline + weight;
730                 if (this.weight == 'default') {
731                     // BC
732                     cfg.cls += ' btn-' + this.weight;
733                 }
734             }
735         } else if (this.theme==='glow') {
736             
737             cfg.tag = 'a';
738             cfg.cls = 'btn-glow roo-button';
739             
740             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
741                 
742                 cfg.cls += ' ' + this.weight;
743             }
744         }
745    
746         
747         if (this.inverse) {
748             this.cls += ' inverse';
749         }
750         
751         
752         if (this.active || this.pressed === true) {
753             cfg.cls += ' active';
754         }
755         
756         if (this.disabled) {
757             cfg.disabled = 'disabled';
758         }
759         
760         if (this.items) {
761             Roo.log('changing to ul' );
762             cfg.tag = 'ul';
763             this.glyphicon = 'caret';
764             if (Roo.bootstrap.version == 4) {
765                 this.fa = 'caret-down';
766             }
767             
768         }
769         
770         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
771          
772         //gsRoo.log(this.parentType);
773         if (this.parentType === 'Navbar' && !this.parent().bar) {
774             Roo.log('changing to li?');
775             
776             cfg.tag = 'li';
777             
778             cfg.cls = '';
779             cfg.cn =  [{
780                 tag : 'a',
781                 cls : 'roo-button',
782                 html : this.html,
783                 href : this.href || '#'
784             }];
785             if (this.menu) {
786                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
787                 cfg.cls += ' dropdown';
788             }   
789             
790             delete cfg.html;
791             
792         }
793         
794        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
795         
796         if (this.glyphicon) {
797             cfg.html = ' ' + cfg.html;
798             
799             cfg.cn = [
800                 {
801                     tag: 'span',
802                     cls: 'glyphicon glyphicon-' + this.glyphicon
803                 }
804             ];
805         }
806         if (this.fa) {
807             cfg.html = ' ' + cfg.html;
808             
809             cfg.cn = [
810                 {
811                     tag: 'i',
812                     cls: 'fa fas fa-' + this.fa
813                 }
814             ];
815         }
816         
817         if (this.badge) {
818             cfg.html += ' ';
819             
820             cfg.tag = 'a';
821             
822 //            cfg.cls='btn roo-button';
823             
824             cfg.href=this.href;
825             
826             var value = cfg.html;
827             
828             if(this.glyphicon){
829                 value = {
830                     tag: 'span',
831                     cls: 'glyphicon glyphicon-' + this.glyphicon,
832                     html: this.html
833                 };
834             }
835             if(this.fa){
836                 value = {
837                     tag: 'i',
838                     cls: 'fa fas fa-' + this.fa,
839                     html: this.html
840                 };
841             }
842             
843             var bw = this.badge_weight.length ? this.badge_weight :
844                 (this.weight.length ? this.weight : 'secondary');
845             bw = bw == 'default' ? 'secondary' : bw;
846             
847             cfg.cn = [
848                 value,
849                 {
850                     tag: 'span',
851                     cls: 'badge badge-' + bw,
852                     html: this.badge
853                 }
854             ];
855             
856             cfg.html='';
857         }
858         
859         if (this.menu) {
860             cfg.cls += ' dropdown';
861             cfg.html = typeof(cfg.html) != 'undefined' ?
862                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
863         }
864         
865         if (cfg.tag !== 'a' && this.href !== '') {
866             throw "Tag must be a to set href.";
867         } else if (this.href.length > 0) {
868             cfg.href = this.href;
869         }
870         
871         if(this.removeClass){
872             cfg.cls = '';
873         }
874         
875         if(this.target){
876             cfg.target = this.target;
877         }
878         
879         return cfg;
880     },
881     initEvents: function() {
882        // Roo.log('init events?');
883 //        Roo.log(this.el.dom);
884         // add the menu...
885         
886         if (typeof (this.menu) != 'undefined') {
887             this.menu.parentType = this.xtype;
888             this.menu.triggerEl = this.el;
889             this.addxtype(Roo.apply({}, this.menu));
890         }
891
892
893        if (this.el.hasClass('roo-button')) {
894             this.el.on('click', this.onClick, this);
895        } else {
896             this.el.select('.roo-button').on('click', this.onClick, this);
897        }
898        
899        if(this.removeClass){
900            this.el.on('click', this.onClick, this);
901        }
902        
903        this.el.enableDisplayMode();
904         
905     },
906     onClick : function(e)
907     {
908         if (this.disabled) {
909             return;
910         }
911         
912         Roo.log('button on click ');
913         if(this.preventDefault){
914             e.preventDefault();
915         }
916         
917         if (this.pressed === true || this.pressed === false) {
918             this.toggleActive(e);
919         }
920         
921         
922         this.fireEvent('click', this, e);
923     },
924     
925     /**
926      * Enables this button
927      */
928     enable : function()
929     {
930         this.disabled = false;
931         this.el.removeClass('disabled');
932     },
933     
934     /**
935      * Disable this button
936      */
937     disable : function()
938     {
939         this.disabled = true;
940         this.el.addClass('disabled');
941     },
942      /**
943      * sets the active state on/off, 
944      * @param {Boolean} state (optional) Force a particular state
945      */
946     setActive : function(v) {
947         
948         this.el[v ? 'addClass' : 'removeClass']('active');
949         this.pressed = v;
950     },
951      /**
952      * toggles the current active state 
953      */
954     toggleActive : function(e)
955     {
956         this.setActive(!this.pressed);
957         this.fireEvent('toggle', this, e, !this.pressed);
958     },
959      /**
960      * get the current active state
961      * @return {boolean} true if it's active
962      */
963     isActive : function()
964     {
965         return this.el.hasClass('active');
966     },
967     /**
968      * set the text of the first selected button
969      */
970     setText : function(str)
971     {
972         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
973     },
974     /**
975      * get the text of the first selected button
976      */
977     getText : function()
978     {
979         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
980     },
981     
982     setWeight : function(str)
983     {
984         this.el.removeClass(this.weightClass);
985         this.weight = str;
986         var outline = this.outline ? 'outline-' : '';
987         if (str == 'default') {
988             this.el.addClass('btn-default btn-outline-secondary');        
989             return;
990         }
991         this.el.addClass('btn-' + outline + str);        
992     }
993     
994     
995 });
996
997  /*
998  * - LGPL
999  *
1000  * column
1001  * 
1002  */
1003
1004 /**
1005  * @class Roo.bootstrap.Column
1006  * @extends Roo.bootstrap.Component
1007  * Bootstrap Column class
1008  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1016  *
1017  * 
1018  * @cfg {Boolean} hidden (true|false) hide the element
1019  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020  * @cfg {String} fa (ban|check|...) font awesome icon
1021  * @cfg {Number} fasize (1|2|....) font awsome size
1022
1023  * @cfg {String} icon (info-sign|check|...) glyphicon name
1024
1025  * @cfg {String} html content of column.
1026  * 
1027  * @constructor
1028  * Create a new Column
1029  * @param {Object} config The config object
1030  */
1031
1032 Roo.bootstrap.Column = function(config){
1033     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1034 };
1035
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1037     
1038     xs: false,
1039     sm: false,
1040     md: false,
1041     lg: false,
1042     xsoff: false,
1043     smoff: false,
1044     mdoff: false,
1045     lgoff: false,
1046     html: '',
1047     offset: 0,
1048     alert: false,
1049     fa: false,
1050     icon : false,
1051     hidden : false,
1052     fasize : 1,
1053     
1054     getAutoCreate : function(){
1055         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1056         
1057         cfg = {
1058             tag: 'div',
1059             cls: 'column'
1060         };
1061         
1062         var settings=this;
1063         ['xs','sm','md','lg'].map(function(size){
1064             //Roo.log( size + ':' + settings[size]);
1065             
1066             if (settings[size+'off'] !== false) {
1067                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1068             }
1069             
1070             if (settings[size] === false) {
1071                 return;
1072             }
1073             
1074             if (!settings[size]) { // 0 = hidden
1075                 cfg.cls += ' hidden-' + size;
1076                 return;
1077             }
1078             cfg.cls += ' col-' + size + '-' + settings[size];
1079             
1080         });
1081         
1082         if (this.hidden) {
1083             cfg.cls += ' hidden';
1084         }
1085         
1086         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1087             cfg.cls +=' alert alert-' + this.alert;
1088         }
1089         
1090         
1091         if (this.html.length) {
1092             cfg.html = this.html;
1093         }
1094         if (this.fa) {
1095             var fasize = '';
1096             if (this.fasize > 1) {
1097                 fasize = ' fa-' + this.fasize + 'x';
1098             }
1099             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1100             
1101             
1102         }
1103         if (this.icon) {
1104             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1105         }
1106         
1107         return cfg;
1108     }
1109    
1110 });
1111
1112  
1113
1114  /*
1115  * - LGPL
1116  *
1117  * page container.
1118  * 
1119  */
1120
1121
1122 /**
1123  * @class Roo.bootstrap.Container
1124  * @extends Roo.bootstrap.Component
1125  * Bootstrap Container class
1126  * @cfg {Boolean} jumbotron is it a jumbotron element
1127  * @cfg {String} html content of element
1128  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1129  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1130  * @cfg {String} header content of header (for panel)
1131  * @cfg {String} footer content of footer (for panel)
1132  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1133  * @cfg {String} tag (header|aside|section) type of HTML tag.
1134  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1135  * @cfg {String} fa font awesome icon
1136  * @cfg {String} icon (info-sign|check|...) glyphicon name
1137  * @cfg {Boolean} hidden (true|false) hide the element
1138  * @cfg {Boolean} expandable (true|false) default false
1139  * @cfg {Boolean} expanded (true|false) default true
1140  * @cfg {String} rheader contet on the right of header
1141  * @cfg {Boolean} clickable (true|false) default false
1142
1143  *     
1144  * @constructor
1145  * Create a new Container
1146  * @param {Object} config The config object
1147  */
1148
1149 Roo.bootstrap.Container = function(config){
1150     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1151     
1152     this.addEvents({
1153         // raw events
1154          /**
1155          * @event expand
1156          * After the panel has been expand
1157          * 
1158          * @param {Roo.bootstrap.Container} this
1159          */
1160         "expand" : true,
1161         /**
1162          * @event collapse
1163          * After the panel has been collapsed
1164          * 
1165          * @param {Roo.bootstrap.Container} this
1166          */
1167         "collapse" : true,
1168         /**
1169          * @event click
1170          * When a element is chick
1171          * @param {Roo.bootstrap.Container} this
1172          * @param {Roo.EventObject} e
1173          */
1174         "click" : true
1175     });
1176 };
1177
1178 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1179     
1180     jumbotron : false,
1181     well: '',
1182     panel : '',
1183     header: '',
1184     footer : '',
1185     sticky: '',
1186     tag : false,
1187     alert : false,
1188     fa: false,
1189     icon : false,
1190     expandable : false,
1191     rheader : '',
1192     expanded : true,
1193     clickable: false,
1194   
1195      
1196     getChildContainer : function() {
1197         
1198         if(!this.el){
1199             return false;
1200         }
1201         
1202         if (this.panel.length) {
1203             return this.el.select('.panel-body',true).first();
1204         }
1205         
1206         return this.el;
1207     },
1208     
1209     
1210     getAutoCreate : function(){
1211         
1212         var cfg = {
1213             tag : this.tag || 'div',
1214             html : '',
1215             cls : ''
1216         };
1217         if (this.jumbotron) {
1218             cfg.cls = 'jumbotron';
1219         }
1220         
1221         
1222         
1223         // - this is applied by the parent..
1224         //if (this.cls) {
1225         //    cfg.cls = this.cls + '';
1226         //}
1227         
1228         if (this.sticky.length) {
1229             
1230             var bd = Roo.get(document.body);
1231             if (!bd.hasClass('bootstrap-sticky')) {
1232                 bd.addClass('bootstrap-sticky');
1233                 Roo.select('html',true).setStyle('height', '100%');
1234             }
1235              
1236             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1237         }
1238         
1239         
1240         if (this.well.length) {
1241             switch (this.well) {
1242                 case 'lg':
1243                 case 'sm':
1244                     cfg.cls +=' well well-' +this.well;
1245                     break;
1246                 default:
1247                     cfg.cls +=' well';
1248                     break;
1249             }
1250         }
1251         
1252         if (this.hidden) {
1253             cfg.cls += ' hidden';
1254         }
1255         
1256         
1257         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1258             cfg.cls +=' alert alert-' + this.alert;
1259         }
1260         
1261         var body = cfg;
1262         
1263         if (this.panel.length) {
1264             cfg.cls += ' panel panel-' + this.panel;
1265             cfg.cn = [];
1266             if (this.header.length) {
1267                 
1268                 var h = [];
1269                 
1270                 if(this.expandable){
1271                     
1272                     cfg.cls = cfg.cls + ' expandable';
1273                     
1274                     h.push({
1275                         tag: 'i',
1276                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1277                     });
1278                     
1279                 }
1280                 
1281                 h.push(
1282                     {
1283                         tag: 'span',
1284                         cls : 'panel-title',
1285                         html : (this.expandable ? '&nbsp;' : '') + this.header
1286                     },
1287                     {
1288                         tag: 'span',
1289                         cls: 'panel-header-right',
1290                         html: this.rheader
1291                     }
1292                 );
1293                 
1294                 cfg.cn.push({
1295                     cls : 'panel-heading',
1296                     style : this.expandable ? 'cursor: pointer' : '',
1297                     cn : h
1298                 });
1299                 
1300             }
1301             
1302             body = false;
1303             cfg.cn.push({
1304                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1305                 html : this.html
1306             });
1307             
1308             
1309             if (this.footer.length) {
1310                 cfg.cn.push({
1311                     cls : 'panel-footer',
1312                     html : this.footer
1313                     
1314                 });
1315             }
1316             
1317         }
1318         
1319         if (body) {
1320             body.html = this.html || cfg.html;
1321             // prefix with the icons..
1322             if (this.fa) {
1323                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1324             }
1325             if (this.icon) {
1326                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1327             }
1328             
1329             
1330         }
1331         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1332             cfg.cls =  'container';
1333         }
1334         
1335         return cfg;
1336     },
1337     
1338     initEvents: function() 
1339     {
1340         if(this.expandable){
1341             var headerEl = this.headerEl();
1342         
1343             if(headerEl){
1344                 headerEl.on('click', this.onToggleClick, this);
1345             }
1346         }
1347         
1348         if(this.clickable){
1349             this.el.on('click', this.onClick, this);
1350         }
1351         
1352     },
1353     
1354     onToggleClick : function()
1355     {
1356         var headerEl = this.headerEl();
1357         
1358         if(!headerEl){
1359             return;
1360         }
1361         
1362         if(this.expanded){
1363             this.collapse();
1364             return;
1365         }
1366         
1367         this.expand();
1368     },
1369     
1370     expand : function()
1371     {
1372         if(this.fireEvent('expand', this)) {
1373             
1374             this.expanded = true;
1375             
1376             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1377             
1378             this.el.select('.panel-body',true).first().removeClass('hide');
1379             
1380             var toggleEl = this.toggleEl();
1381
1382             if(!toggleEl){
1383                 return;
1384             }
1385
1386             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1387         }
1388         
1389     },
1390     
1391     collapse : function()
1392     {
1393         if(this.fireEvent('collapse', this)) {
1394             
1395             this.expanded = false;
1396             
1397             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1398             this.el.select('.panel-body',true).first().addClass('hide');
1399         
1400             var toggleEl = this.toggleEl();
1401
1402             if(!toggleEl){
1403                 return;
1404             }
1405
1406             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1407         }
1408     },
1409     
1410     toggleEl : function()
1411     {
1412         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1413             return;
1414         }
1415         
1416         return this.el.select('.panel-heading .fa',true).first();
1417     },
1418     
1419     headerEl : function()
1420     {
1421         if(!this.el || !this.panel.length || !this.header.length){
1422             return;
1423         }
1424         
1425         return this.el.select('.panel-heading',true).first()
1426     },
1427     
1428     bodyEl : function()
1429     {
1430         if(!this.el || !this.panel.length){
1431             return;
1432         }
1433         
1434         return this.el.select('.panel-body',true).first()
1435     },
1436     
1437     titleEl : function()
1438     {
1439         if(!this.el || !this.panel.length || !this.header.length){
1440             return;
1441         }
1442         
1443         return this.el.select('.panel-title',true).first();
1444     },
1445     
1446     setTitle : function(v)
1447     {
1448         var titleEl = this.titleEl();
1449         
1450         if(!titleEl){
1451             return;
1452         }
1453         
1454         titleEl.dom.innerHTML = v;
1455     },
1456     
1457     getTitle : function()
1458     {
1459         
1460         var titleEl = this.titleEl();
1461         
1462         if(!titleEl){
1463             return '';
1464         }
1465         
1466         return titleEl.dom.innerHTML;
1467     },
1468     
1469     setRightTitle : function(v)
1470     {
1471         var t = this.el.select('.panel-header-right',true).first();
1472         
1473         if(!t){
1474             return;
1475         }
1476         
1477         t.dom.innerHTML = v;
1478     },
1479     
1480     onClick : function(e)
1481     {
1482         e.preventDefault();
1483         
1484         this.fireEvent('click', this, e);
1485     }
1486 });
1487
1488  /*
1489  * - LGPL
1490  *
1491  * image
1492  * 
1493  */
1494
1495
1496 /**
1497  * @class Roo.bootstrap.Img
1498  * @extends Roo.bootstrap.Component
1499  * Bootstrap Img class
1500  * @cfg {Boolean} imgResponsive false | true
1501  * @cfg {String} border rounded | circle | thumbnail
1502  * @cfg {String} src image source
1503  * @cfg {String} alt image alternative text
1504  * @cfg {String} href a tag href
1505  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1506  * @cfg {String} xsUrl xs image source
1507  * @cfg {String} smUrl sm image source
1508  * @cfg {String} mdUrl md image source
1509  * @cfg {String} lgUrl lg image source
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Img = function(config){
1517     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1531     
1532     imgResponsive: true,
1533     border: '',
1534     src: 'about:blank',
1535     href: false,
1536     target: false,
1537     xsUrl: '',
1538     smUrl: '',
1539     mdUrl: '',
1540     lgUrl: '',
1541
1542     getAutoCreate : function()
1543     {   
1544         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1545             return this.createSingleImg();
1546         }
1547         
1548         var cfg = {
1549             tag: 'div',
1550             cls: 'roo-image-responsive-group',
1551             cn: []
1552         };
1553         var _this = this;
1554         
1555         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1556             
1557             if(!_this[size + 'Url']){
1558                 return;
1559             }
1560             
1561             var img = {
1562                 tag: 'img',
1563                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1564                 html: _this.html || cfg.html,
1565                 src: _this[size + 'Url']
1566             };
1567             
1568             img.cls += ' roo-image-responsive-' + size;
1569             
1570             var s = ['xs', 'sm', 'md', 'lg'];
1571             
1572             s.splice(s.indexOf(size), 1);
1573             
1574             Roo.each(s, function(ss){
1575                 img.cls += ' hidden-' + ss;
1576             });
1577             
1578             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1579                 cfg.cls += ' img-' + _this.border;
1580             }
1581             
1582             if(_this.alt){
1583                 cfg.alt = _this.alt;
1584             }
1585             
1586             if(_this.href){
1587                 var a = {
1588                     tag: 'a',
1589                     href: _this.href,
1590                     cn: [
1591                         img
1592                     ]
1593                 };
1594
1595                 if(this.target){
1596                     a.target = _this.target;
1597                 }
1598             }
1599             
1600             cfg.cn.push((_this.href) ? a : img);
1601             
1602         });
1603         
1604         return cfg;
1605     },
1606     
1607     createSingleImg : function()
1608     {
1609         var cfg = {
1610             tag: 'img',
1611             cls: (this.imgResponsive) ? 'img-responsive' : '',
1612             html : null,
1613             src : 'about:blank'  // just incase src get's set to undefined?!?
1614         };
1615         
1616         cfg.html = this.html || cfg.html;
1617         
1618         cfg.src = this.src || cfg.src;
1619         
1620         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1621             cfg.cls += ' img-' + this.border;
1622         }
1623         
1624         if(this.alt){
1625             cfg.alt = this.alt;
1626         }
1627         
1628         if(this.href){
1629             var a = {
1630                 tag: 'a',
1631                 href: this.href,
1632                 cn: [
1633                     cfg
1634                 ]
1635             };
1636             
1637             if(this.target){
1638                 a.target = this.target;
1639             }
1640             
1641         }
1642         
1643         return (this.href) ? a : cfg;
1644     },
1645     
1646     initEvents: function() 
1647     {
1648         if(!this.href){
1649             this.el.on('click', this.onClick, this);
1650         }
1651         
1652     },
1653     
1654     onClick : function(e)
1655     {
1656         Roo.log('img onclick');
1657         this.fireEvent('click', this, e);
1658     },
1659     /**
1660      * Sets the url of the image - used to update it
1661      * @param {String} url the url of the image
1662      */
1663     
1664     setSrc : function(url)
1665     {
1666         this.src =  url;
1667         
1668         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1669             this.el.dom.src =  url;
1670             return;
1671         }
1672         
1673         this.el.select('img', true).first().dom.src =  url;
1674     }
1675     
1676     
1677    
1678 });
1679
1680  /*
1681  * - LGPL
1682  *
1683  * image
1684  * 
1685  */
1686
1687
1688 /**
1689  * @class Roo.bootstrap.Link
1690  * @extends Roo.bootstrap.Component
1691  * Bootstrap Link Class
1692  * @cfg {String} alt image alternative text
1693  * @cfg {String} href a tag href
1694  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1695  * @cfg {String} html the content of the link.
1696  * @cfg {String} anchor name for the anchor link
1697  * @cfg {String} fa - favicon
1698
1699  * @cfg {Boolean} preventDefault (true | false) default false
1700
1701  * 
1702  * @constructor
1703  * Create a new Input
1704  * @param {Object} config The config object
1705  */
1706
1707 Roo.bootstrap.Link = function(config){
1708     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1709     
1710     this.addEvents({
1711         // img events
1712         /**
1713          * @event click
1714          * The img click event for the img.
1715          * @param {Roo.EventObject} e
1716          */
1717         "click" : true
1718     });
1719 };
1720
1721 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1722     
1723     href: false,
1724     target: false,
1725     preventDefault: false,
1726     anchor : false,
1727     alt : false,
1728     fa: false,
1729
1730
1731     getAutoCreate : function()
1732     {
1733         var html = this.html || '';
1734         
1735         if (this.fa !== false) {
1736             html = '<i class="fa fa-' + this.fa + '"></i>';
1737         }
1738         var cfg = {
1739             tag: 'a'
1740         };
1741         // anchor's do not require html/href...
1742         if (this.anchor === false) {
1743             cfg.html = html;
1744             cfg.href = this.href || '#';
1745         } else {
1746             cfg.name = this.anchor;
1747             if (this.html !== false || this.fa !== false) {
1748                 cfg.html = html;
1749             }
1750             if (this.href !== false) {
1751                 cfg.href = this.href;
1752             }
1753         }
1754         
1755         if(this.alt !== false){
1756             cfg.alt = this.alt;
1757         }
1758         
1759         
1760         if(this.target !== false) {
1761             cfg.target = this.target;
1762         }
1763         
1764         return cfg;
1765     },
1766     
1767     initEvents: function() {
1768         
1769         if(!this.href || this.preventDefault){
1770             this.el.on('click', this.onClick, this);
1771         }
1772     },
1773     
1774     onClick : function(e)
1775     {
1776         if(this.preventDefault){
1777             e.preventDefault();
1778         }
1779         //Roo.log('img onclick');
1780         this.fireEvent('click', this, e);
1781     }
1782    
1783 });
1784
1785  /*
1786  * - LGPL
1787  *
1788  * header
1789  * 
1790  */
1791
1792 /**
1793  * @class Roo.bootstrap.Header
1794  * @extends Roo.bootstrap.Component
1795  * Bootstrap Header class
1796  * @cfg {String} html content of header
1797  * @cfg {Number} level (1|2|3|4|5|6) default 1
1798  * 
1799  * @constructor
1800  * Create a new Header
1801  * @param {Object} config The config object
1802  */
1803
1804
1805 Roo.bootstrap.Header  = function(config){
1806     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1807 };
1808
1809 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1810     
1811     //href : false,
1812     html : false,
1813     level : 1,
1814     
1815     
1816     
1817     getAutoCreate : function(){
1818         
1819         
1820         
1821         var cfg = {
1822             tag: 'h' + (1 *this.level),
1823             html: this.html || ''
1824         } ;
1825         
1826         return cfg;
1827     }
1828    
1829 });
1830
1831  
1832
1833  /*
1834  * Based on:
1835  * Ext JS Library 1.1.1
1836  * Copyright(c) 2006-2007, Ext JS, LLC.
1837  *
1838  * Originally Released Under LGPL - original licence link has changed is not relivant.
1839  *
1840  * Fork - LGPL
1841  * <script type="text/javascript">
1842  */
1843  
1844 /**
1845  * @class Roo.bootstrap.MenuMgr
1846  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1847  * @singleton
1848  */
1849 Roo.bootstrap.MenuMgr = function(){
1850    var menus, active, groups = {}, attached = false, lastShow = new Date();
1851
1852    // private - called when first menu is created
1853    function init(){
1854        menus = {};
1855        active = new Roo.util.MixedCollection();
1856        Roo.get(document).addKeyListener(27, function(){
1857            if(active.length > 0){
1858                hideAll();
1859            }
1860        });
1861    }
1862
1863    // private
1864    function hideAll(){
1865        if(active && active.length > 0){
1866            var c = active.clone();
1867            c.each(function(m){
1868                m.hide();
1869            });
1870        }
1871    }
1872
1873    // private
1874    function onHide(m){
1875        active.remove(m);
1876        if(active.length < 1){
1877            Roo.get(document).un("mouseup", onMouseDown);
1878             
1879            attached = false;
1880        }
1881    }
1882
1883    // private
1884    function onShow(m){
1885        var last = active.last();
1886        lastShow = new Date();
1887        active.add(m);
1888        if(!attached){
1889           Roo.get(document).on("mouseup", onMouseDown);
1890            
1891            attached = true;
1892        }
1893        if(m.parentMenu){
1894           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1895           m.parentMenu.activeChild = m;
1896        }else if(last && last.isVisible()){
1897           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1898        }
1899    }
1900
1901    // private
1902    function onBeforeHide(m){
1903        if(m.activeChild){
1904            m.activeChild.hide();
1905        }
1906        if(m.autoHideTimer){
1907            clearTimeout(m.autoHideTimer);
1908            delete m.autoHideTimer;
1909        }
1910    }
1911
1912    // private
1913    function onBeforeShow(m){
1914        var pm = m.parentMenu;
1915        if(!pm && !m.allowOtherMenus){
1916            hideAll();
1917        }else if(pm && pm.activeChild && active != m){
1918            pm.activeChild.hide();
1919        }
1920    }
1921
1922    // private this should really trigger on mouseup..
1923    function onMouseDown(e){
1924         Roo.log("on Mouse Up");
1925         
1926         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1927             Roo.log("MenuManager hideAll");
1928             hideAll();
1929             e.stopEvent();
1930         }
1931         
1932         
1933    }
1934
1935    // private
1936    function onBeforeCheck(mi, state){
1937        if(state){
1938            var g = groups[mi.group];
1939            for(var i = 0, l = g.length; i < l; i++){
1940                if(g[i] != mi){
1941                    g[i].setChecked(false);
1942                }
1943            }
1944        }
1945    }
1946
1947    return {
1948
1949        /**
1950         * Hides all menus that are currently visible
1951         */
1952        hideAll : function(){
1953             hideAll();  
1954        },
1955
1956        // private
1957        register : function(menu){
1958            if(!menus){
1959                init();
1960            }
1961            menus[menu.id] = menu;
1962            menu.on("beforehide", onBeforeHide);
1963            menu.on("hide", onHide);
1964            menu.on("beforeshow", onBeforeShow);
1965            menu.on("show", onShow);
1966            var g = menu.group;
1967            if(g && menu.events["checkchange"]){
1968                if(!groups[g]){
1969                    groups[g] = [];
1970                }
1971                groups[g].push(menu);
1972                menu.on("checkchange", onCheck);
1973            }
1974        },
1975
1976         /**
1977          * Returns a {@link Roo.menu.Menu} object
1978          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1979          * be used to generate and return a new Menu instance.
1980          */
1981        get : function(menu){
1982            if(typeof menu == "string"){ // menu id
1983                return menus[menu];
1984            }else if(menu.events){  // menu instance
1985                return menu;
1986            }
1987            /*else if(typeof menu.length == 'number'){ // array of menu items?
1988                return new Roo.bootstrap.Menu({items:menu});
1989            }else{ // otherwise, must be a config
1990                return new Roo.bootstrap.Menu(menu);
1991            }
1992            */
1993            return false;
1994        },
1995
1996        // private
1997        unregister : function(menu){
1998            delete menus[menu.id];
1999            menu.un("beforehide", onBeforeHide);
2000            menu.un("hide", onHide);
2001            menu.un("beforeshow", onBeforeShow);
2002            menu.un("show", onShow);
2003            var g = menu.group;
2004            if(g && menu.events["checkchange"]){
2005                groups[g].remove(menu);
2006                menu.un("checkchange", onCheck);
2007            }
2008        },
2009
2010        // private
2011        registerCheckable : function(menuItem){
2012            var g = menuItem.group;
2013            if(g){
2014                if(!groups[g]){
2015                    groups[g] = [];
2016                }
2017                groups[g].push(menuItem);
2018                menuItem.on("beforecheckchange", onBeforeCheck);
2019            }
2020        },
2021
2022        // private
2023        unregisterCheckable : function(menuItem){
2024            var g = menuItem.group;
2025            if(g){
2026                groups[g].remove(menuItem);
2027                menuItem.un("beforecheckchange", onBeforeCheck);
2028            }
2029        }
2030    };
2031 }();/*
2032  * - LGPL
2033  *
2034  * menu
2035  * 
2036  */
2037
2038 /**
2039  * @class Roo.bootstrap.Menu
2040  * @extends Roo.bootstrap.Component
2041  * Bootstrap Menu class - container for MenuItems
2042  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2043  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2044  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2045  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2046  * 
2047  * @constructor
2048  * Create a new Menu
2049  * @param {Object} config The config object
2050  */
2051
2052
2053 Roo.bootstrap.Menu = function(config){
2054     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2055     if (this.registerMenu && this.type != 'treeview')  {
2056         Roo.bootstrap.MenuMgr.register(this);
2057     }
2058     
2059     
2060     this.addEvents({
2061         /**
2062          * @event beforeshow
2063          * Fires before this menu is displayed (return false to block)
2064          * @param {Roo.menu.Menu} this
2065          */
2066         beforeshow : true,
2067         /**
2068          * @event beforehide
2069          * Fires before this menu is hidden (return false to block)
2070          * @param {Roo.menu.Menu} this
2071          */
2072         beforehide : true,
2073         /**
2074          * @event show
2075          * Fires after this menu is displayed
2076          * @param {Roo.menu.Menu} this
2077          */
2078         show : true,
2079         /**
2080          * @event hide
2081          * Fires after this menu is hidden
2082          * @param {Roo.menu.Menu} this
2083          */
2084         hide : true,
2085         /**
2086          * @event click
2087          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2088          * @param {Roo.menu.Menu} this
2089          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2090          * @param {Roo.EventObject} e
2091          */
2092         click : true,
2093         /**
2094          * @event mouseover
2095          * Fires when the mouse is hovering over this menu
2096          * @param {Roo.menu.Menu} this
2097          * @param {Roo.EventObject} e
2098          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2099          */
2100         mouseover : true,
2101         /**
2102          * @event mouseout
2103          * Fires when the mouse exits this menu
2104          * @param {Roo.menu.Menu} this
2105          * @param {Roo.EventObject} e
2106          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2107          */
2108         mouseout : true,
2109         /**
2110          * @event itemclick
2111          * Fires when a menu item contained in this menu is clicked
2112          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2113          * @param {Roo.EventObject} e
2114          */
2115         itemclick: true
2116     });
2117     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2118 };
2119
2120 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2121     
2122    /// html : false,
2123     //align : '',
2124     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2125     type: false,
2126     /**
2127      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2128      */
2129     registerMenu : true,
2130     
2131     menuItems :false, // stores the menu items..
2132     
2133     hidden:true,
2134         
2135     parentMenu : false,
2136     
2137     stopEvent : true,
2138     
2139     isLink : false,
2140     
2141     getChildContainer : function() {
2142         return this.el;  
2143     },
2144     
2145     getAutoCreate : function(){
2146          
2147         //if (['right'].indexOf(this.align)!==-1) {
2148         //    cfg.cn[1].cls += ' pull-right'
2149         //}
2150         
2151         
2152         var cfg = {
2153             tag : 'ul',
2154             cls : 'dropdown-menu' ,
2155             style : 'z-index:1000'
2156             
2157         };
2158         
2159         if (this.type === 'submenu') {
2160             cfg.cls = 'submenu active';
2161         }
2162         if (this.type === 'treeview') {
2163             cfg.cls = 'treeview-menu';
2164         }
2165         
2166         return cfg;
2167     },
2168     initEvents : function() {
2169         
2170        // Roo.log("ADD event");
2171        // Roo.log(this.triggerEl.dom);
2172         
2173         this.triggerEl.on('click', this.onTriggerClick, this);
2174         
2175         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2176         
2177         
2178         if (this.triggerEl.hasClass('nav-item')) {
2179             // dropdown toggle on the 'a' in BS4?
2180             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2181         } else {
2182             this.triggerEl.addClass('dropdown-toggle');
2183         }
2184         if (Roo.isTouch) {
2185             this.el.on('touchstart'  , this.onTouch, this);
2186         }
2187         this.el.on('click' , this.onClick, this);
2188
2189         this.el.on("mouseover", this.onMouseOver, this);
2190         this.el.on("mouseout", this.onMouseOut, this);
2191         
2192     },
2193     
2194     findTargetItem : function(e)
2195     {
2196         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2197         if(!t){
2198             return false;
2199         }
2200         //Roo.log(t);         Roo.log(t.id);
2201         if(t && t.id){
2202             //Roo.log(this.menuitems);
2203             return this.menuitems.get(t.id);
2204             
2205             //return this.items.get(t.menuItemId);
2206         }
2207         
2208         return false;
2209     },
2210     
2211     onTouch : function(e) 
2212     {
2213         Roo.log("menu.onTouch");
2214         //e.stopEvent(); this make the user popdown broken
2215         this.onClick(e);
2216     },
2217     
2218     onClick : function(e)
2219     {
2220         Roo.log("menu.onClick");
2221         
2222         var t = this.findTargetItem(e);
2223         if(!t || t.isContainer){
2224             return;
2225         }
2226         Roo.log(e);
2227         /*
2228         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2229             if(t == this.activeItem && t.shouldDeactivate(e)){
2230                 this.activeItem.deactivate();
2231                 delete this.activeItem;
2232                 return;
2233             }
2234             if(t.canActivate){
2235                 this.setActiveItem(t, true);
2236             }
2237             return;
2238             
2239             
2240         }
2241         */
2242        
2243         Roo.log('pass click event');
2244         
2245         t.onClick(e);
2246         
2247         this.fireEvent("click", this, t, e);
2248         
2249         var _this = this;
2250         
2251         if(!t.href.length || t.href == '#'){
2252             (function() { _this.hide(); }).defer(100);
2253         }
2254         
2255     },
2256     
2257     onMouseOver : function(e){
2258         var t  = this.findTargetItem(e);
2259         //Roo.log(t);
2260         //if(t){
2261         //    if(t.canActivate && !t.disabled){
2262         //        this.setActiveItem(t, true);
2263         //    }
2264         //}
2265         
2266         this.fireEvent("mouseover", this, e, t);
2267     },
2268     isVisible : function(){
2269         return !this.hidden;
2270     },
2271      onMouseOut : function(e){
2272         var t  = this.findTargetItem(e);
2273         
2274         //if(t ){
2275         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2276         //        this.activeItem.deactivate();
2277         //        delete this.activeItem;
2278         //    }
2279         //}
2280         this.fireEvent("mouseout", this, e, t);
2281     },
2282     
2283     
2284     /**
2285      * Displays this menu relative to another element
2286      * @param {String/HTMLElement/Roo.Element} element The element to align to
2287      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2288      * the element (defaults to this.defaultAlign)
2289      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2290      */
2291     show : function(el, pos, parentMenu)
2292     {
2293         if (false === this.fireEvent("beforeshow", this)) {
2294             Roo.log("show canceled");
2295             return;
2296         }
2297         this.parentMenu = parentMenu;
2298         if(!this.el){
2299             this.render();
2300         }
2301         
2302         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2303     },
2304      /**
2305      * Displays this menu at a specific xy position
2306      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2307      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2308      */
2309     showAt : function(xy, parentMenu, /* private: */_e){
2310         this.parentMenu = parentMenu;
2311         if(!this.el){
2312             this.render();
2313         }
2314         if(_e !== false){
2315             this.fireEvent("beforeshow", this);
2316             //xy = this.el.adjustForConstraints(xy);
2317         }
2318         
2319         //this.el.show();
2320         this.hideMenuItems();
2321         this.hidden = false;
2322         this.triggerEl.addClass('open');
2323         this.el.addClass('show');
2324         
2325         // reassign x when hitting right
2326         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2327             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2328         }
2329         
2330         // reassign y when hitting bottom
2331         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2332             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2333         }
2334         
2335         // but the list may align on trigger left or trigger top... should it be a properity?
2336         
2337         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2338             this.el.setXY(xy);
2339         }
2340         
2341         this.focus();
2342         this.fireEvent("show", this);
2343     },
2344     
2345     focus : function(){
2346         return;
2347         if(!this.hidden){
2348             this.doFocus.defer(50, this);
2349         }
2350     },
2351
2352     doFocus : function(){
2353         if(!this.hidden){
2354             this.focusEl.focus();
2355         }
2356     },
2357
2358     /**
2359      * Hides this menu and optionally all parent menus
2360      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2361      */
2362     hide : function(deep)
2363     {
2364         if (false === this.fireEvent("beforehide", this)) {
2365             Roo.log("hide canceled");
2366             return;
2367         }
2368         this.hideMenuItems();
2369         if(this.el && this.isVisible()){
2370            
2371             if(this.activeItem){
2372                 this.activeItem.deactivate();
2373                 this.activeItem = null;
2374             }
2375             this.triggerEl.removeClass('open');;
2376             this.el.removeClass('show');
2377             this.hidden = true;
2378             this.fireEvent("hide", this);
2379         }
2380         if(deep === true && this.parentMenu){
2381             this.parentMenu.hide(true);
2382         }
2383     },
2384     
2385     onTriggerClick : function(e)
2386     {
2387         Roo.log('trigger click');
2388         
2389         var target = e.getTarget();
2390         
2391         Roo.log(target.nodeName.toLowerCase());
2392         
2393         if(target.nodeName.toLowerCase() === 'i'){
2394             e.preventDefault();
2395         }
2396         
2397     },
2398     
2399     onTriggerPress  : function(e)
2400     {
2401         Roo.log('trigger press');
2402         //Roo.log(e.getTarget());
2403        // Roo.log(this.triggerEl.dom);
2404        
2405         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2406         var pel = Roo.get(e.getTarget());
2407         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2408             Roo.log('is treeview or dropdown?');
2409             return;
2410         }
2411         
2412         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2413             return;
2414         }
2415         
2416         if (this.isVisible()) {
2417             Roo.log('hide');
2418             this.hide();
2419         } else {
2420             Roo.log('show');
2421             this.show(this.triggerEl, '?', false);
2422         }
2423         
2424         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2425             e.stopEvent();
2426         }
2427         
2428     },
2429        
2430     
2431     hideMenuItems : function()
2432     {
2433         Roo.log("hide Menu Items");
2434         if (!this.el) { 
2435             return;
2436         }
2437         
2438         this.el.select('.open',true).each(function(aa) {
2439             
2440             aa.removeClass('open');
2441          
2442         });
2443     },
2444     addxtypeChild : function (tree, cntr) {
2445         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2446           
2447         this.menuitems.add(comp);
2448         return comp;
2449
2450     },
2451     getEl : function()
2452     {
2453         Roo.log(this.el);
2454         return this.el;
2455     },
2456     
2457     clear : function()
2458     {
2459         this.getEl().dom.innerHTML = '';
2460         this.menuitems.clear();
2461     }
2462 });
2463
2464  
2465  /*
2466  * - LGPL
2467  *
2468  * menu item
2469  * 
2470  */
2471
2472
2473 /**
2474  * @class Roo.bootstrap.MenuItem
2475  * @extends Roo.bootstrap.Component
2476  * Bootstrap MenuItem class
2477  * @cfg {String} html the menu label
2478  * @cfg {String} href the link
2479  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2480  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2481  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2482  * @cfg {String} fa favicon to show on left of menu item.
2483  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2484  * 
2485  * 
2486  * @constructor
2487  * Create a new MenuItem
2488  * @param {Object} config The config object
2489  */
2490
2491
2492 Roo.bootstrap.MenuItem = function(config){
2493     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2494     this.addEvents({
2495         // raw events
2496         /**
2497          * @event click
2498          * The raw click event for the entire grid.
2499          * @param {Roo.bootstrap.MenuItem} this
2500          * @param {Roo.EventObject} e
2501          */
2502         "click" : true
2503     });
2504 };
2505
2506 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2507     
2508     href : false,
2509     html : false,
2510     preventDefault: false,
2511     isContainer : false,
2512     active : false,
2513     fa: false,
2514     
2515     getAutoCreate : function(){
2516         
2517         if(this.isContainer){
2518             return {
2519                 tag: 'li',
2520                 cls: 'dropdown-menu-item '
2521             };
2522         }
2523         var ctag = {
2524             tag: 'span',
2525             html: 'Link'
2526         };
2527         
2528         var anc = {
2529             tag : 'a',
2530             cls : 'dropdown-item',
2531             href : '#',
2532             cn : [  ]
2533         };
2534         
2535         if (this.fa !== false) {
2536             anc.cn.push({
2537                 tag : 'i',
2538                 cls : 'fa fa-' + this.fa
2539             });
2540         }
2541         
2542         anc.cn.push(ctag);
2543         
2544         
2545         var cfg= {
2546             tag: 'li',
2547             cls: 'dropdown-menu-item',
2548             cn: [ anc ]
2549         };
2550         if (this.parent().type == 'treeview') {
2551             cfg.cls = 'treeview-menu';
2552         }
2553         if (this.active) {
2554             cfg.cls += ' active';
2555         }
2556         
2557         
2558         
2559         anc.href = this.href || cfg.cn[0].href ;
2560         ctag.html = this.html || cfg.cn[0].html ;
2561         return cfg;
2562     },
2563     
2564     initEvents: function()
2565     {
2566         if (this.parent().type == 'treeview') {
2567             this.el.select('a').on('click', this.onClick, this);
2568         }
2569         
2570         if (this.menu) {
2571             this.menu.parentType = this.xtype;
2572             this.menu.triggerEl = this.el;
2573             this.menu = this.addxtype(Roo.apply({}, this.menu));
2574         }
2575         
2576     },
2577     onClick : function(e)
2578     {
2579         Roo.log('item on click ');
2580         
2581         if(this.preventDefault){
2582             e.preventDefault();
2583         }
2584         //this.parent().hideMenuItems();
2585         
2586         this.fireEvent('click', this, e);
2587     },
2588     getEl : function()
2589     {
2590         return this.el;
2591     } 
2592 });
2593
2594  
2595
2596  /*
2597  * - LGPL
2598  *
2599  * menu separator
2600  * 
2601  */
2602
2603
2604 /**
2605  * @class Roo.bootstrap.MenuSeparator
2606  * @extends Roo.bootstrap.Component
2607  * Bootstrap MenuSeparator class
2608  * 
2609  * @constructor
2610  * Create a new MenuItem
2611  * @param {Object} config The config object
2612  */
2613
2614
2615 Roo.bootstrap.MenuSeparator = function(config){
2616     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2617 };
2618
2619 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2620     
2621     getAutoCreate : function(){
2622         var cfg = {
2623             cls: 'divider',
2624             tag : 'li'
2625         };
2626         
2627         return cfg;
2628     }
2629    
2630 });
2631
2632  
2633
2634  
2635 /*
2636 * Licence: LGPL
2637 */
2638
2639 /**
2640  * @class Roo.bootstrap.Modal
2641  * @extends Roo.bootstrap.Component
2642  * Bootstrap Modal class
2643  * @cfg {String} title Title of dialog
2644  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2645  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2646  * @cfg {Boolean} specificTitle default false
2647  * @cfg {Array} buttons Array of buttons or standard button set..
2648  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2649  * @cfg {Boolean} animate default true
2650  * @cfg {Boolean} allow_close default true
2651  * @cfg {Boolean} fitwindow default false
2652  * @cfg {String} size (sm|lg) default empty
2653  * @cfg {Number} max_width set the max width of modal
2654  *
2655  *
2656  * @constructor
2657  * Create a new Modal Dialog
2658  * @param {Object} config The config object
2659  */
2660
2661 Roo.bootstrap.Modal = function(config){
2662     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2663     this.addEvents({
2664         // raw events
2665         /**
2666          * @event btnclick
2667          * The raw btnclick event for the button
2668          * @param {Roo.EventObject} e
2669          */
2670         "btnclick" : true,
2671         /**
2672          * @event resize
2673          * Fire when dialog resize
2674          * @param {Roo.bootstrap.Modal} this
2675          * @param {Roo.EventObject} e
2676          */
2677         "resize" : true
2678     });
2679     this.buttons = this.buttons || [];
2680
2681     if (this.tmpl) {
2682         this.tmpl = Roo.factory(this.tmpl);
2683     }
2684
2685 };
2686
2687 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2688
2689     title : 'test dialog',
2690
2691     buttons : false,
2692
2693     // set on load...
2694
2695     html: false,
2696
2697     tmp: false,
2698
2699     specificTitle: false,
2700
2701     buttonPosition: 'right',
2702
2703     allow_close : true,
2704
2705     animate : true,
2706
2707     fitwindow: false,
2708     
2709      // private
2710     dialogEl: false,
2711     bodyEl:  false,
2712     footerEl:  false,
2713     titleEl:  false,
2714     closeEl:  false,
2715
2716     size: '',
2717     
2718     max_width: 0,
2719     
2720     max_height: 0,
2721     
2722     fit_content: false,
2723
2724     onRender : function(ct, position)
2725     {
2726         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2727
2728         if(!this.el){
2729             var cfg = Roo.apply({},  this.getAutoCreate());
2730             cfg.id = Roo.id();
2731             //if(!cfg.name){
2732             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2733             //}
2734             //if (!cfg.name.length) {
2735             //    delete cfg.name;
2736            // }
2737             if (this.cls) {
2738                 cfg.cls += ' ' + this.cls;
2739             }
2740             if (this.style) {
2741                 cfg.style = this.style;
2742             }
2743             this.el = Roo.get(document.body).createChild(cfg, position);
2744         }
2745         //var type = this.el.dom.type;
2746
2747
2748         if(this.tabIndex !== undefined){
2749             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2750         }
2751
2752         this.dialogEl = this.el.select('.modal-dialog',true).first();
2753         this.bodyEl = this.el.select('.modal-body',true).first();
2754         this.closeEl = this.el.select('.modal-header .close', true).first();
2755         this.headerEl = this.el.select('.modal-header',true).first();
2756         this.titleEl = this.el.select('.modal-title',true).first();
2757         this.footerEl = this.el.select('.modal-footer',true).first();
2758
2759         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2760         
2761         //this.el.addClass("x-dlg-modal");
2762
2763         if (this.buttons.length) {
2764             Roo.each(this.buttons, function(bb) {
2765                 var b = Roo.apply({}, bb);
2766                 b.xns = b.xns || Roo.bootstrap;
2767                 b.xtype = b.xtype || 'Button';
2768                 if (typeof(b.listeners) == 'undefined') {
2769                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2770                 }
2771
2772                 var btn = Roo.factory(b);
2773
2774                 btn.render(this.getButtonContainer());
2775
2776             },this);
2777         }
2778         // render the children.
2779         var nitems = [];
2780
2781         if(typeof(this.items) != 'undefined'){
2782             var items = this.items;
2783             delete this.items;
2784
2785             for(var i =0;i < items.length;i++) {
2786                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2787             }
2788         }
2789
2790         this.items = nitems;
2791
2792         // where are these used - they used to be body/close/footer
2793
2794
2795         this.initEvents();
2796         //this.el.addClass([this.fieldClass, this.cls]);
2797
2798     },
2799
2800     getAutoCreate : function()
2801     {
2802         var bdy = {
2803                 cls : 'modal-body',
2804                 html : this.html || ''
2805         };
2806
2807         var title = {
2808             tag: 'h4',
2809             cls : 'modal-title',
2810             html : this.title
2811         };
2812
2813         if(this.specificTitle){
2814             title = this.title;
2815
2816         }
2817
2818         var header = [];
2819         if (this.allow_close && Roo.bootstrap.version == 3) {
2820             header.push({
2821                 tag: 'button',
2822                 cls : 'close',
2823                 html : '&times'
2824             });
2825         }
2826
2827         header.push(title);
2828
2829         if (this.allow_close && Roo.bootstrap.version == 4) {
2830             header.push({
2831                 tag: 'button',
2832                 cls : 'close',
2833                 html : '&times'
2834             });
2835         }
2836         
2837         var size = '';
2838
2839         if(this.size.length){
2840             size = 'modal-' + this.size;
2841         }
2842         
2843         var footer = Roo.bootstrap.version == 3 ?
2844             {
2845                 cls : 'modal-footer',
2846                 cn : [
2847                     {
2848                         tag: 'div',
2849                         cls: 'btn-' + this.buttonPosition
2850                     }
2851                 ]
2852
2853             } :
2854             {  // BS4 uses mr-auto on left buttons....
2855                 cls : 'modal-footer'
2856             };
2857
2858             
2859
2860         
2861         
2862         var modal = {
2863             cls: "modal",
2864              cn : [
2865                 {
2866                     cls: "modal-dialog " + size,
2867                     cn : [
2868                         {
2869                             cls : "modal-content",
2870                             cn : [
2871                                 {
2872                                     cls : 'modal-header',
2873                                     cn : header
2874                                 },
2875                                 bdy,
2876                                 footer
2877                             ]
2878
2879                         }
2880                     ]
2881
2882                 }
2883             ]
2884         };
2885
2886         if(this.animate){
2887             modal.cls += ' fade';
2888         }
2889
2890         return modal;
2891
2892     },
2893     getChildContainer : function() {
2894
2895          return this.bodyEl;
2896
2897     },
2898     getButtonContainer : function() {
2899         
2900          return Roo.bootstrap.version == 4 ?
2901             this.el.select('.modal-footer',true).first()
2902             : this.el.select('.modal-footer div',true).first();
2903
2904     },
2905     initEvents : function()
2906     {
2907         if (this.allow_close) {
2908             this.closeEl.on('click', this.hide, this);
2909         }
2910         Roo.EventManager.onWindowResize(this.resize, this, true);
2911
2912
2913     },
2914   
2915
2916     resize : function()
2917     {
2918         this.maskEl.setSize(
2919             Roo.lib.Dom.getViewWidth(true),
2920             Roo.lib.Dom.getViewHeight(true)
2921         );
2922         
2923         if (this.fitwindow) {
2924             
2925            
2926             this.setSize(
2927                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2928                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2929             );
2930             return;
2931         }
2932         
2933         if(this.max_width !== 0) {
2934             
2935             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2936             
2937             if(this.height) {
2938                 this.setSize(w, this.height);
2939                 return;
2940             }
2941             
2942             if(this.max_height) {
2943                 this.setSize(w,Math.min(
2944                     this.max_height,
2945                     Roo.lib.Dom.getViewportHeight(true) - 60
2946                 ));
2947                 
2948                 return;
2949             }
2950             
2951             if(!this.fit_content) {
2952                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2953                 return;
2954             }
2955             
2956             this.setSize(w, Math.min(
2957                 60 +
2958                 this.headerEl.getHeight() + 
2959                 this.footerEl.getHeight() + 
2960                 this.getChildHeight(this.bodyEl.dom.childNodes),
2961                 Roo.lib.Dom.getViewportHeight(true) - 60)
2962             );
2963         }
2964         
2965     },
2966
2967     setSize : function(w,h)
2968     {
2969         if (!w && !h) {
2970             return;
2971         }
2972         
2973         this.resizeTo(w,h);
2974     },
2975
2976     show : function() {
2977
2978         if (!this.rendered) {
2979             this.render();
2980         }
2981
2982         //this.el.setStyle('display', 'block');
2983         this.el.removeClass('hideing');
2984         this.el.dom.style.display='block';
2985         
2986         Roo.get(document.body).addClass('modal-open');
2987  
2988         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2989             
2990             (function(){
2991                 this.el.addClass('show');
2992                 this.el.addClass('in');
2993             }).defer(50, this);
2994         }else{
2995             this.el.addClass('show');
2996             this.el.addClass('in');
2997         }
2998
2999         // not sure how we can show data in here..
3000         //if (this.tmpl) {
3001         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3002         //}
3003
3004         Roo.get(document.body).addClass("x-body-masked");
3005         
3006         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3007         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3008         this.maskEl.dom.style.display = 'block';
3009         this.maskEl.addClass('show');
3010         
3011         
3012         this.resize();
3013         
3014         this.fireEvent('show', this);
3015
3016         // set zindex here - otherwise it appears to be ignored...
3017         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3018
3019         (function () {
3020             this.items.forEach( function(e) {
3021                 e.layout ? e.layout() : false;
3022
3023             });
3024         }).defer(100,this);
3025
3026     },
3027     hide : function()
3028     {
3029         if(this.fireEvent("beforehide", this) !== false){
3030             
3031             this.maskEl.removeClass('show');
3032             
3033             this.maskEl.dom.style.display = '';
3034             Roo.get(document.body).removeClass("x-body-masked");
3035             this.el.removeClass('in');
3036             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3037
3038             if(this.animate){ // why
3039                 this.el.addClass('hideing');
3040                 this.el.removeClass('show');
3041                 (function(){
3042                     if (!this.el.hasClass('hideing')) {
3043                         return; // it's been shown again...
3044                     }
3045                     
3046                     this.el.dom.style.display='';
3047
3048                     Roo.get(document.body).removeClass('modal-open');
3049                     this.el.removeClass('hideing');
3050                 }).defer(150,this);
3051                 
3052             }else{
3053                 this.el.removeClass('show');
3054                 this.el.dom.style.display='';
3055                 Roo.get(document.body).removeClass('modal-open');
3056
3057             }
3058             this.fireEvent('hide', this);
3059         }
3060     },
3061     isVisible : function()
3062     {
3063         
3064         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3065         
3066     },
3067
3068     addButton : function(str, cb)
3069     {
3070
3071
3072         var b = Roo.apply({}, { html : str } );
3073         b.xns = b.xns || Roo.bootstrap;
3074         b.xtype = b.xtype || 'Button';
3075         if (typeof(b.listeners) == 'undefined') {
3076             b.listeners = { click : cb.createDelegate(this)  };
3077         }
3078
3079         var btn = Roo.factory(b);
3080
3081         btn.render(this.getButtonContainer());
3082
3083         return btn;
3084
3085     },
3086
3087     setDefaultButton : function(btn)
3088     {
3089         //this.el.select('.modal-footer').()
3090     },
3091
3092     resizeTo: function(w,h)
3093     {
3094         this.dialogEl.setWidth(w);
3095         
3096         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3097
3098         this.bodyEl.setHeight(h - diff);
3099         
3100         this.fireEvent('resize', this);
3101     },
3102     
3103     setContentSize  : function(w, h)
3104     {
3105
3106     },
3107     onButtonClick: function(btn,e)
3108     {
3109         //Roo.log([a,b,c]);
3110         this.fireEvent('btnclick', btn.name, e);
3111     },
3112      /**
3113      * Set the title of the Dialog
3114      * @param {String} str new Title
3115      */
3116     setTitle: function(str) {
3117         this.titleEl.dom.innerHTML = str;
3118     },
3119     /**
3120      * Set the body of the Dialog
3121      * @param {String} str new Title
3122      */
3123     setBody: function(str) {
3124         this.bodyEl.dom.innerHTML = str;
3125     },
3126     /**
3127      * Set the body of the Dialog using the template
3128      * @param {Obj} data - apply this data to the template and replace the body contents.
3129      */
3130     applyBody: function(obj)
3131     {
3132         if (!this.tmpl) {
3133             Roo.log("Error - using apply Body without a template");
3134             //code
3135         }
3136         this.tmpl.overwrite(this.bodyEl, obj);
3137     },
3138     
3139     getChildHeight : function(child_nodes)
3140     {
3141         if(
3142             !child_nodes ||
3143             child_nodes.length == 0
3144         ) {
3145             return;
3146         }
3147         
3148         var child_height = 0;
3149         
3150         for(var i = 0; i < child_nodes.length; i++) {
3151             
3152             /*
3153             * for modal with tabs...
3154             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3155                 
3156                 var layout_childs = child_nodes[i].childNodes;
3157                 
3158                 for(var j = 0; j < layout_childs.length; j++) {
3159                     
3160                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3161                         
3162                         var layout_body_childs = layout_childs[j].childNodes;
3163                         
3164                         for(var k = 0; k < layout_body_childs.length; k++) {
3165                             
3166                             if(layout_body_childs[k].classList.contains('navbar')) {
3167                                 child_height += layout_body_childs[k].offsetHeight;
3168                                 continue;
3169                             }
3170                             
3171                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3172                                 
3173                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3174                                 
3175                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3176                                     
3177                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3178                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3179                                         continue;
3180                                     }
3181                                     
3182                                 }
3183                                 
3184                             }
3185                             
3186                         }
3187                     }
3188                 }
3189                 continue;
3190             }
3191             */
3192             
3193             child_height += child_nodes[i].offsetHeight;
3194             // Roo.log(child_nodes[i].offsetHeight);
3195         }
3196         
3197         return child_height;
3198     }
3199
3200 });
3201
3202
3203 Roo.apply(Roo.bootstrap.Modal,  {
3204     /**
3205          * Button config that displays a single OK button
3206          * @type Object
3207          */
3208         OK :  [{
3209             name : 'ok',
3210             weight : 'primary',
3211             html : 'OK'
3212         }],
3213         /**
3214          * Button config that displays Yes and No buttons
3215          * @type Object
3216          */
3217         YESNO : [
3218             {
3219                 name  : 'no',
3220                 html : 'No'
3221             },
3222             {
3223                 name  :'yes',
3224                 weight : 'primary',
3225                 html : 'Yes'
3226             }
3227         ],
3228
3229         /**
3230          * Button config that displays OK and Cancel buttons
3231          * @type Object
3232          */
3233         OKCANCEL : [
3234             {
3235                name : 'cancel',
3236                 html : 'Cancel'
3237             },
3238             {
3239                 name : 'ok',
3240                 weight : 'primary',
3241                 html : 'OK'
3242             }
3243         ],
3244         /**
3245          * Button config that displays Yes, No and Cancel buttons
3246          * @type Object
3247          */
3248         YESNOCANCEL : [
3249             {
3250                 name : 'yes',
3251                 weight : 'primary',
3252                 html : 'Yes'
3253             },
3254             {
3255                 name : 'no',
3256                 html : 'No'
3257             },
3258             {
3259                 name : 'cancel',
3260                 html : 'Cancel'
3261             }
3262         ],
3263         
3264         zIndex : 10001
3265 });
3266 /*
3267  * - LGPL
3268  *
3269  * messagebox - can be used as a replace
3270  * 
3271  */
3272 /**
3273  * @class Roo.MessageBox
3274  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3275  * Example usage:
3276  *<pre><code>
3277 // Basic alert:
3278 Roo.Msg.alert('Status', 'Changes saved successfully.');
3279
3280 // Prompt for user data:
3281 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3282     if (btn == 'ok'){
3283         // process text value...
3284     }
3285 });
3286
3287 // Show a dialog using config options:
3288 Roo.Msg.show({
3289    title:'Save Changes?',
3290    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3291    buttons: Roo.Msg.YESNOCANCEL,
3292    fn: processResult,
3293    animEl: 'elId'
3294 });
3295 </code></pre>
3296  * @singleton
3297  */
3298 Roo.bootstrap.MessageBox = function(){
3299     var dlg, opt, mask, waitTimer;
3300     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3301     var buttons, activeTextEl, bwidth;
3302
3303     
3304     // private
3305     var handleButton = function(button){
3306         dlg.hide();
3307         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3308     };
3309
3310     // private
3311     var handleHide = function(){
3312         if(opt && opt.cls){
3313             dlg.el.removeClass(opt.cls);
3314         }
3315         //if(waitTimer){
3316         //    Roo.TaskMgr.stop(waitTimer);
3317         //    waitTimer = null;
3318         //}
3319     };
3320
3321     // private
3322     var updateButtons = function(b){
3323         var width = 0;
3324         if(!b){
3325             buttons["ok"].hide();
3326             buttons["cancel"].hide();
3327             buttons["yes"].hide();
3328             buttons["no"].hide();
3329             dlg.footerEl.hide();
3330             
3331             return width;
3332         }
3333         dlg.footerEl.show();
3334         for(var k in buttons){
3335             if(typeof buttons[k] != "function"){
3336                 if(b[k]){
3337                     buttons[k].show();
3338                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3339                     width += buttons[k].el.getWidth()+15;
3340                 }else{
3341                     buttons[k].hide();
3342                 }
3343             }
3344         }
3345         return width;
3346     };
3347
3348     // private
3349     var handleEsc = function(d, k, e){
3350         if(opt && opt.closable !== false){
3351             dlg.hide();
3352         }
3353         if(e){
3354             e.stopEvent();
3355         }
3356     };
3357
3358     return {
3359         /**
3360          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3361          * @return {Roo.BasicDialog} The BasicDialog element
3362          */
3363         getDialog : function(){
3364            if(!dlg){
3365                 dlg = new Roo.bootstrap.Modal( {
3366                     //draggable: true,
3367                     //resizable:false,
3368                     //constraintoviewport:false,
3369                     //fixedcenter:true,
3370                     //collapsible : false,
3371                     //shim:true,
3372                     //modal: true,
3373                 //    width: 'auto',
3374                   //  height:100,
3375                     //buttonAlign:"center",
3376                     closeClick : function(){
3377                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3378                             handleButton("no");
3379                         }else{
3380                             handleButton("cancel");
3381                         }
3382                     }
3383                 });
3384                 dlg.render();
3385                 dlg.on("hide", handleHide);
3386                 mask = dlg.mask;
3387                 //dlg.addKeyListener(27, handleEsc);
3388                 buttons = {};
3389                 this.buttons = buttons;
3390                 var bt = this.buttonText;
3391                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3392                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3393                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3394                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3395                 //Roo.log(buttons);
3396                 bodyEl = dlg.bodyEl.createChild({
3397
3398                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3399                         '<textarea class="roo-mb-textarea"></textarea>' +
3400                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3401                 });
3402                 msgEl = bodyEl.dom.firstChild;
3403                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3404                 textboxEl.enableDisplayMode();
3405                 textboxEl.addKeyListener([10,13], function(){
3406                     if(dlg.isVisible() && opt && opt.buttons){
3407                         if(opt.buttons.ok){
3408                             handleButton("ok");
3409                         }else if(opt.buttons.yes){
3410                             handleButton("yes");
3411                         }
3412                     }
3413                 });
3414                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3415                 textareaEl.enableDisplayMode();
3416                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3417                 progressEl.enableDisplayMode();
3418                 
3419                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3420                 var pf = progressEl.dom.firstChild;
3421                 if (pf) {
3422                     pp = Roo.get(pf.firstChild);
3423                     pp.setHeight(pf.offsetHeight);
3424                 }
3425                 
3426             }
3427             return dlg;
3428         },
3429
3430         /**
3431          * Updates the message box body text
3432          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3433          * the XHTML-compliant non-breaking space character '&amp;#160;')
3434          * @return {Roo.MessageBox} This message box
3435          */
3436         updateText : function(text)
3437         {
3438             if(!dlg.isVisible() && !opt.width){
3439                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3440                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3441             }
3442             msgEl.innerHTML = text || '&#160;';
3443       
3444             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3445             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3446             var w = Math.max(
3447                     Math.min(opt.width || cw , this.maxWidth), 
3448                     Math.max(opt.minWidth || this.minWidth, bwidth)
3449             );
3450             if(opt.prompt){
3451                 activeTextEl.setWidth(w);
3452             }
3453             if(dlg.isVisible()){
3454                 dlg.fixedcenter = false;
3455             }
3456             // to big, make it scroll. = But as usual stupid IE does not support
3457             // !important..
3458             
3459             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3460                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3461                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3462             } else {
3463                 bodyEl.dom.style.height = '';
3464                 bodyEl.dom.style.overflowY = '';
3465             }
3466             if (cw > w) {
3467                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3468             } else {
3469                 bodyEl.dom.style.overflowX = '';
3470             }
3471             
3472             dlg.setContentSize(w, bodyEl.getHeight());
3473             if(dlg.isVisible()){
3474                 dlg.fixedcenter = true;
3475             }
3476             return this;
3477         },
3478
3479         /**
3480          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3481          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3482          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3483          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3484          * @return {Roo.MessageBox} This message box
3485          */
3486         updateProgress : function(value, text){
3487             if(text){
3488                 this.updateText(text);
3489             }
3490             
3491             if (pp) { // weird bug on my firefox - for some reason this is not defined
3492                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3493                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3494             }
3495             return this;
3496         },        
3497
3498         /**
3499          * Returns true if the message box is currently displayed
3500          * @return {Boolean} True if the message box is visible, else false
3501          */
3502         isVisible : function(){
3503             return dlg && dlg.isVisible();  
3504         },
3505
3506         /**
3507          * Hides the message box if it is displayed
3508          */
3509         hide : function(){
3510             if(this.isVisible()){
3511                 dlg.hide();
3512             }  
3513         },
3514
3515         /**
3516          * Displays a new message box, or reinitializes an existing message box, based on the config options
3517          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3518          * The following config object properties are supported:
3519          * <pre>
3520 Property    Type             Description
3521 ----------  ---------------  ------------------------------------------------------------------------------------
3522 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3523                                    closes (defaults to undefined)
3524 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3525                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3526 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3527                                    progress and wait dialogs will ignore this property and always hide the
3528                                    close button as they can only be closed programmatically.
3529 cls               String           A custom CSS class to apply to the message box element
3530 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3531                                    displayed (defaults to 75)
3532 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3533                                    function will be btn (the name of the button that was clicked, if applicable,
3534                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3535                                    Progress and wait dialogs will ignore this option since they do not respond to
3536                                    user actions and can only be closed programmatically, so any required function
3537                                    should be called by the same code after it closes the dialog.
3538 icon              String           A CSS class that provides a background image to be used as an icon for
3539                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3540 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3541 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3542 modal             Boolean          False to allow user interaction with the page while the message box is
3543                                    displayed (defaults to true)
3544 msg               String           A string that will replace the existing message box body text (defaults
3545                                    to the XHTML-compliant non-breaking space character '&#160;')
3546 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3547 progress          Boolean          True to display a progress bar (defaults to false)
3548 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3549 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3550 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3551 title             String           The title text
3552 value             String           The string value to set into the active textbox element if displayed
3553 wait              Boolean          True to display a progress bar (defaults to false)
3554 width             Number           The width of the dialog in pixels
3555 </pre>
3556          *
3557          * Example usage:
3558          * <pre><code>
3559 Roo.Msg.show({
3560    title: 'Address',
3561    msg: 'Please enter your address:',
3562    width: 300,
3563    buttons: Roo.MessageBox.OKCANCEL,
3564    multiline: true,
3565    fn: saveAddress,
3566    animEl: 'addAddressBtn'
3567 });
3568 </code></pre>
3569          * @param {Object} config Configuration options
3570          * @return {Roo.MessageBox} This message box
3571          */
3572         show : function(options)
3573         {
3574             
3575             // this causes nightmares if you show one dialog after another
3576             // especially on callbacks..
3577              
3578             if(this.isVisible()){
3579                 
3580                 this.hide();
3581                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3582                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3583                 Roo.log("New Dialog Message:" +  options.msg )
3584                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3585                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3586                 
3587             }
3588             var d = this.getDialog();
3589             opt = options;
3590             d.setTitle(opt.title || "&#160;");
3591             d.closeEl.setDisplayed(opt.closable !== false);
3592             activeTextEl = textboxEl;
3593             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3594             if(opt.prompt){
3595                 if(opt.multiline){
3596                     textboxEl.hide();
3597                     textareaEl.show();
3598                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3599                         opt.multiline : this.defaultTextHeight);
3600                     activeTextEl = textareaEl;
3601                 }else{
3602                     textboxEl.show();
3603                     textareaEl.hide();
3604                 }
3605             }else{
3606                 textboxEl.hide();
3607                 textareaEl.hide();
3608             }
3609             progressEl.setDisplayed(opt.progress === true);
3610             if (opt.progress) {
3611                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3612             }
3613             this.updateProgress(0);
3614             activeTextEl.dom.value = opt.value || "";
3615             if(opt.prompt){
3616                 dlg.setDefaultButton(activeTextEl);
3617             }else{
3618                 var bs = opt.buttons;
3619                 var db = null;
3620                 if(bs && bs.ok){
3621                     db = buttons["ok"];
3622                 }else if(bs && bs.yes){
3623                     db = buttons["yes"];
3624                 }
3625                 dlg.setDefaultButton(db);
3626             }
3627             bwidth = updateButtons(opt.buttons);
3628             this.updateText(opt.msg);
3629             if(opt.cls){
3630                 d.el.addClass(opt.cls);
3631             }
3632             d.proxyDrag = opt.proxyDrag === true;
3633             d.modal = opt.modal !== false;
3634             d.mask = opt.modal !== false ? mask : false;
3635             if(!d.isVisible()){
3636                 // force it to the end of the z-index stack so it gets a cursor in FF
3637                 document.body.appendChild(dlg.el.dom);
3638                 d.animateTarget = null;
3639                 d.show(options.animEl);
3640             }
3641             return this;
3642         },
3643
3644         /**
3645          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3646          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3647          * and closing the message box when the process is complete.
3648          * @param {String} title The title bar text
3649          * @param {String} msg The message box body text
3650          * @return {Roo.MessageBox} This message box
3651          */
3652         progress : function(title, msg){
3653             this.show({
3654                 title : title,
3655                 msg : msg,
3656                 buttons: false,
3657                 progress:true,
3658                 closable:false,
3659                 minWidth: this.minProgressWidth,
3660                 modal : true
3661             });
3662             return this;
3663         },
3664
3665         /**
3666          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3667          * If a callback function is passed it will be called after the user clicks the button, and the
3668          * id of the button that was clicked will be passed as the only parameter to the callback
3669          * (could also be the top-right close button).
3670          * @param {String} title The title bar text
3671          * @param {String} msg The message box body text
3672          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3673          * @param {Object} scope (optional) The scope of the callback function
3674          * @return {Roo.MessageBox} This message box
3675          */
3676         alert : function(title, msg, fn, scope)
3677         {
3678             this.show({
3679                 title : title,
3680                 msg : msg,
3681                 buttons: this.OK,
3682                 fn: fn,
3683                 closable : false,
3684                 scope : scope,
3685                 modal : true
3686             });
3687             return this;
3688         },
3689
3690         /**
3691          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3692          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3693          * You are responsible for closing the message box when the process is complete.
3694          * @param {String} msg The message box body text
3695          * @param {String} title (optional) The title bar text
3696          * @return {Roo.MessageBox} This message box
3697          */
3698         wait : function(msg, title){
3699             this.show({
3700                 title : title,
3701                 msg : msg,
3702                 buttons: false,
3703                 closable:false,
3704                 progress:true,
3705                 modal:true,
3706                 width:300,
3707                 wait:true
3708             });
3709             waitTimer = Roo.TaskMgr.start({
3710                 run: function(i){
3711                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3712                 },
3713                 interval: 1000
3714             });
3715             return this;
3716         },
3717
3718         /**
3719          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3720          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3721          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3722          * @param {String} title The title bar text
3723          * @param {String} msg The message box body text
3724          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3725          * @param {Object} scope (optional) The scope of the callback function
3726          * @return {Roo.MessageBox} This message box
3727          */
3728         confirm : function(title, msg, fn, scope){
3729             this.show({
3730                 title : title,
3731                 msg : msg,
3732                 buttons: this.YESNO,
3733                 fn: fn,
3734                 scope : scope,
3735                 modal : true
3736             });
3737             return this;
3738         },
3739
3740         /**
3741          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3742          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3743          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3744          * (could also be the top-right close button) and the text that was entered will be passed as the two
3745          * parameters to the callback.
3746          * @param {String} title The title bar text
3747          * @param {String} msg The message box body text
3748          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3749          * @param {Object} scope (optional) The scope of the callback function
3750          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3751          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3752          * @return {Roo.MessageBox} This message box
3753          */
3754         prompt : function(title, msg, fn, scope, multiline){
3755             this.show({
3756                 title : title,
3757                 msg : msg,
3758                 buttons: this.OKCANCEL,
3759                 fn: fn,
3760                 minWidth:250,
3761                 scope : scope,
3762                 prompt:true,
3763                 multiline: multiline,
3764                 modal : true
3765             });
3766             return this;
3767         },
3768
3769         /**
3770          * Button config that displays a single OK button
3771          * @type Object
3772          */
3773         OK : {ok:true},
3774         /**
3775          * Button config that displays Yes and No buttons
3776          * @type Object
3777          */
3778         YESNO : {yes:true, no:true},
3779         /**
3780          * Button config that displays OK and Cancel buttons
3781          * @type Object
3782          */
3783         OKCANCEL : {ok:true, cancel:true},
3784         /**
3785          * Button config that displays Yes, No and Cancel buttons
3786          * @type Object
3787          */
3788         YESNOCANCEL : {yes:true, no:true, cancel:true},
3789
3790         /**
3791          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3792          * @type Number
3793          */
3794         defaultTextHeight : 75,
3795         /**
3796          * The maximum width in pixels of the message box (defaults to 600)
3797          * @type Number
3798          */
3799         maxWidth : 600,
3800         /**
3801          * The minimum width in pixels of the message box (defaults to 100)
3802          * @type Number
3803          */
3804         minWidth : 100,
3805         /**
3806          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3807          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3808          * @type Number
3809          */
3810         minProgressWidth : 250,
3811         /**
3812          * An object containing the default button text strings that can be overriden for localized language support.
3813          * Supported properties are: ok, cancel, yes and no.
3814          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3815          * @type Object
3816          */
3817         buttonText : {
3818             ok : "OK",
3819             cancel : "Cancel",
3820             yes : "Yes",
3821             no : "No"
3822         }
3823     };
3824 }();
3825
3826 /**
3827  * Shorthand for {@link Roo.MessageBox}
3828  */
3829 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3830 Roo.Msg = Roo.Msg || Roo.MessageBox;
3831 /*
3832  * - LGPL
3833  *
3834  * navbar
3835  * 
3836  */
3837
3838 /**
3839  * @class Roo.bootstrap.Navbar
3840  * @extends Roo.bootstrap.Component
3841  * Bootstrap Navbar class
3842
3843  * @constructor
3844  * Create a new Navbar
3845  * @param {Object} config The config object
3846  */
3847
3848
3849 Roo.bootstrap.Navbar = function(config){
3850     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3851     this.addEvents({
3852         // raw events
3853         /**
3854          * @event beforetoggle
3855          * Fire before toggle the menu
3856          * @param {Roo.EventObject} e
3857          */
3858         "beforetoggle" : true
3859     });
3860 };
3861
3862 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3863     
3864     
3865    
3866     // private
3867     navItems : false,
3868     loadMask : false,
3869     
3870     
3871     getAutoCreate : function(){
3872         
3873         
3874         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3875         
3876     },
3877     
3878     initEvents :function ()
3879     {
3880         //Roo.log(this.el.select('.navbar-toggle',true));
3881         this.el.select('.navbar-toggle',true).on('click', function() {
3882             if(this.fireEvent('beforetoggle', this) !== false){
3883                 var ce = this.el.select('.navbar-collapse',true).first();
3884                 ce.toggleClass('in'); // old...
3885                 if (ce.hasClass('collapse')) {
3886                     // show it...
3887                     ce.removeClass('collapse');
3888                     ce.addClass('show');
3889                     var h = ce.getHeight();
3890                     Roo.log(h);
3891                     ce.removeClass('show');
3892                     // at this point we should be able to see it..
3893                     ce.addClass('collapsing');
3894                     
3895                     ce.setHeight(0); // resize it ...
3896                     ce.on('transitionend', function() {
3897                         Roo.log('done transition');
3898                         ce.removeClass('collapsing');
3899                         ce.addClass('show');
3900                         ce.removeClass('collapse');
3901
3902                         ce.dom.style.height = '';
3903                     }, this, { single: true} );
3904                     ce.setHeight(h);
3905                     
3906                 } else {
3907                     ce.setHeight(ce.getHeight());
3908                     ce.removeClass('show');
3909                     ce.addClass('collapsing');
3910                     
3911                     ce.on('transitionend', function() {
3912                         ce.dom.style.height = '';
3913                         ce.removeClass('collapsing');
3914                         ce.addClass('collapse');
3915                     }, this, { single: true} );
3916                     ce.setHeight(0);
3917                 }
3918             }
3919             
3920         }, this);
3921         
3922         var mark = {
3923             tag: "div",
3924             cls:"x-dlg-mask"
3925         };
3926         
3927         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3928         
3929         var size = this.el.getSize();
3930         this.maskEl.setSize(size.width, size.height);
3931         this.maskEl.enableDisplayMode("block");
3932         this.maskEl.hide();
3933         
3934         if(this.loadMask){
3935             this.maskEl.show();
3936         }
3937     },
3938     
3939     
3940     getChildContainer : function()
3941     {
3942         if (this.el && this.el.select('.collapse').getCount()) {
3943             return this.el.select('.collapse',true).first();
3944         }
3945         
3946         return this.el;
3947     },
3948     
3949     mask : function()
3950     {
3951         this.maskEl.show();
3952     },
3953     
3954     unmask : function()
3955     {
3956         this.maskEl.hide();
3957     } 
3958     
3959     
3960     
3961     
3962 });
3963
3964
3965
3966  
3967
3968  /*
3969  * - LGPL
3970  *
3971  * navbar
3972  * 
3973  */
3974
3975 /**
3976  * @class Roo.bootstrap.NavSimplebar
3977  * @extends Roo.bootstrap.Navbar
3978  * Bootstrap Sidebar class
3979  *
3980  * @cfg {Boolean} inverse is inverted color
3981  * 
3982  * @cfg {String} type (nav | pills | tabs)
3983  * @cfg {Boolean} arrangement stacked | justified
3984  * @cfg {String} align (left | right) alignment
3985  * 
3986  * @cfg {Boolean} main (true|false) main nav bar? default false
3987  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3988  * 
3989  * @cfg {String} tag (header|footer|nav|div) default is nav 
3990
3991  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3992  * 
3993  * 
3994  * @constructor
3995  * Create a new Sidebar
3996  * @param {Object} config The config object
3997  */
3998
3999
4000 Roo.bootstrap.NavSimplebar = function(config){
4001     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4002 };
4003
4004 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4005     
4006     inverse: false,
4007     
4008     type: false,
4009     arrangement: '',
4010     align : false,
4011     
4012     weight : 'light',
4013     
4014     main : false,
4015     
4016     
4017     tag : false,
4018     
4019     
4020     getAutoCreate : function(){
4021         
4022         
4023         var cfg = {
4024             tag : this.tag || 'div',
4025             cls : 'navbar navbar-expand-lg roo-navbar-simple'
4026         };
4027         if (['light','white'].indexOf(this.weight) > -1) {
4028             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4029         }
4030         cfg.cls += ' bg-' + this.weight;
4031         
4032         if (this.inverse) {
4033             cfg.cls += ' navbar-inverse';
4034             
4035         }
4036         
4037         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4038         
4039         //if (Roo.bootstrap.version == 4) {
4040         //    return cfg;
4041         //}
4042         
4043         cfg.cn = [
4044             {
4045                 cls: 'nav',
4046                 tag : 'ul'
4047             }
4048         ];
4049         
4050          
4051         this.type = this.type || 'nav';
4052         if (['tabs','pills'].indexOf(this.type) != -1) {
4053             cfg.cn[0].cls += ' nav-' + this.type
4054         
4055         
4056         } else {
4057             if (this.type!=='nav') {
4058                 Roo.log('nav type must be nav/tabs/pills')
4059             }
4060             cfg.cn[0].cls += ' navbar-nav'
4061         }
4062         
4063         
4064         
4065         
4066         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4067             cfg.cn[0].cls += ' nav-' + this.arrangement;
4068         }
4069         
4070         
4071         if (this.align === 'right') {
4072             cfg.cn[0].cls += ' navbar-right';
4073         }
4074         
4075         
4076         
4077         
4078         return cfg;
4079     
4080         
4081     }
4082     
4083     
4084     
4085 });
4086
4087
4088
4089  
4090
4091  
4092        /*
4093  * - LGPL
4094  *
4095  * navbar
4096  * navbar-fixed-top
4097  * navbar-expand-md  fixed-top 
4098  */
4099
4100 /**
4101  * @class Roo.bootstrap.NavHeaderbar
4102  * @extends Roo.bootstrap.NavSimplebar
4103  * Bootstrap Sidebar class
4104  *
4105  * @cfg {String} brand what is brand
4106  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4107  * @cfg {String} brand_href href of the brand
4108  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4109  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4110  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4111  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4112  * 
4113  * @constructor
4114  * Create a new Sidebar
4115  * @param {Object} config The config object
4116  */
4117
4118
4119 Roo.bootstrap.NavHeaderbar = function(config){
4120     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4121       
4122 };
4123
4124 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4125     
4126     position: '',
4127     brand: '',
4128     brand_href: false,
4129     srButton : true,
4130     autohide : false,
4131     desktopCenter : false,
4132    
4133     
4134     getAutoCreate : function(){
4135         
4136         var   cfg = {
4137             tag: this.nav || 'nav',
4138             cls: 'navbar navbar-expand-md',
4139             role: 'navigation',
4140             cn: []
4141         };
4142         
4143         var cn = cfg.cn;
4144         if (this.desktopCenter) {
4145             cn.push({cls : 'container', cn : []});
4146             cn = cn[0].cn;
4147         }
4148         
4149         if(this.srButton){
4150             var btn = {
4151                 tag: 'button',
4152                 type: 'button',
4153                 cls: 'navbar-toggle navbar-toggler',
4154                 'data-toggle': 'collapse',
4155                 cn: [
4156                     {
4157                         tag: 'span',
4158                         cls: 'sr-only',
4159                         html: 'Toggle navigation'
4160                     },
4161                     {
4162                         tag: 'span',
4163                         cls: 'icon-bar navbar-toggler-icon'
4164                     },
4165                     {
4166                         tag: 'span',
4167                         cls: 'icon-bar'
4168                     },
4169                     {
4170                         tag: 'span',
4171                         cls: 'icon-bar'
4172                     }
4173                 ]
4174             };
4175             
4176             cn.push( Roo.bootstrap.version == 4 ? btn : {
4177                 tag: 'div',
4178                 cls: 'navbar-header',
4179                 cn: [
4180                     btn
4181                 ]
4182             });
4183         }
4184         
4185         cn.push({
4186             tag: 'div',
4187             cls: 'collapse navbar-collapse',
4188             cn : []
4189         });
4190         
4191         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4192         
4193         if (['light','white'].indexOf(this.weight) > -1) {
4194             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4195         }
4196         cfg.cls += ' bg-' + this.weight;
4197         
4198         
4199         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4200             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4201             
4202             // tag can override this..
4203             
4204             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4205         }
4206         
4207         if (this.brand !== '') {
4208             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4209             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4210                 tag: 'a',
4211                 href: this.brand_href ? this.brand_href : '#',
4212                 cls: 'navbar-brand',
4213                 cn: [
4214                 this.brand
4215                 ]
4216             });
4217         }
4218         
4219         if(this.main){
4220             cfg.cls += ' main-nav';
4221         }
4222         
4223         
4224         return cfg;
4225
4226         
4227     },
4228     getHeaderChildContainer : function()
4229     {
4230         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4231             return this.el.select('.navbar-header',true).first();
4232         }
4233         
4234         return this.getChildContainer();
4235     },
4236     
4237     
4238     initEvents : function()
4239     {
4240         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4241         
4242         if (this.autohide) {
4243             
4244             var prevScroll = 0;
4245             var ft = this.el;
4246             
4247             Roo.get(document).on('scroll',function(e) {
4248                 var ns = Roo.get(document).getScroll().top;
4249                 var os = prevScroll;
4250                 prevScroll = ns;
4251                 
4252                 if(ns > os){
4253                     ft.removeClass('slideDown');
4254                     ft.addClass('slideUp');
4255                     return;
4256                 }
4257                 ft.removeClass('slideUp');
4258                 ft.addClass('slideDown');
4259                  
4260               
4261           },this);
4262         }
4263     }    
4264     
4265 });
4266
4267
4268
4269  
4270
4271  /*
4272  * - LGPL
4273  *
4274  * navbar
4275  * 
4276  */
4277
4278 /**
4279  * @class Roo.bootstrap.NavSidebar
4280  * @extends Roo.bootstrap.Navbar
4281  * Bootstrap Sidebar class
4282  * 
4283  * @constructor
4284  * Create a new Sidebar
4285  * @param {Object} config The config object
4286  */
4287
4288
4289 Roo.bootstrap.NavSidebar = function(config){
4290     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4291 };
4292
4293 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4294     
4295     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4296     
4297     getAutoCreate : function(){
4298         
4299         
4300         return  {
4301             tag: 'div',
4302             cls: 'sidebar sidebar-nav'
4303         };
4304     
4305         
4306     }
4307     
4308     
4309     
4310 });
4311
4312
4313
4314  
4315
4316  /*
4317  * - LGPL
4318  *
4319  * nav group
4320  * 
4321  */
4322
4323 /**
4324  * @class Roo.bootstrap.NavGroup
4325  * @extends Roo.bootstrap.Component
4326  * Bootstrap NavGroup class
4327  * @cfg {String} align (left|right)
4328  * @cfg {Boolean} inverse
4329  * @cfg {String} type (nav|pills|tab) default nav
4330  * @cfg {String} navId - reference Id for navbar.
4331
4332  * 
4333  * @constructor
4334  * Create a new nav group
4335  * @param {Object} config The config object
4336  */
4337
4338 Roo.bootstrap.NavGroup = function(config){
4339     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4340     this.navItems = [];
4341    
4342     Roo.bootstrap.NavGroup.register(this);
4343      this.addEvents({
4344         /**
4345              * @event changed
4346              * Fires when the active item changes
4347              * @param {Roo.bootstrap.NavGroup} this
4348              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4349              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4350          */
4351         'changed': true
4352      });
4353     
4354 };
4355
4356 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4357     
4358     align: '',
4359     inverse: false,
4360     form: false,
4361     type: 'nav',
4362     navId : '',
4363     // private
4364     
4365     navItems : false, 
4366     
4367     getAutoCreate : function()
4368     {
4369         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4370         
4371         cfg = {
4372             tag : 'ul',
4373             cls: 'nav' 
4374         };
4375         if (Roo.bootstrap.version == 4) {
4376             if (['tabs','pills'].indexOf(this.type) != -1) {
4377                 cfg.cls += ' nav-' + this.type; 
4378             } else {
4379                 cfg.cls += ' navbar-nav';
4380             }
4381         } else {
4382             if (['tabs','pills'].indexOf(this.type) != -1) {
4383                 cfg.cls += ' nav-' + this.type
4384             } else {
4385                 if (this.type !== 'nav') {
4386                     Roo.log('nav type must be nav/tabs/pills')
4387                 }
4388                 cfg.cls += ' navbar-nav'
4389             }
4390         }
4391         
4392         if (this.parent() && this.parent().sidebar) {
4393             cfg = {
4394                 tag: 'ul',
4395                 cls: 'dashboard-menu sidebar-menu'
4396             };
4397             
4398             return cfg;
4399         }
4400         
4401         if (this.form === true) {
4402             cfg = {
4403                 tag: 'form',
4404                 cls: 'navbar-form form-inline'
4405             };
4406             
4407             if (this.align === 'right') {
4408                 cfg.cls += ' navbar-right ml-md-auto';
4409             } else {
4410                 cfg.cls += ' navbar-left';
4411             }
4412         }
4413         
4414         if (this.align === 'right') {
4415             cfg.cls += ' navbar-right ml-md-auto';
4416         } else {
4417             cfg.cls += ' mr-auto';
4418         }
4419         
4420         if (this.inverse) {
4421             cfg.cls += ' navbar-inverse';
4422             
4423         }
4424         
4425         
4426         return cfg;
4427     },
4428     /**
4429     * sets the active Navigation item
4430     * @param {Roo.bootstrap.NavItem} the new current navitem
4431     */
4432     setActiveItem : function(item)
4433     {
4434         var prev = false;
4435         Roo.each(this.navItems, function(v){
4436             if (v == item) {
4437                 return ;
4438             }
4439             if (v.isActive()) {
4440                 v.setActive(false, true);
4441                 prev = v;
4442                 
4443             }
4444             
4445         });
4446
4447         item.setActive(true, true);
4448         this.fireEvent('changed', this, item, prev);
4449         
4450         
4451     },
4452     /**
4453     * gets the active Navigation item
4454     * @return {Roo.bootstrap.NavItem} the current navitem
4455     */
4456     getActive : function()
4457     {
4458         
4459         var prev = false;
4460         Roo.each(this.navItems, function(v){
4461             
4462             if (v.isActive()) {
4463                 prev = v;
4464                 
4465             }
4466             
4467         });
4468         return prev;
4469     },
4470     
4471     indexOfNav : function()
4472     {
4473         
4474         var prev = false;
4475         Roo.each(this.navItems, function(v,i){
4476             
4477             if (v.isActive()) {
4478                 prev = i;
4479                 
4480             }
4481             
4482         });
4483         return prev;
4484     },
4485     /**
4486     * adds a Navigation item
4487     * @param {Roo.bootstrap.NavItem} the navitem to add
4488     */
4489     addItem : function(cfg)
4490     {
4491         if (this.form && Roo.bootstrap.version == 4) {
4492             cfg.tag = 'div';
4493         }
4494         var cn = new Roo.bootstrap.NavItem(cfg);
4495         this.register(cn);
4496         cn.parentId = this.id;
4497         cn.onRender(this.el, null);
4498         return cn;
4499     },
4500     /**
4501     * register a Navigation item
4502     * @param {Roo.bootstrap.NavItem} the navitem to add
4503     */
4504     register : function(item)
4505     {
4506         this.navItems.push( item);
4507         item.navId = this.navId;
4508     
4509     },
4510     
4511     /**
4512     * clear all the Navigation item
4513     */
4514    
4515     clearAll : function()
4516     {
4517         this.navItems = [];
4518         this.el.dom.innerHTML = '';
4519     },
4520     
4521     getNavItem: function(tabId)
4522     {
4523         var ret = false;
4524         Roo.each(this.navItems, function(e) {
4525             if (e.tabId == tabId) {
4526                ret =  e;
4527                return false;
4528             }
4529             return true;
4530             
4531         });
4532         return ret;
4533     },
4534     
4535     setActiveNext : function()
4536     {
4537         var i = this.indexOfNav(this.getActive());
4538         if (i > this.navItems.length) {
4539             return;
4540         }
4541         this.setActiveItem(this.navItems[i+1]);
4542     },
4543     setActivePrev : function()
4544     {
4545         var i = this.indexOfNav(this.getActive());
4546         if (i  < 1) {
4547             return;
4548         }
4549         this.setActiveItem(this.navItems[i-1]);
4550     },
4551     clearWasActive : function(except) {
4552         Roo.each(this.navItems, function(e) {
4553             if (e.tabId != except.tabId && e.was_active) {
4554                e.was_active = false;
4555                return false;
4556             }
4557             return true;
4558             
4559         });
4560     },
4561     getWasActive : function ()
4562     {
4563         var r = false;
4564         Roo.each(this.navItems, function(e) {
4565             if (e.was_active) {
4566                r = e;
4567                return false;
4568             }
4569             return true;
4570             
4571         });
4572         return r;
4573     }
4574     
4575     
4576 });
4577
4578  
4579 Roo.apply(Roo.bootstrap.NavGroup, {
4580     
4581     groups: {},
4582      /**
4583     * register a Navigation Group
4584     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4585     */
4586     register : function(navgrp)
4587     {
4588         this.groups[navgrp.navId] = navgrp;
4589         
4590     },
4591     /**
4592     * fetch a Navigation Group based on the navigation ID
4593     * @param {string} the navgroup to add
4594     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4595     */
4596     get: function(navId) {
4597         if (typeof(this.groups[navId]) == 'undefined') {
4598             return false;
4599             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4600         }
4601         return this.groups[navId] ;
4602     }
4603     
4604     
4605     
4606 });
4607
4608  /*
4609  * - LGPL
4610  *
4611  * row
4612  * 
4613  */
4614
4615 /**
4616  * @class Roo.bootstrap.NavItem
4617  * @extends Roo.bootstrap.Component
4618  * Bootstrap Navbar.NavItem class
4619  * @cfg {String} href  link to
4620  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4621
4622  * @cfg {String} html content of button
4623  * @cfg {String} badge text inside badge
4624  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4625  * @cfg {String} glyphicon DEPRICATED - use fa
4626  * @cfg {String} icon DEPRICATED - use fa
4627  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4628  * @cfg {Boolean} active Is item active
4629  * @cfg {Boolean} disabled Is item disabled
4630  
4631  * @cfg {Boolean} preventDefault (true | false) default false
4632  * @cfg {String} tabId the tab that this item activates.
4633  * @cfg {String} tagtype (a|span) render as a href or span?
4634  * @cfg {Boolean} animateRef (true|false) link to element default false  
4635   
4636  * @constructor
4637  * Create a new Navbar Item
4638  * @param {Object} config The config object
4639  */
4640 Roo.bootstrap.NavItem = function(config){
4641     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4642     this.addEvents({
4643         // raw events
4644         /**
4645          * @event click
4646          * The raw click event for the entire grid.
4647          * @param {Roo.EventObject} e
4648          */
4649         "click" : true,
4650          /**
4651             * @event changed
4652             * Fires when the active item active state changes
4653             * @param {Roo.bootstrap.NavItem} this
4654             * @param {boolean} state the new state
4655              
4656          */
4657         'changed': true,
4658         /**
4659             * @event scrollto
4660             * Fires when scroll to element
4661             * @param {Roo.bootstrap.NavItem} this
4662             * @param {Object} options
4663             * @param {Roo.EventObject} e
4664              
4665          */
4666         'scrollto': true
4667     });
4668    
4669 };
4670
4671 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4672     
4673     href: false,
4674     html: '',
4675     badge: '',
4676     icon: false,
4677     fa : false,
4678     glyphicon: false,
4679     active: false,
4680     preventDefault : false,
4681     tabId : false,
4682     tagtype : 'a',
4683     tag: 'li',
4684     disabled : false,
4685     animateRef : false,
4686     was_active : false,
4687     button_weight : '',
4688     button_outline : false,
4689     
4690     navLink: false,
4691     
4692     getAutoCreate : function(){
4693          
4694         var cfg = {
4695             tag: this.tag,
4696             cls: 'nav-item'
4697         };
4698         
4699         if (this.active) {
4700             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4701         }
4702         if (this.disabled) {
4703             cfg.cls += ' disabled';
4704         }
4705         
4706         // BS4 only?
4707         if (this.button_weight.length) {
4708             cfg.tag = this.href ? 'a' : 'button';
4709             cfg.html = this.html || '';
4710             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4711             if (this.href) {
4712                 cfg.href = this.href;
4713             }
4714             if (this.fa) {
4715                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4716             }
4717             
4718             // menu .. should add dropdown-menu class - so no need for carat..
4719             
4720             if (this.badge !== '') {
4721                  
4722                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4723             }
4724             return cfg;
4725         }
4726         
4727         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4728             cfg.cn = [
4729                 {
4730                     tag: this.tagtype,
4731                     href : this.href || "#",
4732                     html: this.html || ''
4733                 }
4734             ];
4735             if (this.tagtype == 'a') {
4736                 cfg.cn[0].cls = 'nav-link';
4737             }
4738             if (this.icon) {
4739                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4740             }
4741             if (this.fa) {
4742                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4743             }
4744             if(this.glyphicon) {
4745                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4746             }
4747             
4748             if (this.menu) {
4749                 
4750                 cfg.cn[0].html += " <span class='caret'></span>";
4751              
4752             }
4753             
4754             if (this.badge !== '') {
4755                  
4756                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4757             }
4758         }
4759         
4760         
4761         
4762         return cfg;
4763     },
4764     onRender : function(ct, position)
4765     {
4766        // Roo.log("Call onRender: " + this.xtype);
4767         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4768             this.tag = 'div';
4769         }
4770         
4771         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4772         this.navLink = this.el.select('.nav-link',true).first();
4773         return ret;
4774     },
4775       
4776     
4777     initEvents: function() 
4778     {
4779         if (typeof (this.menu) != 'undefined') {
4780             this.menu.parentType = this.xtype;
4781             this.menu.triggerEl = this.el;
4782             this.menu = this.addxtype(Roo.apply({}, this.menu));
4783         }
4784         
4785         this.el.select('a',true).on('click', this.onClick, this);
4786         
4787         if(this.tagtype == 'span'){
4788             this.el.select('span',true).on('click', this.onClick, this);
4789         }
4790        
4791         // at this point parent should be available..
4792         this.parent().register(this);
4793     },
4794     
4795     onClick : function(e)
4796     {
4797         if (e.getTarget('.dropdown-menu-item')) {
4798             // did you click on a menu itemm.... - then don't trigger onclick..
4799             return;
4800         }
4801         
4802         if(
4803                 this.preventDefault || 
4804                 this.href == '#' 
4805         ){
4806             Roo.log("NavItem - prevent Default?");
4807             e.preventDefault();
4808         }
4809         
4810         if (this.disabled) {
4811             return;
4812         }
4813         
4814         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4815         if (tg && tg.transition) {
4816             Roo.log("waiting for the transitionend");
4817             return;
4818         }
4819         
4820         
4821         
4822         //Roo.log("fire event clicked");
4823         if(this.fireEvent('click', this, e) === false){
4824             return;
4825         };
4826         
4827         if(this.tagtype == 'span'){
4828             return;
4829         }
4830         
4831         //Roo.log(this.href);
4832         var ael = this.el.select('a',true).first();
4833         //Roo.log(ael);
4834         
4835         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4836             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4837             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4838                 return; // ignore... - it's a 'hash' to another page.
4839             }
4840             Roo.log("NavItem - prevent Default?");
4841             e.preventDefault();
4842             this.scrollToElement(e);
4843         }
4844         
4845         
4846         var p =  this.parent();
4847    
4848         if (['tabs','pills'].indexOf(p.type)!==-1) {
4849             if (typeof(p.setActiveItem) !== 'undefined') {
4850                 p.setActiveItem(this);
4851             }
4852         }
4853         
4854         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4855         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4856             // remove the collapsed menu expand...
4857             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4858         }
4859     },
4860     
4861     isActive: function () {
4862         return this.active
4863     },
4864     setActive : function(state, fire, is_was_active)
4865     {
4866         if (this.active && !state && this.navId) {
4867             this.was_active = true;
4868             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4869             if (nv) {
4870                 nv.clearWasActive(this);
4871             }
4872             
4873         }
4874         this.active = state;
4875         
4876         if (!state ) {
4877             this.el.removeClass('active');
4878             this.navLink ? this.navLink.removeClass('active') : false;
4879         } else if (!this.el.hasClass('active')) {
4880             
4881             this.el.addClass('active');
4882             if (Roo.bootstrap.version == 4 && this.navLink ) {
4883                 this.navLink.addClass('active');
4884             }
4885             
4886         }
4887         if (fire) {
4888             this.fireEvent('changed', this, state);
4889         }
4890         
4891         // show a panel if it's registered and related..
4892         
4893         if (!this.navId || !this.tabId || !state || is_was_active) {
4894             return;
4895         }
4896         
4897         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4898         if (!tg) {
4899             return;
4900         }
4901         var pan = tg.getPanelByName(this.tabId);
4902         if (!pan) {
4903             return;
4904         }
4905         // if we can not flip to new panel - go back to old nav highlight..
4906         if (false == tg.showPanel(pan)) {
4907             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4908             if (nv) {
4909                 var onav = nv.getWasActive();
4910                 if (onav) {
4911                     onav.setActive(true, false, true);
4912                 }
4913             }
4914             
4915         }
4916         
4917         
4918         
4919     },
4920      // this should not be here...
4921     setDisabled : function(state)
4922     {
4923         this.disabled = state;
4924         if (!state ) {
4925             this.el.removeClass('disabled');
4926         } else if (!this.el.hasClass('disabled')) {
4927             this.el.addClass('disabled');
4928         }
4929         
4930     },
4931     
4932     /**
4933      * Fetch the element to display the tooltip on.
4934      * @return {Roo.Element} defaults to this.el
4935      */
4936     tooltipEl : function()
4937     {
4938         return this.el.select('' + this.tagtype + '', true).first();
4939     },
4940     
4941     scrollToElement : function(e)
4942     {
4943         var c = document.body;
4944         
4945         /*
4946          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4947          */
4948         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4949             c = document.documentElement;
4950         }
4951         
4952         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4953         
4954         if(!target){
4955             return;
4956         }
4957
4958         var o = target.calcOffsetsTo(c);
4959         
4960         var options = {
4961             target : target,
4962             value : o[1]
4963         };
4964         
4965         this.fireEvent('scrollto', this, options, e);
4966         
4967         Roo.get(c).scrollTo('top', options.value, true);
4968         
4969         return;
4970     }
4971 });
4972  
4973
4974  /*
4975  * - LGPL
4976  *
4977  * sidebar item
4978  *
4979  *  li
4980  *    <span> icon </span>
4981  *    <span> text </span>
4982  *    <span>badge </span>
4983  */
4984
4985 /**
4986  * @class Roo.bootstrap.NavSidebarItem
4987  * @extends Roo.bootstrap.NavItem
4988  * Bootstrap Navbar.NavSidebarItem class
4989  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4990  * {Boolean} open is the menu open
4991  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4992  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4993  * {String} buttonSize (sm|md|lg)the extra classes for the button
4994  * {Boolean} showArrow show arrow next to the text (default true)
4995  * @constructor
4996  * Create a new Navbar Button
4997  * @param {Object} config The config object
4998  */
4999 Roo.bootstrap.NavSidebarItem = function(config){
5000     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5001     this.addEvents({
5002         // raw events
5003         /**
5004          * @event click
5005          * The raw click event for the entire grid.
5006          * @param {Roo.EventObject} e
5007          */
5008         "click" : true,
5009          /**
5010             * @event changed
5011             * Fires when the active item active state changes
5012             * @param {Roo.bootstrap.NavSidebarItem} this
5013             * @param {boolean} state the new state
5014              
5015          */
5016         'changed': true
5017     });
5018    
5019 };
5020
5021 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5022     
5023     badgeWeight : 'default',
5024     
5025     open: false,
5026     
5027     buttonView : false,
5028     
5029     buttonWeight : 'default',
5030     
5031     buttonSize : 'md',
5032     
5033     showArrow : true,
5034     
5035     getAutoCreate : function(){
5036         
5037         
5038         var a = {
5039                 tag: 'a',
5040                 href : this.href || '#',
5041                 cls: '',
5042                 html : '',
5043                 cn : []
5044         };
5045         
5046         if(this.buttonView){
5047             a = {
5048                 tag: 'button',
5049                 href : this.href || '#',
5050                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5051                 html : this.html,
5052                 cn : []
5053             };
5054         }
5055         
5056         var cfg = {
5057             tag: 'li',
5058             cls: '',
5059             cn: [ a ]
5060         };
5061         
5062         if (this.active) {
5063             cfg.cls += ' active';
5064         }
5065         
5066         if (this.disabled) {
5067             cfg.cls += ' disabled';
5068         }
5069         if (this.open) {
5070             cfg.cls += ' open x-open';
5071         }
5072         // left icon..
5073         if (this.glyphicon || this.icon) {
5074             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5075             a.cn.push({ tag : 'i', cls : c }) ;
5076         }
5077         
5078         if(!this.buttonView){
5079             var span = {
5080                 tag: 'span',
5081                 html : this.html || ''
5082             };
5083
5084             a.cn.push(span);
5085             
5086         }
5087         
5088         if (this.badge !== '') {
5089             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5090         }
5091         
5092         if (this.menu) {
5093             
5094             if(this.showArrow){
5095                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5096             }
5097             
5098             a.cls += ' dropdown-toggle treeview' ;
5099         }
5100         
5101         return cfg;
5102     },
5103     
5104     initEvents : function()
5105     { 
5106         if (typeof (this.menu) != 'undefined') {
5107             this.menu.parentType = this.xtype;
5108             this.menu.triggerEl = this.el;
5109             this.menu = this.addxtype(Roo.apply({}, this.menu));
5110         }
5111         
5112         this.el.on('click', this.onClick, this);
5113         
5114         if(this.badge !== ''){
5115             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5116         }
5117         
5118     },
5119     
5120     onClick : function(e)
5121     {
5122         if(this.disabled){
5123             e.preventDefault();
5124             return;
5125         }
5126         
5127         if(this.preventDefault){
5128             e.preventDefault();
5129         }
5130         
5131         this.fireEvent('click', this, e);
5132     },
5133     
5134     disable : function()
5135     {
5136         this.setDisabled(true);
5137     },
5138     
5139     enable : function()
5140     {
5141         this.setDisabled(false);
5142     },
5143     
5144     setDisabled : function(state)
5145     {
5146         if(this.disabled == state){
5147             return;
5148         }
5149         
5150         this.disabled = state;
5151         
5152         if (state) {
5153             this.el.addClass('disabled');
5154             return;
5155         }
5156         
5157         this.el.removeClass('disabled');
5158         
5159         return;
5160     },
5161     
5162     setActive : function(state)
5163     {
5164         if(this.active == state){
5165             return;
5166         }
5167         
5168         this.active = state;
5169         
5170         if (state) {
5171             this.el.addClass('active');
5172             return;
5173         }
5174         
5175         this.el.removeClass('active');
5176         
5177         return;
5178     },
5179     
5180     isActive: function () 
5181     {
5182         return this.active;
5183     },
5184     
5185     setBadge : function(str)
5186     {
5187         if(!this.badgeEl){
5188             return;
5189         }
5190         
5191         this.badgeEl.dom.innerHTML = str;
5192     }
5193     
5194    
5195      
5196  
5197 });
5198  
5199
5200  /*
5201  * - LGPL
5202  *
5203  * row
5204  * 
5205  */
5206
5207 /**
5208  * @class Roo.bootstrap.Row
5209  * @extends Roo.bootstrap.Component
5210  * Bootstrap Row class (contains columns...)
5211  * 
5212  * @constructor
5213  * Create a new Row
5214  * @param {Object} config The config object
5215  */
5216
5217 Roo.bootstrap.Row = function(config){
5218     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5219 };
5220
5221 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5222     
5223     getAutoCreate : function(){
5224        return {
5225             cls: 'row clearfix'
5226        };
5227     }
5228     
5229     
5230 });
5231
5232  
5233
5234  /*
5235  * - LGPL
5236  *
5237  * element
5238  * 
5239  */
5240
5241 /**
5242  * @class Roo.bootstrap.Element
5243  * @extends Roo.bootstrap.Component
5244  * Bootstrap Element class
5245  * @cfg {String} html contents of the element
5246  * @cfg {String} tag tag of the element
5247  * @cfg {String} cls class of the element
5248  * @cfg {Boolean} preventDefault (true|false) default false
5249  * @cfg {Boolean} clickable (true|false) default false
5250  * 
5251  * @constructor
5252  * Create a new Element
5253  * @param {Object} config The config object
5254  */
5255
5256 Roo.bootstrap.Element = function(config){
5257     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5258     
5259     this.addEvents({
5260         // raw events
5261         /**
5262          * @event click
5263          * When a element is chick
5264          * @param {Roo.bootstrap.Element} this
5265          * @param {Roo.EventObject} e
5266          */
5267         "click" : true
5268     });
5269 };
5270
5271 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5272     
5273     tag: 'div',
5274     cls: '',
5275     html: '',
5276     preventDefault: false, 
5277     clickable: false,
5278     
5279     getAutoCreate : function(){
5280         
5281         var cfg = {
5282             tag: this.tag,
5283             // cls: this.cls, double assign in parent class Component.js :: onRender
5284             html: this.html
5285         };
5286         
5287         return cfg;
5288     },
5289     
5290     initEvents: function() 
5291     {
5292         Roo.bootstrap.Element.superclass.initEvents.call(this);
5293         
5294         if(this.clickable){
5295             this.el.on('click', this.onClick, this);
5296         }
5297         
5298     },
5299     
5300     onClick : function(e)
5301     {
5302         if(this.preventDefault){
5303             e.preventDefault();
5304         }
5305         
5306         this.fireEvent('click', this, e);
5307     },
5308     
5309     getValue : function()
5310     {
5311         return this.el.dom.innerHTML;
5312     },
5313     
5314     setValue : function(value)
5315     {
5316         this.el.dom.innerHTML = value;
5317     }
5318    
5319 });
5320
5321  
5322
5323  /*
5324  * - LGPL
5325  *
5326  * pagination
5327  * 
5328  */
5329
5330 /**
5331  * @class Roo.bootstrap.Pagination
5332  * @extends Roo.bootstrap.Component
5333  * Bootstrap Pagination class
5334  * @cfg {String} size xs | sm | md | lg
5335  * @cfg {Boolean} inverse false | true
5336  * 
5337  * @constructor
5338  * Create a new Pagination
5339  * @param {Object} config The config object
5340  */
5341
5342 Roo.bootstrap.Pagination = function(config){
5343     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5344 };
5345
5346 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5347     
5348     cls: false,
5349     size: false,
5350     inverse: false,
5351     
5352     getAutoCreate : function(){
5353         var cfg = {
5354             tag: 'ul',
5355                 cls: 'pagination'
5356         };
5357         if (this.inverse) {
5358             cfg.cls += ' inverse';
5359         }
5360         if (this.html) {
5361             cfg.html=this.html;
5362         }
5363         if (this.cls) {
5364             cfg.cls += " " + this.cls;
5365         }
5366         return cfg;
5367     }
5368    
5369 });
5370
5371  
5372
5373  /*
5374  * - LGPL
5375  *
5376  * Pagination item
5377  * 
5378  */
5379
5380
5381 /**
5382  * @class Roo.bootstrap.PaginationItem
5383  * @extends Roo.bootstrap.Component
5384  * Bootstrap PaginationItem class
5385  * @cfg {String} html text
5386  * @cfg {String} href the link
5387  * @cfg {Boolean} preventDefault (true | false) default true
5388  * @cfg {Boolean} active (true | false) default false
5389  * @cfg {Boolean} disabled default false
5390  * 
5391  * 
5392  * @constructor
5393  * Create a new PaginationItem
5394  * @param {Object} config The config object
5395  */
5396
5397
5398 Roo.bootstrap.PaginationItem = function(config){
5399     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5400     this.addEvents({
5401         // raw events
5402         /**
5403          * @event click
5404          * The raw click event for the entire grid.
5405          * @param {Roo.EventObject} e
5406          */
5407         "click" : true
5408     });
5409 };
5410
5411 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5412     
5413     href : false,
5414     html : false,
5415     preventDefault: true,
5416     active : false,
5417     cls : false,
5418     disabled: false,
5419     
5420     getAutoCreate : function(){
5421         var cfg= {
5422             tag: 'li',
5423             cn: [
5424                 {
5425                     tag : 'a',
5426                     href : this.href ? this.href : '#',
5427                     html : this.html ? this.html : ''
5428                 }
5429             ]
5430         };
5431         
5432         if(this.cls){
5433             cfg.cls = this.cls;
5434         }
5435         
5436         if(this.disabled){
5437             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5438         }
5439         
5440         if(this.active){
5441             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5442         }
5443         
5444         return cfg;
5445     },
5446     
5447     initEvents: function() {
5448         
5449         this.el.on('click', this.onClick, this);
5450         
5451     },
5452     onClick : function(e)
5453     {
5454         Roo.log('PaginationItem on click ');
5455         if(this.preventDefault){
5456             e.preventDefault();
5457         }
5458         
5459         if(this.disabled){
5460             return;
5461         }
5462         
5463         this.fireEvent('click', this, e);
5464     }
5465    
5466 });
5467
5468  
5469
5470  /*
5471  * - LGPL
5472  *
5473  * slider
5474  * 
5475  */
5476
5477
5478 /**
5479  * @class Roo.bootstrap.Slider
5480  * @extends Roo.bootstrap.Component
5481  * Bootstrap Slider class
5482  *    
5483  * @constructor
5484  * Create a new Slider
5485  * @param {Object} config The config object
5486  */
5487
5488 Roo.bootstrap.Slider = function(config){
5489     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5490 };
5491
5492 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5493     
5494     getAutoCreate : function(){
5495         
5496         var cfg = {
5497             tag: 'div',
5498             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5499             cn: [
5500                 {
5501                     tag: 'a',
5502                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5503                 }
5504             ]
5505         };
5506         
5507         return cfg;
5508     }
5509    
5510 });
5511
5512  /*
5513  * Based on:
5514  * Ext JS Library 1.1.1
5515  * Copyright(c) 2006-2007, Ext JS, LLC.
5516  *
5517  * Originally Released Under LGPL - original licence link has changed is not relivant.
5518  *
5519  * Fork - LGPL
5520  * <script type="text/javascript">
5521  */
5522  
5523
5524 /**
5525  * @class Roo.grid.ColumnModel
5526  * @extends Roo.util.Observable
5527  * This is the default implementation of a ColumnModel used by the Grid. It defines
5528  * the columns in the grid.
5529  * <br>Usage:<br>
5530  <pre><code>
5531  var colModel = new Roo.grid.ColumnModel([
5532         {header: "Ticker", width: 60, sortable: true, locked: true},
5533         {header: "Company Name", width: 150, sortable: true},
5534         {header: "Market Cap.", width: 100, sortable: true},
5535         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5536         {header: "Employees", width: 100, sortable: true, resizable: false}
5537  ]);
5538  </code></pre>
5539  * <p>
5540  
5541  * The config options listed for this class are options which may appear in each
5542  * individual column definition.
5543  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5544  * @constructor
5545  * @param {Object} config An Array of column config objects. See this class's
5546  * config objects for details.
5547 */
5548 Roo.grid.ColumnModel = function(config){
5549         /**
5550      * The config passed into the constructor
5551      */
5552     this.config = config;
5553     this.lookup = {};
5554
5555     // if no id, create one
5556     // if the column does not have a dataIndex mapping,
5557     // map it to the order it is in the config
5558     for(var i = 0, len = config.length; i < len; i++){
5559         var c = config[i];
5560         if(typeof c.dataIndex == "undefined"){
5561             c.dataIndex = i;
5562         }
5563         if(typeof c.renderer == "string"){
5564             c.renderer = Roo.util.Format[c.renderer];
5565         }
5566         if(typeof c.id == "undefined"){
5567             c.id = Roo.id();
5568         }
5569         if(c.editor && c.editor.xtype){
5570             c.editor  = Roo.factory(c.editor, Roo.grid);
5571         }
5572         if(c.editor && c.editor.isFormField){
5573             c.editor = new Roo.grid.GridEditor(c.editor);
5574         }
5575         this.lookup[c.id] = c;
5576     }
5577
5578     /**
5579      * The width of columns which have no width specified (defaults to 100)
5580      * @type Number
5581      */
5582     this.defaultWidth = 100;
5583
5584     /**
5585      * Default sortable of columns which have no sortable specified (defaults to false)
5586      * @type Boolean
5587      */
5588     this.defaultSortable = false;
5589
5590     this.addEvents({
5591         /**
5592              * @event widthchange
5593              * Fires when the width of a column changes.
5594              * @param {ColumnModel} this
5595              * @param {Number} columnIndex The column index
5596              * @param {Number} newWidth The new width
5597              */
5598             "widthchange": true,
5599         /**
5600              * @event headerchange
5601              * Fires when the text of a header changes.
5602              * @param {ColumnModel} this
5603              * @param {Number} columnIndex The column index
5604              * @param {Number} newText The new header text
5605              */
5606             "headerchange": true,
5607         /**
5608              * @event hiddenchange
5609              * Fires when a column is hidden or "unhidden".
5610              * @param {ColumnModel} this
5611              * @param {Number} columnIndex The column index
5612              * @param {Boolean} hidden true if hidden, false otherwise
5613              */
5614             "hiddenchange": true,
5615             /**
5616          * @event columnmoved
5617          * Fires when a column is moved.
5618          * @param {ColumnModel} this
5619          * @param {Number} oldIndex
5620          * @param {Number} newIndex
5621          */
5622         "columnmoved" : true,
5623         /**
5624          * @event columlockchange
5625          * Fires when a column's locked state is changed
5626          * @param {ColumnModel} this
5627          * @param {Number} colIndex
5628          * @param {Boolean} locked true if locked
5629          */
5630         "columnlockchange" : true
5631     });
5632     Roo.grid.ColumnModel.superclass.constructor.call(this);
5633 };
5634 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5635     /**
5636      * @cfg {String} header The header text to display in the Grid view.
5637      */
5638     /**
5639      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5640      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5641      * specified, the column's index is used as an index into the Record's data Array.
5642      */
5643     /**
5644      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5645      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5646      */
5647     /**
5648      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5649      * Defaults to the value of the {@link #defaultSortable} property.
5650      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5651      */
5652     /**
5653      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5654      */
5655     /**
5656      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5657      */
5658     /**
5659      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5660      */
5661     /**
5662      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5663      */
5664     /**
5665      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5666      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5667      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5668      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5669      */
5670        /**
5671      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5672      */
5673     /**
5674      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5675      */
5676     /**
5677      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5678      */
5679     /**
5680      * @cfg {String} cursor (Optional)
5681      */
5682     /**
5683      * @cfg {String} tooltip (Optional)
5684      */
5685     /**
5686      * @cfg {Number} xs (Optional)
5687      */
5688     /**
5689      * @cfg {Number} sm (Optional)
5690      */
5691     /**
5692      * @cfg {Number} md (Optional)
5693      */
5694     /**
5695      * @cfg {Number} lg (Optional)
5696      */
5697     /**
5698      * Returns the id of the column at the specified index.
5699      * @param {Number} index The column index
5700      * @return {String} the id
5701      */
5702     getColumnId : function(index){
5703         return this.config[index].id;
5704     },
5705
5706     /**
5707      * Returns the column for a specified id.
5708      * @param {String} id The column id
5709      * @return {Object} the column
5710      */
5711     getColumnById : function(id){
5712         return this.lookup[id];
5713     },
5714
5715     
5716     /**
5717      * Returns the column for a specified dataIndex.
5718      * @param {String} dataIndex The column dataIndex
5719      * @return {Object|Boolean} the column or false if not found
5720      */
5721     getColumnByDataIndex: function(dataIndex){
5722         var index = this.findColumnIndex(dataIndex);
5723         return index > -1 ? this.config[index] : false;
5724     },
5725     
5726     /**
5727      * Returns the index for a specified column id.
5728      * @param {String} id The column id
5729      * @return {Number} the index, or -1 if not found
5730      */
5731     getIndexById : function(id){
5732         for(var i = 0, len = this.config.length; i < len; i++){
5733             if(this.config[i].id == id){
5734                 return i;
5735             }
5736         }
5737         return -1;
5738     },
5739     
5740     /**
5741      * Returns the index for a specified column dataIndex.
5742      * @param {String} dataIndex The column dataIndex
5743      * @return {Number} the index, or -1 if not found
5744      */
5745     
5746     findColumnIndex : function(dataIndex){
5747         for(var i = 0, len = this.config.length; i < len; i++){
5748             if(this.config[i].dataIndex == dataIndex){
5749                 return i;
5750             }
5751         }
5752         return -1;
5753     },
5754     
5755     
5756     moveColumn : function(oldIndex, newIndex){
5757         var c = this.config[oldIndex];
5758         this.config.splice(oldIndex, 1);
5759         this.config.splice(newIndex, 0, c);
5760         this.dataMap = null;
5761         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5762     },
5763
5764     isLocked : function(colIndex){
5765         return this.config[colIndex].locked === true;
5766     },
5767
5768     setLocked : function(colIndex, value, suppressEvent){
5769         if(this.isLocked(colIndex) == value){
5770             return;
5771         }
5772         this.config[colIndex].locked = value;
5773         if(!suppressEvent){
5774             this.fireEvent("columnlockchange", this, colIndex, value);
5775         }
5776     },
5777
5778     getTotalLockedWidth : function(){
5779         var totalWidth = 0;
5780         for(var i = 0; i < this.config.length; i++){
5781             if(this.isLocked(i) && !this.isHidden(i)){
5782                 this.totalWidth += this.getColumnWidth(i);
5783             }
5784         }
5785         return totalWidth;
5786     },
5787
5788     getLockedCount : function(){
5789         for(var i = 0, len = this.config.length; i < len; i++){
5790             if(!this.isLocked(i)){
5791                 return i;
5792             }
5793         }
5794         
5795         return this.config.length;
5796     },
5797
5798     /**
5799      * Returns the number of columns.
5800      * @return {Number}
5801      */
5802     getColumnCount : function(visibleOnly){
5803         if(visibleOnly === true){
5804             var c = 0;
5805             for(var i = 0, len = this.config.length; i < len; i++){
5806                 if(!this.isHidden(i)){
5807                     c++;
5808                 }
5809             }
5810             return c;
5811         }
5812         return this.config.length;
5813     },
5814
5815     /**
5816      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5817      * @param {Function} fn
5818      * @param {Object} scope (optional)
5819      * @return {Array} result
5820      */
5821     getColumnsBy : function(fn, scope){
5822         var r = [];
5823         for(var i = 0, len = this.config.length; i < len; i++){
5824             var c = this.config[i];
5825             if(fn.call(scope||this, c, i) === true){
5826                 r[r.length] = c;
5827             }
5828         }
5829         return r;
5830     },
5831
5832     /**
5833      * Returns true if the specified column is sortable.
5834      * @param {Number} col The column index
5835      * @return {Boolean}
5836      */
5837     isSortable : function(col){
5838         if(typeof this.config[col].sortable == "undefined"){
5839             return this.defaultSortable;
5840         }
5841         return this.config[col].sortable;
5842     },
5843
5844     /**
5845      * Returns the rendering (formatting) function defined for the column.
5846      * @param {Number} col The column index.
5847      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5848      */
5849     getRenderer : function(col){
5850         if(!this.config[col].renderer){
5851             return Roo.grid.ColumnModel.defaultRenderer;
5852         }
5853         return this.config[col].renderer;
5854     },
5855
5856     /**
5857      * Sets the rendering (formatting) function for a column.
5858      * @param {Number} col The column index
5859      * @param {Function} fn The function to use to process the cell's raw data
5860      * to return HTML markup for the grid view. The render function is called with
5861      * the following parameters:<ul>
5862      * <li>Data value.</li>
5863      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5864      * <li>css A CSS style string to apply to the table cell.</li>
5865      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5866      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5867      * <li>Row index</li>
5868      * <li>Column index</li>
5869      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5870      */
5871     setRenderer : function(col, fn){
5872         this.config[col].renderer = fn;
5873     },
5874
5875     /**
5876      * Returns the width for the specified column.
5877      * @param {Number} col The column index
5878      * @return {Number}
5879      */
5880     getColumnWidth : function(col){
5881         return this.config[col].width * 1 || this.defaultWidth;
5882     },
5883
5884     /**
5885      * Sets the width for a column.
5886      * @param {Number} col The column index
5887      * @param {Number} width The new width
5888      */
5889     setColumnWidth : function(col, width, suppressEvent){
5890         this.config[col].width = width;
5891         this.totalWidth = null;
5892         if(!suppressEvent){
5893              this.fireEvent("widthchange", this, col, width);
5894         }
5895     },
5896
5897     /**
5898      * Returns the total width of all columns.
5899      * @param {Boolean} includeHidden True to include hidden column widths
5900      * @return {Number}
5901      */
5902     getTotalWidth : function(includeHidden){
5903         if(!this.totalWidth){
5904             this.totalWidth = 0;
5905             for(var i = 0, len = this.config.length; i < len; i++){
5906                 if(includeHidden || !this.isHidden(i)){
5907                     this.totalWidth += this.getColumnWidth(i);
5908                 }
5909             }
5910         }
5911         return this.totalWidth;
5912     },
5913
5914     /**
5915      * Returns the header for the specified column.
5916      * @param {Number} col The column index
5917      * @return {String}
5918      */
5919     getColumnHeader : function(col){
5920         return this.config[col].header;
5921     },
5922
5923     /**
5924      * Sets the header for a column.
5925      * @param {Number} col The column index
5926      * @param {String} header The new header
5927      */
5928     setColumnHeader : function(col, header){
5929         this.config[col].header = header;
5930         this.fireEvent("headerchange", this, col, header);
5931     },
5932
5933     /**
5934      * Returns the tooltip for the specified column.
5935      * @param {Number} col The column index
5936      * @return {String}
5937      */
5938     getColumnTooltip : function(col){
5939             return this.config[col].tooltip;
5940     },
5941     /**
5942      * Sets the tooltip for a column.
5943      * @param {Number} col The column index
5944      * @param {String} tooltip The new tooltip
5945      */
5946     setColumnTooltip : function(col, tooltip){
5947             this.config[col].tooltip = tooltip;
5948     },
5949
5950     /**
5951      * Returns the dataIndex for the specified column.
5952      * @param {Number} col The column index
5953      * @return {Number}
5954      */
5955     getDataIndex : function(col){
5956         return this.config[col].dataIndex;
5957     },
5958
5959     /**
5960      * Sets the dataIndex for a column.
5961      * @param {Number} col The column index
5962      * @param {Number} dataIndex The new dataIndex
5963      */
5964     setDataIndex : function(col, dataIndex){
5965         this.config[col].dataIndex = dataIndex;
5966     },
5967
5968     
5969     
5970     /**
5971      * Returns true if the cell is editable.
5972      * @param {Number} colIndex The column index
5973      * @param {Number} rowIndex The row index - this is nto actually used..?
5974      * @return {Boolean}
5975      */
5976     isCellEditable : function(colIndex, rowIndex){
5977         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5978     },
5979
5980     /**
5981      * Returns the editor defined for the cell/column.
5982      * return false or null to disable editing.
5983      * @param {Number} colIndex The column index
5984      * @param {Number} rowIndex The row index
5985      * @return {Object}
5986      */
5987     getCellEditor : function(colIndex, rowIndex){
5988         return this.config[colIndex].editor;
5989     },
5990
5991     /**
5992      * Sets if a column is editable.
5993      * @param {Number} col The column index
5994      * @param {Boolean} editable True if the column is editable
5995      */
5996     setEditable : function(col, editable){
5997         this.config[col].editable = editable;
5998     },
5999
6000
6001     /**
6002      * Returns true if the column is hidden.
6003      * @param {Number} colIndex The column index
6004      * @return {Boolean}
6005      */
6006     isHidden : function(colIndex){
6007         return this.config[colIndex].hidden;
6008     },
6009
6010
6011     /**
6012      * Returns true if the column width cannot be changed
6013      */
6014     isFixed : function(colIndex){
6015         return this.config[colIndex].fixed;
6016     },
6017
6018     /**
6019      * Returns true if the column can be resized
6020      * @return {Boolean}
6021      */
6022     isResizable : function(colIndex){
6023         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6024     },
6025     /**
6026      * Sets if a column is hidden.
6027      * @param {Number} colIndex The column index
6028      * @param {Boolean} hidden True if the column is hidden
6029      */
6030     setHidden : function(colIndex, hidden){
6031         this.config[colIndex].hidden = hidden;
6032         this.totalWidth = null;
6033         this.fireEvent("hiddenchange", this, colIndex, hidden);
6034     },
6035
6036     /**
6037      * Sets the editor for a column.
6038      * @param {Number} col The column index
6039      * @param {Object} editor The editor object
6040      */
6041     setEditor : function(col, editor){
6042         this.config[col].editor = editor;
6043     }
6044 });
6045
6046 Roo.grid.ColumnModel.defaultRenderer = function(value)
6047 {
6048     if(typeof value == "object") {
6049         return value;
6050     }
6051         if(typeof value == "string" && value.length < 1){
6052             return "&#160;";
6053         }
6054     
6055         return String.format("{0}", value);
6056 };
6057
6058 // Alias for backwards compatibility
6059 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6060 /*
6061  * Based on:
6062  * Ext JS Library 1.1.1
6063  * Copyright(c) 2006-2007, Ext JS, LLC.
6064  *
6065  * Originally Released Under LGPL - original licence link has changed is not relivant.
6066  *
6067  * Fork - LGPL
6068  * <script type="text/javascript">
6069  */
6070  
6071 /**
6072  * @class Roo.LoadMask
6073  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6074  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6075  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6076  * element's UpdateManager load indicator and will be destroyed after the initial load.
6077  * @constructor
6078  * Create a new LoadMask
6079  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6080  * @param {Object} config The config object
6081  */
6082 Roo.LoadMask = function(el, config){
6083     this.el = Roo.get(el);
6084     Roo.apply(this, config);
6085     if(this.store){
6086         this.store.on('beforeload', this.onBeforeLoad, this);
6087         this.store.on('load', this.onLoad, this);
6088         this.store.on('loadexception', this.onLoadException, this);
6089         this.removeMask = false;
6090     }else{
6091         var um = this.el.getUpdateManager();
6092         um.showLoadIndicator = false; // disable the default indicator
6093         um.on('beforeupdate', this.onBeforeLoad, this);
6094         um.on('update', this.onLoad, this);
6095         um.on('failure', this.onLoad, this);
6096         this.removeMask = true;
6097     }
6098 };
6099
6100 Roo.LoadMask.prototype = {
6101     /**
6102      * @cfg {Boolean} removeMask
6103      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6104      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6105      */
6106     /**
6107      * @cfg {String} msg
6108      * The text to display in a centered loading message box (defaults to 'Loading...')
6109      */
6110     msg : 'Loading...',
6111     /**
6112      * @cfg {String} msgCls
6113      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6114      */
6115     msgCls : 'x-mask-loading',
6116
6117     /**
6118      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6119      * @type Boolean
6120      */
6121     disabled: false,
6122
6123     /**
6124      * Disables the mask to prevent it from being displayed
6125      */
6126     disable : function(){
6127        this.disabled = true;
6128     },
6129
6130     /**
6131      * Enables the mask so that it can be displayed
6132      */
6133     enable : function(){
6134         this.disabled = false;
6135     },
6136     
6137     onLoadException : function()
6138     {
6139         Roo.log(arguments);
6140         
6141         if (typeof(arguments[3]) != 'undefined') {
6142             Roo.MessageBox.alert("Error loading",arguments[3]);
6143         } 
6144         /*
6145         try {
6146             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6147                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6148             }   
6149         } catch(e) {
6150             
6151         }
6152         */
6153     
6154         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6155     },
6156     // private
6157     onLoad : function()
6158     {
6159         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6160     },
6161
6162     // private
6163     onBeforeLoad : function(){
6164         if(!this.disabled){
6165             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6166         }
6167     },
6168
6169     // private
6170     destroy : function(){
6171         if(this.store){
6172             this.store.un('beforeload', this.onBeforeLoad, this);
6173             this.store.un('load', this.onLoad, this);
6174             this.store.un('loadexception', this.onLoadException, this);
6175         }else{
6176             var um = this.el.getUpdateManager();
6177             um.un('beforeupdate', this.onBeforeLoad, this);
6178             um.un('update', this.onLoad, this);
6179             um.un('failure', this.onLoad, this);
6180         }
6181     }
6182 };/*
6183  * - LGPL
6184  *
6185  * table
6186  * 
6187  */
6188
6189 /**
6190  * @class Roo.bootstrap.Table
6191  * @extends Roo.bootstrap.Component
6192  * Bootstrap Table class
6193  * @cfg {String} cls table class
6194  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6195  * @cfg {String} bgcolor Specifies the background color for a table
6196  * @cfg {Number} border Specifies whether the table cells should have borders or not
6197  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6198  * @cfg {Number} cellspacing Specifies the space between cells
6199  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6200  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6201  * @cfg {String} sortable Specifies that the table should be sortable
6202  * @cfg {String} summary Specifies a summary of the content of a table
6203  * @cfg {Number} width Specifies the width of a table
6204  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6205  * 
6206  * @cfg {boolean} striped Should the rows be alternative striped
6207  * @cfg {boolean} bordered Add borders to the table
6208  * @cfg {boolean} hover Add hover highlighting
6209  * @cfg {boolean} condensed Format condensed
6210  * @cfg {boolean} responsive Format condensed
6211  * @cfg {Boolean} loadMask (true|false) default false
6212  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6213  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6214  * @cfg {Boolean} rowSelection (true|false) default false
6215  * @cfg {Boolean} cellSelection (true|false) default false
6216  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6217  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6218  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6219  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6220  
6221  * 
6222  * @constructor
6223  * Create a new Table
6224  * @param {Object} config The config object
6225  */
6226
6227 Roo.bootstrap.Table = function(config){
6228     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6229     
6230   
6231     
6232     // BC...
6233     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6234     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6235     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6236     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6237     
6238     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6239     if (this.sm) {
6240         this.sm.grid = this;
6241         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6242         this.sm = this.selModel;
6243         this.sm.xmodule = this.xmodule || false;
6244     }
6245     
6246     if (this.cm && typeof(this.cm.config) == 'undefined') {
6247         this.colModel = new Roo.grid.ColumnModel(this.cm);
6248         this.cm = this.colModel;
6249         this.cm.xmodule = this.xmodule || false;
6250     }
6251     if (this.store) {
6252         this.store= Roo.factory(this.store, Roo.data);
6253         this.ds = this.store;
6254         this.ds.xmodule = this.xmodule || false;
6255          
6256     }
6257     if (this.footer && this.store) {
6258         this.footer.dataSource = this.ds;
6259         this.footer = Roo.factory(this.footer);
6260     }
6261     
6262     /** @private */
6263     this.addEvents({
6264         /**
6265          * @event cellclick
6266          * Fires when a cell is clicked
6267          * @param {Roo.bootstrap.Table} this
6268          * @param {Roo.Element} el
6269          * @param {Number} rowIndex
6270          * @param {Number} columnIndex
6271          * @param {Roo.EventObject} e
6272          */
6273         "cellclick" : true,
6274         /**
6275          * @event celldblclick
6276          * Fires when a cell is double clicked
6277          * @param {Roo.bootstrap.Table} this
6278          * @param {Roo.Element} el
6279          * @param {Number} rowIndex
6280          * @param {Number} columnIndex
6281          * @param {Roo.EventObject} e
6282          */
6283         "celldblclick" : true,
6284         /**
6285          * @event rowclick
6286          * Fires when a row is clicked
6287          * @param {Roo.bootstrap.Table} this
6288          * @param {Roo.Element} el
6289          * @param {Number} rowIndex
6290          * @param {Roo.EventObject} e
6291          */
6292         "rowclick" : true,
6293         /**
6294          * @event rowdblclick
6295          * Fires when a row is double clicked
6296          * @param {Roo.bootstrap.Table} this
6297          * @param {Roo.Element} el
6298          * @param {Number} rowIndex
6299          * @param {Roo.EventObject} e
6300          */
6301         "rowdblclick" : true,
6302         /**
6303          * @event mouseover
6304          * Fires when a mouseover occur
6305          * @param {Roo.bootstrap.Table} this
6306          * @param {Roo.Element} el
6307          * @param {Number} rowIndex
6308          * @param {Number} columnIndex
6309          * @param {Roo.EventObject} e
6310          */
6311         "mouseover" : true,
6312         /**
6313          * @event mouseout
6314          * Fires when a mouseout occur
6315          * @param {Roo.bootstrap.Table} this
6316          * @param {Roo.Element} el
6317          * @param {Number} rowIndex
6318          * @param {Number} columnIndex
6319          * @param {Roo.EventObject} e
6320          */
6321         "mouseout" : true,
6322         /**
6323          * @event rowclass
6324          * Fires when a row is rendered, so you can change add a style to it.
6325          * @param {Roo.bootstrap.Table} this
6326          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6327          */
6328         'rowclass' : true,
6329           /**
6330          * @event rowsrendered
6331          * Fires when all the  rows have been rendered
6332          * @param {Roo.bootstrap.Table} this
6333          */
6334         'rowsrendered' : true,
6335         /**
6336          * @event contextmenu
6337          * The raw contextmenu event for the entire grid.
6338          * @param {Roo.EventObject} e
6339          */
6340         "contextmenu" : true,
6341         /**
6342          * @event rowcontextmenu
6343          * Fires when a row is right clicked
6344          * @param {Roo.bootstrap.Table} this
6345          * @param {Number} rowIndex
6346          * @param {Roo.EventObject} e
6347          */
6348         "rowcontextmenu" : true,
6349         /**
6350          * @event cellcontextmenu
6351          * Fires when a cell is right clicked
6352          * @param {Roo.bootstrap.Table} this
6353          * @param {Number} rowIndex
6354          * @param {Number} cellIndex
6355          * @param {Roo.EventObject} e
6356          */
6357          "cellcontextmenu" : true,
6358          /**
6359          * @event headercontextmenu
6360          * Fires when a header is right clicked
6361          * @param {Roo.bootstrap.Table} this
6362          * @param {Number} columnIndex
6363          * @param {Roo.EventObject} e
6364          */
6365         "headercontextmenu" : true
6366     });
6367 };
6368
6369 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6370     
6371     cls: false,
6372     align: false,
6373     bgcolor: false,
6374     border: false,
6375     cellpadding: false,
6376     cellspacing: false,
6377     frame: false,
6378     rules: false,
6379     sortable: false,
6380     summary: false,
6381     width: false,
6382     striped : false,
6383     scrollBody : false,
6384     bordered: false,
6385     hover:  false,
6386     condensed : false,
6387     responsive : false,
6388     sm : false,
6389     cm : false,
6390     store : false,
6391     loadMask : false,
6392     footerShow : true,
6393     headerShow : true,
6394   
6395     rowSelection : false,
6396     cellSelection : false,
6397     layout : false,
6398     
6399     // Roo.Element - the tbody
6400     mainBody: false,
6401     // Roo.Element - thead element
6402     mainHead: false,
6403     
6404     container: false, // used by gridpanel...
6405     
6406     lazyLoad : false,
6407     
6408     CSS : Roo.util.CSS,
6409     
6410     auto_hide_footer : false,
6411     
6412     getAutoCreate : function()
6413     {
6414         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6415         
6416         cfg = {
6417             tag: 'table',
6418             cls : 'table',
6419             cn : []
6420         };
6421         if (this.scrollBody) {
6422             cfg.cls += ' table-body-fixed';
6423         }    
6424         if (this.striped) {
6425             cfg.cls += ' table-striped';
6426         }
6427         
6428         if (this.hover) {
6429             cfg.cls += ' table-hover';
6430         }
6431         if (this.bordered) {
6432             cfg.cls += ' table-bordered';
6433         }
6434         if (this.condensed) {
6435             cfg.cls += ' table-condensed';
6436         }
6437         if (this.responsive) {
6438             cfg.cls += ' table-responsive';
6439         }
6440         
6441         if (this.cls) {
6442             cfg.cls+=  ' ' +this.cls;
6443         }
6444         
6445         // this lot should be simplifed...
6446         var _t = this;
6447         var cp = [
6448             'align',
6449             'bgcolor',
6450             'border',
6451             'cellpadding',
6452             'cellspacing',
6453             'frame',
6454             'rules',
6455             'sortable',
6456             'summary',
6457             'width'
6458         ].forEach(function(k) {
6459             if (_t[k]) {
6460                 cfg[k] = _t[k];
6461             }
6462         });
6463         
6464         
6465         if (this.layout) {
6466             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6467         }
6468         
6469         if(this.store || this.cm){
6470             if(this.headerShow){
6471                 cfg.cn.push(this.renderHeader());
6472             }
6473             
6474             cfg.cn.push(this.renderBody());
6475             
6476             if(this.footerShow){
6477                 cfg.cn.push(this.renderFooter());
6478             }
6479             // where does this come from?
6480             //cfg.cls+=  ' TableGrid';
6481         }
6482         
6483         return { cn : [ cfg ] };
6484     },
6485     
6486     initEvents : function()
6487     {   
6488         if(!this.store || !this.cm){
6489             return;
6490         }
6491         if (this.selModel) {
6492             this.selModel.initEvents();
6493         }
6494         
6495         
6496         //Roo.log('initEvents with ds!!!!');
6497         
6498         this.mainBody = this.el.select('tbody', true).first();
6499         this.mainHead = this.el.select('thead', true).first();
6500         this.mainFoot = this.el.select('tfoot', true).first();
6501         
6502         
6503         
6504         var _this = this;
6505         
6506         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6507             e.on('click', _this.sort, _this);
6508         });
6509         
6510         this.mainBody.on("click", this.onClick, this);
6511         this.mainBody.on("dblclick", this.onDblClick, this);
6512         
6513         // why is this done????? = it breaks dialogs??
6514         //this.parent().el.setStyle('position', 'relative');
6515         
6516         
6517         if (this.footer) {
6518             this.footer.parentId = this.id;
6519             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6520             
6521             if(this.lazyLoad){
6522                 this.el.select('tfoot tr td').first().addClass('hide');
6523             }
6524         } 
6525         
6526         if(this.loadMask) {
6527             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6528         }
6529         
6530         this.store.on('load', this.onLoad, this);
6531         this.store.on('beforeload', this.onBeforeLoad, this);
6532         this.store.on('update', this.onUpdate, this);
6533         this.store.on('add', this.onAdd, this);
6534         this.store.on("clear", this.clear, this);
6535         
6536         this.el.on("contextmenu", this.onContextMenu, this);
6537         
6538         this.mainBody.on('scroll', this.onBodyScroll, this);
6539         
6540         this.cm.on("headerchange", this.onHeaderChange, this);
6541         
6542         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6543         
6544     },
6545     
6546     onContextMenu : function(e, t)
6547     {
6548         this.processEvent("contextmenu", e);
6549     },
6550     
6551     processEvent : function(name, e)
6552     {
6553         if (name != 'touchstart' ) {
6554             this.fireEvent(name, e);    
6555         }
6556         
6557         var t = e.getTarget();
6558         
6559         var cell = Roo.get(t);
6560         
6561         if(!cell){
6562             return;
6563         }
6564         
6565         if(cell.findParent('tfoot', false, true)){
6566             return;
6567         }
6568         
6569         if(cell.findParent('thead', false, true)){
6570             
6571             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6572                 cell = Roo.get(t).findParent('th', false, true);
6573                 if (!cell) {
6574                     Roo.log("failed to find th in thead?");
6575                     Roo.log(e.getTarget());
6576                     return;
6577                 }
6578             }
6579             
6580             var cellIndex = cell.dom.cellIndex;
6581             
6582             var ename = name == 'touchstart' ? 'click' : name;
6583             this.fireEvent("header" + ename, this, cellIndex, e);
6584             
6585             return;
6586         }
6587         
6588         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6589             cell = Roo.get(t).findParent('td', false, true);
6590             if (!cell) {
6591                 Roo.log("failed to find th in tbody?");
6592                 Roo.log(e.getTarget());
6593                 return;
6594             }
6595         }
6596         
6597         var row = cell.findParent('tr', false, true);
6598         var cellIndex = cell.dom.cellIndex;
6599         var rowIndex = row.dom.rowIndex - 1;
6600         
6601         if(row !== false){
6602             
6603             this.fireEvent("row" + name, this, rowIndex, e);
6604             
6605             if(cell !== false){
6606             
6607                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6608             }
6609         }
6610         
6611     },
6612     
6613     onMouseover : function(e, el)
6614     {
6615         var cell = Roo.get(el);
6616         
6617         if(!cell){
6618             return;
6619         }
6620         
6621         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6622             cell = cell.findParent('td', false, true);
6623         }
6624         
6625         var row = cell.findParent('tr', false, true);
6626         var cellIndex = cell.dom.cellIndex;
6627         var rowIndex = row.dom.rowIndex - 1; // start from 0
6628         
6629         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6630         
6631     },
6632     
6633     onMouseout : function(e, el)
6634     {
6635         var cell = Roo.get(el);
6636         
6637         if(!cell){
6638             return;
6639         }
6640         
6641         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6642             cell = cell.findParent('td', false, true);
6643         }
6644         
6645         var row = cell.findParent('tr', false, true);
6646         var cellIndex = cell.dom.cellIndex;
6647         var rowIndex = row.dom.rowIndex - 1; // start from 0
6648         
6649         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6650         
6651     },
6652     
6653     onClick : function(e, el)
6654     {
6655         var cell = Roo.get(el);
6656         
6657         if(!cell || (!this.cellSelection && !this.rowSelection)){
6658             return;
6659         }
6660         
6661         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6662             cell = cell.findParent('td', false, true);
6663         }
6664         
6665         if(!cell || typeof(cell) == 'undefined'){
6666             return;
6667         }
6668         
6669         var row = cell.findParent('tr', false, true);
6670         
6671         if(!row || typeof(row) == 'undefined'){
6672             return;
6673         }
6674         
6675         var cellIndex = cell.dom.cellIndex;
6676         var rowIndex = this.getRowIndex(row);
6677         
6678         // why??? - should these not be based on SelectionModel?
6679         if(this.cellSelection){
6680             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6681         }
6682         
6683         if(this.rowSelection){
6684             this.fireEvent('rowclick', this, row, rowIndex, e);
6685         }
6686         
6687         
6688     },
6689         
6690     onDblClick : function(e,el)
6691     {
6692         var cell = Roo.get(el);
6693         
6694         if(!cell || (!this.cellSelection && !this.rowSelection)){
6695             return;
6696         }
6697         
6698         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6699             cell = cell.findParent('td', false, true);
6700         }
6701         
6702         if(!cell || typeof(cell) == 'undefined'){
6703             return;
6704         }
6705         
6706         var row = cell.findParent('tr', false, true);
6707         
6708         if(!row || typeof(row) == 'undefined'){
6709             return;
6710         }
6711         
6712         var cellIndex = cell.dom.cellIndex;
6713         var rowIndex = this.getRowIndex(row);
6714         
6715         if(this.cellSelection){
6716             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6717         }
6718         
6719         if(this.rowSelection){
6720             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6721         }
6722     },
6723     
6724     sort : function(e,el)
6725     {
6726         var col = Roo.get(el);
6727         
6728         if(!col.hasClass('sortable')){
6729             return;
6730         }
6731         
6732         var sort = col.attr('sort');
6733         var dir = 'ASC';
6734         
6735         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6736             dir = 'DESC';
6737         }
6738         
6739         this.store.sortInfo = {field : sort, direction : dir};
6740         
6741         if (this.footer) {
6742             Roo.log("calling footer first");
6743             this.footer.onClick('first');
6744         } else {
6745         
6746             this.store.load({ params : { start : 0 } });
6747         }
6748     },
6749     
6750     renderHeader : function()
6751     {
6752         var header = {
6753             tag: 'thead',
6754             cn : []
6755         };
6756         
6757         var cm = this.cm;
6758         this.totalWidth = 0;
6759         
6760         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6761             
6762             var config = cm.config[i];
6763             
6764             var c = {
6765                 tag: 'th',
6766                 cls : 'x-hcol-' + i,
6767                 style : '',
6768                 html: cm.getColumnHeader(i)
6769             };
6770             
6771             var hh = '';
6772             
6773             if(typeof(config.sortable) != 'undefined' && config.sortable){
6774                 c.cls = 'sortable';
6775                 c.html = '<i class="glyphicon"></i>' + c.html;
6776             }
6777             
6778             if(typeof(config.lgHeader) != 'undefined'){
6779                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6780             }
6781             
6782             if(typeof(config.mdHeader) != 'undefined'){
6783                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6784             }
6785             
6786             if(typeof(config.smHeader) != 'undefined'){
6787                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6788             }
6789             
6790             if(typeof(config.xsHeader) != 'undefined'){
6791                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6792             }
6793             
6794             if(hh.length){
6795                 c.html = hh;
6796             }
6797             
6798             if(typeof(config.tooltip) != 'undefined'){
6799                 c.tooltip = config.tooltip;
6800             }
6801             
6802             if(typeof(config.colspan) != 'undefined'){
6803                 c.colspan = config.colspan;
6804             }
6805             
6806             if(typeof(config.hidden) != 'undefined' && config.hidden){
6807                 c.style += ' display:none;';
6808             }
6809             
6810             if(typeof(config.dataIndex) != 'undefined'){
6811                 c.sort = config.dataIndex;
6812             }
6813             
6814            
6815             
6816             if(typeof(config.align) != 'undefined' && config.align.length){
6817                 c.style += ' text-align:' + config.align + ';';
6818             }
6819             
6820             if(typeof(config.width) != 'undefined'){
6821                 c.style += ' width:' + config.width + 'px;';
6822                 this.totalWidth += config.width;
6823             } else {
6824                 this.totalWidth += 100; // assume minimum of 100 per column?
6825             }
6826             
6827             if(typeof(config.cls) != 'undefined'){
6828                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6829             }
6830             
6831             ['xs','sm','md','lg'].map(function(size){
6832                 
6833                 if(typeof(config[size]) == 'undefined'){
6834                     return;
6835                 }
6836                 
6837                 if (!config[size]) { // 0 = hidden
6838                     c.cls += ' hidden-' + size;
6839                     return;
6840                 }
6841                 
6842                 c.cls += ' col-' + size + '-' + config[size];
6843
6844             });
6845             
6846             header.cn.push(c)
6847         }
6848         
6849         return header;
6850     },
6851     
6852     renderBody : function()
6853     {
6854         var body = {
6855             tag: 'tbody',
6856             cn : [
6857                 {
6858                     tag: 'tr',
6859                     cn : [
6860                         {
6861                             tag : 'td',
6862                             colspan :  this.cm.getColumnCount()
6863                         }
6864                     ]
6865                 }
6866             ]
6867         };
6868         
6869         return body;
6870     },
6871     
6872     renderFooter : function()
6873     {
6874         var footer = {
6875             tag: 'tfoot',
6876             cn : [
6877                 {
6878                     tag: 'tr',
6879                     cn : [
6880                         {
6881                             tag : 'td',
6882                             colspan :  this.cm.getColumnCount()
6883                         }
6884                     ]
6885                 }
6886             ]
6887         };
6888         
6889         return footer;
6890     },
6891     
6892     
6893     
6894     onLoad : function()
6895     {
6896 //        Roo.log('ds onload');
6897         this.clear();
6898         
6899         var _this = this;
6900         var cm = this.cm;
6901         var ds = this.store;
6902         
6903         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6904             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6905             if (_this.store.sortInfo) {
6906                     
6907                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6908                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6909                 }
6910                 
6911                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6912                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6913                 }
6914             }
6915         });
6916         
6917         var tbody =  this.mainBody;
6918               
6919         if(ds.getCount() > 0){
6920             ds.data.each(function(d,rowIndex){
6921                 var row =  this.renderRow(cm, ds, rowIndex);
6922                 
6923                 tbody.createChild(row);
6924                 
6925                 var _this = this;
6926                 
6927                 if(row.cellObjects.length){
6928                     Roo.each(row.cellObjects, function(r){
6929                         _this.renderCellObject(r);
6930                     })
6931                 }
6932                 
6933             }, this);
6934         }
6935         
6936         var tfoot = this.el.select('tfoot', true).first();
6937         
6938         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6939             
6940             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6941             
6942             var total = this.ds.getTotalCount();
6943             
6944             if(this.footer.pageSize < total){
6945                 this.mainFoot.show();
6946             }
6947         }
6948         
6949         Roo.each(this.el.select('tbody td', true).elements, function(e){
6950             e.on('mouseover', _this.onMouseover, _this);
6951         });
6952         
6953         Roo.each(this.el.select('tbody td', true).elements, function(e){
6954             e.on('mouseout', _this.onMouseout, _this);
6955         });
6956         this.fireEvent('rowsrendered', this);
6957         
6958         this.autoSize();
6959     },
6960     
6961     
6962     onUpdate : function(ds,record)
6963     {
6964         this.refreshRow(record);
6965         this.autoSize();
6966     },
6967     
6968     onRemove : function(ds, record, index, isUpdate){
6969         if(isUpdate !== true){
6970             this.fireEvent("beforerowremoved", this, index, record);
6971         }
6972         var bt = this.mainBody.dom;
6973         
6974         var rows = this.el.select('tbody > tr', true).elements;
6975         
6976         if(typeof(rows[index]) != 'undefined'){
6977             bt.removeChild(rows[index].dom);
6978         }
6979         
6980 //        if(bt.rows[index]){
6981 //            bt.removeChild(bt.rows[index]);
6982 //        }
6983         
6984         if(isUpdate !== true){
6985             //this.stripeRows(index);
6986             //this.syncRowHeights(index, index);
6987             //this.layout();
6988             this.fireEvent("rowremoved", this, index, record);
6989         }
6990     },
6991     
6992     onAdd : function(ds, records, rowIndex)
6993     {
6994         //Roo.log('on Add called');
6995         // - note this does not handle multiple adding very well..
6996         var bt = this.mainBody.dom;
6997         for (var i =0 ; i < records.length;i++) {
6998             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6999             //Roo.log(records[i]);
7000             //Roo.log(this.store.getAt(rowIndex+i));
7001             this.insertRow(this.store, rowIndex + i, false);
7002             return;
7003         }
7004         
7005     },
7006     
7007     
7008     refreshRow : function(record){
7009         var ds = this.store, index;
7010         if(typeof record == 'number'){
7011             index = record;
7012             record = ds.getAt(index);
7013         }else{
7014             index = ds.indexOf(record);
7015         }
7016         this.insertRow(ds, index, true);
7017         this.autoSize();
7018         this.onRemove(ds, record, index+1, true);
7019         this.autoSize();
7020         //this.syncRowHeights(index, index);
7021         //this.layout();
7022         this.fireEvent("rowupdated", this, index, record);
7023     },
7024     
7025     insertRow : function(dm, rowIndex, isUpdate){
7026         
7027         if(!isUpdate){
7028             this.fireEvent("beforerowsinserted", this, rowIndex);
7029         }
7030             //var s = this.getScrollState();
7031         var row = this.renderRow(this.cm, this.store, rowIndex);
7032         // insert before rowIndex..
7033         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7034         
7035         var _this = this;
7036                 
7037         if(row.cellObjects.length){
7038             Roo.each(row.cellObjects, function(r){
7039                 _this.renderCellObject(r);
7040             })
7041         }
7042             
7043         if(!isUpdate){
7044             this.fireEvent("rowsinserted", this, rowIndex);
7045             //this.syncRowHeights(firstRow, lastRow);
7046             //this.stripeRows(firstRow);
7047             //this.layout();
7048         }
7049         
7050     },
7051     
7052     
7053     getRowDom : function(rowIndex)
7054     {
7055         var rows = this.el.select('tbody > tr', true).elements;
7056         
7057         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7058         
7059     },
7060     // returns the object tree for a tr..
7061   
7062     
7063     renderRow : function(cm, ds, rowIndex) 
7064     {
7065         var d = ds.getAt(rowIndex);
7066         
7067         var row = {
7068             tag : 'tr',
7069             cls : 'x-row-' + rowIndex,
7070             cn : []
7071         };
7072             
7073         var cellObjects = [];
7074         
7075         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7076             var config = cm.config[i];
7077             
7078             var renderer = cm.getRenderer(i);
7079             var value = '';
7080             var id = false;
7081             
7082             if(typeof(renderer) !== 'undefined'){
7083                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7084             }
7085             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7086             // and are rendered into the cells after the row is rendered - using the id for the element.
7087             
7088             if(typeof(value) === 'object'){
7089                 id = Roo.id();
7090                 cellObjects.push({
7091                     container : id,
7092                     cfg : value 
7093                 })
7094             }
7095             
7096             var rowcfg = {
7097                 record: d,
7098                 rowIndex : rowIndex,
7099                 colIndex : i,
7100                 rowClass : ''
7101             };
7102
7103             this.fireEvent('rowclass', this, rowcfg);
7104             
7105             var td = {
7106                 tag: 'td',
7107                 cls : rowcfg.rowClass + ' x-col-' + i,
7108                 style: '',
7109                 html: (typeof(value) === 'object') ? '' : value
7110             };
7111             
7112             if (id) {
7113                 td.id = id;
7114             }
7115             
7116             if(typeof(config.colspan) != 'undefined'){
7117                 td.colspan = config.colspan;
7118             }
7119             
7120             if(typeof(config.hidden) != 'undefined' && config.hidden){
7121                 td.style += ' display:none;';
7122             }
7123             
7124             if(typeof(config.align) != 'undefined' && config.align.length){
7125                 td.style += ' text-align:' + config.align + ';';
7126             }
7127             if(typeof(config.valign) != 'undefined' && config.valign.length){
7128                 td.style += ' vertical-align:' + config.valign + ';';
7129             }
7130             
7131             if(typeof(config.width) != 'undefined'){
7132                 td.style += ' width:' +  config.width + 'px;';
7133             }
7134             
7135             if(typeof(config.cursor) != 'undefined'){
7136                 td.style += ' cursor:' +  config.cursor + ';';
7137             }
7138             
7139             if(typeof(config.cls) != 'undefined'){
7140                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7141             }
7142             
7143             ['xs','sm','md','lg'].map(function(size){
7144                 
7145                 if(typeof(config[size]) == 'undefined'){
7146                     return;
7147                 }
7148                 
7149                 if (!config[size]) { // 0 = hidden
7150                     td.cls += ' hidden-' + size;
7151                     return;
7152                 }
7153                 
7154                 td.cls += ' col-' + size + '-' + config[size];
7155
7156             });
7157             
7158             row.cn.push(td);
7159            
7160         }
7161         
7162         row.cellObjects = cellObjects;
7163         
7164         return row;
7165           
7166     },
7167     
7168     
7169     
7170     onBeforeLoad : function()
7171     {
7172         
7173     },
7174      /**
7175      * Remove all rows
7176      */
7177     clear : function()
7178     {
7179         this.el.select('tbody', true).first().dom.innerHTML = '';
7180     },
7181     /**
7182      * Show or hide a row.
7183      * @param {Number} rowIndex to show or hide
7184      * @param {Boolean} state hide
7185      */
7186     setRowVisibility : function(rowIndex, state)
7187     {
7188         var bt = this.mainBody.dom;
7189         
7190         var rows = this.el.select('tbody > tr', true).elements;
7191         
7192         if(typeof(rows[rowIndex]) == 'undefined'){
7193             return;
7194         }
7195         rows[rowIndex].dom.style.display = state ? '' : 'none';
7196     },
7197     
7198     
7199     getSelectionModel : function(){
7200         if(!this.selModel){
7201             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7202         }
7203         return this.selModel;
7204     },
7205     /*
7206      * Render the Roo.bootstrap object from renderder
7207      */
7208     renderCellObject : function(r)
7209     {
7210         var _this = this;
7211         
7212         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7213         
7214         var t = r.cfg.render(r.container);
7215         
7216         if(r.cfg.cn){
7217             Roo.each(r.cfg.cn, function(c){
7218                 var child = {
7219                     container: t.getChildContainer(),
7220                     cfg: c
7221                 };
7222                 _this.renderCellObject(child);
7223             })
7224         }
7225     },
7226     
7227     getRowIndex : function(row)
7228     {
7229         var rowIndex = -1;
7230         
7231         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7232             if(el != row){
7233                 return;
7234             }
7235             
7236             rowIndex = index;
7237         });
7238         
7239         return rowIndex;
7240     },
7241      /**
7242      * Returns the grid's underlying element = used by panel.Grid
7243      * @return {Element} The element
7244      */
7245     getGridEl : function(){
7246         return this.el;
7247     },
7248      /**
7249      * Forces a resize - used by panel.Grid
7250      * @return {Element} The element
7251      */
7252     autoSize : function()
7253     {
7254         //var ctr = Roo.get(this.container.dom.parentElement);
7255         var ctr = Roo.get(this.el.dom);
7256         
7257         var thd = this.getGridEl().select('thead',true).first();
7258         var tbd = this.getGridEl().select('tbody', true).first();
7259         var tfd = this.getGridEl().select('tfoot', true).first();
7260         
7261         var cw = ctr.getWidth();
7262         
7263         if (tbd) {
7264             
7265             tbd.setSize(ctr.getWidth(),
7266                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7267             );
7268             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7269             cw -= barsize;
7270         }
7271         cw = Math.max(cw, this.totalWidth);
7272         this.getGridEl().select('tr',true).setWidth(cw);
7273         // resize 'expandable coloumn?
7274         
7275         return; // we doe not have a view in this design..
7276         
7277     },
7278     onBodyScroll: function()
7279     {
7280         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7281         if(this.mainHead){
7282             this.mainHead.setStyle({
7283                 'position' : 'relative',
7284                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7285             });
7286         }
7287         
7288         if(this.lazyLoad){
7289             
7290             var scrollHeight = this.mainBody.dom.scrollHeight;
7291             
7292             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7293             
7294             var height = this.mainBody.getHeight();
7295             
7296             if(scrollHeight - height == scrollTop) {
7297                 
7298                 var total = this.ds.getTotalCount();
7299                 
7300                 if(this.footer.cursor + this.footer.pageSize < total){
7301                     
7302                     this.footer.ds.load({
7303                         params : {
7304                             start : this.footer.cursor + this.footer.pageSize,
7305                             limit : this.footer.pageSize
7306                         },
7307                         add : true
7308                     });
7309                 }
7310             }
7311             
7312         }
7313     },
7314     
7315     onHeaderChange : function()
7316     {
7317         var header = this.renderHeader();
7318         var table = this.el.select('table', true).first();
7319         
7320         this.mainHead.remove();
7321         this.mainHead = table.createChild(header, this.mainBody, false);
7322     },
7323     
7324     onHiddenChange : function(colModel, colIndex, hidden)
7325     {
7326         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7327         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7328         
7329         this.CSS.updateRule(thSelector, "display", "");
7330         this.CSS.updateRule(tdSelector, "display", "");
7331         
7332         if(hidden){
7333             this.CSS.updateRule(thSelector, "display", "none");
7334             this.CSS.updateRule(tdSelector, "display", "none");
7335         }
7336         
7337         this.onHeaderChange();
7338         this.onLoad();
7339     },
7340     
7341     setColumnWidth: function(col_index, width)
7342     {
7343         // width = "md-2 xs-2..."
7344         if(!this.colModel.config[col_index]) {
7345             return;
7346         }
7347         
7348         var w = width.split(" ");
7349         
7350         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7351         
7352         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7353         
7354         
7355         for(var j = 0; j < w.length; j++) {
7356             
7357             if(!w[j]) {
7358                 continue;
7359             }
7360             
7361             var size_cls = w[j].split("-");
7362             
7363             if(!Number.isInteger(size_cls[1] * 1)) {
7364                 continue;
7365             }
7366             
7367             if(!this.colModel.config[col_index][size_cls[0]]) {
7368                 continue;
7369             }
7370             
7371             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7372                 continue;
7373             }
7374             
7375             h_row[0].classList.replace(
7376                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7377                 "col-"+size_cls[0]+"-"+size_cls[1]
7378             );
7379             
7380             for(var i = 0; i < rows.length; i++) {
7381                 
7382                 var size_cls = w[j].split("-");
7383                 
7384                 if(!Number.isInteger(size_cls[1] * 1)) {
7385                     continue;
7386                 }
7387                 
7388                 if(!this.colModel.config[col_index][size_cls[0]]) {
7389                     continue;
7390                 }
7391                 
7392                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7393                     continue;
7394                 }
7395                 
7396                 rows[i].classList.replace(
7397                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7398                     "col-"+size_cls[0]+"-"+size_cls[1]
7399                 );
7400             }
7401             
7402             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7403         }
7404     }
7405 });
7406
7407  
7408
7409  /*
7410  * - LGPL
7411  *
7412  * table cell
7413  * 
7414  */
7415
7416 /**
7417  * @class Roo.bootstrap.TableCell
7418  * @extends Roo.bootstrap.Component
7419  * Bootstrap TableCell class
7420  * @cfg {String} html cell contain text
7421  * @cfg {String} cls cell class
7422  * @cfg {String} tag cell tag (td|th) default td
7423  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7424  * @cfg {String} align Aligns the content in a cell
7425  * @cfg {String} axis Categorizes cells
7426  * @cfg {String} bgcolor Specifies the background color of a cell
7427  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7428  * @cfg {Number} colspan Specifies the number of columns a cell should span
7429  * @cfg {String} headers Specifies one or more header cells a cell is related to
7430  * @cfg {Number} height Sets the height of a cell
7431  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7432  * @cfg {Number} rowspan Sets the number of rows a cell should span
7433  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7434  * @cfg {String} valign Vertical aligns the content in a cell
7435  * @cfg {Number} width Specifies the width of a cell
7436  * 
7437  * @constructor
7438  * Create a new TableCell
7439  * @param {Object} config The config object
7440  */
7441
7442 Roo.bootstrap.TableCell = function(config){
7443     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7444 };
7445
7446 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7447     
7448     html: false,
7449     cls: false,
7450     tag: false,
7451     abbr: false,
7452     align: false,
7453     axis: false,
7454     bgcolor: false,
7455     charoff: false,
7456     colspan: false,
7457     headers: false,
7458     height: false,
7459     nowrap: false,
7460     rowspan: false,
7461     scope: false,
7462     valign: false,
7463     width: false,
7464     
7465     
7466     getAutoCreate : function(){
7467         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7468         
7469         cfg = {
7470             tag: 'td'
7471         };
7472         
7473         if(this.tag){
7474             cfg.tag = this.tag;
7475         }
7476         
7477         if (this.html) {
7478             cfg.html=this.html
7479         }
7480         if (this.cls) {
7481             cfg.cls=this.cls
7482         }
7483         if (this.abbr) {
7484             cfg.abbr=this.abbr
7485         }
7486         if (this.align) {
7487             cfg.align=this.align
7488         }
7489         if (this.axis) {
7490             cfg.axis=this.axis
7491         }
7492         if (this.bgcolor) {
7493             cfg.bgcolor=this.bgcolor
7494         }
7495         if (this.charoff) {
7496             cfg.charoff=this.charoff
7497         }
7498         if (this.colspan) {
7499             cfg.colspan=this.colspan
7500         }
7501         if (this.headers) {
7502             cfg.headers=this.headers
7503         }
7504         if (this.height) {
7505             cfg.height=this.height
7506         }
7507         if (this.nowrap) {
7508             cfg.nowrap=this.nowrap
7509         }
7510         if (this.rowspan) {
7511             cfg.rowspan=this.rowspan
7512         }
7513         if (this.scope) {
7514             cfg.scope=this.scope
7515         }
7516         if (this.valign) {
7517             cfg.valign=this.valign
7518         }
7519         if (this.width) {
7520             cfg.width=this.width
7521         }
7522         
7523         
7524         return cfg;
7525     }
7526    
7527 });
7528
7529  
7530
7531  /*
7532  * - LGPL
7533  *
7534  * table row
7535  * 
7536  */
7537
7538 /**
7539  * @class Roo.bootstrap.TableRow
7540  * @extends Roo.bootstrap.Component
7541  * Bootstrap TableRow class
7542  * @cfg {String} cls row class
7543  * @cfg {String} align Aligns the content in a table row
7544  * @cfg {String} bgcolor Specifies a background color for a table row
7545  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7546  * @cfg {String} valign Vertical aligns the content in a table row
7547  * 
7548  * @constructor
7549  * Create a new TableRow
7550  * @param {Object} config The config object
7551  */
7552
7553 Roo.bootstrap.TableRow = function(config){
7554     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7555 };
7556
7557 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7558     
7559     cls: false,
7560     align: false,
7561     bgcolor: false,
7562     charoff: false,
7563     valign: false,
7564     
7565     getAutoCreate : function(){
7566         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7567         
7568         cfg = {
7569             tag: 'tr'
7570         };
7571             
7572         if(this.cls){
7573             cfg.cls = this.cls;
7574         }
7575         if(this.align){
7576             cfg.align = this.align;
7577         }
7578         if(this.bgcolor){
7579             cfg.bgcolor = this.bgcolor;
7580         }
7581         if(this.charoff){
7582             cfg.charoff = this.charoff;
7583         }
7584         if(this.valign){
7585             cfg.valign = this.valign;
7586         }
7587         
7588         return cfg;
7589     }
7590    
7591 });
7592
7593  
7594
7595  /*
7596  * - LGPL
7597  *
7598  * table body
7599  * 
7600  */
7601
7602 /**
7603  * @class Roo.bootstrap.TableBody
7604  * @extends Roo.bootstrap.Component
7605  * Bootstrap TableBody class
7606  * @cfg {String} cls element class
7607  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7608  * @cfg {String} align Aligns the content inside the element
7609  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7610  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7611  * 
7612  * @constructor
7613  * Create a new TableBody
7614  * @param {Object} config The config object
7615  */
7616
7617 Roo.bootstrap.TableBody = function(config){
7618     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7619 };
7620
7621 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7622     
7623     cls: false,
7624     tag: false,
7625     align: false,
7626     charoff: false,
7627     valign: false,
7628     
7629     getAutoCreate : function(){
7630         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7631         
7632         cfg = {
7633             tag: 'tbody'
7634         };
7635             
7636         if (this.cls) {
7637             cfg.cls=this.cls
7638         }
7639         if(this.tag){
7640             cfg.tag = this.tag;
7641         }
7642         
7643         if(this.align){
7644             cfg.align = this.align;
7645         }
7646         if(this.charoff){
7647             cfg.charoff = this.charoff;
7648         }
7649         if(this.valign){
7650             cfg.valign = this.valign;
7651         }
7652         
7653         return cfg;
7654     }
7655     
7656     
7657 //    initEvents : function()
7658 //    {
7659 //        
7660 //        if(!this.store){
7661 //            return;
7662 //        }
7663 //        
7664 //        this.store = Roo.factory(this.store, Roo.data);
7665 //        this.store.on('load', this.onLoad, this);
7666 //        
7667 //        this.store.load();
7668 //        
7669 //    },
7670 //    
7671 //    onLoad: function () 
7672 //    {   
7673 //        this.fireEvent('load', this);
7674 //    }
7675 //    
7676 //   
7677 });
7678
7679  
7680
7681  /*
7682  * Based on:
7683  * Ext JS Library 1.1.1
7684  * Copyright(c) 2006-2007, Ext JS, LLC.
7685  *
7686  * Originally Released Under LGPL - original licence link has changed is not relivant.
7687  *
7688  * Fork - LGPL
7689  * <script type="text/javascript">
7690  */
7691
7692 // as we use this in bootstrap.
7693 Roo.namespace('Roo.form');
7694  /**
7695  * @class Roo.form.Action
7696  * Internal Class used to handle form actions
7697  * @constructor
7698  * @param {Roo.form.BasicForm} el The form element or its id
7699  * @param {Object} config Configuration options
7700  */
7701
7702  
7703  
7704 // define the action interface
7705 Roo.form.Action = function(form, options){
7706     this.form = form;
7707     this.options = options || {};
7708 };
7709 /**
7710  * Client Validation Failed
7711  * @const 
7712  */
7713 Roo.form.Action.CLIENT_INVALID = 'client';
7714 /**
7715  * Server Validation Failed
7716  * @const 
7717  */
7718 Roo.form.Action.SERVER_INVALID = 'server';
7719  /**
7720  * Connect to Server Failed
7721  * @const 
7722  */
7723 Roo.form.Action.CONNECT_FAILURE = 'connect';
7724 /**
7725  * Reading Data from Server Failed
7726  * @const 
7727  */
7728 Roo.form.Action.LOAD_FAILURE = 'load';
7729
7730 Roo.form.Action.prototype = {
7731     type : 'default',
7732     failureType : undefined,
7733     response : undefined,
7734     result : undefined,
7735
7736     // interface method
7737     run : function(options){
7738
7739     },
7740
7741     // interface method
7742     success : function(response){
7743
7744     },
7745
7746     // interface method
7747     handleResponse : function(response){
7748
7749     },
7750
7751     // default connection failure
7752     failure : function(response){
7753         
7754         this.response = response;
7755         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7756         this.form.afterAction(this, false);
7757     },
7758
7759     processResponse : function(response){
7760         this.response = response;
7761         if(!response.responseText){
7762             return true;
7763         }
7764         this.result = this.handleResponse(response);
7765         return this.result;
7766     },
7767
7768     // utility functions used internally
7769     getUrl : function(appendParams){
7770         var url = this.options.url || this.form.url || this.form.el.dom.action;
7771         if(appendParams){
7772             var p = this.getParams();
7773             if(p){
7774                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7775             }
7776         }
7777         return url;
7778     },
7779
7780     getMethod : function(){
7781         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7782     },
7783
7784     getParams : function(){
7785         var bp = this.form.baseParams;
7786         var p = this.options.params;
7787         if(p){
7788             if(typeof p == "object"){
7789                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7790             }else if(typeof p == 'string' && bp){
7791                 p += '&' + Roo.urlEncode(bp);
7792             }
7793         }else if(bp){
7794             p = Roo.urlEncode(bp);
7795         }
7796         return p;
7797     },
7798
7799     createCallback : function(){
7800         return {
7801             success: this.success,
7802             failure: this.failure,
7803             scope: this,
7804             timeout: (this.form.timeout*1000),
7805             upload: this.form.fileUpload ? this.success : undefined
7806         };
7807     }
7808 };
7809
7810 Roo.form.Action.Submit = function(form, options){
7811     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7812 };
7813
7814 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7815     type : 'submit',
7816
7817     haveProgress : false,
7818     uploadComplete : false,
7819     
7820     // uploadProgress indicator.
7821     uploadProgress : function()
7822     {
7823         if (!this.form.progressUrl) {
7824             return;
7825         }
7826         
7827         if (!this.haveProgress) {
7828             Roo.MessageBox.progress("Uploading", "Uploading");
7829         }
7830         if (this.uploadComplete) {
7831            Roo.MessageBox.hide();
7832            return;
7833         }
7834         
7835         this.haveProgress = true;
7836    
7837         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7838         
7839         var c = new Roo.data.Connection();
7840         c.request({
7841             url : this.form.progressUrl,
7842             params: {
7843                 id : uid
7844             },
7845             method: 'GET',
7846             success : function(req){
7847                //console.log(data);
7848                 var rdata = false;
7849                 var edata;
7850                 try  {
7851                    rdata = Roo.decode(req.responseText)
7852                 } catch (e) {
7853                     Roo.log("Invalid data from server..");
7854                     Roo.log(edata);
7855                     return;
7856                 }
7857                 if (!rdata || !rdata.success) {
7858                     Roo.log(rdata);
7859                     Roo.MessageBox.alert(Roo.encode(rdata));
7860                     return;
7861                 }
7862                 var data = rdata.data;
7863                 
7864                 if (this.uploadComplete) {
7865                    Roo.MessageBox.hide();
7866                    return;
7867                 }
7868                    
7869                 if (data){
7870                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7871                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7872                     );
7873                 }
7874                 this.uploadProgress.defer(2000,this);
7875             },
7876        
7877             failure: function(data) {
7878                 Roo.log('progress url failed ');
7879                 Roo.log(data);
7880             },
7881             scope : this
7882         });
7883            
7884     },
7885     
7886     
7887     run : function()
7888     {
7889         // run get Values on the form, so it syncs any secondary forms.
7890         this.form.getValues();
7891         
7892         var o = this.options;
7893         var method = this.getMethod();
7894         var isPost = method == 'POST';
7895         if(o.clientValidation === false || this.form.isValid()){
7896             
7897             if (this.form.progressUrl) {
7898                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7899                     (new Date() * 1) + '' + Math.random());
7900                     
7901             } 
7902             
7903             
7904             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7905                 form:this.form.el.dom,
7906                 url:this.getUrl(!isPost),
7907                 method: method,
7908                 params:isPost ? this.getParams() : null,
7909                 isUpload: this.form.fileUpload,
7910                 formData : this.form.formData
7911             }));
7912             
7913             this.uploadProgress();
7914
7915         }else if (o.clientValidation !== false){ // client validation failed
7916             this.failureType = Roo.form.Action.CLIENT_INVALID;
7917             this.form.afterAction(this, false);
7918         }
7919     },
7920
7921     success : function(response)
7922     {
7923         this.uploadComplete= true;
7924         if (this.haveProgress) {
7925             Roo.MessageBox.hide();
7926         }
7927         
7928         
7929         var result = this.processResponse(response);
7930         if(result === true || result.success){
7931             this.form.afterAction(this, true);
7932             return;
7933         }
7934         if(result.errors){
7935             this.form.markInvalid(result.errors);
7936             this.failureType = Roo.form.Action.SERVER_INVALID;
7937         }
7938         this.form.afterAction(this, false);
7939     },
7940     failure : function(response)
7941     {
7942         this.uploadComplete= true;
7943         if (this.haveProgress) {
7944             Roo.MessageBox.hide();
7945         }
7946         
7947         this.response = response;
7948         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7949         this.form.afterAction(this, false);
7950     },
7951     
7952     handleResponse : function(response){
7953         if(this.form.errorReader){
7954             var rs = this.form.errorReader.read(response);
7955             var errors = [];
7956             if(rs.records){
7957                 for(var i = 0, len = rs.records.length; i < len; i++) {
7958                     var r = rs.records[i];
7959                     errors[i] = r.data;
7960                 }
7961             }
7962             if(errors.length < 1){
7963                 errors = null;
7964             }
7965             return {
7966                 success : rs.success,
7967                 errors : errors
7968             };
7969         }
7970         var ret = false;
7971         try {
7972             ret = Roo.decode(response.responseText);
7973         } catch (e) {
7974             ret = {
7975                 success: false,
7976                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7977                 errors : []
7978             };
7979         }
7980         return ret;
7981         
7982     }
7983 });
7984
7985
7986 Roo.form.Action.Load = function(form, options){
7987     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7988     this.reader = this.form.reader;
7989 };
7990
7991 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7992     type : 'load',
7993
7994     run : function(){
7995         
7996         Roo.Ajax.request(Roo.apply(
7997                 this.createCallback(), {
7998                     method:this.getMethod(),
7999                     url:this.getUrl(false),
8000                     params:this.getParams()
8001         }));
8002     },
8003
8004     success : function(response){
8005         
8006         var result = this.processResponse(response);
8007         if(result === true || !result.success || !result.data){
8008             this.failureType = Roo.form.Action.LOAD_FAILURE;
8009             this.form.afterAction(this, false);
8010             return;
8011         }
8012         this.form.clearInvalid();
8013         this.form.setValues(result.data);
8014         this.form.afterAction(this, true);
8015     },
8016
8017     handleResponse : function(response){
8018         if(this.form.reader){
8019             var rs = this.form.reader.read(response);
8020             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8021             return {
8022                 success : rs.success,
8023                 data : data
8024             };
8025         }
8026         return Roo.decode(response.responseText);
8027     }
8028 });
8029
8030 Roo.form.Action.ACTION_TYPES = {
8031     'load' : Roo.form.Action.Load,
8032     'submit' : Roo.form.Action.Submit
8033 };/*
8034  * - LGPL
8035  *
8036  * form
8037  *
8038  */
8039
8040 /**
8041  * @class Roo.bootstrap.Form
8042  * @extends Roo.bootstrap.Component
8043  * Bootstrap Form class
8044  * @cfg {String} method  GET | POST (default POST)
8045  * @cfg {String} labelAlign top | left (default top)
8046  * @cfg {String} align left  | right - for navbars
8047  * @cfg {Boolean} loadMask load mask when submit (default true)
8048
8049  *
8050  * @constructor
8051  * Create a new Form
8052  * @param {Object} config The config object
8053  */
8054
8055
8056 Roo.bootstrap.Form = function(config){
8057     
8058     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8059     
8060     Roo.bootstrap.Form.popover.apply();
8061     
8062     this.addEvents({
8063         /**
8064          * @event clientvalidation
8065          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8066          * @param {Form} this
8067          * @param {Boolean} valid true if the form has passed client-side validation
8068          */
8069         clientvalidation: true,
8070         /**
8071          * @event beforeaction
8072          * Fires before any action is performed. Return false to cancel the action.
8073          * @param {Form} this
8074          * @param {Action} action The action to be performed
8075          */
8076         beforeaction: true,
8077         /**
8078          * @event actionfailed
8079          * Fires when an action fails.
8080          * @param {Form} this
8081          * @param {Action} action The action that failed
8082          */
8083         actionfailed : true,
8084         /**
8085          * @event actioncomplete
8086          * Fires when an action is completed.
8087          * @param {Form} this
8088          * @param {Action} action The action that completed
8089          */
8090         actioncomplete : true
8091     });
8092 };
8093
8094 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8095
8096      /**
8097      * @cfg {String} method
8098      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8099      */
8100     method : 'POST',
8101     /**
8102      * @cfg {String} url
8103      * The URL to use for form actions if one isn't supplied in the action options.
8104      */
8105     /**
8106      * @cfg {Boolean} fileUpload
8107      * Set to true if this form is a file upload.
8108      */
8109
8110     /**
8111      * @cfg {Object} baseParams
8112      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8113      */
8114
8115     /**
8116      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8117      */
8118     timeout: 30,
8119     /**
8120      * @cfg {Sting} align (left|right) for navbar forms
8121      */
8122     align : 'left',
8123
8124     // private
8125     activeAction : null,
8126
8127     /**
8128      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8129      * element by passing it or its id or mask the form itself by passing in true.
8130      * @type Mixed
8131      */
8132     waitMsgTarget : false,
8133
8134     loadMask : true,
8135     
8136     /**
8137      * @cfg {Boolean} errorMask (true|false) default false
8138      */
8139     errorMask : false,
8140     
8141     /**
8142      * @cfg {Number} maskOffset Default 100
8143      */
8144     maskOffset : 100,
8145     
8146     /**
8147      * @cfg {Boolean} maskBody
8148      */
8149     maskBody : false,
8150
8151     getAutoCreate : function(){
8152
8153         var cfg = {
8154             tag: 'form',
8155             method : this.method || 'POST',
8156             id : this.id || Roo.id(),
8157             cls : ''
8158         };
8159         if (this.parent().xtype.match(/^Nav/)) {
8160             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8161
8162         }
8163
8164         if (this.labelAlign == 'left' ) {
8165             cfg.cls += ' form-horizontal';
8166         }
8167
8168
8169         return cfg;
8170     },
8171     initEvents : function()
8172     {
8173         this.el.on('submit', this.onSubmit, this);
8174         // this was added as random key presses on the form where triggering form submit.
8175         this.el.on('keypress', function(e) {
8176             if (e.getCharCode() != 13) {
8177                 return true;
8178             }
8179             // we might need to allow it for textareas.. and some other items.
8180             // check e.getTarget().
8181
8182             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8183                 return true;
8184             }
8185
8186             Roo.log("keypress blocked");
8187
8188             e.preventDefault();
8189             return false;
8190         });
8191         
8192     },
8193     // private
8194     onSubmit : function(e){
8195         e.stopEvent();
8196     },
8197
8198      /**
8199      * Returns true if client-side validation on the form is successful.
8200      * @return Boolean
8201      */
8202     isValid : function(){
8203         var items = this.getItems();
8204         var valid = true;
8205         var target = false;
8206         
8207         items.each(function(f){
8208             
8209             if(f.validate()){
8210                 return;
8211             }
8212             
8213             Roo.log('invalid field: ' + f.name);
8214             
8215             valid = false;
8216
8217             if(!target && f.el.isVisible(true)){
8218                 target = f;
8219             }
8220            
8221         });
8222         
8223         if(this.errorMask && !valid){
8224             Roo.bootstrap.Form.popover.mask(this, target);
8225         }
8226         
8227         return valid;
8228     },
8229     
8230     /**
8231      * Returns true if any fields in this form have changed since their original load.
8232      * @return Boolean
8233      */
8234     isDirty : function(){
8235         var dirty = false;
8236         var items = this.getItems();
8237         items.each(function(f){
8238            if(f.isDirty()){
8239                dirty = true;
8240                return false;
8241            }
8242            return true;
8243         });
8244         return dirty;
8245     },
8246      /**
8247      * Performs a predefined action (submit or load) or custom actions you define on this form.
8248      * @param {String} actionName The name of the action type
8249      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8250      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8251      * accept other config options):
8252      * <pre>
8253 Property          Type             Description
8254 ----------------  ---------------  ----------------------------------------------------------------------------------
8255 url               String           The url for the action (defaults to the form's url)
8256 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8257 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8258 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8259                                    validate the form on the client (defaults to false)
8260      * </pre>
8261      * @return {BasicForm} this
8262      */
8263     doAction : function(action, options){
8264         if(typeof action == 'string'){
8265             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8266         }
8267         if(this.fireEvent('beforeaction', this, action) !== false){
8268             this.beforeAction(action);
8269             action.run.defer(100, action);
8270         }
8271         return this;
8272     },
8273
8274     // private
8275     beforeAction : function(action){
8276         var o = action.options;
8277         
8278         if(this.loadMask){
8279             
8280             if(this.maskBody){
8281                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8282             } else {
8283                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8284             }
8285         }
8286         // not really supported yet.. ??
8287
8288         //if(this.waitMsgTarget === true){
8289         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8290         //}else if(this.waitMsgTarget){
8291         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8292         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8293         //}else {
8294         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8295        // }
8296
8297     },
8298
8299     // private
8300     afterAction : function(action, success){
8301         this.activeAction = null;
8302         var o = action.options;
8303
8304         if(this.loadMask){
8305             
8306             if(this.maskBody){
8307                 Roo.get(document.body).unmask();
8308             } else {
8309                 this.el.unmask();
8310             }
8311         }
8312         
8313         //if(this.waitMsgTarget === true){
8314 //            this.el.unmask();
8315         //}else if(this.waitMsgTarget){
8316         //    this.waitMsgTarget.unmask();
8317         //}else{
8318         //    Roo.MessageBox.updateProgress(1);
8319         //    Roo.MessageBox.hide();
8320        // }
8321         //
8322         if(success){
8323             if(o.reset){
8324                 this.reset();
8325             }
8326             Roo.callback(o.success, o.scope, [this, action]);
8327             this.fireEvent('actioncomplete', this, action);
8328
8329         }else{
8330
8331             // failure condition..
8332             // we have a scenario where updates need confirming.
8333             // eg. if a locking scenario exists..
8334             // we look for { errors : { needs_confirm : true }} in the response.
8335             if (
8336                 (typeof(action.result) != 'undefined')  &&
8337                 (typeof(action.result.errors) != 'undefined')  &&
8338                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8339            ){
8340                 var _t = this;
8341                 Roo.log("not supported yet");
8342                  /*
8343
8344                 Roo.MessageBox.confirm(
8345                     "Change requires confirmation",
8346                     action.result.errorMsg,
8347                     function(r) {
8348                         if (r != 'yes') {
8349                             return;
8350                         }
8351                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8352                     }
8353
8354                 );
8355                 */
8356
8357
8358                 return;
8359             }
8360
8361             Roo.callback(o.failure, o.scope, [this, action]);
8362             // show an error message if no failed handler is set..
8363             if (!this.hasListener('actionfailed')) {
8364                 Roo.log("need to add dialog support");
8365                 /*
8366                 Roo.MessageBox.alert("Error",
8367                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8368                         action.result.errorMsg :
8369                         "Saving Failed, please check your entries or try again"
8370                 );
8371                 */
8372             }
8373
8374             this.fireEvent('actionfailed', this, action);
8375         }
8376
8377     },
8378     /**
8379      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8380      * @param {String} id The value to search for
8381      * @return Field
8382      */
8383     findField : function(id){
8384         var items = this.getItems();
8385         var field = items.get(id);
8386         if(!field){
8387              items.each(function(f){
8388                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8389                     field = f;
8390                     return false;
8391                 }
8392                 return true;
8393             });
8394         }
8395         return field || null;
8396     },
8397      /**
8398      * Mark fields in this form invalid in bulk.
8399      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8400      * @return {BasicForm} this
8401      */
8402     markInvalid : function(errors){
8403         if(errors instanceof Array){
8404             for(var i = 0, len = errors.length; i < len; i++){
8405                 var fieldError = errors[i];
8406                 var f = this.findField(fieldError.id);
8407                 if(f){
8408                     f.markInvalid(fieldError.msg);
8409                 }
8410             }
8411         }else{
8412             var field, id;
8413             for(id in errors){
8414                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8415                     field.markInvalid(errors[id]);
8416                 }
8417             }
8418         }
8419         //Roo.each(this.childForms || [], function (f) {
8420         //    f.markInvalid(errors);
8421         //});
8422
8423         return this;
8424     },
8425
8426     /**
8427      * Set values for fields in this form in bulk.
8428      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8429      * @return {BasicForm} this
8430      */
8431     setValues : function(values){
8432         if(values instanceof Array){ // array of objects
8433             for(var i = 0, len = values.length; i < len; i++){
8434                 var v = values[i];
8435                 var f = this.findField(v.id);
8436                 if(f){
8437                     f.setValue(v.value);
8438                     if(this.trackResetOnLoad){
8439                         f.originalValue = f.getValue();
8440                     }
8441                 }
8442             }
8443         }else{ // object hash
8444             var field, id;
8445             for(id in values){
8446                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8447
8448                     if (field.setFromData &&
8449                         field.valueField &&
8450                         field.displayField &&
8451                         // combos' with local stores can
8452                         // be queried via setValue()
8453                         // to set their value..
8454                         (field.store && !field.store.isLocal)
8455                         ) {
8456                         // it's a combo
8457                         var sd = { };
8458                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8459                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8460                         field.setFromData(sd);
8461
8462                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8463                         
8464                         field.setFromData(values);
8465                         
8466                     } else {
8467                         field.setValue(values[id]);
8468                     }
8469
8470
8471                     if(this.trackResetOnLoad){
8472                         field.originalValue = field.getValue();
8473                     }
8474                 }
8475             }
8476         }
8477
8478         //Roo.each(this.childForms || [], function (f) {
8479         //    f.setValues(values);
8480         //});
8481
8482         return this;
8483     },
8484
8485     /**
8486      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8487      * they are returned as an array.
8488      * @param {Boolean} asString
8489      * @return {Object}
8490      */
8491     getValues : function(asString){
8492         //if (this.childForms) {
8493             // copy values from the child forms
8494         //    Roo.each(this.childForms, function (f) {
8495         //        this.setValues(f.getValues());
8496         //    }, this);
8497         //}
8498
8499
8500
8501         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8502         if(asString === true){
8503             return fs;
8504         }
8505         return Roo.urlDecode(fs);
8506     },
8507
8508     /**
8509      * Returns the fields in this form as an object with key/value pairs.
8510      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8511      * @return {Object}
8512      */
8513     getFieldValues : function(with_hidden)
8514     {
8515         var items = this.getItems();
8516         var ret = {};
8517         items.each(function(f){
8518             
8519             if (!f.getName()) {
8520                 return;
8521             }
8522             
8523             var v = f.getValue();
8524             
8525             if (f.inputType =='radio') {
8526                 if (typeof(ret[f.getName()]) == 'undefined') {
8527                     ret[f.getName()] = ''; // empty..
8528                 }
8529
8530                 if (!f.el.dom.checked) {
8531                     return;
8532
8533                 }
8534                 v = f.el.dom.value;
8535
8536             }
8537             
8538             if(f.xtype == 'MoneyField'){
8539                 ret[f.currencyName] = f.getCurrency();
8540             }
8541
8542             // not sure if this supported any more..
8543             if ((typeof(v) == 'object') && f.getRawValue) {
8544                 v = f.getRawValue() ; // dates..
8545             }
8546             // combo boxes where name != hiddenName...
8547             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8548                 ret[f.name] = f.getRawValue();
8549             }
8550             ret[f.getName()] = v;
8551         });
8552
8553         return ret;
8554     },
8555
8556     /**
8557      * Clears all invalid messages in this form.
8558      * @return {BasicForm} this
8559      */
8560     clearInvalid : function(){
8561         var items = this.getItems();
8562
8563         items.each(function(f){
8564            f.clearInvalid();
8565         });
8566
8567         return this;
8568     },
8569
8570     /**
8571      * Resets this form.
8572      * @return {BasicForm} this
8573      */
8574     reset : function(){
8575         var items = this.getItems();
8576         items.each(function(f){
8577             f.reset();
8578         });
8579
8580         Roo.each(this.childForms || [], function (f) {
8581             f.reset();
8582         });
8583
8584
8585         return this;
8586     },
8587     
8588     getItems : function()
8589     {
8590         var r=new Roo.util.MixedCollection(false, function(o){
8591             return o.id || (o.id = Roo.id());
8592         });
8593         var iter = function(el) {
8594             if (el.inputEl) {
8595                 r.add(el);
8596             }
8597             if (!el.items) {
8598                 return;
8599             }
8600             Roo.each(el.items,function(e) {
8601                 iter(e);
8602             });
8603         };
8604
8605         iter(this);
8606         return r;
8607     },
8608     
8609     hideFields : function(items)
8610     {
8611         Roo.each(items, function(i){
8612             
8613             var f = this.findField(i);
8614             
8615             if(!f){
8616                 return;
8617             }
8618             
8619             f.hide();
8620             
8621         }, this);
8622     },
8623     
8624     showFields : function(items)
8625     {
8626         Roo.each(items, function(i){
8627             
8628             var f = this.findField(i);
8629             
8630             if(!f){
8631                 return;
8632             }
8633             
8634             f.show();
8635             
8636         }, this);
8637     }
8638
8639 });
8640
8641 Roo.apply(Roo.bootstrap.Form, {
8642     
8643     popover : {
8644         
8645         padding : 5,
8646         
8647         isApplied : false,
8648         
8649         isMasked : false,
8650         
8651         form : false,
8652         
8653         target : false,
8654         
8655         toolTip : false,
8656         
8657         intervalID : false,
8658         
8659         maskEl : false,
8660         
8661         apply : function()
8662         {
8663             if(this.isApplied){
8664                 return;
8665             }
8666             
8667             this.maskEl = {
8668                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8669                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8670                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8671                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8672             };
8673             
8674             this.maskEl.top.enableDisplayMode("block");
8675             this.maskEl.left.enableDisplayMode("block");
8676             this.maskEl.bottom.enableDisplayMode("block");
8677             this.maskEl.right.enableDisplayMode("block");
8678             
8679             this.toolTip = new Roo.bootstrap.Tooltip({
8680                 cls : 'roo-form-error-popover',
8681                 alignment : {
8682                     'left' : ['r-l', [-2,0], 'right'],
8683                     'right' : ['l-r', [2,0], 'left'],
8684                     'bottom' : ['tl-bl', [0,2], 'top'],
8685                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8686                 }
8687             });
8688             
8689             this.toolTip.render(Roo.get(document.body));
8690
8691             this.toolTip.el.enableDisplayMode("block");
8692             
8693             Roo.get(document.body).on('click', function(){
8694                 this.unmask();
8695             }, this);
8696             
8697             Roo.get(document.body).on('touchstart', function(){
8698                 this.unmask();
8699             }, this);
8700             
8701             this.isApplied = true
8702         },
8703         
8704         mask : function(form, target)
8705         {
8706             this.form = form;
8707             
8708             this.target = target;
8709             
8710             if(!this.form.errorMask || !target.el){
8711                 return;
8712             }
8713             
8714             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8715             
8716             Roo.log(scrollable);
8717             
8718             var ot = this.target.el.calcOffsetsTo(scrollable);
8719             
8720             var scrollTo = ot[1] - this.form.maskOffset;
8721             
8722             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8723             
8724             scrollable.scrollTo('top', scrollTo);
8725             
8726             var box = this.target.el.getBox();
8727             Roo.log(box);
8728             var zIndex = Roo.bootstrap.Modal.zIndex++;
8729
8730             
8731             this.maskEl.top.setStyle('position', 'absolute');
8732             this.maskEl.top.setStyle('z-index', zIndex);
8733             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8734             this.maskEl.top.setLeft(0);
8735             this.maskEl.top.setTop(0);
8736             this.maskEl.top.show();
8737             
8738             this.maskEl.left.setStyle('position', 'absolute');
8739             this.maskEl.left.setStyle('z-index', zIndex);
8740             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8741             this.maskEl.left.setLeft(0);
8742             this.maskEl.left.setTop(box.y - this.padding);
8743             this.maskEl.left.show();
8744
8745             this.maskEl.bottom.setStyle('position', 'absolute');
8746             this.maskEl.bottom.setStyle('z-index', zIndex);
8747             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8748             this.maskEl.bottom.setLeft(0);
8749             this.maskEl.bottom.setTop(box.bottom + this.padding);
8750             this.maskEl.bottom.show();
8751
8752             this.maskEl.right.setStyle('position', 'absolute');
8753             this.maskEl.right.setStyle('z-index', zIndex);
8754             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8755             this.maskEl.right.setLeft(box.right + this.padding);
8756             this.maskEl.right.setTop(box.y - this.padding);
8757             this.maskEl.right.show();
8758
8759             this.toolTip.bindEl = this.target.el;
8760
8761             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8762
8763             var tip = this.target.blankText;
8764
8765             if(this.target.getValue() !== '' ) {
8766                 
8767                 if (this.target.invalidText.length) {
8768                     tip = this.target.invalidText;
8769                 } else if (this.target.regexText.length){
8770                     tip = this.target.regexText;
8771                 }
8772             }
8773
8774             this.toolTip.show(tip);
8775
8776             this.intervalID = window.setInterval(function() {
8777                 Roo.bootstrap.Form.popover.unmask();
8778             }, 10000);
8779
8780             window.onwheel = function(){ return false;};
8781             
8782             (function(){ this.isMasked = true; }).defer(500, this);
8783             
8784         },
8785         
8786         unmask : function()
8787         {
8788             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8789                 return;
8790             }
8791             
8792             this.maskEl.top.setStyle('position', 'absolute');
8793             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8794             this.maskEl.top.hide();
8795
8796             this.maskEl.left.setStyle('position', 'absolute');
8797             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8798             this.maskEl.left.hide();
8799
8800             this.maskEl.bottom.setStyle('position', 'absolute');
8801             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8802             this.maskEl.bottom.hide();
8803
8804             this.maskEl.right.setStyle('position', 'absolute');
8805             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8806             this.maskEl.right.hide();
8807             
8808             this.toolTip.hide();
8809             
8810             this.toolTip.el.hide();
8811             
8812             window.onwheel = function(){ return true;};
8813             
8814             if(this.intervalID){
8815                 window.clearInterval(this.intervalID);
8816                 this.intervalID = false;
8817             }
8818             
8819             this.isMasked = false;
8820             
8821         }
8822         
8823     }
8824     
8825 });
8826
8827 /*
8828  * Based on:
8829  * Ext JS Library 1.1.1
8830  * Copyright(c) 2006-2007, Ext JS, LLC.
8831  *
8832  * Originally Released Under LGPL - original licence link has changed is not relivant.
8833  *
8834  * Fork - LGPL
8835  * <script type="text/javascript">
8836  */
8837 /**
8838  * @class Roo.form.VTypes
8839  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8840  * @singleton
8841  */
8842 Roo.form.VTypes = function(){
8843     // closure these in so they are only created once.
8844     var alpha = /^[a-zA-Z_]+$/;
8845     var alphanum = /^[a-zA-Z0-9_]+$/;
8846     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8847     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8848
8849     // All these messages and functions are configurable
8850     return {
8851         /**
8852          * The function used to validate email addresses
8853          * @param {String} value The email address
8854          */
8855         'email' : function(v){
8856             return email.test(v);
8857         },
8858         /**
8859          * The error text to display when the email validation function returns false
8860          * @type String
8861          */
8862         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8863         /**
8864          * The keystroke filter mask to be applied on email input
8865          * @type RegExp
8866          */
8867         'emailMask' : /[a-z0-9_\.\-@]/i,
8868
8869         /**
8870          * The function used to validate URLs
8871          * @param {String} value The URL
8872          */
8873         'url' : function(v){
8874             return url.test(v);
8875         },
8876         /**
8877          * The error text to display when the url validation function returns false
8878          * @type String
8879          */
8880         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8881         
8882         /**
8883          * The function used to validate alpha values
8884          * @param {String} value The value
8885          */
8886         'alpha' : function(v){
8887             return alpha.test(v);
8888         },
8889         /**
8890          * The error text to display when the alpha validation function returns false
8891          * @type String
8892          */
8893         'alphaText' : 'This field should only contain letters and _',
8894         /**
8895          * The keystroke filter mask to be applied on alpha input
8896          * @type RegExp
8897          */
8898         'alphaMask' : /[a-z_]/i,
8899
8900         /**
8901          * The function used to validate alphanumeric values
8902          * @param {String} value The value
8903          */
8904         'alphanum' : function(v){
8905             return alphanum.test(v);
8906         },
8907         /**
8908          * The error text to display when the alphanumeric validation function returns false
8909          * @type String
8910          */
8911         'alphanumText' : 'This field should only contain letters, numbers and _',
8912         /**
8913          * The keystroke filter mask to be applied on alphanumeric input
8914          * @type RegExp
8915          */
8916         'alphanumMask' : /[a-z0-9_]/i
8917     };
8918 }();/*
8919  * - LGPL
8920  *
8921  * Input
8922  * 
8923  */
8924
8925 /**
8926  * @class Roo.bootstrap.Input
8927  * @extends Roo.bootstrap.Component
8928  * Bootstrap Input class
8929  * @cfg {Boolean} disabled is it disabled
8930  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8931  * @cfg {String} name name of the input
8932  * @cfg {string} fieldLabel - the label associated
8933  * @cfg {string} placeholder - placeholder to put in text.
8934  * @cfg {string}  before - input group add on before
8935  * @cfg {string} after - input group add on after
8936  * @cfg {string} size - (lg|sm) or leave empty..
8937  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8938  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8939  * @cfg {Number} md colspan out of 12 for computer-sized screens
8940  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8941  * @cfg {string} value default value of the input
8942  * @cfg {Number} labelWidth set the width of label 
8943  * @cfg {Number} labellg set the width of label (1-12)
8944  * @cfg {Number} labelmd set the width of label (1-12)
8945  * @cfg {Number} labelsm set the width of label (1-12)
8946  * @cfg {Number} labelxs set the width of label (1-12)
8947  * @cfg {String} labelAlign (top|left)
8948  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8949  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8950  * @cfg {String} indicatorpos (left|right) default left
8951  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8952  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8953
8954  * @cfg {String} align (left|center|right) Default left
8955  * @cfg {Boolean} forceFeedback (true|false) Default false
8956  * 
8957  * @constructor
8958  * Create a new Input
8959  * @param {Object} config The config object
8960  */
8961
8962 Roo.bootstrap.Input = function(config){
8963     
8964     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8965     
8966     this.addEvents({
8967         /**
8968          * @event focus
8969          * Fires when this field receives input focus.
8970          * @param {Roo.form.Field} this
8971          */
8972         focus : true,
8973         /**
8974          * @event blur
8975          * Fires when this field loses input focus.
8976          * @param {Roo.form.Field} this
8977          */
8978         blur : true,
8979         /**
8980          * @event specialkey
8981          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8982          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8983          * @param {Roo.form.Field} this
8984          * @param {Roo.EventObject} e The event object
8985          */
8986         specialkey : true,
8987         /**
8988          * @event change
8989          * Fires just before the field blurs if the field value has changed.
8990          * @param {Roo.form.Field} this
8991          * @param {Mixed} newValue The new value
8992          * @param {Mixed} oldValue The original value
8993          */
8994         change : true,
8995         /**
8996          * @event invalid
8997          * Fires after the field has been marked as invalid.
8998          * @param {Roo.form.Field} this
8999          * @param {String} msg The validation message
9000          */
9001         invalid : true,
9002         /**
9003          * @event valid
9004          * Fires after the field has been validated with no errors.
9005          * @param {Roo.form.Field} this
9006          */
9007         valid : true,
9008          /**
9009          * @event keyup
9010          * Fires after the key up
9011          * @param {Roo.form.Field} this
9012          * @param {Roo.EventObject}  e The event Object
9013          */
9014         keyup : true
9015     });
9016 };
9017
9018 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9019      /**
9020      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9021       automatic validation (defaults to "keyup").
9022      */
9023     validationEvent : "keyup",
9024      /**
9025      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9026      */
9027     validateOnBlur : true,
9028     /**
9029      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9030      */
9031     validationDelay : 250,
9032      /**
9033      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9034      */
9035     focusClass : "x-form-focus",  // not needed???
9036     
9037        
9038     /**
9039      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9040      */
9041     invalidClass : "has-warning",
9042     
9043     /**
9044      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9045      */
9046     validClass : "has-success",
9047     
9048     /**
9049      * @cfg {Boolean} hasFeedback (true|false) default true
9050      */
9051     hasFeedback : true,
9052     
9053     /**
9054      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9055      */
9056     invalidFeedbackClass : "glyphicon-warning-sign",
9057     
9058     /**
9059      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9060      */
9061     validFeedbackClass : "glyphicon-ok",
9062     
9063     /**
9064      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9065      */
9066     selectOnFocus : false,
9067     
9068      /**
9069      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9070      */
9071     maskRe : null,
9072        /**
9073      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9074      */
9075     vtype : null,
9076     
9077       /**
9078      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9079      */
9080     disableKeyFilter : false,
9081     
9082        /**
9083      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9084      */
9085     disabled : false,
9086      /**
9087      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9088      */
9089     allowBlank : true,
9090     /**
9091      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9092      */
9093     blankText : "Please complete this mandatory field",
9094     
9095      /**
9096      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9097      */
9098     minLength : 0,
9099     /**
9100      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9101      */
9102     maxLength : Number.MAX_VALUE,
9103     /**
9104      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9105      */
9106     minLengthText : "The minimum length for this field is {0}",
9107     /**
9108      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9109      */
9110     maxLengthText : "The maximum length for this field is {0}",
9111   
9112     
9113     /**
9114      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9115      * If available, this function will be called only after the basic validators all return true, and will be passed the
9116      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9117      */
9118     validator : null,
9119     /**
9120      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9121      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9122      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9123      */
9124     regex : null,
9125     /**
9126      * @cfg {String} regexText -- Depricated - use Invalid Text
9127      */
9128     regexText : "",
9129     
9130     /**
9131      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9132      */
9133     invalidText : "",
9134     
9135     
9136     
9137     autocomplete: false,
9138     
9139     
9140     fieldLabel : '',
9141     inputType : 'text',
9142     
9143     name : false,
9144     placeholder: false,
9145     before : false,
9146     after : false,
9147     size : false,
9148     hasFocus : false,
9149     preventMark: false,
9150     isFormField : true,
9151     value : '',
9152     labelWidth : 2,
9153     labelAlign : false,
9154     readOnly : false,
9155     align : false,
9156     formatedValue : false,
9157     forceFeedback : false,
9158     
9159     indicatorpos : 'left',
9160     
9161     labellg : 0,
9162     labelmd : 0,
9163     labelsm : 0,
9164     labelxs : 0,
9165     
9166     capture : '',
9167     accept : '',
9168     
9169     parentLabelAlign : function()
9170     {
9171         var parent = this;
9172         while (parent.parent()) {
9173             parent = parent.parent();
9174             if (typeof(parent.labelAlign) !='undefined') {
9175                 return parent.labelAlign;
9176             }
9177         }
9178         return 'left';
9179         
9180     },
9181     
9182     getAutoCreate : function()
9183     {
9184         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9185         
9186         var id = Roo.id();
9187         
9188         var cfg = {};
9189         
9190         if(this.inputType != 'hidden'){
9191             cfg.cls = 'form-group' //input-group
9192         }
9193         
9194         var input =  {
9195             tag: 'input',
9196             id : id,
9197             type : this.inputType,
9198             value : this.value,
9199             cls : 'form-control',
9200             placeholder : this.placeholder || '',
9201             autocomplete : this.autocomplete || 'new-password'
9202         };
9203         
9204         if(this.capture.length){
9205             input.capture = this.capture;
9206         }
9207         
9208         if(this.accept.length){
9209             input.accept = this.accept + "/*";
9210         }
9211         
9212         if(this.align){
9213             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9214         }
9215         
9216         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9217             input.maxLength = this.maxLength;
9218         }
9219         
9220         if (this.disabled) {
9221             input.disabled=true;
9222         }
9223         
9224         if (this.readOnly) {
9225             input.readonly=true;
9226         }
9227         
9228         if (this.name) {
9229             input.name = this.name;
9230         }
9231         
9232         if (this.size) {
9233             input.cls += ' input-' + this.size;
9234         }
9235         
9236         var settings=this;
9237         ['xs','sm','md','lg'].map(function(size){
9238             if (settings[size]) {
9239                 cfg.cls += ' col-' + size + '-' + settings[size];
9240             }
9241         });
9242         
9243         var inputblock = input;
9244         
9245         var feedback = {
9246             tag: 'span',
9247             cls: 'glyphicon form-control-feedback'
9248         };
9249             
9250         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9251             
9252             inputblock = {
9253                 cls : 'has-feedback',
9254                 cn :  [
9255                     input,
9256                     feedback
9257                 ] 
9258             };  
9259         }
9260         
9261         if (this.before || this.after) {
9262             
9263             inputblock = {
9264                 cls : 'input-group',
9265                 cn :  [] 
9266             };
9267             
9268             if (this.before && typeof(this.before) == 'string') {
9269                 
9270                 inputblock.cn.push({
9271                     tag :'span',
9272                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9273                     html : this.before
9274                 });
9275             }
9276             if (this.before && typeof(this.before) == 'object') {
9277                 this.before = Roo.factory(this.before);
9278                 
9279                 inputblock.cn.push({
9280                     tag :'span',
9281                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9282                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9283                 });
9284             }
9285             
9286             inputblock.cn.push(input);
9287             
9288             if (this.after && typeof(this.after) == 'string') {
9289                 inputblock.cn.push({
9290                     tag :'span',
9291                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9292                     html : this.after
9293                 });
9294             }
9295             if (this.after && typeof(this.after) == 'object') {
9296                 this.after = Roo.factory(this.after);
9297                 
9298                 inputblock.cn.push({
9299                     tag :'span',
9300                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9301                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9302                 });
9303             }
9304             
9305             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9306                 inputblock.cls += ' has-feedback';
9307                 inputblock.cn.push(feedback);
9308             }
9309         };
9310         var indicator = {
9311             tag : 'i',
9312             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9313             tooltip : 'This field is required'
9314         };
9315         if (Roo.bootstrap.version == 4) {
9316             indicator = {
9317                 tag : 'i',
9318                 style : 'display-none'
9319             };
9320         }
9321         if (align ==='left' && this.fieldLabel.length) {
9322             
9323             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9324             
9325             cfg.cn = [
9326                 indicator,
9327                 {
9328                     tag: 'label',
9329                     'for' :  id,
9330                     cls : 'control-label col-form-label',
9331                     html : this.fieldLabel
9332
9333                 },
9334                 {
9335                     cls : "", 
9336                     cn: [
9337                         inputblock
9338                     ]
9339                 }
9340             ];
9341             
9342             var labelCfg = cfg.cn[1];
9343             var contentCfg = cfg.cn[2];
9344             
9345             if(this.indicatorpos == 'right'){
9346                 cfg.cn = [
9347                     {
9348                         tag: 'label',
9349                         'for' :  id,
9350                         cls : 'control-label col-form-label',
9351                         cn : [
9352                             {
9353                                 tag : 'span',
9354                                 html : this.fieldLabel
9355                             },
9356                             indicator
9357                         ]
9358                     },
9359                     {
9360                         cls : "",
9361                         cn: [
9362                             inputblock
9363                         ]
9364                     }
9365
9366                 ];
9367                 
9368                 labelCfg = cfg.cn[0];
9369                 contentCfg = cfg.cn[1];
9370             
9371             }
9372             
9373             if(this.labelWidth > 12){
9374                 labelCfg.style = "width: " + this.labelWidth + 'px';
9375             }
9376             
9377             if(this.labelWidth < 13 && this.labelmd == 0){
9378                 this.labelmd = this.labelWidth;
9379             }
9380             
9381             if(this.labellg > 0){
9382                 labelCfg.cls += ' col-lg-' + this.labellg;
9383                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9384             }
9385             
9386             if(this.labelmd > 0){
9387                 labelCfg.cls += ' col-md-' + this.labelmd;
9388                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9389             }
9390             
9391             if(this.labelsm > 0){
9392                 labelCfg.cls += ' col-sm-' + this.labelsm;
9393                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9394             }
9395             
9396             if(this.labelxs > 0){
9397                 labelCfg.cls += ' col-xs-' + this.labelxs;
9398                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9399             }
9400             
9401             
9402         } else if ( this.fieldLabel.length) {
9403                 
9404             cfg.cn = [
9405                 {
9406                     tag : 'i',
9407                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9408                     tooltip : 'This field is required'
9409                 },
9410                 {
9411                     tag: 'label',
9412                    //cls : 'input-group-addon',
9413                     html : this.fieldLabel
9414
9415                 },
9416
9417                inputblock
9418
9419            ];
9420            
9421            if(this.indicatorpos == 'right'){
9422                 
9423                 cfg.cn = [
9424                     {
9425                         tag: 'label',
9426                        //cls : 'input-group-addon',
9427                         html : this.fieldLabel
9428
9429                     },
9430                     {
9431                         tag : 'i',
9432                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9433                         tooltip : 'This field is required'
9434                     },
9435
9436                    inputblock
9437
9438                ];
9439
9440             }
9441
9442         } else {
9443             
9444             cfg.cn = [
9445
9446                     inputblock
9447
9448             ];
9449                 
9450                 
9451         };
9452         
9453         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9454            cfg.cls += ' navbar-form';
9455         }
9456         
9457         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9458             // on BS4 we do this only if not form 
9459             cfg.cls += ' navbar-form';
9460             cfg.tag = 'li';
9461         }
9462         
9463         return cfg;
9464         
9465     },
9466     /**
9467      * return the real input element.
9468      */
9469     inputEl: function ()
9470     {
9471         return this.el.select('input.form-control',true).first();
9472     },
9473     
9474     tooltipEl : function()
9475     {
9476         return this.inputEl();
9477     },
9478     
9479     indicatorEl : function()
9480     {
9481         if (Roo.bootstrap.version == 4) {
9482             return false; // not enabled in v4 yet.
9483         }
9484         
9485         var indicator = this.el.select('i.roo-required-indicator',true).first();
9486         
9487         if(!indicator){
9488             return false;
9489         }
9490         
9491         return indicator;
9492         
9493     },
9494     
9495     setDisabled : function(v)
9496     {
9497         var i  = this.inputEl().dom;
9498         if (!v) {
9499             i.removeAttribute('disabled');
9500             return;
9501             
9502         }
9503         i.setAttribute('disabled','true');
9504     },
9505     initEvents : function()
9506     {
9507           
9508         this.inputEl().on("keydown" , this.fireKey,  this);
9509         this.inputEl().on("focus", this.onFocus,  this);
9510         this.inputEl().on("blur", this.onBlur,  this);
9511         
9512         this.inputEl().relayEvent('keyup', this);
9513         
9514         this.indicator = this.indicatorEl();
9515         
9516         if(this.indicator){
9517             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9518         }
9519  
9520         // reference to original value for reset
9521         this.originalValue = this.getValue();
9522         //Roo.form.TextField.superclass.initEvents.call(this);
9523         if(this.validationEvent == 'keyup'){
9524             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9525             this.inputEl().on('keyup', this.filterValidation, this);
9526         }
9527         else if(this.validationEvent !== false){
9528             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9529         }
9530         
9531         if(this.selectOnFocus){
9532             this.on("focus", this.preFocus, this);
9533             
9534         }
9535         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9536             this.inputEl().on("keypress", this.filterKeys, this);
9537         } else {
9538             this.inputEl().relayEvent('keypress', this);
9539         }
9540        /* if(this.grow){
9541             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9542             this.el.on("click", this.autoSize,  this);
9543         }
9544         */
9545         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9546             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9547         }
9548         
9549         if (typeof(this.before) == 'object') {
9550             this.before.render(this.el.select('.roo-input-before',true).first());
9551         }
9552         if (typeof(this.after) == 'object') {
9553             this.after.render(this.el.select('.roo-input-after',true).first());
9554         }
9555         
9556         this.inputEl().on('change', this.onChange, this);
9557         
9558     },
9559     filterValidation : function(e){
9560         if(!e.isNavKeyPress()){
9561             this.validationTask.delay(this.validationDelay);
9562         }
9563     },
9564      /**
9565      * Validates the field value
9566      * @return {Boolean} True if the value is valid, else false
9567      */
9568     validate : function(){
9569         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9570         if(this.disabled || this.validateValue(this.getRawValue())){
9571             this.markValid();
9572             return true;
9573         }
9574         
9575         this.markInvalid();
9576         return false;
9577     },
9578     
9579     
9580     /**
9581      * Validates a value according to the field's validation rules and marks the field as invalid
9582      * if the validation fails
9583      * @param {Mixed} value The value to validate
9584      * @return {Boolean} True if the value is valid, else false
9585      */
9586     validateValue : function(value)
9587     {
9588         if(this.getVisibilityEl().hasClass('hidden')){
9589             return true;
9590         }
9591         
9592         if(value.length < 1)  { // if it's blank
9593             if(this.allowBlank){
9594                 return true;
9595             }
9596             return false;
9597         }
9598         
9599         if(value.length < this.minLength){
9600             return false;
9601         }
9602         if(value.length > this.maxLength){
9603             return false;
9604         }
9605         if(this.vtype){
9606             var vt = Roo.form.VTypes;
9607             if(!vt[this.vtype](value, this)){
9608                 return false;
9609             }
9610         }
9611         if(typeof this.validator == "function"){
9612             var msg = this.validator(value);
9613             if(msg !== true){
9614                 return false;
9615             }
9616             if (typeof(msg) == 'string') {
9617                 this.invalidText = msg;
9618             }
9619         }
9620         
9621         if(this.regex && !this.regex.test(value)){
9622             return false;
9623         }
9624         
9625         return true;
9626     },
9627     
9628      // private
9629     fireKey : function(e){
9630         //Roo.log('field ' + e.getKey());
9631         if(e.isNavKeyPress()){
9632             this.fireEvent("specialkey", this, e);
9633         }
9634     },
9635     focus : function (selectText){
9636         if(this.rendered){
9637             this.inputEl().focus();
9638             if(selectText === true){
9639                 this.inputEl().dom.select();
9640             }
9641         }
9642         return this;
9643     } ,
9644     
9645     onFocus : function(){
9646         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9647            // this.el.addClass(this.focusClass);
9648         }
9649         if(!this.hasFocus){
9650             this.hasFocus = true;
9651             this.startValue = this.getValue();
9652             this.fireEvent("focus", this);
9653         }
9654     },
9655     
9656     beforeBlur : Roo.emptyFn,
9657
9658     
9659     // private
9660     onBlur : function(){
9661         this.beforeBlur();
9662         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9663             //this.el.removeClass(this.focusClass);
9664         }
9665         this.hasFocus = false;
9666         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9667             this.validate();
9668         }
9669         var v = this.getValue();
9670         if(String(v) !== String(this.startValue)){
9671             this.fireEvent('change', this, v, this.startValue);
9672         }
9673         this.fireEvent("blur", this);
9674     },
9675     
9676     onChange : function(e)
9677     {
9678         var v = this.getValue();
9679         if(String(v) !== String(this.startValue)){
9680             this.fireEvent('change', this, v, this.startValue);
9681         }
9682         
9683     },
9684     
9685     /**
9686      * Resets the current field value to the originally loaded value and clears any validation messages
9687      */
9688     reset : function(){
9689         this.setValue(this.originalValue);
9690         this.validate();
9691     },
9692      /**
9693      * Returns the name of the field
9694      * @return {Mixed} name The name field
9695      */
9696     getName: function(){
9697         return this.name;
9698     },
9699      /**
9700      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9701      * @return {Mixed} value The field value
9702      */
9703     getValue : function(){
9704         
9705         var v = this.inputEl().getValue();
9706         
9707         return v;
9708     },
9709     /**
9710      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9711      * @return {Mixed} value The field value
9712      */
9713     getRawValue : function(){
9714         var v = this.inputEl().getValue();
9715         
9716         return v;
9717     },
9718     
9719     /**
9720      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9721      * @param {Mixed} value The value to set
9722      */
9723     setRawValue : function(v){
9724         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9725     },
9726     
9727     selectText : function(start, end){
9728         var v = this.getRawValue();
9729         if(v.length > 0){
9730             start = start === undefined ? 0 : start;
9731             end = end === undefined ? v.length : end;
9732             var d = this.inputEl().dom;
9733             if(d.setSelectionRange){
9734                 d.setSelectionRange(start, end);
9735             }else if(d.createTextRange){
9736                 var range = d.createTextRange();
9737                 range.moveStart("character", start);
9738                 range.moveEnd("character", v.length-end);
9739                 range.select();
9740             }
9741         }
9742     },
9743     
9744     /**
9745      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9746      * @param {Mixed} value The value to set
9747      */
9748     setValue : function(v){
9749         this.value = v;
9750         if(this.rendered){
9751             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9752             this.validate();
9753         }
9754     },
9755     
9756     /*
9757     processValue : function(value){
9758         if(this.stripCharsRe){
9759             var newValue = value.replace(this.stripCharsRe, '');
9760             if(newValue !== value){
9761                 this.setRawValue(newValue);
9762                 return newValue;
9763             }
9764         }
9765         return value;
9766     },
9767   */
9768     preFocus : function(){
9769         
9770         if(this.selectOnFocus){
9771             this.inputEl().dom.select();
9772         }
9773     },
9774     filterKeys : function(e){
9775         var k = e.getKey();
9776         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9777             return;
9778         }
9779         var c = e.getCharCode(), cc = String.fromCharCode(c);
9780         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9781             return;
9782         }
9783         if(!this.maskRe.test(cc)){
9784             e.stopEvent();
9785         }
9786     },
9787      /**
9788      * Clear any invalid styles/messages for this field
9789      */
9790     clearInvalid : function(){
9791         
9792         if(!this.el || this.preventMark){ // not rendered
9793             return;
9794         }
9795         
9796         
9797         this.el.removeClass([this.invalidClass, 'is-invalid']);
9798         
9799         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9800             
9801             var feedback = this.el.select('.form-control-feedback', true).first();
9802             
9803             if(feedback){
9804                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9805             }
9806             
9807         }
9808         
9809         if(this.indicator){
9810             this.indicator.removeClass('visible');
9811             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9812         }
9813         
9814         this.fireEvent('valid', this);
9815     },
9816     
9817      /**
9818      * Mark this field as valid
9819      */
9820     markValid : function()
9821     {
9822         if(!this.el  || this.preventMark){ // not rendered...
9823             return;
9824         }
9825         
9826         this.el.removeClass([this.invalidClass, this.validClass]);
9827         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9828
9829         var feedback = this.el.select('.form-control-feedback', true).first();
9830             
9831         if(feedback){
9832             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9833         }
9834         
9835         if(this.indicator){
9836             this.indicator.removeClass('visible');
9837             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9838         }
9839         
9840         if(this.disabled){
9841             return;
9842         }
9843         
9844         if(this.allowBlank && !this.getRawValue().length){
9845             return;
9846         }
9847         if (Roo.bootstrap.version == 3) {
9848             this.el.addClass(this.validClass);
9849         } else {
9850             this.inputEl().addClass('is-valid');
9851         }
9852
9853         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9854             
9855             var feedback = this.el.select('.form-control-feedback', true).first();
9856             
9857             if(feedback){
9858                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9859                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9860             }
9861             
9862         }
9863         
9864         this.fireEvent('valid', this);
9865     },
9866     
9867      /**
9868      * Mark this field as invalid
9869      * @param {String} msg The validation message
9870      */
9871     markInvalid : function(msg)
9872     {
9873         if(!this.el  || this.preventMark){ // not rendered
9874             return;
9875         }
9876         
9877         this.el.removeClass([this.invalidClass, this.validClass]);
9878         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9879         
9880         var feedback = this.el.select('.form-control-feedback', true).first();
9881             
9882         if(feedback){
9883             this.el.select('.form-control-feedback', true).first().removeClass(
9884                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9885         }
9886
9887         if(this.disabled){
9888             return;
9889         }
9890         
9891         if(this.allowBlank && !this.getRawValue().length){
9892             return;
9893         }
9894         
9895         if(this.indicator){
9896             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9897             this.indicator.addClass('visible');
9898         }
9899         if (Roo.bootstrap.version == 3) {
9900             this.el.addClass(this.invalidClass);
9901         } else {
9902             this.inputEl().addClass('is-invalid');
9903         }
9904         
9905         
9906         
9907         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9908             
9909             var feedback = this.el.select('.form-control-feedback', true).first();
9910             
9911             if(feedback){
9912                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9913                 
9914                 if(this.getValue().length || this.forceFeedback){
9915                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9916                 }
9917                 
9918             }
9919             
9920         }
9921         
9922         this.fireEvent('invalid', this, msg);
9923     },
9924     // private
9925     SafariOnKeyDown : function(event)
9926     {
9927         // this is a workaround for a password hang bug on chrome/ webkit.
9928         if (this.inputEl().dom.type != 'password') {
9929             return;
9930         }
9931         
9932         var isSelectAll = false;
9933         
9934         if(this.inputEl().dom.selectionEnd > 0){
9935             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9936         }
9937         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9938             event.preventDefault();
9939             this.setValue('');
9940             return;
9941         }
9942         
9943         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9944             
9945             event.preventDefault();
9946             // this is very hacky as keydown always get's upper case.
9947             //
9948             var cc = String.fromCharCode(event.getCharCode());
9949             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9950             
9951         }
9952     },
9953     adjustWidth : function(tag, w){
9954         tag = tag.toLowerCase();
9955         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9956             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9957                 if(tag == 'input'){
9958                     return w + 2;
9959                 }
9960                 if(tag == 'textarea'){
9961                     return w-2;
9962                 }
9963             }else if(Roo.isOpera){
9964                 if(tag == 'input'){
9965                     return w + 2;
9966                 }
9967                 if(tag == 'textarea'){
9968                     return w-2;
9969                 }
9970             }
9971         }
9972         return w;
9973     },
9974     
9975     setFieldLabel : function(v)
9976     {
9977         if(!this.rendered){
9978             return;
9979         }
9980         
9981         if(this.indicatorEl()){
9982             var ar = this.el.select('label > span',true);
9983             
9984             if (ar.elements.length) {
9985                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9986                 this.fieldLabel = v;
9987                 return;
9988             }
9989             
9990             var br = this.el.select('label',true);
9991             
9992             if(br.elements.length) {
9993                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9994                 this.fieldLabel = v;
9995                 return;
9996             }
9997             
9998             Roo.log('Cannot Found any of label > span || label in input');
9999             return;
10000         }
10001         
10002         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10003         this.fieldLabel = v;
10004         
10005         
10006     }
10007 });
10008
10009  
10010 /*
10011  * - LGPL
10012  *
10013  * Input
10014  * 
10015  */
10016
10017 /**
10018  * @class Roo.bootstrap.TextArea
10019  * @extends Roo.bootstrap.Input
10020  * Bootstrap TextArea class
10021  * @cfg {Number} cols Specifies the visible width of a text area
10022  * @cfg {Number} rows Specifies the visible number of lines in a text area
10023  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10024  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10025  * @cfg {string} html text
10026  * 
10027  * @constructor
10028  * Create a new TextArea
10029  * @param {Object} config The config object
10030  */
10031
10032 Roo.bootstrap.TextArea = function(config){
10033     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10034    
10035 };
10036
10037 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10038      
10039     cols : false,
10040     rows : 5,
10041     readOnly : false,
10042     warp : 'soft',
10043     resize : false,
10044     value: false,
10045     html: false,
10046     
10047     getAutoCreate : function(){
10048         
10049         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10050         
10051         var id = Roo.id();
10052         
10053         var cfg = {};
10054         
10055         if(this.inputType != 'hidden'){
10056             cfg.cls = 'form-group' //input-group
10057         }
10058         
10059         var input =  {
10060             tag: 'textarea',
10061             id : id,
10062             warp : this.warp,
10063             rows : this.rows,
10064             value : this.value || '',
10065             html: this.html || '',
10066             cls : 'form-control',
10067             placeholder : this.placeholder || '' 
10068             
10069         };
10070         
10071         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10072             input.maxLength = this.maxLength;
10073         }
10074         
10075         if(this.resize){
10076             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10077         }
10078         
10079         if(this.cols){
10080             input.cols = this.cols;
10081         }
10082         
10083         if (this.readOnly) {
10084             input.readonly = true;
10085         }
10086         
10087         if (this.name) {
10088             input.name = this.name;
10089         }
10090         
10091         if (this.size) {
10092             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10093         }
10094         
10095         var settings=this;
10096         ['xs','sm','md','lg'].map(function(size){
10097             if (settings[size]) {
10098                 cfg.cls += ' col-' + size + '-' + settings[size];
10099             }
10100         });
10101         
10102         var inputblock = input;
10103         
10104         if(this.hasFeedback && !this.allowBlank){
10105             
10106             var feedback = {
10107                 tag: 'span',
10108                 cls: 'glyphicon form-control-feedback'
10109             };
10110
10111             inputblock = {
10112                 cls : 'has-feedback',
10113                 cn :  [
10114                     input,
10115                     feedback
10116                 ] 
10117             };  
10118         }
10119         
10120         
10121         if (this.before || this.after) {
10122             
10123             inputblock = {
10124                 cls : 'input-group',
10125                 cn :  [] 
10126             };
10127             if (this.before) {
10128                 inputblock.cn.push({
10129                     tag :'span',
10130                     cls : 'input-group-addon',
10131                     html : this.before
10132                 });
10133             }
10134             
10135             inputblock.cn.push(input);
10136             
10137             if(this.hasFeedback && !this.allowBlank){
10138                 inputblock.cls += ' has-feedback';
10139                 inputblock.cn.push(feedback);
10140             }
10141             
10142             if (this.after) {
10143                 inputblock.cn.push({
10144                     tag :'span',
10145                     cls : 'input-group-addon',
10146                     html : this.after
10147                 });
10148             }
10149             
10150         }
10151         
10152         if (align ==='left' && this.fieldLabel.length) {
10153             cfg.cn = [
10154                 {
10155                     tag: 'label',
10156                     'for' :  id,
10157                     cls : 'control-label',
10158                     html : this.fieldLabel
10159                 },
10160                 {
10161                     cls : "",
10162                     cn: [
10163                         inputblock
10164                     ]
10165                 }
10166
10167             ];
10168             
10169             if(this.labelWidth > 12){
10170                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10171             }
10172
10173             if(this.labelWidth < 13 && this.labelmd == 0){
10174                 this.labelmd = this.labelWidth;
10175             }
10176
10177             if(this.labellg > 0){
10178                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10179                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10180             }
10181
10182             if(this.labelmd > 0){
10183                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10184                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10185             }
10186
10187             if(this.labelsm > 0){
10188                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10189                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10190             }
10191
10192             if(this.labelxs > 0){
10193                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10194                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10195             }
10196             
10197         } else if ( this.fieldLabel.length) {
10198             cfg.cn = [
10199
10200                {
10201                    tag: 'label',
10202                    //cls : 'input-group-addon',
10203                    html : this.fieldLabel
10204
10205                },
10206
10207                inputblock
10208
10209            ];
10210
10211         } else {
10212
10213             cfg.cn = [
10214
10215                 inputblock
10216
10217             ];
10218                 
10219         }
10220         
10221         if (this.disabled) {
10222             input.disabled=true;
10223         }
10224         
10225         return cfg;
10226         
10227     },
10228     /**
10229      * return the real textarea element.
10230      */
10231     inputEl: function ()
10232     {
10233         return this.el.select('textarea.form-control',true).first();
10234     },
10235     
10236     /**
10237      * Clear any invalid styles/messages for this field
10238      */
10239     clearInvalid : function()
10240     {
10241         
10242         if(!this.el || this.preventMark){ // not rendered
10243             return;
10244         }
10245         
10246         var label = this.el.select('label', true).first();
10247         var icon = this.el.select('i.fa-star', true).first();
10248         
10249         if(label && icon){
10250             icon.remove();
10251         }
10252         this.el.removeClass( this.validClass);
10253         this.inputEl().removeClass('is-invalid');
10254          
10255         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10256             
10257             var feedback = this.el.select('.form-control-feedback', true).first();
10258             
10259             if(feedback){
10260                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10261             }
10262             
10263         }
10264         
10265         this.fireEvent('valid', this);
10266     },
10267     
10268      /**
10269      * Mark this field as valid
10270      */
10271     markValid : function()
10272     {
10273         if(!this.el  || this.preventMark){ // not rendered
10274             return;
10275         }
10276         
10277         this.el.removeClass([this.invalidClass, this.validClass]);
10278         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10279         
10280         var feedback = this.el.select('.form-control-feedback', true).first();
10281             
10282         if(feedback){
10283             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10284         }
10285
10286         if(this.disabled || this.allowBlank){
10287             return;
10288         }
10289         
10290         var label = this.el.select('label', true).first();
10291         var icon = this.el.select('i.fa-star', true).first();
10292         
10293         if(label && icon){
10294             icon.remove();
10295         }
10296         if (Roo.bootstrap.version == 3) {
10297             this.el.addClass(this.validClass);
10298         } else {
10299             this.inputEl().addClass('is-valid');
10300         }
10301         
10302         
10303         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10304             
10305             var feedback = this.el.select('.form-control-feedback', true).first();
10306             
10307             if(feedback){
10308                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10309                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10310             }
10311             
10312         }
10313         
10314         this.fireEvent('valid', this);
10315     },
10316     
10317      /**
10318      * Mark this field as invalid
10319      * @param {String} msg The validation message
10320      */
10321     markInvalid : function(msg)
10322     {
10323         if(!this.el  || this.preventMark){ // not rendered
10324             return;
10325         }
10326         
10327         this.el.removeClass([this.invalidClass, this.validClass]);
10328         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10329         
10330         var feedback = this.el.select('.form-control-feedback', true).first();
10331             
10332         if(feedback){
10333             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10334         }
10335
10336         if(this.disabled || this.allowBlank){
10337             return;
10338         }
10339         
10340         var label = this.el.select('label', true).first();
10341         var icon = this.el.select('i.fa-star', true).first();
10342         
10343         if(!this.getValue().length && label && !icon){
10344             this.el.createChild({
10345                 tag : 'i',
10346                 cls : 'text-danger fa fa-lg fa-star',
10347                 tooltip : 'This field is required',
10348                 style : 'margin-right:5px;'
10349             }, label, true);
10350         }
10351         
10352         if (Roo.bootstrap.version == 3) {
10353             this.el.addClass(this.invalidClass);
10354         } else {
10355             this.inputEl().addClass('is-invalid');
10356         }
10357         
10358         // fixme ... this may be depricated need to test..
10359         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10360             
10361             var feedback = this.el.select('.form-control-feedback', true).first();
10362             
10363             if(feedback){
10364                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10365                 
10366                 if(this.getValue().length || this.forceFeedback){
10367                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10368                 }
10369                 
10370             }
10371             
10372         }
10373         
10374         this.fireEvent('invalid', this, msg);
10375     }
10376 });
10377
10378  
10379 /*
10380  * - LGPL
10381  *
10382  * trigger field - base class for combo..
10383  * 
10384  */
10385  
10386 /**
10387  * @class Roo.bootstrap.TriggerField
10388  * @extends Roo.bootstrap.Input
10389  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10390  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10391  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10392  * for which you can provide a custom implementation.  For example:
10393  * <pre><code>
10394 var trigger = new Roo.bootstrap.TriggerField();
10395 trigger.onTriggerClick = myTriggerFn;
10396 trigger.applyTo('my-field');
10397 </code></pre>
10398  *
10399  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10400  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10401  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10402  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10403  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10404
10405  * @constructor
10406  * Create a new TriggerField.
10407  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10408  * to the base TextField)
10409  */
10410 Roo.bootstrap.TriggerField = function(config){
10411     this.mimicing = false;
10412     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10413 };
10414
10415 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10416     /**
10417      * @cfg {String} triggerClass A CSS class to apply to the trigger
10418      */
10419      /**
10420      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10421      */
10422     hideTrigger:false,
10423
10424     /**
10425      * @cfg {Boolean} removable (true|false) special filter default false
10426      */
10427     removable : false,
10428     
10429     /** @cfg {Boolean} grow @hide */
10430     /** @cfg {Number} growMin @hide */
10431     /** @cfg {Number} growMax @hide */
10432
10433     /**
10434      * @hide 
10435      * @method
10436      */
10437     autoSize: Roo.emptyFn,
10438     // private
10439     monitorTab : true,
10440     // private
10441     deferHeight : true,
10442
10443     
10444     actionMode : 'wrap',
10445     
10446     caret : false,
10447     
10448     
10449     getAutoCreate : function(){
10450        
10451         var align = this.labelAlign || this.parentLabelAlign();
10452         
10453         var id = Roo.id();
10454         
10455         var cfg = {
10456             cls: 'form-group' //input-group
10457         };
10458         
10459         
10460         var input =  {
10461             tag: 'input',
10462             id : id,
10463             type : this.inputType,
10464             cls : 'form-control',
10465             autocomplete: 'new-password',
10466             placeholder : this.placeholder || '' 
10467             
10468         };
10469         if (this.name) {
10470             input.name = this.name;
10471         }
10472         if (this.size) {
10473             input.cls += ' input-' + this.size;
10474         }
10475         
10476         if (this.disabled) {
10477             input.disabled=true;
10478         }
10479         
10480         var inputblock = input;
10481         
10482         if(this.hasFeedback && !this.allowBlank){
10483             
10484             var feedback = {
10485                 tag: 'span',
10486                 cls: 'glyphicon form-control-feedback'
10487             };
10488             
10489             if(this.removable && !this.editable && !this.tickable){
10490                 inputblock = {
10491                     cls : 'has-feedback',
10492                     cn :  [
10493                         inputblock,
10494                         {
10495                             tag: 'button',
10496                             html : 'x',
10497                             cls : 'roo-combo-removable-btn close'
10498                         },
10499                         feedback
10500                     ] 
10501                 };
10502             } else {
10503                 inputblock = {
10504                     cls : 'has-feedback',
10505                     cn :  [
10506                         inputblock,
10507                         feedback
10508                     ] 
10509                 };
10510             }
10511
10512         } else {
10513             if(this.removable && !this.editable && !this.tickable){
10514                 inputblock = {
10515                     cls : 'roo-removable',
10516                     cn :  [
10517                         inputblock,
10518                         {
10519                             tag: 'button',
10520                             html : 'x',
10521                             cls : 'roo-combo-removable-btn close'
10522                         }
10523                     ] 
10524                 };
10525             }
10526         }
10527         
10528         if (this.before || this.after) {
10529             
10530             inputblock = {
10531                 cls : 'input-group',
10532                 cn :  [] 
10533             };
10534             if (this.before) {
10535                 inputblock.cn.push({
10536                     tag :'span',
10537                     cls : 'input-group-addon input-group-prepend input-group-text',
10538                     html : this.before
10539                 });
10540             }
10541             
10542             inputblock.cn.push(input);
10543             
10544             if(this.hasFeedback && !this.allowBlank){
10545                 inputblock.cls += ' has-feedback';
10546                 inputblock.cn.push(feedback);
10547             }
10548             
10549             if (this.after) {
10550                 inputblock.cn.push({
10551                     tag :'span',
10552                     cls : 'input-group-addon input-group-append input-group-text',
10553                     html : this.after
10554                 });
10555             }
10556             
10557         };
10558         
10559       
10560         
10561         var ibwrap = inputblock;
10562         
10563         if(this.multiple){
10564             ibwrap = {
10565                 tag: 'ul',
10566                 cls: 'roo-select2-choices',
10567                 cn:[
10568                     {
10569                         tag: 'li',
10570                         cls: 'roo-select2-search-field',
10571                         cn: [
10572
10573                             inputblock
10574                         ]
10575                     }
10576                 ]
10577             };
10578                 
10579         }
10580         
10581         var combobox = {
10582             cls: 'roo-select2-container input-group',
10583             cn: [
10584                  {
10585                     tag: 'input',
10586                     type : 'hidden',
10587                     cls: 'form-hidden-field'
10588                 },
10589                 ibwrap
10590             ]
10591         };
10592         
10593         if(!this.multiple && this.showToggleBtn){
10594             
10595             var caret = {
10596                         tag: 'span',
10597                         cls: 'caret'
10598              };
10599             if (this.caret != false) {
10600                 caret = {
10601                      tag: 'i',
10602                      cls: 'fa fa-' + this.caret
10603                 };
10604                 
10605             }
10606             
10607             combobox.cn.push({
10608                 tag :'span',
10609                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10610                 cn : [
10611                     caret,
10612                     {
10613                         tag: 'span',
10614                         cls: 'combobox-clear',
10615                         cn  : [
10616                             {
10617                                 tag : 'i',
10618                                 cls: 'icon-remove'
10619                             }
10620                         ]
10621                     }
10622                 ]
10623
10624             })
10625         }
10626         
10627         if(this.multiple){
10628             combobox.cls += ' roo-select2-container-multi';
10629         }
10630          var indicator = {
10631             tag : 'i',
10632             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10633             tooltip : 'This field is required'
10634         };
10635         if (Roo.bootstrap.version == 4) {
10636             indicator = {
10637                 tag : 'i',
10638                 style : 'display:none'
10639             };
10640         }
10641         
10642         
10643         if (align ==='left' && this.fieldLabel.length) {
10644             
10645             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10646
10647             cfg.cn = [
10648                 indicator,
10649                 {
10650                     tag: 'label',
10651                     'for' :  id,
10652                     cls : 'control-label',
10653                     html : this.fieldLabel
10654
10655                 },
10656                 {
10657                     cls : "", 
10658                     cn: [
10659                         combobox
10660                     ]
10661                 }
10662
10663             ];
10664             
10665             var labelCfg = cfg.cn[1];
10666             var contentCfg = cfg.cn[2];
10667             
10668             if(this.indicatorpos == 'right'){
10669                 cfg.cn = [
10670                     {
10671                         tag: 'label',
10672                         'for' :  id,
10673                         cls : 'control-label',
10674                         cn : [
10675                             {
10676                                 tag : 'span',
10677                                 html : this.fieldLabel
10678                             },
10679                             indicator
10680                         ]
10681                     },
10682                     {
10683                         cls : "", 
10684                         cn: [
10685                             combobox
10686                         ]
10687                     }
10688
10689                 ];
10690                 
10691                 labelCfg = cfg.cn[0];
10692                 contentCfg = cfg.cn[1];
10693             }
10694             
10695             if(this.labelWidth > 12){
10696                 labelCfg.style = "width: " + this.labelWidth + 'px';
10697             }
10698             
10699             if(this.labelWidth < 13 && this.labelmd == 0){
10700                 this.labelmd = this.labelWidth;
10701             }
10702             
10703             if(this.labellg > 0){
10704                 labelCfg.cls += ' col-lg-' + this.labellg;
10705                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10706             }
10707             
10708             if(this.labelmd > 0){
10709                 labelCfg.cls += ' col-md-' + this.labelmd;
10710                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10711             }
10712             
10713             if(this.labelsm > 0){
10714                 labelCfg.cls += ' col-sm-' + this.labelsm;
10715                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10716             }
10717             
10718             if(this.labelxs > 0){
10719                 labelCfg.cls += ' col-xs-' + this.labelxs;
10720                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10721             }
10722             
10723         } else if ( this.fieldLabel.length) {
10724 //                Roo.log(" label");
10725             cfg.cn = [
10726                 indicator,
10727                {
10728                    tag: 'label',
10729                    //cls : 'input-group-addon',
10730                    html : this.fieldLabel
10731
10732                },
10733
10734                combobox
10735
10736             ];
10737             
10738             if(this.indicatorpos == 'right'){
10739                 
10740                 cfg.cn = [
10741                     {
10742                        tag: 'label',
10743                        cn : [
10744                            {
10745                                tag : 'span',
10746                                html : this.fieldLabel
10747                            },
10748                            indicator
10749                        ]
10750
10751                     },
10752                     combobox
10753
10754                 ];
10755
10756             }
10757
10758         } else {
10759             
10760 //                Roo.log(" no label && no align");
10761                 cfg = combobox
10762                      
10763                 
10764         }
10765         
10766         var settings=this;
10767         ['xs','sm','md','lg'].map(function(size){
10768             if (settings[size]) {
10769                 cfg.cls += ' col-' + size + '-' + settings[size];
10770             }
10771         });
10772         
10773         return cfg;
10774         
10775     },
10776     
10777     
10778     
10779     // private
10780     onResize : function(w, h){
10781 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10782 //        if(typeof w == 'number'){
10783 //            var x = w - this.trigger.getWidth();
10784 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10785 //            this.trigger.setStyle('left', x+'px');
10786 //        }
10787     },
10788
10789     // private
10790     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10791
10792     // private
10793     getResizeEl : function(){
10794         return this.inputEl();
10795     },
10796
10797     // private
10798     getPositionEl : function(){
10799         return this.inputEl();
10800     },
10801
10802     // private
10803     alignErrorIcon : function(){
10804         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10805     },
10806
10807     // private
10808     initEvents : function(){
10809         
10810         this.createList();
10811         
10812         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10813         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10814         if(!this.multiple && this.showToggleBtn){
10815             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10816             if(this.hideTrigger){
10817                 this.trigger.setDisplayed(false);
10818             }
10819             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10820         }
10821         
10822         if(this.multiple){
10823             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10824         }
10825         
10826         if(this.removable && !this.editable && !this.tickable){
10827             var close = this.closeTriggerEl();
10828             
10829             if(close){
10830                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10831                 close.on('click', this.removeBtnClick, this, close);
10832             }
10833         }
10834         
10835         //this.trigger.addClassOnOver('x-form-trigger-over');
10836         //this.trigger.addClassOnClick('x-form-trigger-click');
10837         
10838         //if(!this.width){
10839         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10840         //}
10841     },
10842     
10843     closeTriggerEl : function()
10844     {
10845         var close = this.el.select('.roo-combo-removable-btn', true).first();
10846         return close ? close : false;
10847     },
10848     
10849     removeBtnClick : function(e, h, el)
10850     {
10851         e.preventDefault();
10852         
10853         if(this.fireEvent("remove", this) !== false){
10854             this.reset();
10855             this.fireEvent("afterremove", this)
10856         }
10857     },
10858     
10859     createList : function()
10860     {
10861         this.list = Roo.get(document.body).createChild({
10862             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10863             cls: 'typeahead typeahead-long dropdown-menu',
10864             style: 'display:none'
10865         });
10866         
10867         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10868         
10869     },
10870
10871     // private
10872     initTrigger : function(){
10873        
10874     },
10875
10876     // private
10877     onDestroy : function(){
10878         if(this.trigger){
10879             this.trigger.removeAllListeners();
10880           //  this.trigger.remove();
10881         }
10882         //if(this.wrap){
10883         //    this.wrap.remove();
10884         //}
10885         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10886     },
10887
10888     // private
10889     onFocus : function(){
10890         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10891         /*
10892         if(!this.mimicing){
10893             this.wrap.addClass('x-trigger-wrap-focus');
10894             this.mimicing = true;
10895             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10896             if(this.monitorTab){
10897                 this.el.on("keydown", this.checkTab, this);
10898             }
10899         }
10900         */
10901     },
10902
10903     // private
10904     checkTab : function(e){
10905         if(e.getKey() == e.TAB){
10906             this.triggerBlur();
10907         }
10908     },
10909
10910     // private
10911     onBlur : function(){
10912         // do nothing
10913     },
10914
10915     // private
10916     mimicBlur : function(e, t){
10917         /*
10918         if(!this.wrap.contains(t) && this.validateBlur()){
10919             this.triggerBlur();
10920         }
10921         */
10922     },
10923
10924     // private
10925     triggerBlur : function(){
10926         this.mimicing = false;
10927         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10928         if(this.monitorTab){
10929             this.el.un("keydown", this.checkTab, this);
10930         }
10931         //this.wrap.removeClass('x-trigger-wrap-focus');
10932         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10933     },
10934
10935     // private
10936     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10937     validateBlur : function(e, t){
10938         return true;
10939     },
10940
10941     // private
10942     onDisable : function(){
10943         this.inputEl().dom.disabled = true;
10944         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10945         //if(this.wrap){
10946         //    this.wrap.addClass('x-item-disabled');
10947         //}
10948     },
10949
10950     // private
10951     onEnable : function(){
10952         this.inputEl().dom.disabled = false;
10953         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10954         //if(this.wrap){
10955         //    this.el.removeClass('x-item-disabled');
10956         //}
10957     },
10958
10959     // private
10960     onShow : function(){
10961         var ae = this.getActionEl();
10962         
10963         if(ae){
10964             ae.dom.style.display = '';
10965             ae.dom.style.visibility = 'visible';
10966         }
10967     },
10968
10969     // private
10970     
10971     onHide : function(){
10972         var ae = this.getActionEl();
10973         ae.dom.style.display = 'none';
10974     },
10975
10976     /**
10977      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10978      * by an implementing function.
10979      * @method
10980      * @param {EventObject} e
10981      */
10982     onTriggerClick : Roo.emptyFn
10983 });
10984  /*
10985  * Based on:
10986  * Ext JS Library 1.1.1
10987  * Copyright(c) 2006-2007, Ext JS, LLC.
10988  *
10989  * Originally Released Under LGPL - original licence link has changed is not relivant.
10990  *
10991  * Fork - LGPL
10992  * <script type="text/javascript">
10993  */
10994
10995
10996 /**
10997  * @class Roo.data.SortTypes
10998  * @singleton
10999  * Defines the default sorting (casting?) comparison functions used when sorting data.
11000  */
11001 Roo.data.SortTypes = {
11002     /**
11003      * Default sort that does nothing
11004      * @param {Mixed} s The value being converted
11005      * @return {Mixed} The comparison value
11006      */
11007     none : function(s){
11008         return s;
11009     },
11010     
11011     /**
11012      * The regular expression used to strip tags
11013      * @type {RegExp}
11014      * @property
11015      */
11016     stripTagsRE : /<\/?[^>]+>/gi,
11017     
11018     /**
11019      * Strips all HTML tags to sort on text only
11020      * @param {Mixed} s The value being converted
11021      * @return {String} The comparison value
11022      */
11023     asText : function(s){
11024         return String(s).replace(this.stripTagsRE, "");
11025     },
11026     
11027     /**
11028      * Strips all HTML tags to sort on text only - Case insensitive
11029      * @param {Mixed} s The value being converted
11030      * @return {String} The comparison value
11031      */
11032     asUCText : function(s){
11033         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11034     },
11035     
11036     /**
11037      * Case insensitive string
11038      * @param {Mixed} s The value being converted
11039      * @return {String} The comparison value
11040      */
11041     asUCString : function(s) {
11042         return String(s).toUpperCase();
11043     },
11044     
11045     /**
11046      * Date sorting
11047      * @param {Mixed} s The value being converted
11048      * @return {Number} The comparison value
11049      */
11050     asDate : function(s) {
11051         if(!s){
11052             return 0;
11053         }
11054         if(s instanceof Date){
11055             return s.getTime();
11056         }
11057         return Date.parse(String(s));
11058     },
11059     
11060     /**
11061      * Float sorting
11062      * @param {Mixed} s The value being converted
11063      * @return {Float} The comparison value
11064      */
11065     asFloat : function(s) {
11066         var val = parseFloat(String(s).replace(/,/g, ""));
11067         if(isNaN(val)) {
11068             val = 0;
11069         }
11070         return val;
11071     },
11072     
11073     /**
11074      * Integer sorting
11075      * @param {Mixed} s The value being converted
11076      * @return {Number} The comparison value
11077      */
11078     asInt : function(s) {
11079         var val = parseInt(String(s).replace(/,/g, ""));
11080         if(isNaN(val)) {
11081             val = 0;
11082         }
11083         return val;
11084     }
11085 };/*
11086  * Based on:
11087  * Ext JS Library 1.1.1
11088  * Copyright(c) 2006-2007, Ext JS, LLC.
11089  *
11090  * Originally Released Under LGPL - original licence link has changed is not relivant.
11091  *
11092  * Fork - LGPL
11093  * <script type="text/javascript">
11094  */
11095
11096 /**
11097 * @class Roo.data.Record
11098  * Instances of this class encapsulate both record <em>definition</em> information, and record
11099  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11100  * to access Records cached in an {@link Roo.data.Store} object.<br>
11101  * <p>
11102  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11103  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11104  * objects.<br>
11105  * <p>
11106  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11107  * @constructor
11108  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11109  * {@link #create}. The parameters are the same.
11110  * @param {Array} data An associative Array of data values keyed by the field name.
11111  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11112  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11113  * not specified an integer id is generated.
11114  */
11115 Roo.data.Record = function(data, id){
11116     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11117     this.data = data;
11118 };
11119
11120 /**
11121  * Generate a constructor for a specific record layout.
11122  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11123  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11124  * Each field definition object may contain the following properties: <ul>
11125  * <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,
11126  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11127  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11128  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11129  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11130  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11131  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11132  * this may be omitted.</p></li>
11133  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11134  * <ul><li>auto (Default, implies no conversion)</li>
11135  * <li>string</li>
11136  * <li>int</li>
11137  * <li>float</li>
11138  * <li>boolean</li>
11139  * <li>date</li></ul></p></li>
11140  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11141  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11142  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11143  * by the Reader into an object that will be stored in the Record. It is passed the
11144  * following parameters:<ul>
11145  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11146  * </ul></p></li>
11147  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11148  * </ul>
11149  * <br>usage:<br><pre><code>
11150 var TopicRecord = Roo.data.Record.create(
11151     {name: 'title', mapping: 'topic_title'},
11152     {name: 'author', mapping: 'username'},
11153     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11154     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11155     {name: 'lastPoster', mapping: 'user2'},
11156     {name: 'excerpt', mapping: 'post_text'}
11157 );
11158
11159 var myNewRecord = new TopicRecord({
11160     title: 'Do my job please',
11161     author: 'noobie',
11162     totalPosts: 1,
11163     lastPost: new Date(),
11164     lastPoster: 'Animal',
11165     excerpt: 'No way dude!'
11166 });
11167 myStore.add(myNewRecord);
11168 </code></pre>
11169  * @method create
11170  * @static
11171  */
11172 Roo.data.Record.create = function(o){
11173     var f = function(){
11174         f.superclass.constructor.apply(this, arguments);
11175     };
11176     Roo.extend(f, Roo.data.Record);
11177     var p = f.prototype;
11178     p.fields = new Roo.util.MixedCollection(false, function(field){
11179         return field.name;
11180     });
11181     for(var i = 0, len = o.length; i < len; i++){
11182         p.fields.add(new Roo.data.Field(o[i]));
11183     }
11184     f.getField = function(name){
11185         return p.fields.get(name);  
11186     };
11187     return f;
11188 };
11189
11190 Roo.data.Record.AUTO_ID = 1000;
11191 Roo.data.Record.EDIT = 'edit';
11192 Roo.data.Record.REJECT = 'reject';
11193 Roo.data.Record.COMMIT = 'commit';
11194
11195 Roo.data.Record.prototype = {
11196     /**
11197      * Readonly flag - true if this record has been modified.
11198      * @type Boolean
11199      */
11200     dirty : false,
11201     editing : false,
11202     error: null,
11203     modified: null,
11204
11205     // private
11206     join : function(store){
11207         this.store = store;
11208     },
11209
11210     /**
11211      * Set the named field to the specified value.
11212      * @param {String} name The name of the field to set.
11213      * @param {Object} value The value to set the field to.
11214      */
11215     set : function(name, value){
11216         if(this.data[name] == value){
11217             return;
11218         }
11219         this.dirty = true;
11220         if(!this.modified){
11221             this.modified = {};
11222         }
11223         if(typeof this.modified[name] == 'undefined'){
11224             this.modified[name] = this.data[name];
11225         }
11226         this.data[name] = value;
11227         if(!this.editing && this.store){
11228             this.store.afterEdit(this);
11229         }       
11230     },
11231
11232     /**
11233      * Get the value of the named field.
11234      * @param {String} name The name of the field to get the value of.
11235      * @return {Object} The value of the field.
11236      */
11237     get : function(name){
11238         return this.data[name]; 
11239     },
11240
11241     // private
11242     beginEdit : function(){
11243         this.editing = true;
11244         this.modified = {}; 
11245     },
11246
11247     // private
11248     cancelEdit : function(){
11249         this.editing = false;
11250         delete this.modified;
11251     },
11252
11253     // private
11254     endEdit : function(){
11255         this.editing = false;
11256         if(this.dirty && this.store){
11257             this.store.afterEdit(this);
11258         }
11259     },
11260
11261     /**
11262      * Usually called by the {@link Roo.data.Store} which owns the Record.
11263      * Rejects all changes made to the Record since either creation, or the last commit operation.
11264      * Modified fields are reverted to their original values.
11265      * <p>
11266      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11267      * of reject operations.
11268      */
11269     reject : function(){
11270         var m = this.modified;
11271         for(var n in m){
11272             if(typeof m[n] != "function"){
11273                 this.data[n] = m[n];
11274             }
11275         }
11276         this.dirty = false;
11277         delete this.modified;
11278         this.editing = false;
11279         if(this.store){
11280             this.store.afterReject(this);
11281         }
11282     },
11283
11284     /**
11285      * Usually called by the {@link Roo.data.Store} which owns the Record.
11286      * Commits all changes made to the Record since either creation, or the last commit operation.
11287      * <p>
11288      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11289      * of commit operations.
11290      */
11291     commit : function(){
11292         this.dirty = false;
11293         delete this.modified;
11294         this.editing = false;
11295         if(this.store){
11296             this.store.afterCommit(this);
11297         }
11298     },
11299
11300     // private
11301     hasError : function(){
11302         return this.error != null;
11303     },
11304
11305     // private
11306     clearError : function(){
11307         this.error = null;
11308     },
11309
11310     /**
11311      * Creates a copy of this record.
11312      * @param {String} id (optional) A new record id if you don't want to use this record's id
11313      * @return {Record}
11314      */
11315     copy : function(newId) {
11316         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11317     }
11318 };/*
11319  * Based on:
11320  * Ext JS Library 1.1.1
11321  * Copyright(c) 2006-2007, Ext JS, LLC.
11322  *
11323  * Originally Released Under LGPL - original licence link has changed is not relivant.
11324  *
11325  * Fork - LGPL
11326  * <script type="text/javascript">
11327  */
11328
11329
11330
11331 /**
11332  * @class Roo.data.Store
11333  * @extends Roo.util.Observable
11334  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11335  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11336  * <p>
11337  * 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
11338  * has no knowledge of the format of the data returned by the Proxy.<br>
11339  * <p>
11340  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11341  * instances from the data object. These records are cached and made available through accessor functions.
11342  * @constructor
11343  * Creates a new Store.
11344  * @param {Object} config A config object containing the objects needed for the Store to access data,
11345  * and read the data into Records.
11346  */
11347 Roo.data.Store = function(config){
11348     this.data = new Roo.util.MixedCollection(false);
11349     this.data.getKey = function(o){
11350         return o.id;
11351     };
11352     this.baseParams = {};
11353     // private
11354     this.paramNames = {
11355         "start" : "start",
11356         "limit" : "limit",
11357         "sort" : "sort",
11358         "dir" : "dir",
11359         "multisort" : "_multisort"
11360     };
11361
11362     if(config && config.data){
11363         this.inlineData = config.data;
11364         delete config.data;
11365     }
11366
11367     Roo.apply(this, config);
11368     
11369     if(this.reader){ // reader passed
11370         this.reader = Roo.factory(this.reader, Roo.data);
11371         this.reader.xmodule = this.xmodule || false;
11372         if(!this.recordType){
11373             this.recordType = this.reader.recordType;
11374         }
11375         if(this.reader.onMetaChange){
11376             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11377         }
11378     }
11379
11380     if(this.recordType){
11381         this.fields = this.recordType.prototype.fields;
11382     }
11383     this.modified = [];
11384
11385     this.addEvents({
11386         /**
11387          * @event datachanged
11388          * Fires when the data cache has changed, and a widget which is using this Store
11389          * as a Record cache should refresh its view.
11390          * @param {Store} this
11391          */
11392         datachanged : true,
11393         /**
11394          * @event metachange
11395          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11396          * @param {Store} this
11397          * @param {Object} meta The JSON metadata
11398          */
11399         metachange : true,
11400         /**
11401          * @event add
11402          * Fires when Records have been added to the Store
11403          * @param {Store} this
11404          * @param {Roo.data.Record[]} records The array of Records added
11405          * @param {Number} index The index at which the record(s) were added
11406          */
11407         add : true,
11408         /**
11409          * @event remove
11410          * Fires when a Record has been removed from the Store
11411          * @param {Store} this
11412          * @param {Roo.data.Record} record The Record that was removed
11413          * @param {Number} index The index at which the record was removed
11414          */
11415         remove : true,
11416         /**
11417          * @event update
11418          * Fires when a Record has been updated
11419          * @param {Store} this
11420          * @param {Roo.data.Record} record The Record that was updated
11421          * @param {String} operation The update operation being performed.  Value may be one of:
11422          * <pre><code>
11423  Roo.data.Record.EDIT
11424  Roo.data.Record.REJECT
11425  Roo.data.Record.COMMIT
11426          * </code></pre>
11427          */
11428         update : true,
11429         /**
11430          * @event clear
11431          * Fires when the data cache has been cleared.
11432          * @param {Store} this
11433          */
11434         clear : true,
11435         /**
11436          * @event beforeload
11437          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11438          * the load action will be canceled.
11439          * @param {Store} this
11440          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11441          */
11442         beforeload : true,
11443         /**
11444          * @event beforeloadadd
11445          * Fires after a new set of Records has been loaded.
11446          * @param {Store} this
11447          * @param {Roo.data.Record[]} records The Records that were loaded
11448          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11449          */
11450         beforeloadadd : true,
11451         /**
11452          * @event load
11453          * Fires after a new set of Records has been loaded, before they are added to the store.
11454          * @param {Store} this
11455          * @param {Roo.data.Record[]} records The Records that were loaded
11456          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11457          * @params {Object} return from reader
11458          */
11459         load : true,
11460         /**
11461          * @event loadexception
11462          * Fires if an exception occurs in the Proxy during loading.
11463          * Called with the signature of the Proxy's "loadexception" event.
11464          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11465          * 
11466          * @param {Proxy} 
11467          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11468          * @param {Object} load options 
11469          * @param {Object} jsonData from your request (normally this contains the Exception)
11470          */
11471         loadexception : true
11472     });
11473     
11474     if(this.proxy){
11475         this.proxy = Roo.factory(this.proxy, Roo.data);
11476         this.proxy.xmodule = this.xmodule || false;
11477         this.relayEvents(this.proxy,  ["loadexception"]);
11478     }
11479     this.sortToggle = {};
11480     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11481
11482     Roo.data.Store.superclass.constructor.call(this);
11483
11484     if(this.inlineData){
11485         this.loadData(this.inlineData);
11486         delete this.inlineData;
11487     }
11488 };
11489
11490 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11491      /**
11492     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11493     * without a remote query - used by combo/forms at present.
11494     */
11495     
11496     /**
11497     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11498     */
11499     /**
11500     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11501     */
11502     /**
11503     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11504     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11505     */
11506     /**
11507     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11508     * on any HTTP request
11509     */
11510     /**
11511     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11512     */
11513     /**
11514     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11515     */
11516     multiSort: false,
11517     /**
11518     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11519     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11520     */
11521     remoteSort : false,
11522
11523     /**
11524     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11525      * loaded or when a record is removed. (defaults to false).
11526     */
11527     pruneModifiedRecords : false,
11528
11529     // private
11530     lastOptions : null,
11531
11532     /**
11533      * Add Records to the Store and fires the add event.
11534      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11535      */
11536     add : function(records){
11537         records = [].concat(records);
11538         for(var i = 0, len = records.length; i < len; i++){
11539             records[i].join(this);
11540         }
11541         var index = this.data.length;
11542         this.data.addAll(records);
11543         this.fireEvent("add", this, records, index);
11544     },
11545
11546     /**
11547      * Remove a Record from the Store and fires the remove event.
11548      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11549      */
11550     remove : function(record){
11551         var index = this.data.indexOf(record);
11552         this.data.removeAt(index);
11553  
11554         if(this.pruneModifiedRecords){
11555             this.modified.remove(record);
11556         }
11557         this.fireEvent("remove", this, record, index);
11558     },
11559
11560     /**
11561      * Remove all Records from the Store and fires the clear event.
11562      */
11563     removeAll : function(){
11564         this.data.clear();
11565         if(this.pruneModifiedRecords){
11566             this.modified = [];
11567         }
11568         this.fireEvent("clear", this);
11569     },
11570
11571     /**
11572      * Inserts Records to the Store at the given index and fires the add event.
11573      * @param {Number} index The start index at which to insert the passed Records.
11574      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11575      */
11576     insert : function(index, records){
11577         records = [].concat(records);
11578         for(var i = 0, len = records.length; i < len; i++){
11579             this.data.insert(index, records[i]);
11580             records[i].join(this);
11581         }
11582         this.fireEvent("add", this, records, index);
11583     },
11584
11585     /**
11586      * Get the index within the cache of the passed Record.
11587      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11588      * @return {Number} The index of the passed Record. Returns -1 if not found.
11589      */
11590     indexOf : function(record){
11591         return this.data.indexOf(record);
11592     },
11593
11594     /**
11595      * Get the index within the cache of the Record with the passed id.
11596      * @param {String} id The id of the Record to find.
11597      * @return {Number} The index of the Record. Returns -1 if not found.
11598      */
11599     indexOfId : function(id){
11600         return this.data.indexOfKey(id);
11601     },
11602
11603     /**
11604      * Get the Record with the specified id.
11605      * @param {String} id The id of the Record to find.
11606      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11607      */
11608     getById : function(id){
11609         return this.data.key(id);
11610     },
11611
11612     /**
11613      * Get the Record at the specified index.
11614      * @param {Number} index The index of the Record to find.
11615      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11616      */
11617     getAt : function(index){
11618         return this.data.itemAt(index);
11619     },
11620
11621     /**
11622      * Returns a range of Records between specified indices.
11623      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11624      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11625      * @return {Roo.data.Record[]} An array of Records
11626      */
11627     getRange : function(start, end){
11628         return this.data.getRange(start, end);
11629     },
11630
11631     // private
11632     storeOptions : function(o){
11633         o = Roo.apply({}, o);
11634         delete o.callback;
11635         delete o.scope;
11636         this.lastOptions = o;
11637     },
11638
11639     /**
11640      * Loads the Record cache from the configured Proxy using the configured Reader.
11641      * <p>
11642      * If using remote paging, then the first load call must specify the <em>start</em>
11643      * and <em>limit</em> properties in the options.params property to establish the initial
11644      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11645      * <p>
11646      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11647      * and this call will return before the new data has been loaded. Perform any post-processing
11648      * in a callback function, or in a "load" event handler.</strong>
11649      * <p>
11650      * @param {Object} options An object containing properties which control loading options:<ul>
11651      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11652      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11653      * passed the following arguments:<ul>
11654      * <li>r : Roo.data.Record[]</li>
11655      * <li>options: Options object from the load call</li>
11656      * <li>success: Boolean success indicator</li></ul></li>
11657      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11658      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11659      * </ul>
11660      */
11661     load : function(options){
11662         options = options || {};
11663         if(this.fireEvent("beforeload", this, options) !== false){
11664             this.storeOptions(options);
11665             var p = Roo.apply(options.params || {}, this.baseParams);
11666             // if meta was not loaded from remote source.. try requesting it.
11667             if (!this.reader.metaFromRemote) {
11668                 p._requestMeta = 1;
11669             }
11670             if(this.sortInfo && this.remoteSort){
11671                 var pn = this.paramNames;
11672                 p[pn["sort"]] = this.sortInfo.field;
11673                 p[pn["dir"]] = this.sortInfo.direction;
11674             }
11675             if (this.multiSort) {
11676                 var pn = this.paramNames;
11677                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11678             }
11679             
11680             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11681         }
11682     },
11683
11684     /**
11685      * Reloads the Record cache from the configured Proxy using the configured Reader and
11686      * the options from the last load operation performed.
11687      * @param {Object} options (optional) An object containing properties which may override the options
11688      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11689      * the most recently used options are reused).
11690      */
11691     reload : function(options){
11692         this.load(Roo.applyIf(options||{}, this.lastOptions));
11693     },
11694
11695     // private
11696     // Called as a callback by the Reader during a load operation.
11697     loadRecords : function(o, options, success){
11698         if(!o || success === false){
11699             if(success !== false){
11700                 this.fireEvent("load", this, [], options, o);
11701             }
11702             if(options.callback){
11703                 options.callback.call(options.scope || this, [], options, false);
11704             }
11705             return;
11706         }
11707         // if data returned failure - throw an exception.
11708         if (o.success === false) {
11709             // show a message if no listener is registered.
11710             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11711                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11712             }
11713             // loadmask wil be hooked into this..
11714             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11715             return;
11716         }
11717         var r = o.records, t = o.totalRecords || r.length;
11718         
11719         this.fireEvent("beforeloadadd", this, r, options, o);
11720         
11721         if(!options || options.add !== true){
11722             if(this.pruneModifiedRecords){
11723                 this.modified = [];
11724             }
11725             for(var i = 0, len = r.length; i < len; i++){
11726                 r[i].join(this);
11727             }
11728             if(this.snapshot){
11729                 this.data = this.snapshot;
11730                 delete this.snapshot;
11731             }
11732             this.data.clear();
11733             this.data.addAll(r);
11734             this.totalLength = t;
11735             this.applySort();
11736             this.fireEvent("datachanged", this);
11737         }else{
11738             this.totalLength = Math.max(t, this.data.length+r.length);
11739             this.add(r);
11740         }
11741         
11742         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11743                 
11744             var e = new Roo.data.Record({});
11745
11746             e.set(this.parent.displayField, this.parent.emptyTitle);
11747             e.set(this.parent.valueField, '');
11748
11749             this.insert(0, e);
11750         }
11751             
11752         this.fireEvent("load", this, r, options, o);
11753         if(options.callback){
11754             options.callback.call(options.scope || this, r, options, true);
11755         }
11756     },
11757
11758
11759     /**
11760      * Loads data from a passed data block. A Reader which understands the format of the data
11761      * must have been configured in the constructor.
11762      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11763      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11764      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11765      */
11766     loadData : function(o, append){
11767         var r = this.reader.readRecords(o);
11768         this.loadRecords(r, {add: append}, true);
11769     },
11770
11771     /**
11772      * Gets the number of cached records.
11773      * <p>
11774      * <em>If using paging, this may not be the total size of the dataset. If the data object
11775      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11776      * the data set size</em>
11777      */
11778     getCount : function(){
11779         return this.data.length || 0;
11780     },
11781
11782     /**
11783      * Gets the total number of records in the dataset as returned by the server.
11784      * <p>
11785      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11786      * the dataset size</em>
11787      */
11788     getTotalCount : function(){
11789         return this.totalLength || 0;
11790     },
11791
11792     /**
11793      * Returns the sort state of the Store as an object with two properties:
11794      * <pre><code>
11795  field {String} The name of the field by which the Records are sorted
11796  direction {String} The sort order, "ASC" or "DESC"
11797      * </code></pre>
11798      */
11799     getSortState : function(){
11800         return this.sortInfo;
11801     },
11802
11803     // private
11804     applySort : function(){
11805         if(this.sortInfo && !this.remoteSort){
11806             var s = this.sortInfo, f = s.field;
11807             var st = this.fields.get(f).sortType;
11808             var fn = function(r1, r2){
11809                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11810                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11811             };
11812             this.data.sort(s.direction, fn);
11813             if(this.snapshot && this.snapshot != this.data){
11814                 this.snapshot.sort(s.direction, fn);
11815             }
11816         }
11817     },
11818
11819     /**
11820      * Sets the default sort column and order to be used by the next load operation.
11821      * @param {String} fieldName The name of the field to sort by.
11822      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11823      */
11824     setDefaultSort : function(field, dir){
11825         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11826     },
11827
11828     /**
11829      * Sort the Records.
11830      * If remote sorting is used, the sort is performed on the server, and the cache is
11831      * reloaded. If local sorting is used, the cache is sorted internally.
11832      * @param {String} fieldName The name of the field to sort by.
11833      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11834      */
11835     sort : function(fieldName, dir){
11836         var f = this.fields.get(fieldName);
11837         if(!dir){
11838             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11839             
11840             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11841                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11842             }else{
11843                 dir = f.sortDir;
11844             }
11845         }
11846         this.sortToggle[f.name] = dir;
11847         this.sortInfo = {field: f.name, direction: dir};
11848         if(!this.remoteSort){
11849             this.applySort();
11850             this.fireEvent("datachanged", this);
11851         }else{
11852             this.load(this.lastOptions);
11853         }
11854     },
11855
11856     /**
11857      * Calls the specified function for each of the Records in the cache.
11858      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11859      * Returning <em>false</em> aborts and exits the iteration.
11860      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11861      */
11862     each : function(fn, scope){
11863         this.data.each(fn, scope);
11864     },
11865
11866     /**
11867      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11868      * (e.g., during paging).
11869      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11870      */
11871     getModifiedRecords : function(){
11872         return this.modified;
11873     },
11874
11875     // private
11876     createFilterFn : function(property, value, anyMatch){
11877         if(!value.exec){ // not a regex
11878             value = String(value);
11879             if(value.length == 0){
11880                 return false;
11881             }
11882             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11883         }
11884         return function(r){
11885             return value.test(r.data[property]);
11886         };
11887     },
11888
11889     /**
11890      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11891      * @param {String} property A field on your records
11892      * @param {Number} start The record index to start at (defaults to 0)
11893      * @param {Number} end The last record index to include (defaults to length - 1)
11894      * @return {Number} The sum
11895      */
11896     sum : function(property, start, end){
11897         var rs = this.data.items, v = 0;
11898         start = start || 0;
11899         end = (end || end === 0) ? end : rs.length-1;
11900
11901         for(var i = start; i <= end; i++){
11902             v += (rs[i].data[property] || 0);
11903         }
11904         return v;
11905     },
11906
11907     /**
11908      * Filter the records by a specified property.
11909      * @param {String} field A field on your records
11910      * @param {String/RegExp} value Either a string that the field
11911      * should start with or a RegExp to test against the field
11912      * @param {Boolean} anyMatch True to match any part not just the beginning
11913      */
11914     filter : function(property, value, anyMatch){
11915         var fn = this.createFilterFn(property, value, anyMatch);
11916         return fn ? this.filterBy(fn) : this.clearFilter();
11917     },
11918
11919     /**
11920      * Filter by a function. The specified function will be called with each
11921      * record in this data source. If the function returns true the record is included,
11922      * otherwise it is filtered.
11923      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11924      * @param {Object} scope (optional) The scope of the function (defaults to this)
11925      */
11926     filterBy : function(fn, scope){
11927         this.snapshot = this.snapshot || this.data;
11928         this.data = this.queryBy(fn, scope||this);
11929         this.fireEvent("datachanged", this);
11930     },
11931
11932     /**
11933      * Query the records by a specified property.
11934      * @param {String} field A field on your records
11935      * @param {String/RegExp} value Either a string that the field
11936      * should start with or a RegExp to test against the field
11937      * @param {Boolean} anyMatch True to match any part not just the beginning
11938      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11939      */
11940     query : function(property, value, anyMatch){
11941         var fn = this.createFilterFn(property, value, anyMatch);
11942         return fn ? this.queryBy(fn) : this.data.clone();
11943     },
11944
11945     /**
11946      * Query by a function. The specified function will be called with each
11947      * record in this data source. If the function returns true the record is included
11948      * in the results.
11949      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11950      * @param {Object} scope (optional) The scope of the function (defaults to this)
11951       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11952      **/
11953     queryBy : function(fn, scope){
11954         var data = this.snapshot || this.data;
11955         return data.filterBy(fn, scope||this);
11956     },
11957
11958     /**
11959      * Collects unique values for a particular dataIndex from this store.
11960      * @param {String} dataIndex The property to collect
11961      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11962      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11963      * @return {Array} An array of the unique values
11964      **/
11965     collect : function(dataIndex, allowNull, bypassFilter){
11966         var d = (bypassFilter === true && this.snapshot) ?
11967                 this.snapshot.items : this.data.items;
11968         var v, sv, r = [], l = {};
11969         for(var i = 0, len = d.length; i < len; i++){
11970             v = d[i].data[dataIndex];
11971             sv = String(v);
11972             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11973                 l[sv] = true;
11974                 r[r.length] = v;
11975             }
11976         }
11977         return r;
11978     },
11979
11980     /**
11981      * Revert to a view of the Record cache with no filtering applied.
11982      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11983      */
11984     clearFilter : function(suppressEvent){
11985         if(this.snapshot && this.snapshot != this.data){
11986             this.data = this.snapshot;
11987             delete this.snapshot;
11988             if(suppressEvent !== true){
11989                 this.fireEvent("datachanged", this);
11990             }
11991         }
11992     },
11993
11994     // private
11995     afterEdit : function(record){
11996         if(this.modified.indexOf(record) == -1){
11997             this.modified.push(record);
11998         }
11999         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12000     },
12001     
12002     // private
12003     afterReject : function(record){
12004         this.modified.remove(record);
12005         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12006     },
12007
12008     // private
12009     afterCommit : function(record){
12010         this.modified.remove(record);
12011         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12012     },
12013
12014     /**
12015      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12016      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12017      */
12018     commitChanges : function(){
12019         var m = this.modified.slice(0);
12020         this.modified = [];
12021         for(var i = 0, len = m.length; i < len; i++){
12022             m[i].commit();
12023         }
12024     },
12025
12026     /**
12027      * Cancel outstanding changes on all changed records.
12028      */
12029     rejectChanges : function(){
12030         var m = this.modified.slice(0);
12031         this.modified = [];
12032         for(var i = 0, len = m.length; i < len; i++){
12033             m[i].reject();
12034         }
12035     },
12036
12037     onMetaChange : function(meta, rtype, o){
12038         this.recordType = rtype;
12039         this.fields = rtype.prototype.fields;
12040         delete this.snapshot;
12041         this.sortInfo = meta.sortInfo || this.sortInfo;
12042         this.modified = [];
12043         this.fireEvent('metachange', this, this.reader.meta);
12044     },
12045     
12046     moveIndex : function(data, type)
12047     {
12048         var index = this.indexOf(data);
12049         
12050         var newIndex = index + type;
12051         
12052         this.remove(data);
12053         
12054         this.insert(newIndex, data);
12055         
12056     }
12057 });/*
12058  * Based on:
12059  * Ext JS Library 1.1.1
12060  * Copyright(c) 2006-2007, Ext JS, LLC.
12061  *
12062  * Originally Released Under LGPL - original licence link has changed is not relivant.
12063  *
12064  * Fork - LGPL
12065  * <script type="text/javascript">
12066  */
12067
12068 /**
12069  * @class Roo.data.SimpleStore
12070  * @extends Roo.data.Store
12071  * Small helper class to make creating Stores from Array data easier.
12072  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12073  * @cfg {Array} fields An array of field definition objects, or field name strings.
12074  * @cfg {Array} data The multi-dimensional array of data
12075  * @constructor
12076  * @param {Object} config
12077  */
12078 Roo.data.SimpleStore = function(config){
12079     Roo.data.SimpleStore.superclass.constructor.call(this, {
12080         isLocal : true,
12081         reader: new Roo.data.ArrayReader({
12082                 id: config.id
12083             },
12084             Roo.data.Record.create(config.fields)
12085         ),
12086         proxy : new Roo.data.MemoryProxy(config.data)
12087     });
12088     this.load();
12089 };
12090 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12091  * Based on:
12092  * Ext JS Library 1.1.1
12093  * Copyright(c) 2006-2007, Ext JS, LLC.
12094  *
12095  * Originally Released Under LGPL - original licence link has changed is not relivant.
12096  *
12097  * Fork - LGPL
12098  * <script type="text/javascript">
12099  */
12100
12101 /**
12102 /**
12103  * @extends Roo.data.Store
12104  * @class Roo.data.JsonStore
12105  * Small helper class to make creating Stores for JSON data easier. <br/>
12106 <pre><code>
12107 var store = new Roo.data.JsonStore({
12108     url: 'get-images.php',
12109     root: 'images',
12110     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12111 });
12112 </code></pre>
12113  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12114  * JsonReader and HttpProxy (unless inline data is provided).</b>
12115  * @cfg {Array} fields An array of field definition objects, or field name strings.
12116  * @constructor
12117  * @param {Object} config
12118  */
12119 Roo.data.JsonStore = function(c){
12120     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12121         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12122         reader: new Roo.data.JsonReader(c, c.fields)
12123     }));
12124 };
12125 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12126  * Based on:
12127  * Ext JS Library 1.1.1
12128  * Copyright(c) 2006-2007, Ext JS, LLC.
12129  *
12130  * Originally Released Under LGPL - original licence link has changed is not relivant.
12131  *
12132  * Fork - LGPL
12133  * <script type="text/javascript">
12134  */
12135
12136  
12137 Roo.data.Field = function(config){
12138     if(typeof config == "string"){
12139         config = {name: config};
12140     }
12141     Roo.apply(this, config);
12142     
12143     if(!this.type){
12144         this.type = "auto";
12145     }
12146     
12147     var st = Roo.data.SortTypes;
12148     // named sortTypes are supported, here we look them up
12149     if(typeof this.sortType == "string"){
12150         this.sortType = st[this.sortType];
12151     }
12152     
12153     // set default sortType for strings and dates
12154     if(!this.sortType){
12155         switch(this.type){
12156             case "string":
12157                 this.sortType = st.asUCString;
12158                 break;
12159             case "date":
12160                 this.sortType = st.asDate;
12161                 break;
12162             default:
12163                 this.sortType = st.none;
12164         }
12165     }
12166
12167     // define once
12168     var stripRe = /[\$,%]/g;
12169
12170     // prebuilt conversion function for this field, instead of
12171     // switching every time we're reading a value
12172     if(!this.convert){
12173         var cv, dateFormat = this.dateFormat;
12174         switch(this.type){
12175             case "":
12176             case "auto":
12177             case undefined:
12178                 cv = function(v){ return v; };
12179                 break;
12180             case "string":
12181                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12182                 break;
12183             case "int":
12184                 cv = function(v){
12185                     return v !== undefined && v !== null && v !== '' ?
12186                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12187                     };
12188                 break;
12189             case "float":
12190                 cv = function(v){
12191                     return v !== undefined && v !== null && v !== '' ?
12192                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12193                     };
12194                 break;
12195             case "bool":
12196             case "boolean":
12197                 cv = function(v){ return v === true || v === "true" || v == 1; };
12198                 break;
12199             case "date":
12200                 cv = function(v){
12201                     if(!v){
12202                         return '';
12203                     }
12204                     if(v instanceof Date){
12205                         return v;
12206                     }
12207                     if(dateFormat){
12208                         if(dateFormat == "timestamp"){
12209                             return new Date(v*1000);
12210                         }
12211                         return Date.parseDate(v, dateFormat);
12212                     }
12213                     var parsed = Date.parse(v);
12214                     return parsed ? new Date(parsed) : null;
12215                 };
12216              break;
12217             
12218         }
12219         this.convert = cv;
12220     }
12221 };
12222
12223 Roo.data.Field.prototype = {
12224     dateFormat: null,
12225     defaultValue: "",
12226     mapping: null,
12227     sortType : null,
12228     sortDir : "ASC"
12229 };/*
12230  * Based on:
12231  * Ext JS Library 1.1.1
12232  * Copyright(c) 2006-2007, Ext JS, LLC.
12233  *
12234  * Originally Released Under LGPL - original licence link has changed is not relivant.
12235  *
12236  * Fork - LGPL
12237  * <script type="text/javascript">
12238  */
12239  
12240 // Base class for reading structured data from a data source.  This class is intended to be
12241 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12242
12243 /**
12244  * @class Roo.data.DataReader
12245  * Base class for reading structured data from a data source.  This class is intended to be
12246  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12247  */
12248
12249 Roo.data.DataReader = function(meta, recordType){
12250     
12251     this.meta = meta;
12252     
12253     this.recordType = recordType instanceof Array ? 
12254         Roo.data.Record.create(recordType) : recordType;
12255 };
12256
12257 Roo.data.DataReader.prototype = {
12258      /**
12259      * Create an empty record
12260      * @param {Object} data (optional) - overlay some values
12261      * @return {Roo.data.Record} record created.
12262      */
12263     newRow :  function(d) {
12264         var da =  {};
12265         this.recordType.prototype.fields.each(function(c) {
12266             switch( c.type) {
12267                 case 'int' : da[c.name] = 0; break;
12268                 case 'date' : da[c.name] = new Date(); break;
12269                 case 'float' : da[c.name] = 0.0; break;
12270                 case 'boolean' : da[c.name] = false; break;
12271                 default : da[c.name] = ""; break;
12272             }
12273             
12274         });
12275         return new this.recordType(Roo.apply(da, d));
12276     }
12277     
12278 };/*
12279  * Based on:
12280  * Ext JS Library 1.1.1
12281  * Copyright(c) 2006-2007, Ext JS, LLC.
12282  *
12283  * Originally Released Under LGPL - original licence link has changed is not relivant.
12284  *
12285  * Fork - LGPL
12286  * <script type="text/javascript">
12287  */
12288
12289 /**
12290  * @class Roo.data.DataProxy
12291  * @extends Roo.data.Observable
12292  * This class is an abstract base class for implementations which provide retrieval of
12293  * unformatted data objects.<br>
12294  * <p>
12295  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12296  * (of the appropriate type which knows how to parse the data object) to provide a block of
12297  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12298  * <p>
12299  * Custom implementations must implement the load method as described in
12300  * {@link Roo.data.HttpProxy#load}.
12301  */
12302 Roo.data.DataProxy = function(){
12303     this.addEvents({
12304         /**
12305          * @event beforeload
12306          * Fires before a network request is made to retrieve a data object.
12307          * @param {Object} This DataProxy object.
12308          * @param {Object} params The params parameter to the load function.
12309          */
12310         beforeload : true,
12311         /**
12312          * @event load
12313          * Fires before the load method's callback is called.
12314          * @param {Object} This DataProxy object.
12315          * @param {Object} o The data object.
12316          * @param {Object} arg The callback argument object passed to the load function.
12317          */
12318         load : true,
12319         /**
12320          * @event loadexception
12321          * Fires if an Exception occurs during data retrieval.
12322          * @param {Object} This DataProxy object.
12323          * @param {Object} o The data object.
12324          * @param {Object} arg The callback argument object passed to the load function.
12325          * @param {Object} e The Exception.
12326          */
12327         loadexception : true
12328     });
12329     Roo.data.DataProxy.superclass.constructor.call(this);
12330 };
12331
12332 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12333
12334     /**
12335      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12336      */
12337 /*
12338  * Based on:
12339  * Ext JS Library 1.1.1
12340  * Copyright(c) 2006-2007, Ext JS, LLC.
12341  *
12342  * Originally Released Under LGPL - original licence link has changed is not relivant.
12343  *
12344  * Fork - LGPL
12345  * <script type="text/javascript">
12346  */
12347 /**
12348  * @class Roo.data.MemoryProxy
12349  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12350  * to the Reader when its load method is called.
12351  * @constructor
12352  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12353  */
12354 Roo.data.MemoryProxy = function(data){
12355     if (data.data) {
12356         data = data.data;
12357     }
12358     Roo.data.MemoryProxy.superclass.constructor.call(this);
12359     this.data = data;
12360 };
12361
12362 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12363     
12364     /**
12365      * Load data from the requested source (in this case an in-memory
12366      * data object passed to the constructor), read the data object into
12367      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12368      * process that block using the passed callback.
12369      * @param {Object} params This parameter is not used by the MemoryProxy class.
12370      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12371      * object into a block of Roo.data.Records.
12372      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12373      * The function must be passed <ul>
12374      * <li>The Record block object</li>
12375      * <li>The "arg" argument from the load function</li>
12376      * <li>A boolean success indicator</li>
12377      * </ul>
12378      * @param {Object} scope The scope in which to call the callback
12379      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12380      */
12381     load : function(params, reader, callback, scope, arg){
12382         params = params || {};
12383         var result;
12384         try {
12385             result = reader.readRecords(params.data ? params.data :this.data);
12386         }catch(e){
12387             this.fireEvent("loadexception", this, arg, null, e);
12388             callback.call(scope, null, arg, false);
12389             return;
12390         }
12391         callback.call(scope, result, arg, true);
12392     },
12393     
12394     // private
12395     update : function(params, records){
12396         
12397     }
12398 });/*
12399  * Based on:
12400  * Ext JS Library 1.1.1
12401  * Copyright(c) 2006-2007, Ext JS, LLC.
12402  *
12403  * Originally Released Under LGPL - original licence link has changed is not relivant.
12404  *
12405  * Fork - LGPL
12406  * <script type="text/javascript">
12407  */
12408 /**
12409  * @class Roo.data.HttpProxy
12410  * @extends Roo.data.DataProxy
12411  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12412  * configured to reference a certain URL.<br><br>
12413  * <p>
12414  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12415  * from which the running page was served.<br><br>
12416  * <p>
12417  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12418  * <p>
12419  * Be aware that to enable the browser to parse an XML document, the server must set
12420  * the Content-Type header in the HTTP response to "text/xml".
12421  * @constructor
12422  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12423  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12424  * will be used to make the request.
12425  */
12426 Roo.data.HttpProxy = function(conn){
12427     Roo.data.HttpProxy.superclass.constructor.call(this);
12428     // is conn a conn config or a real conn?
12429     this.conn = conn;
12430     this.useAjax = !conn || !conn.events;
12431   
12432 };
12433
12434 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12435     // thse are take from connection...
12436     
12437     /**
12438      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12439      */
12440     /**
12441      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12442      * extra parameters to each request made by this object. (defaults to undefined)
12443      */
12444     /**
12445      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12446      *  to each request made by this object. (defaults to undefined)
12447      */
12448     /**
12449      * @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)
12450      */
12451     /**
12452      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12453      */
12454      /**
12455      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12456      * @type Boolean
12457      */
12458   
12459
12460     /**
12461      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12462      * @type Boolean
12463      */
12464     /**
12465      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12466      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12467      * a finer-grained basis than the DataProxy events.
12468      */
12469     getConnection : function(){
12470         return this.useAjax ? Roo.Ajax : this.conn;
12471     },
12472
12473     /**
12474      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12475      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12476      * process that block using the passed callback.
12477      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12478      * for the request to the remote server.
12479      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12480      * object into a block of Roo.data.Records.
12481      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12482      * The function must be passed <ul>
12483      * <li>The Record block object</li>
12484      * <li>The "arg" argument from the load function</li>
12485      * <li>A boolean success indicator</li>
12486      * </ul>
12487      * @param {Object} scope The scope in which to call the callback
12488      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12489      */
12490     load : function(params, reader, callback, scope, arg){
12491         if(this.fireEvent("beforeload", this, params) !== false){
12492             var  o = {
12493                 params : params || {},
12494                 request: {
12495                     callback : callback,
12496                     scope : scope,
12497                     arg : arg
12498                 },
12499                 reader: reader,
12500                 callback : this.loadResponse,
12501                 scope: this
12502             };
12503             if(this.useAjax){
12504                 Roo.applyIf(o, this.conn);
12505                 if(this.activeRequest){
12506                     Roo.Ajax.abort(this.activeRequest);
12507                 }
12508                 this.activeRequest = Roo.Ajax.request(o);
12509             }else{
12510                 this.conn.request(o);
12511             }
12512         }else{
12513             callback.call(scope||this, null, arg, false);
12514         }
12515     },
12516
12517     // private
12518     loadResponse : function(o, success, response){
12519         delete this.activeRequest;
12520         if(!success){
12521             this.fireEvent("loadexception", this, o, response);
12522             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12523             return;
12524         }
12525         var result;
12526         try {
12527             result = o.reader.read(response);
12528         }catch(e){
12529             this.fireEvent("loadexception", this, o, response, e);
12530             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12531             return;
12532         }
12533         
12534         this.fireEvent("load", this, o, o.request.arg);
12535         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12536     },
12537
12538     // private
12539     update : function(dataSet){
12540
12541     },
12542
12543     // private
12544     updateResponse : function(dataSet){
12545
12546     }
12547 });/*
12548  * Based on:
12549  * Ext JS Library 1.1.1
12550  * Copyright(c) 2006-2007, Ext JS, LLC.
12551  *
12552  * Originally Released Under LGPL - original licence link has changed is not relivant.
12553  *
12554  * Fork - LGPL
12555  * <script type="text/javascript">
12556  */
12557
12558 /**
12559  * @class Roo.data.ScriptTagProxy
12560  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12561  * other than the originating domain of the running page.<br><br>
12562  * <p>
12563  * <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
12564  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12565  * <p>
12566  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12567  * source code that is used as the source inside a &lt;script> tag.<br><br>
12568  * <p>
12569  * In order for the browser to process the returned data, the server must wrap the data object
12570  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12571  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12572  * depending on whether the callback name was passed:
12573  * <p>
12574  * <pre><code>
12575 boolean scriptTag = false;
12576 String cb = request.getParameter("callback");
12577 if (cb != null) {
12578     scriptTag = true;
12579     response.setContentType("text/javascript");
12580 } else {
12581     response.setContentType("application/x-json");
12582 }
12583 Writer out = response.getWriter();
12584 if (scriptTag) {
12585     out.write(cb + "(");
12586 }
12587 out.print(dataBlock.toJsonString());
12588 if (scriptTag) {
12589     out.write(");");
12590 }
12591 </pre></code>
12592  *
12593  * @constructor
12594  * @param {Object} config A configuration object.
12595  */
12596 Roo.data.ScriptTagProxy = function(config){
12597     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12598     Roo.apply(this, config);
12599     this.head = document.getElementsByTagName("head")[0];
12600 };
12601
12602 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12603
12604 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12605     /**
12606      * @cfg {String} url The URL from which to request the data object.
12607      */
12608     /**
12609      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12610      */
12611     timeout : 30000,
12612     /**
12613      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12614      * the server the name of the callback function set up by the load call to process the returned data object.
12615      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12616      * javascript output which calls this named function passing the data object as its only parameter.
12617      */
12618     callbackParam : "callback",
12619     /**
12620      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12621      * name to the request.
12622      */
12623     nocache : true,
12624
12625     /**
12626      * Load data from the configured URL, read the data object into
12627      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12628      * process that block using the passed callback.
12629      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12630      * for the request to the remote server.
12631      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12632      * object into a block of Roo.data.Records.
12633      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12634      * The function must be passed <ul>
12635      * <li>The Record block object</li>
12636      * <li>The "arg" argument from the load function</li>
12637      * <li>A boolean success indicator</li>
12638      * </ul>
12639      * @param {Object} scope The scope in which to call the callback
12640      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12641      */
12642     load : function(params, reader, callback, scope, arg){
12643         if(this.fireEvent("beforeload", this, params) !== false){
12644
12645             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12646
12647             var url = this.url;
12648             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12649             if(this.nocache){
12650                 url += "&_dc=" + (new Date().getTime());
12651             }
12652             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12653             var trans = {
12654                 id : transId,
12655                 cb : "stcCallback"+transId,
12656                 scriptId : "stcScript"+transId,
12657                 params : params,
12658                 arg : arg,
12659                 url : url,
12660                 callback : callback,
12661                 scope : scope,
12662                 reader : reader
12663             };
12664             var conn = this;
12665
12666             window[trans.cb] = function(o){
12667                 conn.handleResponse(o, trans);
12668             };
12669
12670             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12671
12672             if(this.autoAbort !== false){
12673                 this.abort();
12674             }
12675
12676             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12677
12678             var script = document.createElement("script");
12679             script.setAttribute("src", url);
12680             script.setAttribute("type", "text/javascript");
12681             script.setAttribute("id", trans.scriptId);
12682             this.head.appendChild(script);
12683
12684             this.trans = trans;
12685         }else{
12686             callback.call(scope||this, null, arg, false);
12687         }
12688     },
12689
12690     // private
12691     isLoading : function(){
12692         return this.trans ? true : false;
12693     },
12694
12695     /**
12696      * Abort the current server request.
12697      */
12698     abort : function(){
12699         if(this.isLoading()){
12700             this.destroyTrans(this.trans);
12701         }
12702     },
12703
12704     // private
12705     destroyTrans : function(trans, isLoaded){
12706         this.head.removeChild(document.getElementById(trans.scriptId));
12707         clearTimeout(trans.timeoutId);
12708         if(isLoaded){
12709             window[trans.cb] = undefined;
12710             try{
12711                 delete window[trans.cb];
12712             }catch(e){}
12713         }else{
12714             // if hasn't been loaded, wait for load to remove it to prevent script error
12715             window[trans.cb] = function(){
12716                 window[trans.cb] = undefined;
12717                 try{
12718                     delete window[trans.cb];
12719                 }catch(e){}
12720             };
12721         }
12722     },
12723
12724     // private
12725     handleResponse : function(o, trans){
12726         this.trans = false;
12727         this.destroyTrans(trans, true);
12728         var result;
12729         try {
12730             result = trans.reader.readRecords(o);
12731         }catch(e){
12732             this.fireEvent("loadexception", this, o, trans.arg, e);
12733             trans.callback.call(trans.scope||window, null, trans.arg, false);
12734             return;
12735         }
12736         this.fireEvent("load", this, o, trans.arg);
12737         trans.callback.call(trans.scope||window, result, trans.arg, true);
12738     },
12739
12740     // private
12741     handleFailure : function(trans){
12742         this.trans = false;
12743         this.destroyTrans(trans, false);
12744         this.fireEvent("loadexception", this, null, trans.arg);
12745         trans.callback.call(trans.scope||window, null, trans.arg, false);
12746     }
12747 });/*
12748  * Based on:
12749  * Ext JS Library 1.1.1
12750  * Copyright(c) 2006-2007, Ext JS, LLC.
12751  *
12752  * Originally Released Under LGPL - original licence link has changed is not relivant.
12753  *
12754  * Fork - LGPL
12755  * <script type="text/javascript">
12756  */
12757
12758 /**
12759  * @class Roo.data.JsonReader
12760  * @extends Roo.data.DataReader
12761  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12762  * based on mappings in a provided Roo.data.Record constructor.
12763  * 
12764  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12765  * in the reply previously. 
12766  * 
12767  * <p>
12768  * Example code:
12769  * <pre><code>
12770 var RecordDef = Roo.data.Record.create([
12771     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12772     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12773 ]);
12774 var myReader = new Roo.data.JsonReader({
12775     totalProperty: "results",    // The property which contains the total dataset size (optional)
12776     root: "rows",                // The property which contains an Array of row objects
12777     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12778 }, RecordDef);
12779 </code></pre>
12780  * <p>
12781  * This would consume a JSON file like this:
12782  * <pre><code>
12783 { 'results': 2, 'rows': [
12784     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12785     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12786 }
12787 </code></pre>
12788  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12789  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12790  * paged from the remote server.
12791  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12792  * @cfg {String} root name of the property which contains the Array of row objects.
12793  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12794  * @cfg {Array} fields Array of field definition objects
12795  * @constructor
12796  * Create a new JsonReader
12797  * @param {Object} meta Metadata configuration options
12798  * @param {Object} recordType Either an Array of field definition objects,
12799  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12800  */
12801 Roo.data.JsonReader = function(meta, recordType){
12802     
12803     meta = meta || {};
12804     // set some defaults:
12805     Roo.applyIf(meta, {
12806         totalProperty: 'total',
12807         successProperty : 'success',
12808         root : 'data',
12809         id : 'id'
12810     });
12811     
12812     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12813 };
12814 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12815     
12816     /**
12817      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12818      * Used by Store query builder to append _requestMeta to params.
12819      * 
12820      */
12821     metaFromRemote : false,
12822     /**
12823      * This method is only used by a DataProxy which has retrieved data from a remote server.
12824      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12825      * @return {Object} data A data block which is used by an Roo.data.Store object as
12826      * a cache of Roo.data.Records.
12827      */
12828     read : function(response){
12829         var json = response.responseText;
12830        
12831         var o = /* eval:var:o */ eval("("+json+")");
12832         if(!o) {
12833             throw {message: "JsonReader.read: Json object not found"};
12834         }
12835         
12836         if(o.metaData){
12837             
12838             delete this.ef;
12839             this.metaFromRemote = true;
12840             this.meta = o.metaData;
12841             this.recordType = Roo.data.Record.create(o.metaData.fields);
12842             this.onMetaChange(this.meta, this.recordType, o);
12843         }
12844         return this.readRecords(o);
12845     },
12846
12847     // private function a store will implement
12848     onMetaChange : function(meta, recordType, o){
12849
12850     },
12851
12852     /**
12853          * @ignore
12854          */
12855     simpleAccess: function(obj, subsc) {
12856         return obj[subsc];
12857     },
12858
12859         /**
12860          * @ignore
12861          */
12862     getJsonAccessor: function(){
12863         var re = /[\[\.]/;
12864         return function(expr) {
12865             try {
12866                 return(re.test(expr))
12867                     ? new Function("obj", "return obj." + expr)
12868                     : function(obj){
12869                         return obj[expr];
12870                     };
12871             } catch(e){}
12872             return Roo.emptyFn;
12873         };
12874     }(),
12875
12876     /**
12877      * Create a data block containing Roo.data.Records from an XML document.
12878      * @param {Object} o An object which contains an Array of row objects in the property specified
12879      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12880      * which contains the total size of the dataset.
12881      * @return {Object} data A data block which is used by an Roo.data.Store object as
12882      * a cache of Roo.data.Records.
12883      */
12884     readRecords : function(o){
12885         /**
12886          * After any data loads, the raw JSON data is available for further custom processing.
12887          * @type Object
12888          */
12889         this.o = o;
12890         var s = this.meta, Record = this.recordType,
12891             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12892
12893 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12894         if (!this.ef) {
12895             if(s.totalProperty) {
12896                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12897                 }
12898                 if(s.successProperty) {
12899                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12900                 }
12901                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12902                 if (s.id) {
12903                         var g = this.getJsonAccessor(s.id);
12904                         this.getId = function(rec) {
12905                                 var r = g(rec);  
12906                                 return (r === undefined || r === "") ? null : r;
12907                         };
12908                 } else {
12909                         this.getId = function(){return null;};
12910                 }
12911             this.ef = [];
12912             for(var jj = 0; jj < fl; jj++){
12913                 f = fi[jj];
12914                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12915                 this.ef[jj] = this.getJsonAccessor(map);
12916             }
12917         }
12918
12919         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12920         if(s.totalProperty){
12921             var vt = parseInt(this.getTotal(o), 10);
12922             if(!isNaN(vt)){
12923                 totalRecords = vt;
12924             }
12925         }
12926         if(s.successProperty){
12927             var vs = this.getSuccess(o);
12928             if(vs === false || vs === 'false'){
12929                 success = false;
12930             }
12931         }
12932         var records = [];
12933         for(var i = 0; i < c; i++){
12934                 var n = root[i];
12935             var values = {};
12936             var id = this.getId(n);
12937             for(var j = 0; j < fl; j++){
12938                 f = fi[j];
12939             var v = this.ef[j](n);
12940             if (!f.convert) {
12941                 Roo.log('missing convert for ' + f.name);
12942                 Roo.log(f);
12943                 continue;
12944             }
12945             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12946             }
12947             var record = new Record(values, id);
12948             record.json = n;
12949             records[i] = record;
12950         }
12951         return {
12952             raw : o,
12953             success : success,
12954             records : records,
12955             totalRecords : totalRecords
12956         };
12957     }
12958 });/*
12959  * Based on:
12960  * Ext JS Library 1.1.1
12961  * Copyright(c) 2006-2007, Ext JS, LLC.
12962  *
12963  * Originally Released Under LGPL - original licence link has changed is not relivant.
12964  *
12965  * Fork - LGPL
12966  * <script type="text/javascript">
12967  */
12968
12969 /**
12970  * @class Roo.data.ArrayReader
12971  * @extends Roo.data.DataReader
12972  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12973  * Each element of that Array represents a row of data fields. The
12974  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12975  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12976  * <p>
12977  * Example code:.
12978  * <pre><code>
12979 var RecordDef = Roo.data.Record.create([
12980     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12981     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12982 ]);
12983 var myReader = new Roo.data.ArrayReader({
12984     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12985 }, RecordDef);
12986 </code></pre>
12987  * <p>
12988  * This would consume an Array like this:
12989  * <pre><code>
12990 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12991   </code></pre>
12992  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12993  * @constructor
12994  * Create a new JsonReader
12995  * @param {Object} meta Metadata configuration options.
12996  * @param {Object} recordType Either an Array of field definition objects
12997  * @cfg {Array} fields Array of field definition objects
12998  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12999  * as specified to {@link Roo.data.Record#create},
13000  * or an {@link Roo.data.Record} object
13001  * created using {@link Roo.data.Record#create}.
13002  */
13003 Roo.data.ArrayReader = function(meta, recordType){
13004     
13005      
13006     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13007 };
13008
13009 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13010     /**
13011      * Create a data block containing Roo.data.Records from an XML document.
13012      * @param {Object} o An Array of row objects which represents the dataset.
13013      * @return {Object} data A data block which is used by an Roo.data.Store object as
13014      * a cache of Roo.data.Records.
13015      */
13016     readRecords : function(o){
13017         var sid = this.meta ? this.meta.id : null;
13018         var recordType = this.recordType, fields = recordType.prototype.fields;
13019         var records = [];
13020         var root = o;
13021             for(var i = 0; i < root.length; i++){
13022                     var n = root[i];
13023                 var values = {};
13024                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13025                 for(var j = 0, jlen = fields.length; j < jlen; j++){
13026                 var f = fields.items[j];
13027                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13028                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13029                 v = f.convert(v);
13030                 values[f.name] = v;
13031             }
13032                 var record = new recordType(values, id);
13033                 record.json = n;
13034                 records[records.length] = record;
13035             }
13036             return {
13037                 records : records,
13038                 totalRecords : records.length
13039             };
13040     }
13041 });/*
13042  * - LGPL
13043  * * 
13044  */
13045
13046 /**
13047  * @class Roo.bootstrap.ComboBox
13048  * @extends Roo.bootstrap.TriggerField
13049  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13050  * @cfg {Boolean} append (true|false) default false
13051  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13052  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13053  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13054  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13055  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13056  * @cfg {Boolean} animate default true
13057  * @cfg {Boolean} emptyResultText only for touch device
13058  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13059  * @cfg {String} emptyTitle default ''
13060  * @constructor
13061  * Create a new ComboBox.
13062  * @param {Object} config Configuration options
13063  */
13064 Roo.bootstrap.ComboBox = function(config){
13065     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13066     this.addEvents({
13067         /**
13068          * @event expand
13069          * Fires when the dropdown list is expanded
13070         * @param {Roo.bootstrap.ComboBox} combo This combo box
13071         */
13072         'expand' : true,
13073         /**
13074          * @event collapse
13075          * Fires when the dropdown list is collapsed
13076         * @param {Roo.bootstrap.ComboBox} combo This combo box
13077         */
13078         'collapse' : true,
13079         /**
13080          * @event beforeselect
13081          * Fires before a list item is selected. Return false to cancel the selection.
13082         * @param {Roo.bootstrap.ComboBox} combo This combo box
13083         * @param {Roo.data.Record} record The data record returned from the underlying store
13084         * @param {Number} index The index of the selected item in the dropdown list
13085         */
13086         'beforeselect' : true,
13087         /**
13088          * @event select
13089          * Fires when a list item is selected
13090         * @param {Roo.bootstrap.ComboBox} combo This combo box
13091         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13092         * @param {Number} index The index of the selected item in the dropdown list
13093         */
13094         'select' : true,
13095         /**
13096          * @event beforequery
13097          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13098          * The event object passed has these properties:
13099         * @param {Roo.bootstrap.ComboBox} combo This combo box
13100         * @param {String} query The query
13101         * @param {Boolean} forceAll true to force "all" query
13102         * @param {Boolean} cancel true to cancel the query
13103         * @param {Object} e The query event object
13104         */
13105         'beforequery': true,
13106          /**
13107          * @event add
13108          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13109         * @param {Roo.bootstrap.ComboBox} combo This combo box
13110         */
13111         'add' : true,
13112         /**
13113          * @event edit
13114          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13115         * @param {Roo.bootstrap.ComboBox} combo This combo box
13116         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13117         */
13118         'edit' : true,
13119         /**
13120          * @event remove
13121          * Fires when the remove value from the combobox array
13122         * @param {Roo.bootstrap.ComboBox} combo This combo box
13123         */
13124         'remove' : true,
13125         /**
13126          * @event afterremove
13127          * Fires when the remove value from the combobox array
13128         * @param {Roo.bootstrap.ComboBox} combo This combo box
13129         */
13130         'afterremove' : true,
13131         /**
13132          * @event specialfilter
13133          * Fires when specialfilter
13134             * @param {Roo.bootstrap.ComboBox} combo This combo box
13135             */
13136         'specialfilter' : true,
13137         /**
13138          * @event tick
13139          * Fires when tick the element
13140             * @param {Roo.bootstrap.ComboBox} combo This combo box
13141             */
13142         'tick' : true,
13143         /**
13144          * @event touchviewdisplay
13145          * Fires when touch view require special display (default is using displayField)
13146             * @param {Roo.bootstrap.ComboBox} combo This combo box
13147             * @param {Object} cfg set html .
13148             */
13149         'touchviewdisplay' : true
13150         
13151     });
13152     
13153     this.item = [];
13154     this.tickItems = [];
13155     
13156     this.selectedIndex = -1;
13157     if(this.mode == 'local'){
13158         if(config.queryDelay === undefined){
13159             this.queryDelay = 10;
13160         }
13161         if(config.minChars === undefined){
13162             this.minChars = 0;
13163         }
13164     }
13165 };
13166
13167 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13168      
13169     /**
13170      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13171      * rendering into an Roo.Editor, defaults to false)
13172      */
13173     /**
13174      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13175      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13176      */
13177     /**
13178      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13179      */
13180     /**
13181      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13182      * the dropdown list (defaults to undefined, with no header element)
13183      */
13184
13185      /**
13186      * @cfg {String/Roo.Template} tpl The template to use to render the output
13187      */
13188      
13189      /**
13190      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13191      */
13192     listWidth: undefined,
13193     /**
13194      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13195      * mode = 'remote' or 'text' if mode = 'local')
13196      */
13197     displayField: undefined,
13198     
13199     /**
13200      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13201      * mode = 'remote' or 'value' if mode = 'local'). 
13202      * Note: use of a valueField requires the user make a selection
13203      * in order for a value to be mapped.
13204      */
13205     valueField: undefined,
13206     /**
13207      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13208      */
13209     modalTitle : '',
13210     
13211     /**
13212      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13213      * field's data value (defaults to the underlying DOM element's name)
13214      */
13215     hiddenName: undefined,
13216     /**
13217      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13218      */
13219     listClass: '',
13220     /**
13221      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13222      */
13223     selectedClass: 'active',
13224     
13225     /**
13226      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13227      */
13228     shadow:'sides',
13229     /**
13230      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13231      * anchor positions (defaults to 'tl-bl')
13232      */
13233     listAlign: 'tl-bl?',
13234     /**
13235      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13236      */
13237     maxHeight: 300,
13238     /**
13239      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13240      * query specified by the allQuery config option (defaults to 'query')
13241      */
13242     triggerAction: 'query',
13243     /**
13244      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13245      * (defaults to 4, does not apply if editable = false)
13246      */
13247     minChars : 4,
13248     /**
13249      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13250      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13251      */
13252     typeAhead: false,
13253     /**
13254      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13255      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13256      */
13257     queryDelay: 500,
13258     /**
13259      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13260      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13261      */
13262     pageSize: 0,
13263     /**
13264      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13265      * when editable = true (defaults to false)
13266      */
13267     selectOnFocus:false,
13268     /**
13269      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13270      */
13271     queryParam: 'query',
13272     /**
13273      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13274      * when mode = 'remote' (defaults to 'Loading...')
13275      */
13276     loadingText: 'Loading...',
13277     /**
13278      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13279      */
13280     resizable: false,
13281     /**
13282      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13283      */
13284     handleHeight : 8,
13285     /**
13286      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13287      * traditional select (defaults to true)
13288      */
13289     editable: true,
13290     /**
13291      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13292      */
13293     allQuery: '',
13294     /**
13295      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13296      */
13297     mode: 'remote',
13298     /**
13299      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13300      * listWidth has a higher value)
13301      */
13302     minListWidth : 70,
13303     /**
13304      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13305      * allow the user to set arbitrary text into the field (defaults to false)
13306      */
13307     forceSelection:false,
13308     /**
13309      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13310      * if typeAhead = true (defaults to 250)
13311      */
13312     typeAheadDelay : 250,
13313     /**
13314      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13315      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13316      */
13317     valueNotFoundText : undefined,
13318     /**
13319      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13320      */
13321     blockFocus : false,
13322     
13323     /**
13324      * @cfg {Boolean} disableClear Disable showing of clear button.
13325      */
13326     disableClear : false,
13327     /**
13328      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13329      */
13330     alwaysQuery : false,
13331     
13332     /**
13333      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13334      */
13335     multiple : false,
13336     
13337     /**
13338      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13339      */
13340     invalidClass : "has-warning",
13341     
13342     /**
13343      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13344      */
13345     validClass : "has-success",
13346     
13347     /**
13348      * @cfg {Boolean} specialFilter (true|false) special filter default false
13349      */
13350     specialFilter : false,
13351     
13352     /**
13353      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13354      */
13355     mobileTouchView : true,
13356     
13357     /**
13358      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13359      */
13360     useNativeIOS : false,
13361     
13362     /**
13363      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13364      */
13365     mobile_restrict_height : false,
13366     
13367     ios_options : false,
13368     
13369     //private
13370     addicon : false,
13371     editicon: false,
13372     
13373     page: 0,
13374     hasQuery: false,
13375     append: false,
13376     loadNext: false,
13377     autoFocus : true,
13378     tickable : false,
13379     btnPosition : 'right',
13380     triggerList : true,
13381     showToggleBtn : true,
13382     animate : true,
13383     emptyResultText: 'Empty',
13384     triggerText : 'Select',
13385     emptyTitle : '',
13386     
13387     // element that contains real text value.. (when hidden is used..)
13388     
13389     getAutoCreate : function()
13390     {   
13391         var cfg = false;
13392         //render
13393         /*
13394          * Render classic select for iso
13395          */
13396         
13397         if(Roo.isIOS && this.useNativeIOS){
13398             cfg = this.getAutoCreateNativeIOS();
13399             return cfg;
13400         }
13401         
13402         /*
13403          * Touch Devices
13404          */
13405         
13406         if(Roo.isTouch && this.mobileTouchView){
13407             cfg = this.getAutoCreateTouchView();
13408             return cfg;;
13409         }
13410         
13411         /*
13412          *  Normal ComboBox
13413          */
13414         if(!this.tickable){
13415             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13416             return cfg;
13417         }
13418         
13419         /*
13420          *  ComboBox with tickable selections
13421          */
13422              
13423         var align = this.labelAlign || this.parentLabelAlign();
13424         
13425         cfg = {
13426             cls : 'form-group roo-combobox-tickable' //input-group
13427         };
13428         
13429         var btn_text_select = '';
13430         var btn_text_done = '';
13431         var btn_text_cancel = '';
13432         
13433         if (this.btn_text_show) {
13434             btn_text_select = 'Select';
13435             btn_text_done = 'Done';
13436             btn_text_cancel = 'Cancel'; 
13437         }
13438         
13439         var buttons = {
13440             tag : 'div',
13441             cls : 'tickable-buttons',
13442             cn : [
13443                 {
13444                     tag : 'button',
13445                     type : 'button',
13446                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13447                     //html : this.triggerText
13448                     html: btn_text_select
13449                 },
13450                 {
13451                     tag : 'button',
13452                     type : 'button',
13453                     name : 'ok',
13454                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13455                     //html : 'Done'
13456                     html: btn_text_done
13457                 },
13458                 {
13459                     tag : 'button',
13460                     type : 'button',
13461                     name : 'cancel',
13462                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13463                     //html : 'Cancel'
13464                     html: btn_text_cancel
13465                 }
13466             ]
13467         };
13468         
13469         if(this.editable){
13470             buttons.cn.unshift({
13471                 tag: 'input',
13472                 cls: 'roo-select2-search-field-input'
13473             });
13474         }
13475         
13476         var _this = this;
13477         
13478         Roo.each(buttons.cn, function(c){
13479             if (_this.size) {
13480                 c.cls += ' btn-' + _this.size;
13481             }
13482
13483             if (_this.disabled) {
13484                 c.disabled = true;
13485             }
13486         });
13487         
13488         var box = {
13489             tag: 'div',
13490             style : 'display: contents',
13491             cn: [
13492                 {
13493                     tag: 'input',
13494                     type : 'hidden',
13495                     cls: 'form-hidden-field'
13496                 },
13497                 {
13498                     tag: 'ul',
13499                     cls: 'roo-select2-choices',
13500                     cn:[
13501                         {
13502                             tag: 'li',
13503                             cls: 'roo-select2-search-field',
13504                             cn: [
13505                                 buttons
13506                             ]
13507                         }
13508                     ]
13509                 }
13510             ]
13511         };
13512         
13513         var combobox = {
13514             cls: 'roo-select2-container input-group roo-select2-container-multi',
13515             cn: [
13516                 
13517                 box
13518 //                {
13519 //                    tag: 'ul',
13520 //                    cls: 'typeahead typeahead-long dropdown-menu',
13521 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13522 //                }
13523             ]
13524         };
13525         
13526         if(this.hasFeedback && !this.allowBlank){
13527             
13528             var feedback = {
13529                 tag: 'span',
13530                 cls: 'glyphicon form-control-feedback'
13531             };
13532
13533             combobox.cn.push(feedback);
13534         }
13535         
13536         var indicator = {
13537             tag : 'i',
13538             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13539             tooltip : 'This field is required'
13540         };
13541         if (Roo.bootstrap.version == 4) {
13542             indicator = {
13543                 tag : 'i',
13544                 style : 'display:none'
13545             };
13546         }
13547         if (align ==='left' && this.fieldLabel.length) {
13548             
13549             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13550             
13551             cfg.cn = [
13552                 indicator,
13553                 {
13554                     tag: 'label',
13555                     'for' :  id,
13556                     cls : 'control-label col-form-label',
13557                     html : this.fieldLabel
13558
13559                 },
13560                 {
13561                     cls : "", 
13562                     cn: [
13563                         combobox
13564                     ]
13565                 }
13566
13567             ];
13568             
13569             var labelCfg = cfg.cn[1];
13570             var contentCfg = cfg.cn[2];
13571             
13572
13573             if(this.indicatorpos == 'right'){
13574                 
13575                 cfg.cn = [
13576                     {
13577                         tag: 'label',
13578                         'for' :  id,
13579                         cls : 'control-label col-form-label',
13580                         cn : [
13581                             {
13582                                 tag : 'span',
13583                                 html : this.fieldLabel
13584                             },
13585                             indicator
13586                         ]
13587                     },
13588                     {
13589                         cls : "",
13590                         cn: [
13591                             combobox
13592                         ]
13593                     }
13594
13595                 ];
13596                 
13597                 
13598                 
13599                 labelCfg = cfg.cn[0];
13600                 contentCfg = cfg.cn[1];
13601             
13602             }
13603             
13604             if(this.labelWidth > 12){
13605                 labelCfg.style = "width: " + this.labelWidth + 'px';
13606             }
13607             
13608             if(this.labelWidth < 13 && this.labelmd == 0){
13609                 this.labelmd = this.labelWidth;
13610             }
13611             
13612             if(this.labellg > 0){
13613                 labelCfg.cls += ' col-lg-' + this.labellg;
13614                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13615             }
13616             
13617             if(this.labelmd > 0){
13618                 labelCfg.cls += ' col-md-' + this.labelmd;
13619                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13620             }
13621             
13622             if(this.labelsm > 0){
13623                 labelCfg.cls += ' col-sm-' + this.labelsm;
13624                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13625             }
13626             
13627             if(this.labelxs > 0){
13628                 labelCfg.cls += ' col-xs-' + this.labelxs;
13629                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13630             }
13631                 
13632                 
13633         } else if ( this.fieldLabel.length) {
13634 //                Roo.log(" label");
13635                  cfg.cn = [
13636                    indicator,
13637                     {
13638                         tag: 'label',
13639                         //cls : 'input-group-addon',
13640                         html : this.fieldLabel
13641                     },
13642                     combobox
13643                 ];
13644                 
13645                 if(this.indicatorpos == 'right'){
13646                     cfg.cn = [
13647                         {
13648                             tag: 'label',
13649                             //cls : 'input-group-addon',
13650                             html : this.fieldLabel
13651                         },
13652                         indicator,
13653                         combobox
13654                     ];
13655                     
13656                 }
13657
13658         } else {
13659             
13660 //                Roo.log(" no label && no align");
13661                 cfg = combobox
13662                      
13663                 
13664         }
13665          
13666         var settings=this;
13667         ['xs','sm','md','lg'].map(function(size){
13668             if (settings[size]) {
13669                 cfg.cls += ' col-' + size + '-' + settings[size];
13670             }
13671         });
13672         
13673         return cfg;
13674         
13675     },
13676     
13677     _initEventsCalled : false,
13678     
13679     // private
13680     initEvents: function()
13681     {   
13682         if (this._initEventsCalled) { // as we call render... prevent looping...
13683             return;
13684         }
13685         this._initEventsCalled = true;
13686         
13687         if (!this.store) {
13688             throw "can not find store for combo";
13689         }
13690         
13691         this.indicator = this.indicatorEl();
13692         
13693         this.store = Roo.factory(this.store, Roo.data);
13694         this.store.parent = this;
13695         
13696         // if we are building from html. then this element is so complex, that we can not really
13697         // use the rendered HTML.
13698         // so we have to trash and replace the previous code.
13699         if (Roo.XComponent.build_from_html) {
13700             // remove this element....
13701             var e = this.el.dom, k=0;
13702             while (e ) { e = e.previousSibling;  ++k;}
13703
13704             this.el.remove();
13705             
13706             this.el=false;
13707             this.rendered = false;
13708             
13709             this.render(this.parent().getChildContainer(true), k);
13710         }
13711         
13712         if(Roo.isIOS && this.useNativeIOS){
13713             this.initIOSView();
13714             return;
13715         }
13716         
13717         /*
13718          * Touch Devices
13719          */
13720         
13721         if(Roo.isTouch && this.mobileTouchView){
13722             this.initTouchView();
13723             return;
13724         }
13725         
13726         if(this.tickable){
13727             this.initTickableEvents();
13728             return;
13729         }
13730         
13731         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13732         
13733         if(this.hiddenName){
13734             
13735             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13736             
13737             this.hiddenField.dom.value =
13738                 this.hiddenValue !== undefined ? this.hiddenValue :
13739                 this.value !== undefined ? this.value : '';
13740
13741             // prevent input submission
13742             this.el.dom.removeAttribute('name');
13743             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13744              
13745              
13746         }
13747         //if(Roo.isGecko){
13748         //    this.el.dom.setAttribute('autocomplete', 'off');
13749         //}
13750         
13751         var cls = 'x-combo-list';
13752         
13753         //this.list = new Roo.Layer({
13754         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13755         //});
13756         
13757         var _this = this;
13758         
13759         (function(){
13760             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13761             _this.list.setWidth(lw);
13762         }).defer(100);
13763         
13764         this.list.on('mouseover', this.onViewOver, this);
13765         this.list.on('mousemove', this.onViewMove, this);
13766         this.list.on('scroll', this.onViewScroll, this);
13767         
13768         /*
13769         this.list.swallowEvent('mousewheel');
13770         this.assetHeight = 0;
13771
13772         if(this.title){
13773             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13774             this.assetHeight += this.header.getHeight();
13775         }
13776
13777         this.innerList = this.list.createChild({cls:cls+'-inner'});
13778         this.innerList.on('mouseover', this.onViewOver, this);
13779         this.innerList.on('mousemove', this.onViewMove, this);
13780         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13781         
13782         if(this.allowBlank && !this.pageSize && !this.disableClear){
13783             this.footer = this.list.createChild({cls:cls+'-ft'});
13784             this.pageTb = new Roo.Toolbar(this.footer);
13785            
13786         }
13787         if(this.pageSize){
13788             this.footer = this.list.createChild({cls:cls+'-ft'});
13789             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13790                     {pageSize: this.pageSize});
13791             
13792         }
13793         
13794         if (this.pageTb && this.allowBlank && !this.disableClear) {
13795             var _this = this;
13796             this.pageTb.add(new Roo.Toolbar.Fill(), {
13797                 cls: 'x-btn-icon x-btn-clear',
13798                 text: '&#160;',
13799                 handler: function()
13800                 {
13801                     _this.collapse();
13802                     _this.clearValue();
13803                     _this.onSelect(false, -1);
13804                 }
13805             });
13806         }
13807         if (this.footer) {
13808             this.assetHeight += this.footer.getHeight();
13809         }
13810         */
13811             
13812         if(!this.tpl){
13813             this.tpl = Roo.bootstrap.version == 4 ?
13814                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13815                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13816         }
13817
13818         this.view = new Roo.View(this.list, this.tpl, {
13819             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13820         });
13821         //this.view.wrapEl.setDisplayed(false);
13822         this.view.on('click', this.onViewClick, this);
13823         
13824         
13825         this.store.on('beforeload', this.onBeforeLoad, this);
13826         this.store.on('load', this.onLoad, this);
13827         this.store.on('loadexception', this.onLoadException, this);
13828         /*
13829         if(this.resizable){
13830             this.resizer = new Roo.Resizable(this.list,  {
13831                pinned:true, handles:'se'
13832             });
13833             this.resizer.on('resize', function(r, w, h){
13834                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13835                 this.listWidth = w;
13836                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13837                 this.restrictHeight();
13838             }, this);
13839             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13840         }
13841         */
13842         if(!this.editable){
13843             this.editable = true;
13844             this.setEditable(false);
13845         }
13846         
13847         /*
13848         
13849         if (typeof(this.events.add.listeners) != 'undefined') {
13850             
13851             this.addicon = this.wrap.createChild(
13852                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13853        
13854             this.addicon.on('click', function(e) {
13855                 this.fireEvent('add', this);
13856             }, this);
13857         }
13858         if (typeof(this.events.edit.listeners) != 'undefined') {
13859             
13860             this.editicon = this.wrap.createChild(
13861                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13862             if (this.addicon) {
13863                 this.editicon.setStyle('margin-left', '40px');
13864             }
13865             this.editicon.on('click', function(e) {
13866                 
13867                 // we fire even  if inothing is selected..
13868                 this.fireEvent('edit', this, this.lastData );
13869                 
13870             }, this);
13871         }
13872         */
13873         
13874         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13875             "up" : function(e){
13876                 this.inKeyMode = true;
13877                 this.selectPrev();
13878             },
13879
13880             "down" : function(e){
13881                 if(!this.isExpanded()){
13882                     this.onTriggerClick();
13883                 }else{
13884                     this.inKeyMode = true;
13885                     this.selectNext();
13886                 }
13887             },
13888
13889             "enter" : function(e){
13890 //                this.onViewClick();
13891                 //return true;
13892                 this.collapse();
13893                 
13894                 if(this.fireEvent("specialkey", this, e)){
13895                     this.onViewClick(false);
13896                 }
13897                 
13898                 return true;
13899             },
13900
13901             "esc" : function(e){
13902                 this.collapse();
13903             },
13904
13905             "tab" : function(e){
13906                 this.collapse();
13907                 
13908                 if(this.fireEvent("specialkey", this, e)){
13909                     this.onViewClick(false);
13910                 }
13911                 
13912                 return true;
13913             },
13914
13915             scope : this,
13916
13917             doRelay : function(foo, bar, hname){
13918                 if(hname == 'down' || this.scope.isExpanded()){
13919                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13920                 }
13921                 return true;
13922             },
13923
13924             forceKeyDown: true
13925         });
13926         
13927         
13928         this.queryDelay = Math.max(this.queryDelay || 10,
13929                 this.mode == 'local' ? 10 : 250);
13930         
13931         
13932         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13933         
13934         if(this.typeAhead){
13935             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13936         }
13937         if(this.editable !== false){
13938             this.inputEl().on("keyup", this.onKeyUp, this);
13939         }
13940         if(this.forceSelection){
13941             this.inputEl().on('blur', this.doForce, this);
13942         }
13943         
13944         if(this.multiple){
13945             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13946             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13947         }
13948     },
13949     
13950     initTickableEvents: function()
13951     {   
13952         this.createList();
13953         
13954         if(this.hiddenName){
13955             
13956             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13957             
13958             this.hiddenField.dom.value =
13959                 this.hiddenValue !== undefined ? this.hiddenValue :
13960                 this.value !== undefined ? this.value : '';
13961
13962             // prevent input submission
13963             this.el.dom.removeAttribute('name');
13964             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13965              
13966              
13967         }
13968         
13969 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13970         
13971         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13972         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13973         if(this.triggerList){
13974             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13975         }
13976          
13977         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13978         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13979         
13980         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13981         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13982         
13983         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13984         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13985         
13986         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13987         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13988         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13989         
13990         this.okBtn.hide();
13991         this.cancelBtn.hide();
13992         
13993         var _this = this;
13994         
13995         (function(){
13996             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13997             _this.list.setWidth(lw);
13998         }).defer(100);
13999         
14000         this.list.on('mouseover', this.onViewOver, this);
14001         this.list.on('mousemove', this.onViewMove, this);
14002         
14003         this.list.on('scroll', this.onViewScroll, this);
14004         
14005         if(!this.tpl){
14006             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14007                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14008         }
14009
14010         this.view = new Roo.View(this.list, this.tpl, {
14011             singleSelect:true,
14012             tickable:true,
14013             parent:this,
14014             store: this.store,
14015             selectedClass: this.selectedClass
14016         });
14017         
14018         //this.view.wrapEl.setDisplayed(false);
14019         this.view.on('click', this.onViewClick, this);
14020         
14021         
14022         
14023         this.store.on('beforeload', this.onBeforeLoad, this);
14024         this.store.on('load', this.onLoad, this);
14025         this.store.on('loadexception', this.onLoadException, this);
14026         
14027         if(this.editable){
14028             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14029                 "up" : function(e){
14030                     this.inKeyMode = true;
14031                     this.selectPrev();
14032                 },
14033
14034                 "down" : function(e){
14035                     this.inKeyMode = true;
14036                     this.selectNext();
14037                 },
14038
14039                 "enter" : function(e){
14040                     if(this.fireEvent("specialkey", this, e)){
14041                         this.onViewClick(false);
14042                     }
14043                     
14044                     return true;
14045                 },
14046
14047                 "esc" : function(e){
14048                     this.onTickableFooterButtonClick(e, false, false);
14049                 },
14050
14051                 "tab" : function(e){
14052                     this.fireEvent("specialkey", this, e);
14053                     
14054                     this.onTickableFooterButtonClick(e, false, false);
14055                     
14056                     return true;
14057                 },
14058
14059                 scope : this,
14060
14061                 doRelay : function(e, fn, key){
14062                     if(this.scope.isExpanded()){
14063                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14064                     }
14065                     return true;
14066                 },
14067
14068                 forceKeyDown: true
14069             });
14070         }
14071         
14072         this.queryDelay = Math.max(this.queryDelay || 10,
14073                 this.mode == 'local' ? 10 : 250);
14074         
14075         
14076         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14077         
14078         if(this.typeAhead){
14079             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14080         }
14081         
14082         if(this.editable !== false){
14083             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14084         }
14085         
14086         this.indicator = this.indicatorEl();
14087         
14088         if(this.indicator){
14089             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14090             this.indicator.hide();
14091         }
14092         
14093     },
14094
14095     onDestroy : function(){
14096         if(this.view){
14097             this.view.setStore(null);
14098             this.view.el.removeAllListeners();
14099             this.view.el.remove();
14100             this.view.purgeListeners();
14101         }
14102         if(this.list){
14103             this.list.dom.innerHTML  = '';
14104         }
14105         
14106         if(this.store){
14107             this.store.un('beforeload', this.onBeforeLoad, this);
14108             this.store.un('load', this.onLoad, this);
14109             this.store.un('loadexception', this.onLoadException, this);
14110         }
14111         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14112     },
14113
14114     // private
14115     fireKey : function(e){
14116         if(e.isNavKeyPress() && !this.list.isVisible()){
14117             this.fireEvent("specialkey", this, e);
14118         }
14119     },
14120
14121     // private
14122     onResize: function(w, h){
14123 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14124 //        
14125 //        if(typeof w != 'number'){
14126 //            // we do not handle it!?!?
14127 //            return;
14128 //        }
14129 //        var tw = this.trigger.getWidth();
14130 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14131 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14132 //        var x = w - tw;
14133 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14134 //            
14135 //        //this.trigger.setStyle('left', x+'px');
14136 //        
14137 //        if(this.list && this.listWidth === undefined){
14138 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14139 //            this.list.setWidth(lw);
14140 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14141 //        }
14142         
14143     
14144         
14145     },
14146
14147     /**
14148      * Allow or prevent the user from directly editing the field text.  If false is passed,
14149      * the user will only be able to select from the items defined in the dropdown list.  This method
14150      * is the runtime equivalent of setting the 'editable' config option at config time.
14151      * @param {Boolean} value True to allow the user to directly edit the field text
14152      */
14153     setEditable : function(value){
14154         if(value == this.editable){
14155             return;
14156         }
14157         this.editable = value;
14158         if(!value){
14159             this.inputEl().dom.setAttribute('readOnly', true);
14160             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14161             this.inputEl().addClass('x-combo-noedit');
14162         }else{
14163             this.inputEl().dom.setAttribute('readOnly', false);
14164             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14165             this.inputEl().removeClass('x-combo-noedit');
14166         }
14167     },
14168
14169     // private
14170     
14171     onBeforeLoad : function(combo,opts){
14172         if(!this.hasFocus){
14173             return;
14174         }
14175          if (!opts.add) {
14176             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14177          }
14178         this.restrictHeight();
14179         this.selectedIndex = -1;
14180     },
14181
14182     // private
14183     onLoad : function(){
14184         
14185         this.hasQuery = false;
14186         
14187         if(!this.hasFocus){
14188             return;
14189         }
14190         
14191         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14192             this.loading.hide();
14193         }
14194         
14195         if(this.store.getCount() > 0){
14196             
14197             this.expand();
14198             this.restrictHeight();
14199             if(this.lastQuery == this.allQuery){
14200                 if(this.editable && !this.tickable){
14201                     this.inputEl().dom.select();
14202                 }
14203                 
14204                 if(
14205                     !this.selectByValue(this.value, true) &&
14206                     this.autoFocus && 
14207                     (
14208                         !this.store.lastOptions ||
14209                         typeof(this.store.lastOptions.add) == 'undefined' || 
14210                         this.store.lastOptions.add != true
14211                     )
14212                 ){
14213                     this.select(0, true);
14214                 }
14215             }else{
14216                 if(this.autoFocus){
14217                     this.selectNext();
14218                 }
14219                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14220                     this.taTask.delay(this.typeAheadDelay);
14221                 }
14222             }
14223         }else{
14224             this.onEmptyResults();
14225         }
14226         
14227         //this.el.focus();
14228     },
14229     // private
14230     onLoadException : function()
14231     {
14232         this.hasQuery = false;
14233         
14234         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14235             this.loading.hide();
14236         }
14237         
14238         if(this.tickable && this.editable){
14239             return;
14240         }
14241         
14242         this.collapse();
14243         // only causes errors at present
14244         //Roo.log(this.store.reader.jsonData);
14245         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14246             // fixme
14247             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14248         //}
14249         
14250         
14251     },
14252     // private
14253     onTypeAhead : function(){
14254         if(this.store.getCount() > 0){
14255             var r = this.store.getAt(0);
14256             var newValue = r.data[this.displayField];
14257             var len = newValue.length;
14258             var selStart = this.getRawValue().length;
14259             
14260             if(selStart != len){
14261                 this.setRawValue(newValue);
14262                 this.selectText(selStart, newValue.length);
14263             }
14264         }
14265     },
14266
14267     // private
14268     onSelect : function(record, index){
14269         
14270         if(this.fireEvent('beforeselect', this, record, index) !== false){
14271         
14272             this.setFromData(index > -1 ? record.data : false);
14273             
14274             this.collapse();
14275             this.fireEvent('select', this, record, index);
14276         }
14277     },
14278
14279     /**
14280      * Returns the currently selected field value or empty string if no value is set.
14281      * @return {String} value The selected value
14282      */
14283     getValue : function()
14284     {
14285         if(Roo.isIOS && this.useNativeIOS){
14286             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14287         }
14288         
14289         if(this.multiple){
14290             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14291         }
14292         
14293         if(this.valueField){
14294             return typeof this.value != 'undefined' ? this.value : '';
14295         }else{
14296             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14297         }
14298     },
14299     
14300     getRawValue : function()
14301     {
14302         if(Roo.isIOS && this.useNativeIOS){
14303             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14304         }
14305         
14306         var v = this.inputEl().getValue();
14307         
14308         return v;
14309     },
14310
14311     /**
14312      * Clears any text/value currently set in the field
14313      */
14314     clearValue : function(){
14315         
14316         if(this.hiddenField){
14317             this.hiddenField.dom.value = '';
14318         }
14319         this.value = '';
14320         this.setRawValue('');
14321         this.lastSelectionText = '';
14322         this.lastData = false;
14323         
14324         var close = this.closeTriggerEl();
14325         
14326         if(close){
14327             close.hide();
14328         }
14329         
14330         this.validate();
14331         
14332     },
14333
14334     /**
14335      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14336      * will be displayed in the field.  If the value does not match the data value of an existing item,
14337      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14338      * Otherwise the field will be blank (although the value will still be set).
14339      * @param {String} value The value to match
14340      */
14341     setValue : function(v)
14342     {
14343         if(Roo.isIOS && this.useNativeIOS){
14344             this.setIOSValue(v);
14345             return;
14346         }
14347         
14348         if(this.multiple){
14349             this.syncValue();
14350             return;
14351         }
14352         
14353         var text = v;
14354         if(this.valueField){
14355             var r = this.findRecord(this.valueField, v);
14356             if(r){
14357                 text = r.data[this.displayField];
14358             }else if(this.valueNotFoundText !== undefined){
14359                 text = this.valueNotFoundText;
14360             }
14361         }
14362         this.lastSelectionText = text;
14363         if(this.hiddenField){
14364             this.hiddenField.dom.value = v;
14365         }
14366         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14367         this.value = v;
14368         
14369         var close = this.closeTriggerEl();
14370         
14371         if(close){
14372             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14373         }
14374         
14375         this.validate();
14376     },
14377     /**
14378      * @property {Object} the last set data for the element
14379      */
14380     
14381     lastData : false,
14382     /**
14383      * Sets the value of the field based on a object which is related to the record format for the store.
14384      * @param {Object} value the value to set as. or false on reset?
14385      */
14386     setFromData : function(o){
14387         
14388         if(this.multiple){
14389             this.addItem(o);
14390             return;
14391         }
14392             
14393         var dv = ''; // display value
14394         var vv = ''; // value value..
14395         this.lastData = o;
14396         if (this.displayField) {
14397             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14398         } else {
14399             // this is an error condition!!!
14400             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14401         }
14402         
14403         if(this.valueField){
14404             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14405         }
14406         
14407         var close = this.closeTriggerEl();
14408         
14409         if(close){
14410             if(dv.length || vv * 1 > 0){
14411                 close.show() ;
14412                 this.blockFocus=true;
14413             } else {
14414                 close.hide();
14415             }             
14416         }
14417         
14418         if(this.hiddenField){
14419             this.hiddenField.dom.value = vv;
14420             
14421             this.lastSelectionText = dv;
14422             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14423             this.value = vv;
14424             return;
14425         }
14426         // no hidden field.. - we store the value in 'value', but still display
14427         // display field!!!!
14428         this.lastSelectionText = dv;
14429         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14430         this.value = vv;
14431         
14432         
14433         
14434     },
14435     // private
14436     reset : function(){
14437         // overridden so that last data is reset..
14438         
14439         if(this.multiple){
14440             this.clearItem();
14441             return;
14442         }
14443         
14444         this.setValue(this.originalValue);
14445         //this.clearInvalid();
14446         this.lastData = false;
14447         if (this.view) {
14448             this.view.clearSelections();
14449         }
14450         
14451         this.validate();
14452     },
14453     // private
14454     findRecord : function(prop, value){
14455         var record;
14456         if(this.store.getCount() > 0){
14457             this.store.each(function(r){
14458                 if(r.data[prop] == value){
14459                     record = r;
14460                     return false;
14461                 }
14462                 return true;
14463             });
14464         }
14465         return record;
14466     },
14467     
14468     getName: function()
14469     {
14470         // returns hidden if it's set..
14471         if (!this.rendered) {return ''};
14472         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14473         
14474     },
14475     // private
14476     onViewMove : function(e, t){
14477         this.inKeyMode = false;
14478     },
14479
14480     // private
14481     onViewOver : function(e, t){
14482         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14483             return;
14484         }
14485         var item = this.view.findItemFromChild(t);
14486         
14487         if(item){
14488             var index = this.view.indexOf(item);
14489             this.select(index, false);
14490         }
14491     },
14492
14493     // private
14494     onViewClick : function(view, doFocus, el, e)
14495     {
14496         var index = this.view.getSelectedIndexes()[0];
14497         
14498         var r = this.store.getAt(index);
14499         
14500         if(this.tickable){
14501             
14502             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14503                 return;
14504             }
14505             
14506             var rm = false;
14507             var _this = this;
14508             
14509             Roo.each(this.tickItems, function(v,k){
14510                 
14511                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14512                     Roo.log(v);
14513                     _this.tickItems.splice(k, 1);
14514                     
14515                     if(typeof(e) == 'undefined' && view == false){
14516                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14517                     }
14518                     
14519                     rm = true;
14520                     return;
14521                 }
14522             });
14523             
14524             if(rm){
14525                 return;
14526             }
14527             
14528             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14529                 this.tickItems.push(r.data);
14530             }
14531             
14532             if(typeof(e) == 'undefined' && view == false){
14533                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14534             }
14535                     
14536             return;
14537         }
14538         
14539         if(r){
14540             this.onSelect(r, index);
14541         }
14542         if(doFocus !== false && !this.blockFocus){
14543             this.inputEl().focus();
14544         }
14545     },
14546
14547     // private
14548     restrictHeight : function(){
14549         //this.innerList.dom.style.height = '';
14550         //var inner = this.innerList.dom;
14551         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14552         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14553         //this.list.beginUpdate();
14554         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14555         this.list.alignTo(this.inputEl(), this.listAlign);
14556         this.list.alignTo(this.inputEl(), this.listAlign);
14557         //this.list.endUpdate();
14558     },
14559
14560     // private
14561     onEmptyResults : function(){
14562         
14563         if(this.tickable && this.editable){
14564             this.hasFocus = false;
14565             this.restrictHeight();
14566             return;
14567         }
14568         
14569         this.collapse();
14570     },
14571
14572     /**
14573      * Returns true if the dropdown list is expanded, else false.
14574      */
14575     isExpanded : function(){
14576         return this.list.isVisible();
14577     },
14578
14579     /**
14580      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14581      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14582      * @param {String} value The data value of the item to select
14583      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14584      * selected item if it is not currently in view (defaults to true)
14585      * @return {Boolean} True if the value matched an item in the list, else false
14586      */
14587     selectByValue : function(v, scrollIntoView){
14588         if(v !== undefined && v !== null){
14589             var r = this.findRecord(this.valueField || this.displayField, v);
14590             if(r){
14591                 this.select(this.store.indexOf(r), scrollIntoView);
14592                 return true;
14593             }
14594         }
14595         return false;
14596     },
14597
14598     /**
14599      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14600      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14601      * @param {Number} index The zero-based index of the list item to select
14602      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14603      * selected item if it is not currently in view (defaults to true)
14604      */
14605     select : function(index, scrollIntoView){
14606         this.selectedIndex = index;
14607         this.view.select(index);
14608         if(scrollIntoView !== false){
14609             var el = this.view.getNode(index);
14610             /*
14611              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14612              */
14613             if(el){
14614                 this.list.scrollChildIntoView(el, false);
14615             }
14616         }
14617     },
14618
14619     // private
14620     selectNext : function(){
14621         var ct = this.store.getCount();
14622         if(ct > 0){
14623             if(this.selectedIndex == -1){
14624                 this.select(0);
14625             }else if(this.selectedIndex < ct-1){
14626                 this.select(this.selectedIndex+1);
14627             }
14628         }
14629     },
14630
14631     // private
14632     selectPrev : function(){
14633         var ct = this.store.getCount();
14634         if(ct > 0){
14635             if(this.selectedIndex == -1){
14636                 this.select(0);
14637             }else if(this.selectedIndex != 0){
14638                 this.select(this.selectedIndex-1);
14639             }
14640         }
14641     },
14642
14643     // private
14644     onKeyUp : function(e){
14645         if(this.editable !== false && !e.isSpecialKey()){
14646             this.lastKey = e.getKey();
14647             this.dqTask.delay(this.queryDelay);
14648         }
14649     },
14650
14651     // private
14652     validateBlur : function(){
14653         return !this.list || !this.list.isVisible();   
14654     },
14655
14656     // private
14657     initQuery : function(){
14658         
14659         var v = this.getRawValue();
14660         
14661         if(this.tickable && this.editable){
14662             v = this.tickableInputEl().getValue();
14663         }
14664         
14665         this.doQuery(v);
14666     },
14667
14668     // private
14669     doForce : function(){
14670         if(this.inputEl().dom.value.length > 0){
14671             this.inputEl().dom.value =
14672                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14673              
14674         }
14675     },
14676
14677     /**
14678      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14679      * query allowing the query action to be canceled if needed.
14680      * @param {String} query The SQL query to execute
14681      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14682      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14683      * saved in the current store (defaults to false)
14684      */
14685     doQuery : function(q, forceAll){
14686         
14687         if(q === undefined || q === null){
14688             q = '';
14689         }
14690         var qe = {
14691             query: q,
14692             forceAll: forceAll,
14693             combo: this,
14694             cancel:false
14695         };
14696         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14697             return false;
14698         }
14699         q = qe.query;
14700         
14701         forceAll = qe.forceAll;
14702         if(forceAll === true || (q.length >= this.minChars)){
14703             
14704             this.hasQuery = true;
14705             
14706             if(this.lastQuery != q || this.alwaysQuery){
14707                 this.lastQuery = q;
14708                 if(this.mode == 'local'){
14709                     this.selectedIndex = -1;
14710                     if(forceAll){
14711                         this.store.clearFilter();
14712                     }else{
14713                         
14714                         if(this.specialFilter){
14715                             this.fireEvent('specialfilter', this);
14716                             this.onLoad();
14717                             return;
14718                         }
14719                         
14720                         this.store.filter(this.displayField, q);
14721                     }
14722                     
14723                     this.store.fireEvent("datachanged", this.store);
14724                     
14725                     this.onLoad();
14726                     
14727                     
14728                 }else{
14729                     
14730                     this.store.baseParams[this.queryParam] = q;
14731                     
14732                     var options = {params : this.getParams(q)};
14733                     
14734                     if(this.loadNext){
14735                         options.add = true;
14736                         options.params.start = this.page * this.pageSize;
14737                     }
14738                     
14739                     this.store.load(options);
14740                     
14741                     /*
14742                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14743                      *  we should expand the list on onLoad
14744                      *  so command out it
14745                      */
14746 //                    this.expand();
14747                 }
14748             }else{
14749                 this.selectedIndex = -1;
14750                 this.onLoad();   
14751             }
14752         }
14753         
14754         this.loadNext = false;
14755     },
14756     
14757     // private
14758     getParams : function(q){
14759         var p = {};
14760         //p[this.queryParam] = q;
14761         
14762         if(this.pageSize){
14763             p.start = 0;
14764             p.limit = this.pageSize;
14765         }
14766         return p;
14767     },
14768
14769     /**
14770      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14771      */
14772     collapse : function(){
14773         if(!this.isExpanded()){
14774             return;
14775         }
14776         
14777         this.list.hide();
14778         
14779         this.hasFocus = false;
14780         
14781         if(this.tickable){
14782             this.okBtn.hide();
14783             this.cancelBtn.hide();
14784             this.trigger.show();
14785             
14786             if(this.editable){
14787                 this.tickableInputEl().dom.value = '';
14788                 this.tickableInputEl().blur();
14789             }
14790             
14791         }
14792         
14793         Roo.get(document).un('mousedown', this.collapseIf, this);
14794         Roo.get(document).un('mousewheel', this.collapseIf, this);
14795         if (!this.editable) {
14796             Roo.get(document).un('keydown', this.listKeyPress, this);
14797         }
14798         this.fireEvent('collapse', this);
14799         
14800         this.validate();
14801     },
14802
14803     // private
14804     collapseIf : function(e){
14805         var in_combo  = e.within(this.el);
14806         var in_list =  e.within(this.list);
14807         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14808         
14809         if (in_combo || in_list || is_list) {
14810             //e.stopPropagation();
14811             return;
14812         }
14813         
14814         if(this.tickable){
14815             this.onTickableFooterButtonClick(e, false, false);
14816         }
14817
14818         this.collapse();
14819         
14820     },
14821
14822     /**
14823      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14824      */
14825     expand : function(){
14826        
14827         if(this.isExpanded() || !this.hasFocus){
14828             return;
14829         }
14830         
14831         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14832         this.list.setWidth(lw);
14833         
14834         Roo.log('expand');
14835         
14836         this.list.show();
14837         
14838         this.restrictHeight();
14839         
14840         if(this.tickable){
14841             
14842             this.tickItems = Roo.apply([], this.item);
14843             
14844             this.okBtn.show();
14845             this.cancelBtn.show();
14846             this.trigger.hide();
14847             
14848             if(this.editable){
14849                 this.tickableInputEl().focus();
14850             }
14851             
14852         }
14853         
14854         Roo.get(document).on('mousedown', this.collapseIf, this);
14855         Roo.get(document).on('mousewheel', this.collapseIf, this);
14856         if (!this.editable) {
14857             Roo.get(document).on('keydown', this.listKeyPress, this);
14858         }
14859         
14860         this.fireEvent('expand', this);
14861     },
14862
14863     // private
14864     // Implements the default empty TriggerField.onTriggerClick function
14865     onTriggerClick : function(e)
14866     {
14867         Roo.log('trigger click');
14868         
14869         if(this.disabled || !this.triggerList){
14870             return;
14871         }
14872         
14873         this.page = 0;
14874         this.loadNext = false;
14875         
14876         if(this.isExpanded()){
14877             this.collapse();
14878             if (!this.blockFocus) {
14879                 this.inputEl().focus();
14880             }
14881             
14882         }else {
14883             this.hasFocus = true;
14884             if(this.triggerAction == 'all') {
14885                 this.doQuery(this.allQuery, true);
14886             } else {
14887                 this.doQuery(this.getRawValue());
14888             }
14889             if (!this.blockFocus) {
14890                 this.inputEl().focus();
14891             }
14892         }
14893     },
14894     
14895     onTickableTriggerClick : function(e)
14896     {
14897         if(this.disabled){
14898             return;
14899         }
14900         
14901         this.page = 0;
14902         this.loadNext = false;
14903         this.hasFocus = true;
14904         
14905         if(this.triggerAction == 'all') {
14906             this.doQuery(this.allQuery, true);
14907         } else {
14908             this.doQuery(this.getRawValue());
14909         }
14910     },
14911     
14912     onSearchFieldClick : function(e)
14913     {
14914         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14915             this.onTickableFooterButtonClick(e, false, false);
14916             return;
14917         }
14918         
14919         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14920             return;
14921         }
14922         
14923         this.page = 0;
14924         this.loadNext = false;
14925         this.hasFocus = true;
14926         
14927         if(this.triggerAction == 'all') {
14928             this.doQuery(this.allQuery, true);
14929         } else {
14930             this.doQuery(this.getRawValue());
14931         }
14932     },
14933     
14934     listKeyPress : function(e)
14935     {
14936         //Roo.log('listkeypress');
14937         // scroll to first matching element based on key pres..
14938         if (e.isSpecialKey()) {
14939             return false;
14940         }
14941         var k = String.fromCharCode(e.getKey()).toUpperCase();
14942         //Roo.log(k);
14943         var match  = false;
14944         var csel = this.view.getSelectedNodes();
14945         var cselitem = false;
14946         if (csel.length) {
14947             var ix = this.view.indexOf(csel[0]);
14948             cselitem  = this.store.getAt(ix);
14949             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14950                 cselitem = false;
14951             }
14952             
14953         }
14954         
14955         this.store.each(function(v) { 
14956             if (cselitem) {
14957                 // start at existing selection.
14958                 if (cselitem.id == v.id) {
14959                     cselitem = false;
14960                 }
14961                 return true;
14962             }
14963                 
14964             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14965                 match = this.store.indexOf(v);
14966                 return false;
14967             }
14968             return true;
14969         }, this);
14970         
14971         if (match === false) {
14972             return true; // no more action?
14973         }
14974         // scroll to?
14975         this.view.select(match);
14976         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14977         sn.scrollIntoView(sn.dom.parentNode, false);
14978     },
14979     
14980     onViewScroll : function(e, t){
14981         
14982         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){
14983             return;
14984         }
14985         
14986         this.hasQuery = true;
14987         
14988         this.loading = this.list.select('.loading', true).first();
14989         
14990         if(this.loading === null){
14991             this.list.createChild({
14992                 tag: 'div',
14993                 cls: 'loading roo-select2-more-results roo-select2-active',
14994                 html: 'Loading more results...'
14995             });
14996             
14997             this.loading = this.list.select('.loading', true).first();
14998             
14999             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15000             
15001             this.loading.hide();
15002         }
15003         
15004         this.loading.show();
15005         
15006         var _combo = this;
15007         
15008         this.page++;
15009         this.loadNext = true;
15010         
15011         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15012         
15013         return;
15014     },
15015     
15016     addItem : function(o)
15017     {   
15018         var dv = ''; // display value
15019         
15020         if (this.displayField) {
15021             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15022         } else {
15023             // this is an error condition!!!
15024             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15025         }
15026         
15027         if(!dv.length){
15028             return;
15029         }
15030         
15031         var choice = this.choices.createChild({
15032             tag: 'li',
15033             cls: 'roo-select2-search-choice',
15034             cn: [
15035                 {
15036                     tag: 'div',
15037                     html: dv
15038                 },
15039                 {
15040                     tag: 'a',
15041                     href: '#',
15042                     cls: 'roo-select2-search-choice-close fa fa-times',
15043                     tabindex: '-1'
15044                 }
15045             ]
15046             
15047         }, this.searchField);
15048         
15049         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15050         
15051         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15052         
15053         this.item.push(o);
15054         
15055         this.lastData = o;
15056         
15057         this.syncValue();
15058         
15059         this.inputEl().dom.value = '';
15060         
15061         this.validate();
15062     },
15063     
15064     onRemoveItem : function(e, _self, o)
15065     {
15066         e.preventDefault();
15067         
15068         this.lastItem = Roo.apply([], this.item);
15069         
15070         var index = this.item.indexOf(o.data) * 1;
15071         
15072         if( index < 0){
15073             Roo.log('not this item?!');
15074             return;
15075         }
15076         
15077         this.item.splice(index, 1);
15078         o.item.remove();
15079         
15080         this.syncValue();
15081         
15082         this.fireEvent('remove', this, e);
15083         
15084         this.validate();
15085         
15086     },
15087     
15088     syncValue : function()
15089     {
15090         if(!this.item.length){
15091             this.clearValue();
15092             return;
15093         }
15094             
15095         var value = [];
15096         var _this = this;
15097         Roo.each(this.item, function(i){
15098             if(_this.valueField){
15099                 value.push(i[_this.valueField]);
15100                 return;
15101             }
15102
15103             value.push(i);
15104         });
15105
15106         this.value = value.join(',');
15107
15108         if(this.hiddenField){
15109             this.hiddenField.dom.value = this.value;
15110         }
15111         
15112         this.store.fireEvent("datachanged", this.store);
15113         
15114         this.validate();
15115     },
15116     
15117     clearItem : function()
15118     {
15119         if(!this.multiple){
15120             return;
15121         }
15122         
15123         this.item = [];
15124         
15125         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15126            c.remove();
15127         });
15128         
15129         this.syncValue();
15130         
15131         this.validate();
15132         
15133         if(this.tickable && !Roo.isTouch){
15134             this.view.refresh();
15135         }
15136     },
15137     
15138     inputEl: function ()
15139     {
15140         if(Roo.isIOS && this.useNativeIOS){
15141             return this.el.select('select.roo-ios-select', true).first();
15142         }
15143         
15144         if(Roo.isTouch && this.mobileTouchView){
15145             return this.el.select('input.form-control',true).first();
15146         }
15147         
15148         if(this.tickable){
15149             return this.searchField;
15150         }
15151         
15152         return this.el.select('input.form-control',true).first();
15153     },
15154     
15155     onTickableFooterButtonClick : function(e, btn, el)
15156     {
15157         e.preventDefault();
15158         
15159         this.lastItem = Roo.apply([], this.item);
15160         
15161         if(btn && btn.name == 'cancel'){
15162             this.tickItems = Roo.apply([], this.item);
15163             this.collapse();
15164             return;
15165         }
15166         
15167         this.clearItem();
15168         
15169         var _this = this;
15170         
15171         Roo.each(this.tickItems, function(o){
15172             _this.addItem(o);
15173         });
15174         
15175         this.collapse();
15176         
15177     },
15178     
15179     validate : function()
15180     {
15181         if(this.getVisibilityEl().hasClass('hidden')){
15182             return true;
15183         }
15184         
15185         var v = this.getRawValue();
15186         
15187         if(this.multiple){
15188             v = this.getValue();
15189         }
15190         
15191         if(this.disabled || this.allowBlank || v.length){
15192             this.markValid();
15193             return true;
15194         }
15195         
15196         this.markInvalid();
15197         return false;
15198     },
15199     
15200     tickableInputEl : function()
15201     {
15202         if(!this.tickable || !this.editable){
15203             return this.inputEl();
15204         }
15205         
15206         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15207     },
15208     
15209     
15210     getAutoCreateTouchView : function()
15211     {
15212         var id = Roo.id();
15213         
15214         var cfg = {
15215             cls: 'form-group' //input-group
15216         };
15217         
15218         var input =  {
15219             tag: 'input',
15220             id : id,
15221             type : this.inputType,
15222             cls : 'form-control x-combo-noedit',
15223             autocomplete: 'new-password',
15224             placeholder : this.placeholder || '',
15225             readonly : true
15226         };
15227         
15228         if (this.name) {
15229             input.name = this.name;
15230         }
15231         
15232         if (this.size) {
15233             input.cls += ' input-' + this.size;
15234         }
15235         
15236         if (this.disabled) {
15237             input.disabled = true;
15238         }
15239         
15240         var inputblock = {
15241             cls : '',
15242             cn : [
15243                 input
15244             ]
15245         };
15246         
15247         if(this.before){
15248             inputblock.cls += ' input-group';
15249             
15250             inputblock.cn.unshift({
15251                 tag :'span',
15252                 cls : 'input-group-addon input-group-prepend input-group-text',
15253                 html : this.before
15254             });
15255         }
15256         
15257         if(this.removable && !this.multiple){
15258             inputblock.cls += ' roo-removable';
15259             
15260             inputblock.cn.push({
15261                 tag: 'button',
15262                 html : 'x',
15263                 cls : 'roo-combo-removable-btn close'
15264             });
15265         }
15266
15267         if(this.hasFeedback && !this.allowBlank){
15268             
15269             inputblock.cls += ' has-feedback';
15270             
15271             inputblock.cn.push({
15272                 tag: 'span',
15273                 cls: 'glyphicon form-control-feedback'
15274             });
15275             
15276         }
15277         
15278         if (this.after) {
15279             
15280             inputblock.cls += (this.before) ? '' : ' input-group';
15281             
15282             inputblock.cn.push({
15283                 tag :'span',
15284                 cls : 'input-group-addon input-group-append input-group-text',
15285                 html : this.after
15286             });
15287         }
15288
15289         
15290         var ibwrap = inputblock;
15291         
15292         if(this.multiple){
15293             ibwrap = {
15294                 tag: 'ul',
15295                 cls: 'roo-select2-choices',
15296                 cn:[
15297                     {
15298                         tag: 'li',
15299                         cls: 'roo-select2-search-field',
15300                         cn: [
15301
15302                             inputblock
15303                         ]
15304                     }
15305                 ]
15306             };
15307         
15308             
15309         }
15310         
15311         var combobox = {
15312             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15313             cn: [
15314                 {
15315                     tag: 'input',
15316                     type : 'hidden',
15317                     cls: 'form-hidden-field'
15318                 },
15319                 ibwrap
15320             ]
15321         };
15322         
15323         if(!this.multiple && this.showToggleBtn){
15324             
15325             var caret = {
15326                         tag: 'span',
15327                         cls: 'caret'
15328             };
15329             
15330             if (this.caret != false) {
15331                 caret = {
15332                      tag: 'i',
15333                      cls: 'fa fa-' + this.caret
15334                 };
15335                 
15336             }
15337             
15338             combobox.cn.push({
15339                 tag :'span',
15340                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15341                 cn : [
15342                     caret,
15343                     {
15344                         tag: 'span',
15345                         cls: 'combobox-clear',
15346                         cn  : [
15347                             {
15348                                 tag : 'i',
15349                                 cls: 'icon-remove'
15350                             }
15351                         ]
15352                     }
15353                 ]
15354
15355             })
15356         }
15357         
15358         if(this.multiple){
15359             combobox.cls += ' roo-select2-container-multi';
15360         }
15361         
15362         var align = this.labelAlign || this.parentLabelAlign();
15363         
15364         if (align ==='left' && this.fieldLabel.length) {
15365
15366             cfg.cn = [
15367                 {
15368                    tag : 'i',
15369                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15370                    tooltip : 'This field is required'
15371                 },
15372                 {
15373                     tag: 'label',
15374                     cls : 'control-label col-form-label',
15375                     html : this.fieldLabel
15376
15377                 },
15378                 {
15379                     cls : '', 
15380                     cn: [
15381                         combobox
15382                     ]
15383                 }
15384             ];
15385             
15386             var labelCfg = cfg.cn[1];
15387             var contentCfg = cfg.cn[2];
15388             
15389
15390             if(this.indicatorpos == 'right'){
15391                 cfg.cn = [
15392                     {
15393                         tag: 'label',
15394                         'for' :  id,
15395                         cls : 'control-label col-form-label',
15396                         cn : [
15397                             {
15398                                 tag : 'span',
15399                                 html : this.fieldLabel
15400                             },
15401                             {
15402                                 tag : 'i',
15403                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15404                                 tooltip : 'This field is required'
15405                             }
15406                         ]
15407                     },
15408                     {
15409                         cls : "",
15410                         cn: [
15411                             combobox
15412                         ]
15413                     }
15414
15415                 ];
15416                 
15417                 labelCfg = cfg.cn[0];
15418                 contentCfg = cfg.cn[1];
15419             }
15420             
15421            
15422             
15423             if(this.labelWidth > 12){
15424                 labelCfg.style = "width: " + this.labelWidth + 'px';
15425             }
15426             
15427             if(this.labelWidth < 13 && this.labelmd == 0){
15428                 this.labelmd = this.labelWidth;
15429             }
15430             
15431             if(this.labellg > 0){
15432                 labelCfg.cls += ' col-lg-' + this.labellg;
15433                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15434             }
15435             
15436             if(this.labelmd > 0){
15437                 labelCfg.cls += ' col-md-' + this.labelmd;
15438                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15439             }
15440             
15441             if(this.labelsm > 0){
15442                 labelCfg.cls += ' col-sm-' + this.labelsm;
15443                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15444             }
15445             
15446             if(this.labelxs > 0){
15447                 labelCfg.cls += ' col-xs-' + this.labelxs;
15448                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15449             }
15450                 
15451                 
15452         } else if ( this.fieldLabel.length) {
15453             cfg.cn = [
15454                 {
15455                    tag : 'i',
15456                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15457                    tooltip : 'This field is required'
15458                 },
15459                 {
15460                     tag: 'label',
15461                     cls : 'control-label',
15462                     html : this.fieldLabel
15463
15464                 },
15465                 {
15466                     cls : '', 
15467                     cn: [
15468                         combobox
15469                     ]
15470                 }
15471             ];
15472             
15473             if(this.indicatorpos == 'right'){
15474                 cfg.cn = [
15475                     {
15476                         tag: 'label',
15477                         cls : 'control-label',
15478                         html : this.fieldLabel,
15479                         cn : [
15480                             {
15481                                tag : 'i',
15482                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15483                                tooltip : 'This field is required'
15484                             }
15485                         ]
15486                     },
15487                     {
15488                         cls : '', 
15489                         cn: [
15490                             combobox
15491                         ]
15492                     }
15493                 ];
15494             }
15495         } else {
15496             cfg.cn = combobox;    
15497         }
15498         
15499         
15500         var settings = this;
15501         
15502         ['xs','sm','md','lg'].map(function(size){
15503             if (settings[size]) {
15504                 cfg.cls += ' col-' + size + '-' + settings[size];
15505             }
15506         });
15507         
15508         return cfg;
15509     },
15510     
15511     initTouchView : function()
15512     {
15513         this.renderTouchView();
15514         
15515         this.touchViewEl.on('scroll', function(){
15516             this.el.dom.scrollTop = 0;
15517         }, this);
15518         
15519         this.originalValue = this.getValue();
15520         
15521         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15522         
15523         this.inputEl().on("click", this.showTouchView, this);
15524         if (this.triggerEl) {
15525             this.triggerEl.on("click", this.showTouchView, this);
15526         }
15527         
15528         
15529         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15530         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15531         
15532         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15533         
15534         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15535         this.store.on('load', this.onTouchViewLoad, this);
15536         this.store.on('loadexception', this.onTouchViewLoadException, this);
15537         
15538         if(this.hiddenName){
15539             
15540             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15541             
15542             this.hiddenField.dom.value =
15543                 this.hiddenValue !== undefined ? this.hiddenValue :
15544                 this.value !== undefined ? this.value : '';
15545         
15546             this.el.dom.removeAttribute('name');
15547             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15548         }
15549         
15550         if(this.multiple){
15551             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15552             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15553         }
15554         
15555         if(this.removable && !this.multiple){
15556             var close = this.closeTriggerEl();
15557             if(close){
15558                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15559                 close.on('click', this.removeBtnClick, this, close);
15560             }
15561         }
15562         /*
15563          * fix the bug in Safari iOS8
15564          */
15565         this.inputEl().on("focus", function(e){
15566             document.activeElement.blur();
15567         }, this);
15568         
15569         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15570         
15571         return;
15572         
15573         
15574     },
15575     
15576     renderTouchView : function()
15577     {
15578         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15579         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15580         
15581         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15582         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15583         
15584         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15585         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15586         this.touchViewBodyEl.setStyle('overflow', 'auto');
15587         
15588         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15589         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15590         
15591         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15592         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15593         
15594     },
15595     
15596     showTouchView : function()
15597     {
15598         if(this.disabled){
15599             return;
15600         }
15601         
15602         this.touchViewHeaderEl.hide();
15603
15604         if(this.modalTitle.length){
15605             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15606             this.touchViewHeaderEl.show();
15607         }
15608
15609         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15610         this.touchViewEl.show();
15611
15612         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15613         
15614         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15615         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15616
15617         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15618
15619         if(this.modalTitle.length){
15620             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15621         }
15622         
15623         this.touchViewBodyEl.setHeight(bodyHeight);
15624
15625         if(this.animate){
15626             var _this = this;
15627             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15628         }else{
15629             this.touchViewEl.addClass('in');
15630         }
15631         
15632         if(this._touchViewMask){
15633             Roo.get(document.body).addClass("x-body-masked");
15634             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15635             this._touchViewMask.setStyle('z-index', 10000);
15636             this._touchViewMask.addClass('show');
15637         }
15638         
15639         this.doTouchViewQuery();
15640         
15641     },
15642     
15643     hideTouchView : function()
15644     {
15645         this.touchViewEl.removeClass('in');
15646
15647         if(this.animate){
15648             var _this = this;
15649             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15650         }else{
15651             this.touchViewEl.setStyle('display', 'none');
15652         }
15653         
15654         if(this._touchViewMask){
15655             this._touchViewMask.removeClass('show');
15656             Roo.get(document.body).removeClass("x-body-masked");
15657         }
15658     },
15659     
15660     setTouchViewValue : function()
15661     {
15662         if(this.multiple){
15663             this.clearItem();
15664         
15665             var _this = this;
15666
15667             Roo.each(this.tickItems, function(o){
15668                 this.addItem(o);
15669             }, this);
15670         }
15671         
15672         this.hideTouchView();
15673     },
15674     
15675     doTouchViewQuery : function()
15676     {
15677         var qe = {
15678             query: '',
15679             forceAll: true,
15680             combo: this,
15681             cancel:false
15682         };
15683         
15684         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15685             return false;
15686         }
15687         
15688         if(!this.alwaysQuery || this.mode == 'local'){
15689             this.onTouchViewLoad();
15690             return;
15691         }
15692         
15693         this.store.load();
15694     },
15695     
15696     onTouchViewBeforeLoad : function(combo,opts)
15697     {
15698         return;
15699     },
15700
15701     // private
15702     onTouchViewLoad : function()
15703     {
15704         if(this.store.getCount() < 1){
15705             this.onTouchViewEmptyResults();
15706             return;
15707         }
15708         
15709         this.clearTouchView();
15710         
15711         var rawValue = this.getRawValue();
15712         
15713         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15714         
15715         this.tickItems = [];
15716         
15717         this.store.data.each(function(d, rowIndex){
15718             var row = this.touchViewListGroup.createChild(template);
15719             
15720             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15721                 row.addClass(d.data.cls);
15722             }
15723             
15724             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15725                 var cfg = {
15726                     data : d.data,
15727                     html : d.data[this.displayField]
15728                 };
15729                 
15730                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15731                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15732                 }
15733             }
15734             row.removeClass('selected');
15735             if(!this.multiple && this.valueField &&
15736                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15737             {
15738                 // radio buttons..
15739                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15740                 row.addClass('selected');
15741             }
15742             
15743             if(this.multiple && this.valueField &&
15744                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15745             {
15746                 
15747                 // checkboxes...
15748                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15749                 this.tickItems.push(d.data);
15750             }
15751             
15752             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15753             
15754         }, this);
15755         
15756         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15757         
15758         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15759
15760         if(this.modalTitle.length){
15761             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15762         }
15763
15764         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15765         
15766         if(this.mobile_restrict_height && listHeight < bodyHeight){
15767             this.touchViewBodyEl.setHeight(listHeight);
15768         }
15769         
15770         var _this = this;
15771         
15772         if(firstChecked && listHeight > bodyHeight){
15773             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15774         }
15775         
15776     },
15777     
15778     onTouchViewLoadException : function()
15779     {
15780         this.hideTouchView();
15781     },
15782     
15783     onTouchViewEmptyResults : function()
15784     {
15785         this.clearTouchView();
15786         
15787         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15788         
15789         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15790         
15791     },
15792     
15793     clearTouchView : function()
15794     {
15795         this.touchViewListGroup.dom.innerHTML = '';
15796     },
15797     
15798     onTouchViewClick : function(e, el, o)
15799     {
15800         e.preventDefault();
15801         
15802         var row = o.row;
15803         var rowIndex = o.rowIndex;
15804         
15805         var r = this.store.getAt(rowIndex);
15806         
15807         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15808             
15809             if(!this.multiple){
15810                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15811                     c.dom.removeAttribute('checked');
15812                 }, this);
15813
15814                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15815
15816                 this.setFromData(r.data);
15817
15818                 var close = this.closeTriggerEl();
15819
15820                 if(close){
15821                     close.show();
15822                 }
15823
15824                 this.hideTouchView();
15825
15826                 this.fireEvent('select', this, r, rowIndex);
15827
15828                 return;
15829             }
15830
15831             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15832                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15833                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15834                 return;
15835             }
15836
15837             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15838             this.addItem(r.data);
15839             this.tickItems.push(r.data);
15840         }
15841     },
15842     
15843     getAutoCreateNativeIOS : function()
15844     {
15845         var cfg = {
15846             cls: 'form-group' //input-group,
15847         };
15848         
15849         var combobox =  {
15850             tag: 'select',
15851             cls : 'roo-ios-select'
15852         };
15853         
15854         if (this.name) {
15855             combobox.name = this.name;
15856         }
15857         
15858         if (this.disabled) {
15859             combobox.disabled = true;
15860         }
15861         
15862         var settings = this;
15863         
15864         ['xs','sm','md','lg'].map(function(size){
15865             if (settings[size]) {
15866                 cfg.cls += ' col-' + size + '-' + settings[size];
15867             }
15868         });
15869         
15870         cfg.cn = combobox;
15871         
15872         return cfg;
15873         
15874     },
15875     
15876     initIOSView : function()
15877     {
15878         this.store.on('load', this.onIOSViewLoad, this);
15879         
15880         return;
15881     },
15882     
15883     onIOSViewLoad : function()
15884     {
15885         if(this.store.getCount() < 1){
15886             return;
15887         }
15888         
15889         this.clearIOSView();
15890         
15891         if(this.allowBlank) {
15892             
15893             var default_text = '-- SELECT --';
15894             
15895             if(this.placeholder.length){
15896                 default_text = this.placeholder;
15897             }
15898             
15899             if(this.emptyTitle.length){
15900                 default_text += ' - ' + this.emptyTitle + ' -';
15901             }
15902             
15903             var opt = this.inputEl().createChild({
15904                 tag: 'option',
15905                 value : 0,
15906                 html : default_text
15907             });
15908             
15909             var o = {};
15910             o[this.valueField] = 0;
15911             o[this.displayField] = default_text;
15912             
15913             this.ios_options.push({
15914                 data : o,
15915                 el : opt
15916             });
15917             
15918         }
15919         
15920         this.store.data.each(function(d, rowIndex){
15921             
15922             var html = '';
15923             
15924             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15925                 html = d.data[this.displayField];
15926             }
15927             
15928             var value = '';
15929             
15930             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15931                 value = d.data[this.valueField];
15932             }
15933             
15934             var option = {
15935                 tag: 'option',
15936                 value : value,
15937                 html : html
15938             };
15939             
15940             if(this.value == d.data[this.valueField]){
15941                 option['selected'] = true;
15942             }
15943             
15944             var opt = this.inputEl().createChild(option);
15945             
15946             this.ios_options.push({
15947                 data : d.data,
15948                 el : opt
15949             });
15950             
15951         }, this);
15952         
15953         this.inputEl().on('change', function(){
15954            this.fireEvent('select', this);
15955         }, this);
15956         
15957     },
15958     
15959     clearIOSView: function()
15960     {
15961         this.inputEl().dom.innerHTML = '';
15962         
15963         this.ios_options = [];
15964     },
15965     
15966     setIOSValue: function(v)
15967     {
15968         this.value = v;
15969         
15970         if(!this.ios_options){
15971             return;
15972         }
15973         
15974         Roo.each(this.ios_options, function(opts){
15975            
15976            opts.el.dom.removeAttribute('selected');
15977            
15978            if(opts.data[this.valueField] != v){
15979                return;
15980            }
15981            
15982            opts.el.dom.setAttribute('selected', true);
15983            
15984         }, this);
15985     }
15986
15987     /** 
15988     * @cfg {Boolean} grow 
15989     * @hide 
15990     */
15991     /** 
15992     * @cfg {Number} growMin 
15993     * @hide 
15994     */
15995     /** 
15996     * @cfg {Number} growMax 
15997     * @hide 
15998     */
15999     /**
16000      * @hide
16001      * @method autoSize
16002      */
16003 });
16004
16005 Roo.apply(Roo.bootstrap.ComboBox,  {
16006     
16007     header : {
16008         tag: 'div',
16009         cls: 'modal-header',
16010         cn: [
16011             {
16012                 tag: 'h4',
16013                 cls: 'modal-title'
16014             }
16015         ]
16016     },
16017     
16018     body : {
16019         tag: 'div',
16020         cls: 'modal-body',
16021         cn: [
16022             {
16023                 tag: 'ul',
16024                 cls: 'list-group'
16025             }
16026         ]
16027     },
16028     
16029     listItemRadio : {
16030         tag: 'li',
16031         cls: 'list-group-item',
16032         cn: [
16033             {
16034                 tag: 'span',
16035                 cls: 'roo-combobox-list-group-item-value'
16036             },
16037             {
16038                 tag: 'div',
16039                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16040                 cn: [
16041                     {
16042                         tag: 'input',
16043                         type: 'radio'
16044                     },
16045                     {
16046                         tag: 'label'
16047                     }
16048                 ]
16049             }
16050         ]
16051     },
16052     
16053     listItemCheckbox : {
16054         tag: 'li',
16055         cls: 'list-group-item',
16056         cn: [
16057             {
16058                 tag: 'span',
16059                 cls: 'roo-combobox-list-group-item-value'
16060             },
16061             {
16062                 tag: 'div',
16063                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16064                 cn: [
16065                     {
16066                         tag: 'input',
16067                         type: 'checkbox'
16068                     },
16069                     {
16070                         tag: 'label'
16071                     }
16072                 ]
16073             }
16074         ]
16075     },
16076     
16077     emptyResult : {
16078         tag: 'div',
16079         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16080     },
16081     
16082     footer : {
16083         tag: 'div',
16084         cls: 'modal-footer',
16085         cn: [
16086             {
16087                 tag: 'div',
16088                 cls: 'row',
16089                 cn: [
16090                     {
16091                         tag: 'div',
16092                         cls: 'col-xs-6 text-left',
16093                         cn: {
16094                             tag: 'button',
16095                             cls: 'btn btn-danger roo-touch-view-cancel',
16096                             html: 'Cancel'
16097                         }
16098                     },
16099                     {
16100                         tag: 'div',
16101                         cls: 'col-xs-6 text-right',
16102                         cn: {
16103                             tag: 'button',
16104                             cls: 'btn btn-success roo-touch-view-ok',
16105                             html: 'OK'
16106                         }
16107                     }
16108                 ]
16109             }
16110         ]
16111         
16112     }
16113 });
16114
16115 Roo.apply(Roo.bootstrap.ComboBox,  {
16116     
16117     touchViewTemplate : {
16118         tag: 'div',
16119         cls: 'modal fade roo-combobox-touch-view',
16120         cn: [
16121             {
16122                 tag: 'div',
16123                 cls: 'modal-dialog',
16124                 style : 'position:fixed', // we have to fix position....
16125                 cn: [
16126                     {
16127                         tag: 'div',
16128                         cls: 'modal-content',
16129                         cn: [
16130                             Roo.bootstrap.ComboBox.header,
16131                             Roo.bootstrap.ComboBox.body,
16132                             Roo.bootstrap.ComboBox.footer
16133                         ]
16134                     }
16135                 ]
16136             }
16137         ]
16138     }
16139 });/*
16140  * Based on:
16141  * Ext JS Library 1.1.1
16142  * Copyright(c) 2006-2007, Ext JS, LLC.
16143  *
16144  * Originally Released Under LGPL - original licence link has changed is not relivant.
16145  *
16146  * Fork - LGPL
16147  * <script type="text/javascript">
16148  */
16149
16150 /**
16151  * @class Roo.View
16152  * @extends Roo.util.Observable
16153  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16154  * This class also supports single and multi selection modes. <br>
16155  * Create a data model bound view:
16156  <pre><code>
16157  var store = new Roo.data.Store(...);
16158
16159  var view = new Roo.View({
16160     el : "my-element",
16161     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16162  
16163     singleSelect: true,
16164     selectedClass: "ydataview-selected",
16165     store: store
16166  });
16167
16168  // listen for node click?
16169  view.on("click", function(vw, index, node, e){
16170  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16171  });
16172
16173  // load XML data
16174  dataModel.load("foobar.xml");
16175  </code></pre>
16176  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16177  * <br><br>
16178  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16179  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16180  * 
16181  * Note: old style constructor is still suported (container, template, config)
16182  * 
16183  * @constructor
16184  * Create a new View
16185  * @param {Object} config The config object
16186  * 
16187  */
16188 Roo.View = function(config, depreciated_tpl, depreciated_config){
16189     
16190     this.parent = false;
16191     
16192     if (typeof(depreciated_tpl) == 'undefined') {
16193         // new way.. - universal constructor.
16194         Roo.apply(this, config);
16195         this.el  = Roo.get(this.el);
16196     } else {
16197         // old format..
16198         this.el  = Roo.get(config);
16199         this.tpl = depreciated_tpl;
16200         Roo.apply(this, depreciated_config);
16201     }
16202     this.wrapEl  = this.el.wrap().wrap();
16203     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16204     
16205     
16206     if(typeof(this.tpl) == "string"){
16207         this.tpl = new Roo.Template(this.tpl);
16208     } else {
16209         // support xtype ctors..
16210         this.tpl = new Roo.factory(this.tpl, Roo);
16211     }
16212     
16213     
16214     this.tpl.compile();
16215     
16216     /** @private */
16217     this.addEvents({
16218         /**
16219          * @event beforeclick
16220          * Fires before a click is processed. Returns false to cancel the default action.
16221          * @param {Roo.View} this
16222          * @param {Number} index The index of the target node
16223          * @param {HTMLElement} node The target node
16224          * @param {Roo.EventObject} e The raw event object
16225          */
16226             "beforeclick" : true,
16227         /**
16228          * @event click
16229          * Fires when a template node is clicked.
16230          * @param {Roo.View} this
16231          * @param {Number} index The index of the target node
16232          * @param {HTMLElement} node The target node
16233          * @param {Roo.EventObject} e The raw event object
16234          */
16235             "click" : true,
16236         /**
16237          * @event dblclick
16238          * Fires when a template node is double clicked.
16239          * @param {Roo.View} this
16240          * @param {Number} index The index of the target node
16241          * @param {HTMLElement} node The target node
16242          * @param {Roo.EventObject} e The raw event object
16243          */
16244             "dblclick" : true,
16245         /**
16246          * @event contextmenu
16247          * Fires when a template node is right clicked.
16248          * @param {Roo.View} this
16249          * @param {Number} index The index of the target node
16250          * @param {HTMLElement} node The target node
16251          * @param {Roo.EventObject} e The raw event object
16252          */
16253             "contextmenu" : true,
16254         /**
16255          * @event selectionchange
16256          * Fires when the selected nodes change.
16257          * @param {Roo.View} this
16258          * @param {Array} selections Array of the selected nodes
16259          */
16260             "selectionchange" : true,
16261     
16262         /**
16263          * @event beforeselect
16264          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16265          * @param {Roo.View} this
16266          * @param {HTMLElement} node The node to be selected
16267          * @param {Array} selections Array of currently selected nodes
16268          */
16269             "beforeselect" : true,
16270         /**
16271          * @event preparedata
16272          * Fires on every row to render, to allow you to change the data.
16273          * @param {Roo.View} this
16274          * @param {Object} data to be rendered (change this)
16275          */
16276           "preparedata" : true
16277           
16278           
16279         });
16280
16281
16282
16283     this.el.on({
16284         "click": this.onClick,
16285         "dblclick": this.onDblClick,
16286         "contextmenu": this.onContextMenu,
16287         scope:this
16288     });
16289
16290     this.selections = [];
16291     this.nodes = [];
16292     this.cmp = new Roo.CompositeElementLite([]);
16293     if(this.store){
16294         this.store = Roo.factory(this.store, Roo.data);
16295         this.setStore(this.store, true);
16296     }
16297     
16298     if ( this.footer && this.footer.xtype) {
16299            
16300          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16301         
16302         this.footer.dataSource = this.store;
16303         this.footer.container = fctr;
16304         this.footer = Roo.factory(this.footer, Roo);
16305         fctr.insertFirst(this.el);
16306         
16307         // this is a bit insane - as the paging toolbar seems to detach the el..
16308 //        dom.parentNode.parentNode.parentNode
16309          // they get detached?
16310     }
16311     
16312     
16313     Roo.View.superclass.constructor.call(this);
16314     
16315     
16316 };
16317
16318 Roo.extend(Roo.View, Roo.util.Observable, {
16319     
16320      /**
16321      * @cfg {Roo.data.Store} store Data store to load data from.
16322      */
16323     store : false,
16324     
16325     /**
16326      * @cfg {String|Roo.Element} el The container element.
16327      */
16328     el : '',
16329     
16330     /**
16331      * @cfg {String|Roo.Template} tpl The template used by this View 
16332      */
16333     tpl : false,
16334     /**
16335      * @cfg {String} dataName the named area of the template to use as the data area
16336      *                          Works with domtemplates roo-name="name"
16337      */
16338     dataName: false,
16339     /**
16340      * @cfg {String} selectedClass The css class to add to selected nodes
16341      */
16342     selectedClass : "x-view-selected",
16343      /**
16344      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16345      */
16346     emptyText : "",
16347     
16348     /**
16349      * @cfg {String} text to display on mask (default Loading)
16350      */
16351     mask : false,
16352     /**
16353      * @cfg {Boolean} multiSelect Allow multiple selection
16354      */
16355     multiSelect : false,
16356     /**
16357      * @cfg {Boolean} singleSelect Allow single selection
16358      */
16359     singleSelect:  false,
16360     
16361     /**
16362      * @cfg {Boolean} toggleSelect - selecting 
16363      */
16364     toggleSelect : false,
16365     
16366     /**
16367      * @cfg {Boolean} tickable - selecting 
16368      */
16369     tickable : false,
16370     
16371     /**
16372      * Returns the element this view is bound to.
16373      * @return {Roo.Element}
16374      */
16375     getEl : function(){
16376         return this.wrapEl;
16377     },
16378     
16379     
16380
16381     /**
16382      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16383      */
16384     refresh : function(){
16385         //Roo.log('refresh');
16386         var t = this.tpl;
16387         
16388         // if we are using something like 'domtemplate', then
16389         // the what gets used is:
16390         // t.applySubtemplate(NAME, data, wrapping data..)
16391         // the outer template then get' applied with
16392         //     the store 'extra data'
16393         // and the body get's added to the
16394         //      roo-name="data" node?
16395         //      <span class='roo-tpl-{name}'></span> ?????
16396         
16397         
16398         
16399         this.clearSelections();
16400         this.el.update("");
16401         var html = [];
16402         var records = this.store.getRange();
16403         if(records.length < 1) {
16404             
16405             // is this valid??  = should it render a template??
16406             
16407             this.el.update(this.emptyText);
16408             return;
16409         }
16410         var el = this.el;
16411         if (this.dataName) {
16412             this.el.update(t.apply(this.store.meta)); //????
16413             el = this.el.child('.roo-tpl-' + this.dataName);
16414         }
16415         
16416         for(var i = 0, len = records.length; i < len; i++){
16417             var data = this.prepareData(records[i].data, i, records[i]);
16418             this.fireEvent("preparedata", this, data, i, records[i]);
16419             
16420             var d = Roo.apply({}, data);
16421             
16422             if(this.tickable){
16423                 Roo.apply(d, {'roo-id' : Roo.id()});
16424                 
16425                 var _this = this;
16426             
16427                 Roo.each(this.parent.item, function(item){
16428                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16429                         return;
16430                     }
16431                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16432                 });
16433             }
16434             
16435             html[html.length] = Roo.util.Format.trim(
16436                 this.dataName ?
16437                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16438                     t.apply(d)
16439             );
16440         }
16441         
16442         
16443         
16444         el.update(html.join(""));
16445         this.nodes = el.dom.childNodes;
16446         this.updateIndexes(0);
16447     },
16448     
16449
16450     /**
16451      * Function to override to reformat the data that is sent to
16452      * the template for each node.
16453      * DEPRICATED - use the preparedata event handler.
16454      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16455      * a JSON object for an UpdateManager bound view).
16456      */
16457     prepareData : function(data, index, record)
16458     {
16459         this.fireEvent("preparedata", this, data, index, record);
16460         return data;
16461     },
16462
16463     onUpdate : function(ds, record){
16464         // Roo.log('on update');   
16465         this.clearSelections();
16466         var index = this.store.indexOf(record);
16467         var n = this.nodes[index];
16468         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16469         n.parentNode.removeChild(n);
16470         this.updateIndexes(index, index);
16471     },
16472
16473     
16474     
16475 // --------- FIXME     
16476     onAdd : function(ds, records, index)
16477     {
16478         //Roo.log(['on Add', ds, records, index] );        
16479         this.clearSelections();
16480         if(this.nodes.length == 0){
16481             this.refresh();
16482             return;
16483         }
16484         var n = this.nodes[index];
16485         for(var i = 0, len = records.length; i < len; i++){
16486             var d = this.prepareData(records[i].data, i, records[i]);
16487             if(n){
16488                 this.tpl.insertBefore(n, d);
16489             }else{
16490                 
16491                 this.tpl.append(this.el, d);
16492             }
16493         }
16494         this.updateIndexes(index);
16495     },
16496
16497     onRemove : function(ds, record, index){
16498        // Roo.log('onRemove');
16499         this.clearSelections();
16500         var el = this.dataName  ?
16501             this.el.child('.roo-tpl-' + this.dataName) :
16502             this.el; 
16503         
16504         el.dom.removeChild(this.nodes[index]);
16505         this.updateIndexes(index);
16506     },
16507
16508     /**
16509      * Refresh an individual node.
16510      * @param {Number} index
16511      */
16512     refreshNode : function(index){
16513         this.onUpdate(this.store, this.store.getAt(index));
16514     },
16515
16516     updateIndexes : function(startIndex, endIndex){
16517         var ns = this.nodes;
16518         startIndex = startIndex || 0;
16519         endIndex = endIndex || ns.length - 1;
16520         for(var i = startIndex; i <= endIndex; i++){
16521             ns[i].nodeIndex = i;
16522         }
16523     },
16524
16525     /**
16526      * Changes the data store this view uses and refresh the view.
16527      * @param {Store} store
16528      */
16529     setStore : function(store, initial){
16530         if(!initial && this.store){
16531             this.store.un("datachanged", this.refresh);
16532             this.store.un("add", this.onAdd);
16533             this.store.un("remove", this.onRemove);
16534             this.store.un("update", this.onUpdate);
16535             this.store.un("clear", this.refresh);
16536             this.store.un("beforeload", this.onBeforeLoad);
16537             this.store.un("load", this.onLoad);
16538             this.store.un("loadexception", this.onLoad);
16539         }
16540         if(store){
16541           
16542             store.on("datachanged", this.refresh, this);
16543             store.on("add", this.onAdd, this);
16544             store.on("remove", this.onRemove, this);
16545             store.on("update", this.onUpdate, this);
16546             store.on("clear", this.refresh, this);
16547             store.on("beforeload", this.onBeforeLoad, this);
16548             store.on("load", this.onLoad, this);
16549             store.on("loadexception", this.onLoad, this);
16550         }
16551         
16552         if(store){
16553             this.refresh();
16554         }
16555     },
16556     /**
16557      * onbeforeLoad - masks the loading area.
16558      *
16559      */
16560     onBeforeLoad : function(store,opts)
16561     {
16562          //Roo.log('onBeforeLoad');   
16563         if (!opts.add) {
16564             this.el.update("");
16565         }
16566         this.el.mask(this.mask ? this.mask : "Loading" ); 
16567     },
16568     onLoad : function ()
16569     {
16570         this.el.unmask();
16571     },
16572     
16573
16574     /**
16575      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16576      * @param {HTMLElement} node
16577      * @return {HTMLElement} The template node
16578      */
16579     findItemFromChild : function(node){
16580         var el = this.dataName  ?
16581             this.el.child('.roo-tpl-' + this.dataName,true) :
16582             this.el.dom; 
16583         
16584         if(!node || node.parentNode == el){
16585                     return node;
16586             }
16587             var p = node.parentNode;
16588             while(p && p != el){
16589             if(p.parentNode == el){
16590                 return p;
16591             }
16592             p = p.parentNode;
16593         }
16594             return null;
16595     },
16596
16597     /** @ignore */
16598     onClick : function(e){
16599         var item = this.findItemFromChild(e.getTarget());
16600         if(item){
16601             var index = this.indexOf(item);
16602             if(this.onItemClick(item, index, e) !== false){
16603                 this.fireEvent("click", this, index, item, e);
16604             }
16605         }else{
16606             this.clearSelections();
16607         }
16608     },
16609
16610     /** @ignore */
16611     onContextMenu : function(e){
16612         var item = this.findItemFromChild(e.getTarget());
16613         if(item){
16614             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16615         }
16616     },
16617
16618     /** @ignore */
16619     onDblClick : function(e){
16620         var item = this.findItemFromChild(e.getTarget());
16621         if(item){
16622             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16623         }
16624     },
16625
16626     onItemClick : function(item, index, e)
16627     {
16628         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16629             return false;
16630         }
16631         if (this.toggleSelect) {
16632             var m = this.isSelected(item) ? 'unselect' : 'select';
16633             //Roo.log(m);
16634             var _t = this;
16635             _t[m](item, true, false);
16636             return true;
16637         }
16638         if(this.multiSelect || this.singleSelect){
16639             if(this.multiSelect && e.shiftKey && this.lastSelection){
16640                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16641             }else{
16642                 this.select(item, this.multiSelect && e.ctrlKey);
16643                 this.lastSelection = item;
16644             }
16645             
16646             if(!this.tickable){
16647                 e.preventDefault();
16648             }
16649             
16650         }
16651         return true;
16652     },
16653
16654     /**
16655      * Get the number of selected nodes.
16656      * @return {Number}
16657      */
16658     getSelectionCount : function(){
16659         return this.selections.length;
16660     },
16661
16662     /**
16663      * Get the currently selected nodes.
16664      * @return {Array} An array of HTMLElements
16665      */
16666     getSelectedNodes : function(){
16667         return this.selections;
16668     },
16669
16670     /**
16671      * Get the indexes of the selected nodes.
16672      * @return {Array}
16673      */
16674     getSelectedIndexes : function(){
16675         var indexes = [], s = this.selections;
16676         for(var i = 0, len = s.length; i < len; i++){
16677             indexes.push(s[i].nodeIndex);
16678         }
16679         return indexes;
16680     },
16681
16682     /**
16683      * Clear all selections
16684      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16685      */
16686     clearSelections : function(suppressEvent){
16687         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16688             this.cmp.elements = this.selections;
16689             this.cmp.removeClass(this.selectedClass);
16690             this.selections = [];
16691             if(!suppressEvent){
16692                 this.fireEvent("selectionchange", this, this.selections);
16693             }
16694         }
16695     },
16696
16697     /**
16698      * Returns true if the passed node is selected
16699      * @param {HTMLElement/Number} node The node or node index
16700      * @return {Boolean}
16701      */
16702     isSelected : function(node){
16703         var s = this.selections;
16704         if(s.length < 1){
16705             return false;
16706         }
16707         node = this.getNode(node);
16708         return s.indexOf(node) !== -1;
16709     },
16710
16711     /**
16712      * Selects nodes.
16713      * @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
16714      * @param {Boolean} keepExisting (optional) true to keep existing selections
16715      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16716      */
16717     select : function(nodeInfo, keepExisting, suppressEvent){
16718         if(nodeInfo instanceof Array){
16719             if(!keepExisting){
16720                 this.clearSelections(true);
16721             }
16722             for(var i = 0, len = nodeInfo.length; i < len; i++){
16723                 this.select(nodeInfo[i], true, true);
16724             }
16725             return;
16726         } 
16727         var node = this.getNode(nodeInfo);
16728         if(!node || this.isSelected(node)){
16729             return; // already selected.
16730         }
16731         if(!keepExisting){
16732             this.clearSelections(true);
16733         }
16734         
16735         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16736             Roo.fly(node).addClass(this.selectedClass);
16737             this.selections.push(node);
16738             if(!suppressEvent){
16739                 this.fireEvent("selectionchange", this, this.selections);
16740             }
16741         }
16742         
16743         
16744     },
16745       /**
16746      * Unselects nodes.
16747      * @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
16748      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16749      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16750      */
16751     unselect : function(nodeInfo, keepExisting, suppressEvent)
16752     {
16753         if(nodeInfo instanceof Array){
16754             Roo.each(this.selections, function(s) {
16755                 this.unselect(s, nodeInfo);
16756             }, this);
16757             return;
16758         }
16759         var node = this.getNode(nodeInfo);
16760         if(!node || !this.isSelected(node)){
16761             //Roo.log("not selected");
16762             return; // not selected.
16763         }
16764         // fireevent???
16765         var ns = [];
16766         Roo.each(this.selections, function(s) {
16767             if (s == node ) {
16768                 Roo.fly(node).removeClass(this.selectedClass);
16769
16770                 return;
16771             }
16772             ns.push(s);
16773         },this);
16774         
16775         this.selections= ns;
16776         this.fireEvent("selectionchange", this, this.selections);
16777     },
16778
16779     /**
16780      * Gets a template node.
16781      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16782      * @return {HTMLElement} The node or null if it wasn't found
16783      */
16784     getNode : function(nodeInfo){
16785         if(typeof nodeInfo == "string"){
16786             return document.getElementById(nodeInfo);
16787         }else if(typeof nodeInfo == "number"){
16788             return this.nodes[nodeInfo];
16789         }
16790         return nodeInfo;
16791     },
16792
16793     /**
16794      * Gets a range template nodes.
16795      * @param {Number} startIndex
16796      * @param {Number} endIndex
16797      * @return {Array} An array of nodes
16798      */
16799     getNodes : function(start, end){
16800         var ns = this.nodes;
16801         start = start || 0;
16802         end = typeof end == "undefined" ? ns.length - 1 : end;
16803         var nodes = [];
16804         if(start <= end){
16805             for(var i = start; i <= end; i++){
16806                 nodes.push(ns[i]);
16807             }
16808         } else{
16809             for(var i = start; i >= end; i--){
16810                 nodes.push(ns[i]);
16811             }
16812         }
16813         return nodes;
16814     },
16815
16816     /**
16817      * Finds the index of the passed node
16818      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16819      * @return {Number} The index of the node or -1
16820      */
16821     indexOf : function(node){
16822         node = this.getNode(node);
16823         if(typeof node.nodeIndex == "number"){
16824             return node.nodeIndex;
16825         }
16826         var ns = this.nodes;
16827         for(var i = 0, len = ns.length; i < len; i++){
16828             if(ns[i] == node){
16829                 return i;
16830             }
16831         }
16832         return -1;
16833     }
16834 });
16835 /*
16836  * - LGPL
16837  *
16838  * based on jquery fullcalendar
16839  * 
16840  */
16841
16842 Roo.bootstrap = Roo.bootstrap || {};
16843 /**
16844  * @class Roo.bootstrap.Calendar
16845  * @extends Roo.bootstrap.Component
16846  * Bootstrap Calendar class
16847  * @cfg {Boolean} loadMask (true|false) default false
16848  * @cfg {Object} header generate the user specific header of the calendar, default false
16849
16850  * @constructor
16851  * Create a new Container
16852  * @param {Object} config The config object
16853  */
16854
16855
16856
16857 Roo.bootstrap.Calendar = function(config){
16858     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16859      this.addEvents({
16860         /**
16861              * @event select
16862              * Fires when a date is selected
16863              * @param {DatePicker} this
16864              * @param {Date} date The selected date
16865              */
16866         'select': true,
16867         /**
16868              * @event monthchange
16869              * Fires when the displayed month changes 
16870              * @param {DatePicker} this
16871              * @param {Date} date The selected month
16872              */
16873         'monthchange': true,
16874         /**
16875              * @event evententer
16876              * Fires when mouse over an event
16877              * @param {Calendar} this
16878              * @param {event} Event
16879              */
16880         'evententer': true,
16881         /**
16882              * @event eventleave
16883              * Fires when the mouse leaves an
16884              * @param {Calendar} this
16885              * @param {event}
16886              */
16887         'eventleave': true,
16888         /**
16889              * @event eventclick
16890              * Fires when the mouse click an
16891              * @param {Calendar} this
16892              * @param {event}
16893              */
16894         'eventclick': true
16895         
16896     });
16897
16898 };
16899
16900 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16901     
16902      /**
16903      * @cfg {Number} startDay
16904      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16905      */
16906     startDay : 0,
16907     
16908     loadMask : false,
16909     
16910     header : false,
16911       
16912     getAutoCreate : function(){
16913         
16914         
16915         var fc_button = function(name, corner, style, content ) {
16916             return Roo.apply({},{
16917                 tag : 'span',
16918                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16919                          (corner.length ?
16920                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16921                             ''
16922                         ),
16923                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16924                 unselectable: 'on'
16925             });
16926         };
16927         
16928         var header = {};
16929         
16930         if(!this.header){
16931             header = {
16932                 tag : 'table',
16933                 cls : 'fc-header',
16934                 style : 'width:100%',
16935                 cn : [
16936                     {
16937                         tag: 'tr',
16938                         cn : [
16939                             {
16940                                 tag : 'td',
16941                                 cls : 'fc-header-left',
16942                                 cn : [
16943                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16944                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16945                                     { tag: 'span', cls: 'fc-header-space' },
16946                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16947
16948
16949                                 ]
16950                             },
16951
16952                             {
16953                                 tag : 'td',
16954                                 cls : 'fc-header-center',
16955                                 cn : [
16956                                     {
16957                                         tag: 'span',
16958                                         cls: 'fc-header-title',
16959                                         cn : {
16960                                             tag: 'H2',
16961                                             html : 'month / year'
16962                                         }
16963                                     }
16964
16965                                 ]
16966                             },
16967                             {
16968                                 tag : 'td',
16969                                 cls : 'fc-header-right',
16970                                 cn : [
16971                               /*      fc_button('month', 'left', '', 'month' ),
16972                                     fc_button('week', '', '', 'week' ),
16973                                     fc_button('day', 'right', '', 'day' )
16974                                 */    
16975
16976                                 ]
16977                             }
16978
16979                         ]
16980                     }
16981                 ]
16982             };
16983         }
16984         
16985         header = this.header;
16986         
16987        
16988         var cal_heads = function() {
16989             var ret = [];
16990             // fixme - handle this.
16991             
16992             for (var i =0; i < Date.dayNames.length; i++) {
16993                 var d = Date.dayNames[i];
16994                 ret.push({
16995                     tag: 'th',
16996                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16997                     html : d.substring(0,3)
16998                 });
16999                 
17000             }
17001             ret[0].cls += ' fc-first';
17002             ret[6].cls += ' fc-last';
17003             return ret;
17004         };
17005         var cal_cell = function(n) {
17006             return  {
17007                 tag: 'td',
17008                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17009                 cn : [
17010                     {
17011                         cn : [
17012                             {
17013                                 cls: 'fc-day-number',
17014                                 html: 'D'
17015                             },
17016                             {
17017                                 cls: 'fc-day-content',
17018                              
17019                                 cn : [
17020                                      {
17021                                         style: 'position: relative;' // height: 17px;
17022                                     }
17023                                 ]
17024                             }
17025                             
17026                             
17027                         ]
17028                     }
17029                 ]
17030                 
17031             }
17032         };
17033         var cal_rows = function() {
17034             
17035             var ret = [];
17036             for (var r = 0; r < 6; r++) {
17037                 var row= {
17038                     tag : 'tr',
17039                     cls : 'fc-week',
17040                     cn : []
17041                 };
17042                 
17043                 for (var i =0; i < Date.dayNames.length; i++) {
17044                     var d = Date.dayNames[i];
17045                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17046
17047                 }
17048                 row.cn[0].cls+=' fc-first';
17049                 row.cn[0].cn[0].style = 'min-height:90px';
17050                 row.cn[6].cls+=' fc-last';
17051                 ret.push(row);
17052                 
17053             }
17054             ret[0].cls += ' fc-first';
17055             ret[4].cls += ' fc-prev-last';
17056             ret[5].cls += ' fc-last';
17057             return ret;
17058             
17059         };
17060         
17061         var cal_table = {
17062             tag: 'table',
17063             cls: 'fc-border-separate',
17064             style : 'width:100%',
17065             cellspacing  : 0,
17066             cn : [
17067                 { 
17068                     tag: 'thead',
17069                     cn : [
17070                         { 
17071                             tag: 'tr',
17072                             cls : 'fc-first fc-last',
17073                             cn : cal_heads()
17074                         }
17075                     ]
17076                 },
17077                 { 
17078                     tag: 'tbody',
17079                     cn : cal_rows()
17080                 }
17081                   
17082             ]
17083         };
17084          
17085          var cfg = {
17086             cls : 'fc fc-ltr',
17087             cn : [
17088                 header,
17089                 {
17090                     cls : 'fc-content',
17091                     style : "position: relative;",
17092                     cn : [
17093                         {
17094                             cls : 'fc-view fc-view-month fc-grid',
17095                             style : 'position: relative',
17096                             unselectable : 'on',
17097                             cn : [
17098                                 {
17099                                     cls : 'fc-event-container',
17100                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17101                                 },
17102                                 cal_table
17103                             ]
17104                         }
17105                     ]
17106     
17107                 }
17108            ] 
17109             
17110         };
17111         
17112          
17113         
17114         return cfg;
17115     },
17116     
17117     
17118     initEvents : function()
17119     {
17120         if(!this.store){
17121             throw "can not find store for calendar";
17122         }
17123         
17124         var mark = {
17125             tag: "div",
17126             cls:"x-dlg-mask",
17127             style: "text-align:center",
17128             cn: [
17129                 {
17130                     tag: "div",
17131                     style: "background-color:white;width:50%;margin:250 auto",
17132                     cn: [
17133                         {
17134                             tag: "img",
17135                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17136                         },
17137                         {
17138                             tag: "span",
17139                             html: "Loading"
17140                         }
17141                         
17142                     ]
17143                 }
17144             ]
17145         };
17146         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17147         
17148         var size = this.el.select('.fc-content', true).first().getSize();
17149         this.maskEl.setSize(size.width, size.height);
17150         this.maskEl.enableDisplayMode("block");
17151         if(!this.loadMask){
17152             this.maskEl.hide();
17153         }
17154         
17155         this.store = Roo.factory(this.store, Roo.data);
17156         this.store.on('load', this.onLoad, this);
17157         this.store.on('beforeload', this.onBeforeLoad, this);
17158         
17159         this.resize();
17160         
17161         this.cells = this.el.select('.fc-day',true);
17162         //Roo.log(this.cells);
17163         this.textNodes = this.el.query('.fc-day-number');
17164         this.cells.addClassOnOver('fc-state-hover');
17165         
17166         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17167         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17168         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17169         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17170         
17171         this.on('monthchange', this.onMonthChange, this);
17172         
17173         this.update(new Date().clearTime());
17174     },
17175     
17176     resize : function() {
17177         var sz  = this.el.getSize();
17178         
17179         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17180         this.el.select('.fc-day-content div',true).setHeight(34);
17181     },
17182     
17183     
17184     // private
17185     showPrevMonth : function(e){
17186         this.update(this.activeDate.add("mo", -1));
17187     },
17188     showToday : function(e){
17189         this.update(new Date().clearTime());
17190     },
17191     // private
17192     showNextMonth : function(e){
17193         this.update(this.activeDate.add("mo", 1));
17194     },
17195
17196     // private
17197     showPrevYear : function(){
17198         this.update(this.activeDate.add("y", -1));
17199     },
17200
17201     // private
17202     showNextYear : function(){
17203         this.update(this.activeDate.add("y", 1));
17204     },
17205
17206     
17207    // private
17208     update : function(date)
17209     {
17210         var vd = this.activeDate;
17211         this.activeDate = date;
17212 //        if(vd && this.el){
17213 //            var t = date.getTime();
17214 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17215 //                Roo.log('using add remove');
17216 //                
17217 //                this.fireEvent('monthchange', this, date);
17218 //                
17219 //                this.cells.removeClass("fc-state-highlight");
17220 //                this.cells.each(function(c){
17221 //                   if(c.dateValue == t){
17222 //                       c.addClass("fc-state-highlight");
17223 //                       setTimeout(function(){
17224 //                            try{c.dom.firstChild.focus();}catch(e){}
17225 //                       }, 50);
17226 //                       return false;
17227 //                   }
17228 //                   return true;
17229 //                });
17230 //                return;
17231 //            }
17232 //        }
17233         
17234         var days = date.getDaysInMonth();
17235         
17236         var firstOfMonth = date.getFirstDateOfMonth();
17237         var startingPos = firstOfMonth.getDay()-this.startDay;
17238         
17239         if(startingPos < this.startDay){
17240             startingPos += 7;
17241         }
17242         
17243         var pm = date.add(Date.MONTH, -1);
17244         var prevStart = pm.getDaysInMonth()-startingPos;
17245 //        
17246         this.cells = this.el.select('.fc-day',true);
17247         this.textNodes = this.el.query('.fc-day-number');
17248         this.cells.addClassOnOver('fc-state-hover');
17249         
17250         var cells = this.cells.elements;
17251         var textEls = this.textNodes;
17252         
17253         Roo.each(cells, function(cell){
17254             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17255         });
17256         
17257         days += startingPos;
17258
17259         // convert everything to numbers so it's fast
17260         var day = 86400000;
17261         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17262         //Roo.log(d);
17263         //Roo.log(pm);
17264         //Roo.log(prevStart);
17265         
17266         var today = new Date().clearTime().getTime();
17267         var sel = date.clearTime().getTime();
17268         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17269         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17270         var ddMatch = this.disabledDatesRE;
17271         var ddText = this.disabledDatesText;
17272         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17273         var ddaysText = this.disabledDaysText;
17274         var format = this.format;
17275         
17276         var setCellClass = function(cal, cell){
17277             cell.row = 0;
17278             cell.events = [];
17279             cell.more = [];
17280             //Roo.log('set Cell Class');
17281             cell.title = "";
17282             var t = d.getTime();
17283             
17284             //Roo.log(d);
17285             
17286             cell.dateValue = t;
17287             if(t == today){
17288                 cell.className += " fc-today";
17289                 cell.className += " fc-state-highlight";
17290                 cell.title = cal.todayText;
17291             }
17292             if(t == sel){
17293                 // disable highlight in other month..
17294                 //cell.className += " fc-state-highlight";
17295                 
17296             }
17297             // disabling
17298             if(t < min) {
17299                 cell.className = " fc-state-disabled";
17300                 cell.title = cal.minText;
17301                 return;
17302             }
17303             if(t > max) {
17304                 cell.className = " fc-state-disabled";
17305                 cell.title = cal.maxText;
17306                 return;
17307             }
17308             if(ddays){
17309                 if(ddays.indexOf(d.getDay()) != -1){
17310                     cell.title = ddaysText;
17311                     cell.className = " fc-state-disabled";
17312                 }
17313             }
17314             if(ddMatch && format){
17315                 var fvalue = d.dateFormat(format);
17316                 if(ddMatch.test(fvalue)){
17317                     cell.title = ddText.replace("%0", fvalue);
17318                     cell.className = " fc-state-disabled";
17319                 }
17320             }
17321             
17322             if (!cell.initialClassName) {
17323                 cell.initialClassName = cell.dom.className;
17324             }
17325             
17326             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17327         };
17328
17329         var i = 0;
17330         
17331         for(; i < startingPos; i++) {
17332             textEls[i].innerHTML = (++prevStart);
17333             d.setDate(d.getDate()+1);
17334             
17335             cells[i].className = "fc-past fc-other-month";
17336             setCellClass(this, cells[i]);
17337         }
17338         
17339         var intDay = 0;
17340         
17341         for(; i < days; i++){
17342             intDay = i - startingPos + 1;
17343             textEls[i].innerHTML = (intDay);
17344             d.setDate(d.getDate()+1);
17345             
17346             cells[i].className = ''; // "x-date-active";
17347             setCellClass(this, cells[i]);
17348         }
17349         var extraDays = 0;
17350         
17351         for(; i < 42; i++) {
17352             textEls[i].innerHTML = (++extraDays);
17353             d.setDate(d.getDate()+1);
17354             
17355             cells[i].className = "fc-future fc-other-month";
17356             setCellClass(this, cells[i]);
17357         }
17358         
17359         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17360         
17361         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17362         
17363         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17364         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17365         
17366         if(totalRows != 6){
17367             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17368             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17369         }
17370         
17371         this.fireEvent('monthchange', this, date);
17372         
17373         
17374         /*
17375         if(!this.internalRender){
17376             var main = this.el.dom.firstChild;
17377             var w = main.offsetWidth;
17378             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17379             Roo.fly(main).setWidth(w);
17380             this.internalRender = true;
17381             // opera does not respect the auto grow header center column
17382             // then, after it gets a width opera refuses to recalculate
17383             // without a second pass
17384             if(Roo.isOpera && !this.secondPass){
17385                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17386                 this.secondPass = true;
17387                 this.update.defer(10, this, [date]);
17388             }
17389         }
17390         */
17391         
17392     },
17393     
17394     findCell : function(dt) {
17395         dt = dt.clearTime().getTime();
17396         var ret = false;
17397         this.cells.each(function(c){
17398             //Roo.log("check " +c.dateValue + '?=' + dt);
17399             if(c.dateValue == dt){
17400                 ret = c;
17401                 return false;
17402             }
17403             return true;
17404         });
17405         
17406         return ret;
17407     },
17408     
17409     findCells : function(ev) {
17410         var s = ev.start.clone().clearTime().getTime();
17411        // Roo.log(s);
17412         var e= ev.end.clone().clearTime().getTime();
17413        // Roo.log(e);
17414         var ret = [];
17415         this.cells.each(function(c){
17416              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17417             
17418             if(c.dateValue > e){
17419                 return ;
17420             }
17421             if(c.dateValue < s){
17422                 return ;
17423             }
17424             ret.push(c);
17425         });
17426         
17427         return ret;    
17428     },
17429     
17430 //    findBestRow: function(cells)
17431 //    {
17432 //        var ret = 0;
17433 //        
17434 //        for (var i =0 ; i < cells.length;i++) {
17435 //            ret  = Math.max(cells[i].rows || 0,ret);
17436 //        }
17437 //        return ret;
17438 //        
17439 //    },
17440     
17441     
17442     addItem : function(ev)
17443     {
17444         // look for vertical location slot in
17445         var cells = this.findCells(ev);
17446         
17447 //        ev.row = this.findBestRow(cells);
17448         
17449         // work out the location.
17450         
17451         var crow = false;
17452         var rows = [];
17453         for(var i =0; i < cells.length; i++) {
17454             
17455             cells[i].row = cells[0].row;
17456             
17457             if(i == 0){
17458                 cells[i].row = cells[i].row + 1;
17459             }
17460             
17461             if (!crow) {
17462                 crow = {
17463                     start : cells[i],
17464                     end :  cells[i]
17465                 };
17466                 continue;
17467             }
17468             if (crow.start.getY() == cells[i].getY()) {
17469                 // on same row.
17470                 crow.end = cells[i];
17471                 continue;
17472             }
17473             // different row.
17474             rows.push(crow);
17475             crow = {
17476                 start: cells[i],
17477                 end : cells[i]
17478             };
17479             
17480         }
17481         
17482         rows.push(crow);
17483         ev.els = [];
17484         ev.rows = rows;
17485         ev.cells = cells;
17486         
17487         cells[0].events.push(ev);
17488         
17489         this.calevents.push(ev);
17490     },
17491     
17492     clearEvents: function() {
17493         
17494         if(!this.calevents){
17495             return;
17496         }
17497         
17498         Roo.each(this.cells.elements, function(c){
17499             c.row = 0;
17500             c.events = [];
17501             c.more = [];
17502         });
17503         
17504         Roo.each(this.calevents, function(e) {
17505             Roo.each(e.els, function(el) {
17506                 el.un('mouseenter' ,this.onEventEnter, this);
17507                 el.un('mouseleave' ,this.onEventLeave, this);
17508                 el.remove();
17509             },this);
17510         },this);
17511         
17512         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17513             e.remove();
17514         });
17515         
17516     },
17517     
17518     renderEvents: function()
17519     {   
17520         var _this = this;
17521         
17522         this.cells.each(function(c) {
17523             
17524             if(c.row < 5){
17525                 return;
17526             }
17527             
17528             var ev = c.events;
17529             
17530             var r = 4;
17531             if(c.row != c.events.length){
17532                 r = 4 - (4 - (c.row - c.events.length));
17533             }
17534             
17535             c.events = ev.slice(0, r);
17536             c.more = ev.slice(r);
17537             
17538             if(c.more.length && c.more.length == 1){
17539                 c.events.push(c.more.pop());
17540             }
17541             
17542             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17543             
17544         });
17545             
17546         this.cells.each(function(c) {
17547             
17548             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17549             
17550             
17551             for (var e = 0; e < c.events.length; e++){
17552                 var ev = c.events[e];
17553                 var rows = ev.rows;
17554                 
17555                 for(var i = 0; i < rows.length; i++) {
17556                 
17557                     // how many rows should it span..
17558
17559                     var  cfg = {
17560                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17561                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17562
17563                         unselectable : "on",
17564                         cn : [
17565                             {
17566                                 cls: 'fc-event-inner',
17567                                 cn : [
17568     //                                {
17569     //                                  tag:'span',
17570     //                                  cls: 'fc-event-time',
17571     //                                  html : cells.length > 1 ? '' : ev.time
17572     //                                },
17573                                     {
17574                                       tag:'span',
17575                                       cls: 'fc-event-title',
17576                                       html : String.format('{0}', ev.title)
17577                                     }
17578
17579
17580                                 ]
17581                             },
17582                             {
17583                                 cls: 'ui-resizable-handle ui-resizable-e',
17584                                 html : '&nbsp;&nbsp;&nbsp'
17585                             }
17586
17587                         ]
17588                     };
17589
17590                     if (i == 0) {
17591                         cfg.cls += ' fc-event-start';
17592                     }
17593                     if ((i+1) == rows.length) {
17594                         cfg.cls += ' fc-event-end';
17595                     }
17596
17597                     var ctr = _this.el.select('.fc-event-container',true).first();
17598                     var cg = ctr.createChild(cfg);
17599
17600                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17601                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17602
17603                     var r = (c.more.length) ? 1 : 0;
17604                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17605                     cg.setWidth(ebox.right - sbox.x -2);
17606
17607                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17608                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17609                     cg.on('click', _this.onEventClick, _this, ev);
17610
17611                     ev.els.push(cg);
17612                     
17613                 }
17614                 
17615             }
17616             
17617             
17618             if(c.more.length){
17619                 var  cfg = {
17620                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17621                     style : 'position: absolute',
17622                     unselectable : "on",
17623                     cn : [
17624                         {
17625                             cls: 'fc-event-inner',
17626                             cn : [
17627                                 {
17628                                   tag:'span',
17629                                   cls: 'fc-event-title',
17630                                   html : 'More'
17631                                 }
17632
17633
17634                             ]
17635                         },
17636                         {
17637                             cls: 'ui-resizable-handle ui-resizable-e',
17638                             html : '&nbsp;&nbsp;&nbsp'
17639                         }
17640
17641                     ]
17642                 };
17643
17644                 var ctr = _this.el.select('.fc-event-container',true).first();
17645                 var cg = ctr.createChild(cfg);
17646
17647                 var sbox = c.select('.fc-day-content',true).first().getBox();
17648                 var ebox = c.select('.fc-day-content',true).first().getBox();
17649                 //Roo.log(cg);
17650                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17651                 cg.setWidth(ebox.right - sbox.x -2);
17652
17653                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17654                 
17655             }
17656             
17657         });
17658         
17659         
17660         
17661     },
17662     
17663     onEventEnter: function (e, el,event,d) {
17664         this.fireEvent('evententer', this, el, event);
17665     },
17666     
17667     onEventLeave: function (e, el,event,d) {
17668         this.fireEvent('eventleave', this, el, event);
17669     },
17670     
17671     onEventClick: function (e, el,event,d) {
17672         this.fireEvent('eventclick', this, el, event);
17673     },
17674     
17675     onMonthChange: function () {
17676         this.store.load();
17677     },
17678     
17679     onMoreEventClick: function(e, el, more)
17680     {
17681         var _this = this;
17682         
17683         this.calpopover.placement = 'right';
17684         this.calpopover.setTitle('More');
17685         
17686         this.calpopover.setContent('');
17687         
17688         var ctr = this.calpopover.el.select('.popover-content', true).first();
17689         
17690         Roo.each(more, function(m){
17691             var cfg = {
17692                 cls : 'fc-event-hori fc-event-draggable',
17693                 html : m.title
17694             };
17695             var cg = ctr.createChild(cfg);
17696             
17697             cg.on('click', _this.onEventClick, _this, m);
17698         });
17699         
17700         this.calpopover.show(el);
17701         
17702         
17703     },
17704     
17705     onLoad: function () 
17706     {   
17707         this.calevents = [];
17708         var cal = this;
17709         
17710         if(this.store.getCount() > 0){
17711             this.store.data.each(function(d){
17712                cal.addItem({
17713                     id : d.data.id,
17714                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17715                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17716                     time : d.data.start_time,
17717                     title : d.data.title,
17718                     description : d.data.description,
17719                     venue : d.data.venue
17720                 });
17721             });
17722         }
17723         
17724         this.renderEvents();
17725         
17726         if(this.calevents.length && this.loadMask){
17727             this.maskEl.hide();
17728         }
17729     },
17730     
17731     onBeforeLoad: function()
17732     {
17733         this.clearEvents();
17734         if(this.loadMask){
17735             this.maskEl.show();
17736         }
17737     }
17738 });
17739
17740  
17741  /*
17742  * - LGPL
17743  *
17744  * element
17745  * 
17746  */
17747
17748 /**
17749  * @class Roo.bootstrap.Popover
17750  * @extends Roo.bootstrap.Component
17751  * Bootstrap Popover class
17752  * @cfg {String} html contents of the popover   (or false to use children..)
17753  * @cfg {String} title of popover (or false to hide)
17754  * @cfg {String} placement how it is placed
17755  * @cfg {String} trigger click || hover (or false to trigger manually)
17756  * @cfg {String} over what (parent or false to trigger manually.)
17757  * @cfg {Number} delay - delay before showing
17758  
17759  * @constructor
17760  * Create a new Popover
17761  * @param {Object} config The config object
17762  */
17763
17764 Roo.bootstrap.Popover = function(config){
17765     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17766     
17767     this.addEvents({
17768         // raw events
17769          /**
17770          * @event show
17771          * After the popover show
17772          * 
17773          * @param {Roo.bootstrap.Popover} this
17774          */
17775         "show" : true,
17776         /**
17777          * @event hide
17778          * After the popover hide
17779          * 
17780          * @param {Roo.bootstrap.Popover} this
17781          */
17782         "hide" : true
17783     });
17784 };
17785
17786 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17787     
17788     title: 'Fill in a title',
17789     html: false,
17790     
17791     placement : 'right',
17792     trigger : 'hover', // hover
17793     
17794     delay : 0,
17795     
17796     over: 'parent',
17797     
17798     can_build_overlaid : false,
17799     
17800     getChildContainer : function()
17801     {
17802         return this.el.select('.popover-content',true).first();
17803     },
17804     
17805     getAutoCreate : function(){
17806          
17807         var cfg = {
17808            cls : 'popover roo-dynamic',
17809            style: 'display:block',
17810            cn : [
17811                 {
17812                     cls : 'arrow'
17813                 },
17814                 {
17815                     cls : 'popover-inner',
17816                     cn : [
17817                         {
17818                             tag: 'h3',
17819                             cls: 'popover-title popover-header',
17820                             html : this.title
17821                         },
17822                         {
17823                             cls : 'popover-content popover-body',
17824                             html : this.html
17825                         }
17826                     ]
17827                     
17828                 }
17829            ]
17830         };
17831         
17832         return cfg;
17833     },
17834     setTitle: function(str)
17835     {
17836         this.title = str;
17837         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17838     },
17839     setContent: function(str)
17840     {
17841         this.html = str;
17842         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17843     },
17844     // as it get's added to the bottom of the page.
17845     onRender : function(ct, position)
17846     {
17847         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17848         if(!this.el){
17849             var cfg = Roo.apply({},  this.getAutoCreate());
17850             cfg.id = Roo.id();
17851             
17852             if (this.cls) {
17853                 cfg.cls += ' ' + this.cls;
17854             }
17855             if (this.style) {
17856                 cfg.style = this.style;
17857             }
17858             //Roo.log("adding to ");
17859             this.el = Roo.get(document.body).createChild(cfg, position);
17860 //            Roo.log(this.el);
17861         }
17862         this.initEvents();
17863     },
17864     
17865     initEvents : function()
17866     {
17867         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17868         this.el.enableDisplayMode('block');
17869         this.el.hide();
17870         if (this.over === false) {
17871             return; 
17872         }
17873         if (this.triggers === false) {
17874             return;
17875         }
17876         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17877         var triggers = this.trigger ? this.trigger.split(' ') : [];
17878         Roo.each(triggers, function(trigger) {
17879         
17880             if (trigger == 'click') {
17881                 on_el.on('click', this.toggle, this);
17882             } else if (trigger != 'manual') {
17883                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17884                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17885       
17886                 on_el.on(eventIn  ,this.enter, this);
17887                 on_el.on(eventOut, this.leave, this);
17888             }
17889         }, this);
17890         
17891     },
17892     
17893     
17894     // private
17895     timeout : null,
17896     hoverState : null,
17897     
17898     toggle : function () {
17899         this.hoverState == 'in' ? this.leave() : this.enter();
17900     },
17901     
17902     enter : function () {
17903         
17904         clearTimeout(this.timeout);
17905     
17906         this.hoverState = 'in';
17907     
17908         if (!this.delay || !this.delay.show) {
17909             this.show();
17910             return;
17911         }
17912         var _t = this;
17913         this.timeout = setTimeout(function () {
17914             if (_t.hoverState == 'in') {
17915                 _t.show();
17916             }
17917         }, this.delay.show)
17918     },
17919     
17920     leave : function() {
17921         clearTimeout(this.timeout);
17922     
17923         this.hoverState = 'out';
17924     
17925         if (!this.delay || !this.delay.hide) {
17926             this.hide();
17927             return;
17928         }
17929         var _t = this;
17930         this.timeout = setTimeout(function () {
17931             if (_t.hoverState == 'out') {
17932                 _t.hide();
17933             }
17934         }, this.delay.hide)
17935     },
17936     
17937     show : function (on_el)
17938     {
17939         if (!on_el) {
17940             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17941         }
17942         
17943         // set content.
17944         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17945         if (this.html !== false) {
17946             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17947         }
17948         this.el.removeClass([
17949             'fade','top','bottom', 'left', 'right','in',
17950             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17951         ]);
17952         if (!this.title.length) {
17953             this.el.select('.popover-title',true).hide();
17954         }
17955         
17956         var placement = typeof this.placement == 'function' ?
17957             this.placement.call(this, this.el, on_el) :
17958             this.placement;
17959             
17960         var autoToken = /\s?auto?\s?/i;
17961         var autoPlace = autoToken.test(placement);
17962         if (autoPlace) {
17963             placement = placement.replace(autoToken, '') || 'top';
17964         }
17965         
17966         //this.el.detach()
17967         //this.el.setXY([0,0]);
17968         this.el.show();
17969         this.el.dom.style.display='block';
17970         this.el.addClass(placement);
17971         
17972         //this.el.appendTo(on_el);
17973         
17974         var p = this.getPosition();
17975         var box = this.el.getBox();
17976         
17977         if (autoPlace) {
17978             // fixme..
17979         }
17980         var align = Roo.bootstrap.Popover.alignment[placement];
17981         
17982 //        Roo.log(align);
17983         this.el.alignTo(on_el, align[0],align[1]);
17984         //var arrow = this.el.select('.arrow',true).first();
17985         //arrow.set(align[2], 
17986         
17987         this.el.addClass('in');
17988         
17989         
17990         if (this.el.hasClass('fade')) {
17991             // fade it?
17992         }
17993         
17994         this.hoverState = 'in';
17995         
17996         this.fireEvent('show', this);
17997         
17998     },
17999     hide : function()
18000     {
18001         this.el.setXY([0,0]);
18002         this.el.removeClass('in');
18003         this.el.hide();
18004         this.hoverState = null;
18005         
18006         this.fireEvent('hide', this);
18007     }
18008     
18009 });
18010
18011 Roo.bootstrap.Popover.alignment = {
18012     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18013     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18014     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18015     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18016 };
18017
18018  /*
18019  * - LGPL
18020  *
18021  * Progress
18022  * 
18023  */
18024
18025 /**
18026  * @class Roo.bootstrap.Progress
18027  * @extends Roo.bootstrap.Component
18028  * Bootstrap Progress class
18029  * @cfg {Boolean} striped striped of the progress bar
18030  * @cfg {Boolean} active animated of the progress bar
18031  * 
18032  * 
18033  * @constructor
18034  * Create a new Progress
18035  * @param {Object} config The config object
18036  */
18037
18038 Roo.bootstrap.Progress = function(config){
18039     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18040 };
18041
18042 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18043     
18044     striped : false,
18045     active: false,
18046     
18047     getAutoCreate : function(){
18048         var cfg = {
18049             tag: 'div',
18050             cls: 'progress'
18051         };
18052         
18053         
18054         if(this.striped){
18055             cfg.cls += ' progress-striped';
18056         }
18057       
18058         if(this.active){
18059             cfg.cls += ' active';
18060         }
18061         
18062         
18063         return cfg;
18064     }
18065    
18066 });
18067
18068  
18069
18070  /*
18071  * - LGPL
18072  *
18073  * ProgressBar
18074  * 
18075  */
18076
18077 /**
18078  * @class Roo.bootstrap.ProgressBar
18079  * @extends Roo.bootstrap.Component
18080  * Bootstrap ProgressBar class
18081  * @cfg {Number} aria_valuenow aria-value now
18082  * @cfg {Number} aria_valuemin aria-value min
18083  * @cfg {Number} aria_valuemax aria-value max
18084  * @cfg {String} label label for the progress bar
18085  * @cfg {String} panel (success | info | warning | danger )
18086  * @cfg {String} role role of the progress bar
18087  * @cfg {String} sr_only text
18088  * 
18089  * 
18090  * @constructor
18091  * Create a new ProgressBar
18092  * @param {Object} config The config object
18093  */
18094
18095 Roo.bootstrap.ProgressBar = function(config){
18096     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18097 };
18098
18099 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18100     
18101     aria_valuenow : 0,
18102     aria_valuemin : 0,
18103     aria_valuemax : 100,
18104     label : false,
18105     panel : false,
18106     role : false,
18107     sr_only: false,
18108     
18109     getAutoCreate : function()
18110     {
18111         
18112         var cfg = {
18113             tag: 'div',
18114             cls: 'progress-bar',
18115             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18116         };
18117         
18118         if(this.sr_only){
18119             cfg.cn = {
18120                 tag: 'span',
18121                 cls: 'sr-only',
18122                 html: this.sr_only
18123             }
18124         }
18125         
18126         if(this.role){
18127             cfg.role = this.role;
18128         }
18129         
18130         if(this.aria_valuenow){
18131             cfg['aria-valuenow'] = this.aria_valuenow;
18132         }
18133         
18134         if(this.aria_valuemin){
18135             cfg['aria-valuemin'] = this.aria_valuemin;
18136         }
18137         
18138         if(this.aria_valuemax){
18139             cfg['aria-valuemax'] = this.aria_valuemax;
18140         }
18141         
18142         if(this.label && !this.sr_only){
18143             cfg.html = this.label;
18144         }
18145         
18146         if(this.panel){
18147             cfg.cls += ' progress-bar-' + this.panel;
18148         }
18149         
18150         return cfg;
18151     },
18152     
18153     update : function(aria_valuenow)
18154     {
18155         this.aria_valuenow = aria_valuenow;
18156         
18157         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18158     }
18159    
18160 });
18161
18162  
18163
18164  /*
18165  * - LGPL
18166  *
18167  * column
18168  * 
18169  */
18170
18171 /**
18172  * @class Roo.bootstrap.TabGroup
18173  * @extends Roo.bootstrap.Column
18174  * Bootstrap Column class
18175  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18176  * @cfg {Boolean} carousel true to make the group behave like a carousel
18177  * @cfg {Boolean} bullets show bullets for the panels
18178  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18179  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18180  * @cfg {Boolean} showarrow (true|false) show arrow default true
18181  * 
18182  * @constructor
18183  * Create a new TabGroup
18184  * @param {Object} config The config object
18185  */
18186
18187 Roo.bootstrap.TabGroup = function(config){
18188     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18189     if (!this.navId) {
18190         this.navId = Roo.id();
18191     }
18192     this.tabs = [];
18193     Roo.bootstrap.TabGroup.register(this);
18194     
18195 };
18196
18197 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18198     
18199     carousel : false,
18200     transition : false,
18201     bullets : 0,
18202     timer : 0,
18203     autoslide : false,
18204     slideFn : false,
18205     slideOnTouch : false,
18206     showarrow : true,
18207     
18208     getAutoCreate : function()
18209     {
18210         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18211         
18212         cfg.cls += ' tab-content';
18213         
18214         if (this.carousel) {
18215             cfg.cls += ' carousel slide';
18216             
18217             cfg.cn = [{
18218                cls : 'carousel-inner',
18219                cn : []
18220             }];
18221         
18222             if(this.bullets  && !Roo.isTouch){
18223                 
18224                 var bullets = {
18225                     cls : 'carousel-bullets',
18226                     cn : []
18227                 };
18228                
18229                 if(this.bullets_cls){
18230                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18231                 }
18232                 
18233                 bullets.cn.push({
18234                     cls : 'clear'
18235                 });
18236                 
18237                 cfg.cn[0].cn.push(bullets);
18238             }
18239             
18240             if(this.showarrow){
18241                 cfg.cn[0].cn.push({
18242                     tag : 'div',
18243                     class : 'carousel-arrow',
18244                     cn : [
18245                         {
18246                             tag : 'div',
18247                             class : 'carousel-prev',
18248                             cn : [
18249                                 {
18250                                     tag : 'i',
18251                                     class : 'fa fa-chevron-left'
18252                                 }
18253                             ]
18254                         },
18255                         {
18256                             tag : 'div',
18257                             class : 'carousel-next',
18258                             cn : [
18259                                 {
18260                                     tag : 'i',
18261                                     class : 'fa fa-chevron-right'
18262                                 }
18263                             ]
18264                         }
18265                     ]
18266                 });
18267             }
18268             
18269         }
18270         
18271         return cfg;
18272     },
18273     
18274     initEvents:  function()
18275     {
18276 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18277 //            this.el.on("touchstart", this.onTouchStart, this);
18278 //        }
18279         
18280         if(this.autoslide){
18281             var _this = this;
18282             
18283             this.slideFn = window.setInterval(function() {
18284                 _this.showPanelNext();
18285             }, this.timer);
18286         }
18287         
18288         if(this.showarrow){
18289             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18290             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18291         }
18292         
18293         
18294     },
18295     
18296 //    onTouchStart : function(e, el, o)
18297 //    {
18298 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18299 //            return;
18300 //        }
18301 //        
18302 //        this.showPanelNext();
18303 //    },
18304     
18305     
18306     getChildContainer : function()
18307     {
18308         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18309     },
18310     
18311     /**
18312     * register a Navigation item
18313     * @param {Roo.bootstrap.NavItem} the navitem to add
18314     */
18315     register : function(item)
18316     {
18317         this.tabs.push( item);
18318         item.navId = this.navId; // not really needed..
18319         this.addBullet();
18320     
18321     },
18322     
18323     getActivePanel : function()
18324     {
18325         var r = false;
18326         Roo.each(this.tabs, function(t) {
18327             if (t.active) {
18328                 r = t;
18329                 return false;
18330             }
18331             return null;
18332         });
18333         return r;
18334         
18335     },
18336     getPanelByName : function(n)
18337     {
18338         var r = false;
18339         Roo.each(this.tabs, function(t) {
18340             if (t.tabId == n) {
18341                 r = t;
18342                 return false;
18343             }
18344             return null;
18345         });
18346         return r;
18347     },
18348     indexOfPanel : function(p)
18349     {
18350         var r = false;
18351         Roo.each(this.tabs, function(t,i) {
18352             if (t.tabId == p.tabId) {
18353                 r = i;
18354                 return false;
18355             }
18356             return null;
18357         });
18358         return r;
18359     },
18360     /**
18361      * show a specific panel
18362      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18363      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18364      */
18365     showPanel : function (pan)
18366     {
18367         if(this.transition || typeof(pan) == 'undefined'){
18368             Roo.log("waiting for the transitionend");
18369             return false;
18370         }
18371         
18372         if (typeof(pan) == 'number') {
18373             pan = this.tabs[pan];
18374         }
18375         
18376         if (typeof(pan) == 'string') {
18377             pan = this.getPanelByName(pan);
18378         }
18379         
18380         var cur = this.getActivePanel();
18381         
18382         if(!pan || !cur){
18383             Roo.log('pan or acitve pan is undefined');
18384             return false;
18385         }
18386         
18387         if (pan.tabId == this.getActivePanel().tabId) {
18388             return true;
18389         }
18390         
18391         if (false === cur.fireEvent('beforedeactivate')) {
18392             return false;
18393         }
18394         
18395         if(this.bullets > 0 && !Roo.isTouch){
18396             this.setActiveBullet(this.indexOfPanel(pan));
18397         }
18398         
18399         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18400             
18401             //class="carousel-item carousel-item-next carousel-item-left"
18402             
18403             this.transition = true;
18404             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18405             var lr = dir == 'next' ? 'left' : 'right';
18406             pan.el.addClass(dir); // or prev
18407             pan.el.addClass('carousel-item-' + dir); // or prev
18408             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18409             cur.el.addClass(lr); // or right
18410             pan.el.addClass(lr);
18411             cur.el.addClass('carousel-item-' +lr); // or right
18412             pan.el.addClass('carousel-item-' +lr);
18413             
18414             
18415             var _this = this;
18416             cur.el.on('transitionend', function() {
18417                 Roo.log("trans end?");
18418                 
18419                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18420                 pan.setActive(true);
18421                 
18422                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18423                 cur.setActive(false);
18424                 
18425                 _this.transition = false;
18426                 
18427             }, this, { single:  true } );
18428             
18429             return true;
18430         }
18431         
18432         cur.setActive(false);
18433         pan.setActive(true);
18434         
18435         return true;
18436         
18437     },
18438     showPanelNext : function()
18439     {
18440         var i = this.indexOfPanel(this.getActivePanel());
18441         
18442         if (i >= this.tabs.length - 1 && !this.autoslide) {
18443             return;
18444         }
18445         
18446         if (i >= this.tabs.length - 1 && this.autoslide) {
18447             i = -1;
18448         }
18449         
18450         this.showPanel(this.tabs[i+1]);
18451     },
18452     
18453     showPanelPrev : function()
18454     {
18455         var i = this.indexOfPanel(this.getActivePanel());
18456         
18457         if (i  < 1 && !this.autoslide) {
18458             return;
18459         }
18460         
18461         if (i < 1 && this.autoslide) {
18462             i = this.tabs.length;
18463         }
18464         
18465         this.showPanel(this.tabs[i-1]);
18466     },
18467     
18468     
18469     addBullet: function()
18470     {
18471         if(!this.bullets || Roo.isTouch){
18472             return;
18473         }
18474         var ctr = this.el.select('.carousel-bullets',true).first();
18475         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18476         var bullet = ctr.createChild({
18477             cls : 'bullet bullet-' + i
18478         },ctr.dom.lastChild);
18479         
18480         
18481         var _this = this;
18482         
18483         bullet.on('click', (function(e, el, o, ii, t){
18484
18485             e.preventDefault();
18486
18487             this.showPanel(ii);
18488
18489             if(this.autoslide && this.slideFn){
18490                 clearInterval(this.slideFn);
18491                 this.slideFn = window.setInterval(function() {
18492                     _this.showPanelNext();
18493                 }, this.timer);
18494             }
18495
18496         }).createDelegate(this, [i, bullet], true));
18497                 
18498         
18499     },
18500      
18501     setActiveBullet : function(i)
18502     {
18503         if(Roo.isTouch){
18504             return;
18505         }
18506         
18507         Roo.each(this.el.select('.bullet', true).elements, function(el){
18508             el.removeClass('selected');
18509         });
18510
18511         var bullet = this.el.select('.bullet-' + i, true).first();
18512         
18513         if(!bullet){
18514             return;
18515         }
18516         
18517         bullet.addClass('selected');
18518     }
18519     
18520     
18521   
18522 });
18523
18524  
18525
18526  
18527  
18528 Roo.apply(Roo.bootstrap.TabGroup, {
18529     
18530     groups: {},
18531      /**
18532     * register a Navigation Group
18533     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18534     */
18535     register : function(navgrp)
18536     {
18537         this.groups[navgrp.navId] = navgrp;
18538         
18539     },
18540     /**
18541     * fetch a Navigation Group based on the navigation ID
18542     * if one does not exist , it will get created.
18543     * @param {string} the navgroup to add
18544     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18545     */
18546     get: function(navId) {
18547         if (typeof(this.groups[navId]) == 'undefined') {
18548             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18549         }
18550         return this.groups[navId] ;
18551     }
18552     
18553     
18554     
18555 });
18556
18557  /*
18558  * - LGPL
18559  *
18560  * TabPanel
18561  * 
18562  */
18563
18564 /**
18565  * @class Roo.bootstrap.TabPanel
18566  * @extends Roo.bootstrap.Component
18567  * Bootstrap TabPanel class
18568  * @cfg {Boolean} active panel active
18569  * @cfg {String} html panel content
18570  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18571  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18572  * @cfg {String} href click to link..
18573  * 
18574  * 
18575  * @constructor
18576  * Create a new TabPanel
18577  * @param {Object} config The config object
18578  */
18579
18580 Roo.bootstrap.TabPanel = function(config){
18581     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18582     this.addEvents({
18583         /**
18584              * @event changed
18585              * Fires when the active status changes
18586              * @param {Roo.bootstrap.TabPanel} this
18587              * @param {Boolean} state the new state
18588             
18589          */
18590         'changed': true,
18591         /**
18592              * @event beforedeactivate
18593              * Fires before a tab is de-activated - can be used to do validation on a form.
18594              * @param {Roo.bootstrap.TabPanel} this
18595              * @return {Boolean} false if there is an error
18596             
18597          */
18598         'beforedeactivate': true
18599      });
18600     
18601     this.tabId = this.tabId || Roo.id();
18602   
18603 };
18604
18605 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18606     
18607     active: false,
18608     html: false,
18609     tabId: false,
18610     navId : false,
18611     href : '',
18612     
18613     getAutoCreate : function(){
18614         
18615         
18616         var cfg = {
18617             tag: 'div',
18618             // item is needed for carousel - not sure if it has any effect otherwise
18619             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18620             html: this.html || ''
18621         };
18622         
18623         if(this.active){
18624             cfg.cls += ' active';
18625         }
18626         
18627         if(this.tabId){
18628             cfg.tabId = this.tabId;
18629         }
18630         
18631         
18632         
18633         return cfg;
18634     },
18635     
18636     initEvents:  function()
18637     {
18638         var p = this.parent();
18639         
18640         this.navId = this.navId || p.navId;
18641         
18642         if (typeof(this.navId) != 'undefined') {
18643             // not really needed.. but just in case.. parent should be a NavGroup.
18644             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18645             
18646             tg.register(this);
18647             
18648             var i = tg.tabs.length - 1;
18649             
18650             if(this.active && tg.bullets > 0 && i < tg.bullets){
18651                 tg.setActiveBullet(i);
18652             }
18653         }
18654         
18655         this.el.on('click', this.onClick, this);
18656         
18657         if(Roo.isTouch){
18658             this.el.on("touchstart", this.onTouchStart, this);
18659             this.el.on("touchmove", this.onTouchMove, this);
18660             this.el.on("touchend", this.onTouchEnd, this);
18661         }
18662         
18663     },
18664     
18665     onRender : function(ct, position)
18666     {
18667         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18668     },
18669     
18670     setActive : function(state)
18671     {
18672         Roo.log("panel - set active " + this.tabId + "=" + state);
18673         
18674         this.active = state;
18675         if (!state) {
18676             this.el.removeClass('active');
18677             
18678         } else  if (!this.el.hasClass('active')) {
18679             this.el.addClass('active');
18680         }
18681         
18682         this.fireEvent('changed', this, state);
18683     },
18684     
18685     onClick : function(e)
18686     {
18687         e.preventDefault();
18688         
18689         if(!this.href.length){
18690             return;
18691         }
18692         
18693         window.location.href = this.href;
18694     },
18695     
18696     startX : 0,
18697     startY : 0,
18698     endX : 0,
18699     endY : 0,
18700     swiping : false,
18701     
18702     onTouchStart : function(e)
18703     {
18704         this.swiping = false;
18705         
18706         this.startX = e.browserEvent.touches[0].clientX;
18707         this.startY = e.browserEvent.touches[0].clientY;
18708     },
18709     
18710     onTouchMove : function(e)
18711     {
18712         this.swiping = true;
18713         
18714         this.endX = e.browserEvent.touches[0].clientX;
18715         this.endY = e.browserEvent.touches[0].clientY;
18716     },
18717     
18718     onTouchEnd : function(e)
18719     {
18720         if(!this.swiping){
18721             this.onClick(e);
18722             return;
18723         }
18724         
18725         var tabGroup = this.parent();
18726         
18727         if(this.endX > this.startX){ // swiping right
18728             tabGroup.showPanelPrev();
18729             return;
18730         }
18731         
18732         if(this.startX > this.endX){ // swiping left
18733             tabGroup.showPanelNext();
18734             return;
18735         }
18736     }
18737     
18738     
18739 });
18740  
18741
18742  
18743
18744  /*
18745  * - LGPL
18746  *
18747  * DateField
18748  * 
18749  */
18750
18751 /**
18752  * @class Roo.bootstrap.DateField
18753  * @extends Roo.bootstrap.Input
18754  * Bootstrap DateField class
18755  * @cfg {Number} weekStart default 0
18756  * @cfg {String} viewMode default empty, (months|years)
18757  * @cfg {String} minViewMode default empty, (months|years)
18758  * @cfg {Number} startDate default -Infinity
18759  * @cfg {Number} endDate default Infinity
18760  * @cfg {Boolean} todayHighlight default false
18761  * @cfg {Boolean} todayBtn default false
18762  * @cfg {Boolean} calendarWeeks default false
18763  * @cfg {Object} daysOfWeekDisabled default empty
18764  * @cfg {Boolean} singleMode default false (true | false)
18765  * 
18766  * @cfg {Boolean} keyboardNavigation default true
18767  * @cfg {String} language default en
18768  * 
18769  * @constructor
18770  * Create a new DateField
18771  * @param {Object} config The config object
18772  */
18773
18774 Roo.bootstrap.DateField = function(config){
18775     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18776      this.addEvents({
18777             /**
18778              * @event show
18779              * Fires when this field show.
18780              * @param {Roo.bootstrap.DateField} this
18781              * @param {Mixed} date The date value
18782              */
18783             show : true,
18784             /**
18785              * @event show
18786              * Fires when this field hide.
18787              * @param {Roo.bootstrap.DateField} this
18788              * @param {Mixed} date The date value
18789              */
18790             hide : true,
18791             /**
18792              * @event select
18793              * Fires when select a date.
18794              * @param {Roo.bootstrap.DateField} this
18795              * @param {Mixed} date The date value
18796              */
18797             select : true,
18798             /**
18799              * @event beforeselect
18800              * Fires when before select a date.
18801              * @param {Roo.bootstrap.DateField} this
18802              * @param {Mixed} date The date value
18803              */
18804             beforeselect : true
18805         });
18806 };
18807
18808 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18809     
18810     /**
18811      * @cfg {String} format
18812      * The default date format string which can be overriden for localization support.  The format must be
18813      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18814      */
18815     format : "m/d/y",
18816     /**
18817      * @cfg {String} altFormats
18818      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18819      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18820      */
18821     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18822     
18823     weekStart : 0,
18824     
18825     viewMode : '',
18826     
18827     minViewMode : '',
18828     
18829     todayHighlight : false,
18830     
18831     todayBtn: false,
18832     
18833     language: 'en',
18834     
18835     keyboardNavigation: true,
18836     
18837     calendarWeeks: false,
18838     
18839     startDate: -Infinity,
18840     
18841     endDate: Infinity,
18842     
18843     daysOfWeekDisabled: [],
18844     
18845     _events: [],
18846     
18847     singleMode : false,
18848     
18849     UTCDate: function()
18850     {
18851         return new Date(Date.UTC.apply(Date, arguments));
18852     },
18853     
18854     UTCToday: function()
18855     {
18856         var today = new Date();
18857         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18858     },
18859     
18860     getDate: function() {
18861             var d = this.getUTCDate();
18862             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18863     },
18864     
18865     getUTCDate: function() {
18866             return this.date;
18867     },
18868     
18869     setDate: function(d) {
18870             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18871     },
18872     
18873     setUTCDate: function(d) {
18874             this.date = d;
18875             this.setValue(this.formatDate(this.date));
18876     },
18877         
18878     onRender: function(ct, position)
18879     {
18880         
18881         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18882         
18883         this.language = this.language || 'en';
18884         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18885         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18886         
18887         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18888         this.format = this.format || 'm/d/y';
18889         this.isInline = false;
18890         this.isInput = true;
18891         this.component = this.el.select('.add-on', true).first() || false;
18892         this.component = (this.component && this.component.length === 0) ? false : this.component;
18893         this.hasInput = this.component && this.inputEl().length;
18894         
18895         if (typeof(this.minViewMode === 'string')) {
18896             switch (this.minViewMode) {
18897                 case 'months':
18898                     this.minViewMode = 1;
18899                     break;
18900                 case 'years':
18901                     this.minViewMode = 2;
18902                     break;
18903                 default:
18904                     this.minViewMode = 0;
18905                     break;
18906             }
18907         }
18908         
18909         if (typeof(this.viewMode === 'string')) {
18910             switch (this.viewMode) {
18911                 case 'months':
18912                     this.viewMode = 1;
18913                     break;
18914                 case 'years':
18915                     this.viewMode = 2;
18916                     break;
18917                 default:
18918                     this.viewMode = 0;
18919                     break;
18920             }
18921         }
18922                 
18923         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18924         
18925 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18926         
18927         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18928         
18929         this.picker().on('mousedown', this.onMousedown, this);
18930         this.picker().on('click', this.onClick, this);
18931         
18932         this.picker().addClass('datepicker-dropdown');
18933         
18934         this.startViewMode = this.viewMode;
18935         
18936         if(this.singleMode){
18937             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18938                 v.setVisibilityMode(Roo.Element.DISPLAY);
18939                 v.hide();
18940             });
18941             
18942             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18943                 v.setStyle('width', '189px');
18944             });
18945         }
18946         
18947         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18948             if(!this.calendarWeeks){
18949                 v.remove();
18950                 return;
18951             }
18952             
18953             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18954             v.attr('colspan', function(i, val){
18955                 return parseInt(val) + 1;
18956             });
18957         });
18958                         
18959         
18960         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18961         
18962         this.setStartDate(this.startDate);
18963         this.setEndDate(this.endDate);
18964         
18965         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18966         
18967         this.fillDow();
18968         this.fillMonths();
18969         this.update();
18970         this.showMode();
18971         
18972         if(this.isInline) {
18973             this.showPopup();
18974         }
18975     },
18976     
18977     picker : function()
18978     {
18979         return this.pickerEl;
18980 //        return this.el.select('.datepicker', true).first();
18981     },
18982     
18983     fillDow: function()
18984     {
18985         var dowCnt = this.weekStart;
18986         
18987         var dow = {
18988             tag: 'tr',
18989             cn: [
18990                 
18991             ]
18992         };
18993         
18994         if(this.calendarWeeks){
18995             dow.cn.push({
18996                 tag: 'th',
18997                 cls: 'cw',
18998                 html: '&nbsp;'
18999             })
19000         }
19001         
19002         while (dowCnt < this.weekStart + 7) {
19003             dow.cn.push({
19004                 tag: 'th',
19005                 cls: 'dow',
19006                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19007             });
19008         }
19009         
19010         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19011     },
19012     
19013     fillMonths: function()
19014     {    
19015         var i = 0;
19016         var months = this.picker().select('>.datepicker-months td', true).first();
19017         
19018         months.dom.innerHTML = '';
19019         
19020         while (i < 12) {
19021             var month = {
19022                 tag: 'span',
19023                 cls: 'month',
19024                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19025             };
19026             
19027             months.createChild(month);
19028         }
19029         
19030     },
19031     
19032     update: function()
19033     {
19034         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;
19035         
19036         if (this.date < this.startDate) {
19037             this.viewDate = new Date(this.startDate);
19038         } else if (this.date > this.endDate) {
19039             this.viewDate = new Date(this.endDate);
19040         } else {
19041             this.viewDate = new Date(this.date);
19042         }
19043         
19044         this.fill();
19045     },
19046     
19047     fill: function() 
19048     {
19049         var d = new Date(this.viewDate),
19050                 year = d.getUTCFullYear(),
19051                 month = d.getUTCMonth(),
19052                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19053                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19054                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19055                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19056                 currentDate = this.date && this.date.valueOf(),
19057                 today = this.UTCToday();
19058         
19059         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19060         
19061 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19062         
19063 //        this.picker.select('>tfoot th.today').
19064 //                                              .text(dates[this.language].today)
19065 //                                              .toggle(this.todayBtn !== false);
19066     
19067         this.updateNavArrows();
19068         this.fillMonths();
19069                                                 
19070         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19071         
19072         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19073          
19074         prevMonth.setUTCDate(day);
19075         
19076         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19077         
19078         var nextMonth = new Date(prevMonth);
19079         
19080         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19081         
19082         nextMonth = nextMonth.valueOf();
19083         
19084         var fillMonths = false;
19085         
19086         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19087         
19088         while(prevMonth.valueOf() <= nextMonth) {
19089             var clsName = '';
19090             
19091             if (prevMonth.getUTCDay() === this.weekStart) {
19092                 if(fillMonths){
19093                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19094                 }
19095                     
19096                 fillMonths = {
19097                     tag: 'tr',
19098                     cn: []
19099                 };
19100                 
19101                 if(this.calendarWeeks){
19102                     // ISO 8601: First week contains first thursday.
19103                     // ISO also states week starts on Monday, but we can be more abstract here.
19104                     var
19105                     // Start of current week: based on weekstart/current date
19106                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19107                     // Thursday of this week
19108                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19109                     // First Thursday of year, year from thursday
19110                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19111                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19112                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19113                     
19114                     fillMonths.cn.push({
19115                         tag: 'td',
19116                         cls: 'cw',
19117                         html: calWeek
19118                     });
19119                 }
19120             }
19121             
19122             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19123                 clsName += ' old';
19124             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19125                 clsName += ' new';
19126             }
19127             if (this.todayHighlight &&
19128                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19129                 prevMonth.getUTCMonth() == today.getMonth() &&
19130                 prevMonth.getUTCDate() == today.getDate()) {
19131                 clsName += ' today';
19132             }
19133             
19134             if (currentDate && prevMonth.valueOf() === currentDate) {
19135                 clsName += ' active';
19136             }
19137             
19138             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19139                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19140                     clsName += ' disabled';
19141             }
19142             
19143             fillMonths.cn.push({
19144                 tag: 'td',
19145                 cls: 'day ' + clsName,
19146                 html: prevMonth.getDate()
19147             });
19148             
19149             prevMonth.setDate(prevMonth.getDate()+1);
19150         }
19151           
19152         var currentYear = this.date && this.date.getUTCFullYear();
19153         var currentMonth = this.date && this.date.getUTCMonth();
19154         
19155         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19156         
19157         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19158             v.removeClass('active');
19159             
19160             if(currentYear === year && k === currentMonth){
19161                 v.addClass('active');
19162             }
19163             
19164             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19165                 v.addClass('disabled');
19166             }
19167             
19168         });
19169         
19170         
19171         year = parseInt(year/10, 10) * 10;
19172         
19173         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19174         
19175         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19176         
19177         year -= 1;
19178         for (var i = -1; i < 11; i++) {
19179             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19180                 tag: 'span',
19181                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19182                 html: year
19183             });
19184             
19185             year += 1;
19186         }
19187     },
19188     
19189     showMode: function(dir) 
19190     {
19191         if (dir) {
19192             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19193         }
19194         
19195         Roo.each(this.picker().select('>div',true).elements, function(v){
19196             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19197             v.hide();
19198         });
19199         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19200     },
19201     
19202     place: function()
19203     {
19204         if(this.isInline) {
19205             return;
19206         }
19207         
19208         this.picker().removeClass(['bottom', 'top']);
19209         
19210         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19211             /*
19212              * place to the top of element!
19213              *
19214              */
19215             
19216             this.picker().addClass('top');
19217             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19218             
19219             return;
19220         }
19221         
19222         this.picker().addClass('bottom');
19223         
19224         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19225     },
19226     
19227     parseDate : function(value)
19228     {
19229         if(!value || value instanceof Date){
19230             return value;
19231         }
19232         var v = Date.parseDate(value, this.format);
19233         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19234             v = Date.parseDate(value, 'Y-m-d');
19235         }
19236         if(!v && this.altFormats){
19237             if(!this.altFormatsArray){
19238                 this.altFormatsArray = this.altFormats.split("|");
19239             }
19240             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19241                 v = Date.parseDate(value, this.altFormatsArray[i]);
19242             }
19243         }
19244         return v;
19245     },
19246     
19247     formatDate : function(date, fmt)
19248     {   
19249         return (!date || !(date instanceof Date)) ?
19250         date : date.dateFormat(fmt || this.format);
19251     },
19252     
19253     onFocus : function()
19254     {
19255         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19256         this.showPopup();
19257     },
19258     
19259     onBlur : function()
19260     {
19261         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19262         
19263         var d = this.inputEl().getValue();
19264         
19265         this.setValue(d);
19266                 
19267         this.hidePopup();
19268     },
19269     
19270     showPopup : function()
19271     {
19272         this.picker().show();
19273         this.update();
19274         this.place();
19275         
19276         this.fireEvent('showpopup', this, this.date);
19277     },
19278     
19279     hidePopup : function()
19280     {
19281         if(this.isInline) {
19282             return;
19283         }
19284         this.picker().hide();
19285         this.viewMode = this.startViewMode;
19286         this.showMode();
19287         
19288         this.fireEvent('hidepopup', this, this.date);
19289         
19290     },
19291     
19292     onMousedown: function(e)
19293     {
19294         e.stopPropagation();
19295         e.preventDefault();
19296     },
19297     
19298     keyup: function(e)
19299     {
19300         Roo.bootstrap.DateField.superclass.keyup.call(this);
19301         this.update();
19302     },
19303
19304     setValue: function(v)
19305     {
19306         if(this.fireEvent('beforeselect', this, v) !== false){
19307             var d = new Date(this.parseDate(v) ).clearTime();
19308         
19309             if(isNaN(d.getTime())){
19310                 this.date = this.viewDate = '';
19311                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19312                 return;
19313             }
19314
19315             v = this.formatDate(d);
19316
19317             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19318
19319             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19320
19321             this.update();
19322
19323             this.fireEvent('select', this, this.date);
19324         }
19325     },
19326     
19327     getValue: function()
19328     {
19329         return this.formatDate(this.date);
19330     },
19331     
19332     fireKey: function(e)
19333     {
19334         if (!this.picker().isVisible()){
19335             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19336                 this.showPopup();
19337             }
19338             return;
19339         }
19340         
19341         var dateChanged = false,
19342         dir, day, month,
19343         newDate, newViewDate;
19344         
19345         switch(e.keyCode){
19346             case 27: // escape
19347                 this.hidePopup();
19348                 e.preventDefault();
19349                 break;
19350             case 37: // left
19351             case 39: // right
19352                 if (!this.keyboardNavigation) {
19353                     break;
19354                 }
19355                 dir = e.keyCode == 37 ? -1 : 1;
19356                 
19357                 if (e.ctrlKey){
19358                     newDate = this.moveYear(this.date, dir);
19359                     newViewDate = this.moveYear(this.viewDate, dir);
19360                 } else if (e.shiftKey){
19361                     newDate = this.moveMonth(this.date, dir);
19362                     newViewDate = this.moveMonth(this.viewDate, dir);
19363                 } else {
19364                     newDate = new Date(this.date);
19365                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19366                     newViewDate = new Date(this.viewDate);
19367                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19368                 }
19369                 if (this.dateWithinRange(newDate)){
19370                     this.date = newDate;
19371                     this.viewDate = newViewDate;
19372                     this.setValue(this.formatDate(this.date));
19373 //                    this.update();
19374                     e.preventDefault();
19375                     dateChanged = true;
19376                 }
19377                 break;
19378             case 38: // up
19379             case 40: // down
19380                 if (!this.keyboardNavigation) {
19381                     break;
19382                 }
19383                 dir = e.keyCode == 38 ? -1 : 1;
19384                 if (e.ctrlKey){
19385                     newDate = this.moveYear(this.date, dir);
19386                     newViewDate = this.moveYear(this.viewDate, dir);
19387                 } else if (e.shiftKey){
19388                     newDate = this.moveMonth(this.date, dir);
19389                     newViewDate = this.moveMonth(this.viewDate, dir);
19390                 } else {
19391                     newDate = new Date(this.date);
19392                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19393                     newViewDate = new Date(this.viewDate);
19394                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19395                 }
19396                 if (this.dateWithinRange(newDate)){
19397                     this.date = newDate;
19398                     this.viewDate = newViewDate;
19399                     this.setValue(this.formatDate(this.date));
19400 //                    this.update();
19401                     e.preventDefault();
19402                     dateChanged = true;
19403                 }
19404                 break;
19405             case 13: // enter
19406                 this.setValue(this.formatDate(this.date));
19407                 this.hidePopup();
19408                 e.preventDefault();
19409                 break;
19410             case 9: // tab
19411                 this.setValue(this.formatDate(this.date));
19412                 this.hidePopup();
19413                 break;
19414             case 16: // shift
19415             case 17: // ctrl
19416             case 18: // alt
19417                 break;
19418             default :
19419                 this.hidePopup();
19420                 
19421         }
19422     },
19423     
19424     
19425     onClick: function(e) 
19426     {
19427         e.stopPropagation();
19428         e.preventDefault();
19429         
19430         var target = e.getTarget();
19431         
19432         if(target.nodeName.toLowerCase() === 'i'){
19433             target = Roo.get(target).dom.parentNode;
19434         }
19435         
19436         var nodeName = target.nodeName;
19437         var className = target.className;
19438         var html = target.innerHTML;
19439         //Roo.log(nodeName);
19440         
19441         switch(nodeName.toLowerCase()) {
19442             case 'th':
19443                 switch(className) {
19444                     case 'switch':
19445                         this.showMode(1);
19446                         break;
19447                     case 'prev':
19448                     case 'next':
19449                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19450                         switch(this.viewMode){
19451                                 case 0:
19452                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19453                                         break;
19454                                 case 1:
19455                                 case 2:
19456                                         this.viewDate = this.moveYear(this.viewDate, dir);
19457                                         break;
19458                         }
19459                         this.fill();
19460                         break;
19461                     case 'today':
19462                         var date = new Date();
19463                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19464 //                        this.fill()
19465                         this.setValue(this.formatDate(this.date));
19466                         
19467                         this.hidePopup();
19468                         break;
19469                 }
19470                 break;
19471             case 'span':
19472                 if (className.indexOf('disabled') < 0) {
19473                     this.viewDate.setUTCDate(1);
19474                     if (className.indexOf('month') > -1) {
19475                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19476                     } else {
19477                         var year = parseInt(html, 10) || 0;
19478                         this.viewDate.setUTCFullYear(year);
19479                         
19480                     }
19481                     
19482                     if(this.singleMode){
19483                         this.setValue(this.formatDate(this.viewDate));
19484                         this.hidePopup();
19485                         return;
19486                     }
19487                     
19488                     this.showMode(-1);
19489                     this.fill();
19490                 }
19491                 break;
19492                 
19493             case 'td':
19494                 //Roo.log(className);
19495                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19496                     var day = parseInt(html, 10) || 1;
19497                     var year = this.viewDate.getUTCFullYear(),
19498                         month = this.viewDate.getUTCMonth();
19499
19500                     if (className.indexOf('old') > -1) {
19501                         if(month === 0 ){
19502                             month = 11;
19503                             year -= 1;
19504                         }else{
19505                             month -= 1;
19506                         }
19507                     } else if (className.indexOf('new') > -1) {
19508                         if (month == 11) {
19509                             month = 0;
19510                             year += 1;
19511                         } else {
19512                             month += 1;
19513                         }
19514                     }
19515                     //Roo.log([year,month,day]);
19516                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19517                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19518 //                    this.fill();
19519                     //Roo.log(this.formatDate(this.date));
19520                     this.setValue(this.formatDate(this.date));
19521                     this.hidePopup();
19522                 }
19523                 break;
19524         }
19525     },
19526     
19527     setStartDate: function(startDate)
19528     {
19529         this.startDate = startDate || -Infinity;
19530         if (this.startDate !== -Infinity) {
19531             this.startDate = this.parseDate(this.startDate);
19532         }
19533         this.update();
19534         this.updateNavArrows();
19535     },
19536
19537     setEndDate: function(endDate)
19538     {
19539         this.endDate = endDate || Infinity;
19540         if (this.endDate !== Infinity) {
19541             this.endDate = this.parseDate(this.endDate);
19542         }
19543         this.update();
19544         this.updateNavArrows();
19545     },
19546     
19547     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19548     {
19549         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19550         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19551             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19552         }
19553         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19554             return parseInt(d, 10);
19555         });
19556         this.update();
19557         this.updateNavArrows();
19558     },
19559     
19560     updateNavArrows: function() 
19561     {
19562         if(this.singleMode){
19563             return;
19564         }
19565         
19566         var d = new Date(this.viewDate),
19567         year = d.getUTCFullYear(),
19568         month = d.getUTCMonth();
19569         
19570         Roo.each(this.picker().select('.prev', true).elements, function(v){
19571             v.show();
19572             switch (this.viewMode) {
19573                 case 0:
19574
19575                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19576                         v.hide();
19577                     }
19578                     break;
19579                 case 1:
19580                 case 2:
19581                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19582                         v.hide();
19583                     }
19584                     break;
19585             }
19586         });
19587         
19588         Roo.each(this.picker().select('.next', true).elements, function(v){
19589             v.show();
19590             switch (this.viewMode) {
19591                 case 0:
19592
19593                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19594                         v.hide();
19595                     }
19596                     break;
19597                 case 1:
19598                 case 2:
19599                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19600                         v.hide();
19601                     }
19602                     break;
19603             }
19604         })
19605     },
19606     
19607     moveMonth: function(date, dir)
19608     {
19609         if (!dir) {
19610             return date;
19611         }
19612         var new_date = new Date(date.valueOf()),
19613         day = new_date.getUTCDate(),
19614         month = new_date.getUTCMonth(),
19615         mag = Math.abs(dir),
19616         new_month, test;
19617         dir = dir > 0 ? 1 : -1;
19618         if (mag == 1){
19619             test = dir == -1
19620             // If going back one month, make sure month is not current month
19621             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19622             ? function(){
19623                 return new_date.getUTCMonth() == month;
19624             }
19625             // If going forward one month, make sure month is as expected
19626             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19627             : function(){
19628                 return new_date.getUTCMonth() != new_month;
19629             };
19630             new_month = month + dir;
19631             new_date.setUTCMonth(new_month);
19632             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19633             if (new_month < 0 || new_month > 11) {
19634                 new_month = (new_month + 12) % 12;
19635             }
19636         } else {
19637             // For magnitudes >1, move one month at a time...
19638             for (var i=0; i<mag; i++) {
19639                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19640                 new_date = this.moveMonth(new_date, dir);
19641             }
19642             // ...then reset the day, keeping it in the new month
19643             new_month = new_date.getUTCMonth();
19644             new_date.setUTCDate(day);
19645             test = function(){
19646                 return new_month != new_date.getUTCMonth();
19647             };
19648         }
19649         // Common date-resetting loop -- if date is beyond end of month, make it
19650         // end of month
19651         while (test()){
19652             new_date.setUTCDate(--day);
19653             new_date.setUTCMonth(new_month);
19654         }
19655         return new_date;
19656     },
19657
19658     moveYear: function(date, dir)
19659     {
19660         return this.moveMonth(date, dir*12);
19661     },
19662
19663     dateWithinRange: function(date)
19664     {
19665         return date >= this.startDate && date <= this.endDate;
19666     },
19667
19668     
19669     remove: function() 
19670     {
19671         this.picker().remove();
19672     },
19673     
19674     validateValue : function(value)
19675     {
19676         if(this.getVisibilityEl().hasClass('hidden')){
19677             return true;
19678         }
19679         
19680         if(value.length < 1)  {
19681             if(this.allowBlank){
19682                 return true;
19683             }
19684             return false;
19685         }
19686         
19687         if(value.length < this.minLength){
19688             return false;
19689         }
19690         if(value.length > this.maxLength){
19691             return false;
19692         }
19693         if(this.vtype){
19694             var vt = Roo.form.VTypes;
19695             if(!vt[this.vtype](value, this)){
19696                 return false;
19697             }
19698         }
19699         if(typeof this.validator == "function"){
19700             var msg = this.validator(value);
19701             if(msg !== true){
19702                 return false;
19703             }
19704         }
19705         
19706         if(this.regex && !this.regex.test(value)){
19707             return false;
19708         }
19709         
19710         if(typeof(this.parseDate(value)) == 'undefined'){
19711             return false;
19712         }
19713         
19714         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19715             return false;
19716         }      
19717         
19718         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19719             return false;
19720         } 
19721         
19722         
19723         return true;
19724     },
19725     
19726     reset : function()
19727     {
19728         this.date = this.viewDate = '';
19729         
19730         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19731     }
19732    
19733 });
19734
19735 Roo.apply(Roo.bootstrap.DateField,  {
19736     
19737     head : {
19738         tag: 'thead',
19739         cn: [
19740         {
19741             tag: 'tr',
19742             cn: [
19743             {
19744                 tag: 'th',
19745                 cls: 'prev',
19746                 html: '<i class="fa fa-arrow-left"/>'
19747             },
19748             {
19749                 tag: 'th',
19750                 cls: 'switch',
19751                 colspan: '5'
19752             },
19753             {
19754                 tag: 'th',
19755                 cls: 'next',
19756                 html: '<i class="fa fa-arrow-right"/>'
19757             }
19758
19759             ]
19760         }
19761         ]
19762     },
19763     
19764     content : {
19765         tag: 'tbody',
19766         cn: [
19767         {
19768             tag: 'tr',
19769             cn: [
19770             {
19771                 tag: 'td',
19772                 colspan: '7'
19773             }
19774             ]
19775         }
19776         ]
19777     },
19778     
19779     footer : {
19780         tag: 'tfoot',
19781         cn: [
19782         {
19783             tag: 'tr',
19784             cn: [
19785             {
19786                 tag: 'th',
19787                 colspan: '7',
19788                 cls: 'today'
19789             }
19790                     
19791             ]
19792         }
19793         ]
19794     },
19795     
19796     dates:{
19797         en: {
19798             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19799             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19800             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19801             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19802             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19803             today: "Today"
19804         }
19805     },
19806     
19807     modes: [
19808     {
19809         clsName: 'days',
19810         navFnc: 'Month',
19811         navStep: 1
19812     },
19813     {
19814         clsName: 'months',
19815         navFnc: 'FullYear',
19816         navStep: 1
19817     },
19818     {
19819         clsName: 'years',
19820         navFnc: 'FullYear',
19821         navStep: 10
19822     }]
19823 });
19824
19825 Roo.apply(Roo.bootstrap.DateField,  {
19826   
19827     template : {
19828         tag: 'div',
19829         cls: 'datepicker dropdown-menu roo-dynamic',
19830         cn: [
19831         {
19832             tag: 'div',
19833             cls: 'datepicker-days',
19834             cn: [
19835             {
19836                 tag: 'table',
19837                 cls: 'table-condensed',
19838                 cn:[
19839                 Roo.bootstrap.DateField.head,
19840                 {
19841                     tag: 'tbody'
19842                 },
19843                 Roo.bootstrap.DateField.footer
19844                 ]
19845             }
19846             ]
19847         },
19848         {
19849             tag: 'div',
19850             cls: 'datepicker-months',
19851             cn: [
19852             {
19853                 tag: 'table',
19854                 cls: 'table-condensed',
19855                 cn:[
19856                 Roo.bootstrap.DateField.head,
19857                 Roo.bootstrap.DateField.content,
19858                 Roo.bootstrap.DateField.footer
19859                 ]
19860             }
19861             ]
19862         },
19863         {
19864             tag: 'div',
19865             cls: 'datepicker-years',
19866             cn: [
19867             {
19868                 tag: 'table',
19869                 cls: 'table-condensed',
19870                 cn:[
19871                 Roo.bootstrap.DateField.head,
19872                 Roo.bootstrap.DateField.content,
19873                 Roo.bootstrap.DateField.footer
19874                 ]
19875             }
19876             ]
19877         }
19878         ]
19879     }
19880 });
19881
19882  
19883
19884  /*
19885  * - LGPL
19886  *
19887  * TimeField
19888  * 
19889  */
19890
19891 /**
19892  * @class Roo.bootstrap.TimeField
19893  * @extends Roo.bootstrap.Input
19894  * Bootstrap DateField class
19895  * 
19896  * 
19897  * @constructor
19898  * Create a new TimeField
19899  * @param {Object} config The config object
19900  */
19901
19902 Roo.bootstrap.TimeField = function(config){
19903     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19904     this.addEvents({
19905             /**
19906              * @event show
19907              * Fires when this field show.
19908              * @param {Roo.bootstrap.DateField} thisthis
19909              * @param {Mixed} date The date value
19910              */
19911             show : true,
19912             /**
19913              * @event show
19914              * Fires when this field hide.
19915              * @param {Roo.bootstrap.DateField} this
19916              * @param {Mixed} date The date value
19917              */
19918             hide : true,
19919             /**
19920              * @event select
19921              * Fires when select a date.
19922              * @param {Roo.bootstrap.DateField} this
19923              * @param {Mixed} date The date value
19924              */
19925             select : true
19926         });
19927 };
19928
19929 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19930     
19931     /**
19932      * @cfg {String} format
19933      * The default time format string which can be overriden for localization support.  The format must be
19934      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19935      */
19936     format : "H:i",
19937        
19938     onRender: function(ct, position)
19939     {
19940         
19941         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19942                 
19943         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19944         
19945         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19946         
19947         this.pop = this.picker().select('>.datepicker-time',true).first();
19948         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19949         
19950         this.picker().on('mousedown', this.onMousedown, this);
19951         this.picker().on('click', this.onClick, this);
19952         
19953         this.picker().addClass('datepicker-dropdown');
19954     
19955         this.fillTime();
19956         this.update();
19957             
19958         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19959         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19960         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19961         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19962         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19963         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19964
19965     },
19966     
19967     fireKey: function(e){
19968         if (!this.picker().isVisible()){
19969             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19970                 this.show();
19971             }
19972             return;
19973         }
19974
19975         e.preventDefault();
19976         
19977         switch(e.keyCode){
19978             case 27: // escape
19979                 this.hide();
19980                 break;
19981             case 37: // left
19982             case 39: // right
19983                 this.onTogglePeriod();
19984                 break;
19985             case 38: // up
19986                 this.onIncrementMinutes();
19987                 break;
19988             case 40: // down
19989                 this.onDecrementMinutes();
19990                 break;
19991             case 13: // enter
19992             case 9: // tab
19993                 this.setTime();
19994                 break;
19995         }
19996     },
19997     
19998     onClick: function(e) {
19999         e.stopPropagation();
20000         e.preventDefault();
20001     },
20002     
20003     picker : function()
20004     {
20005         return this.el.select('.datepicker', true).first();
20006     },
20007     
20008     fillTime: function()
20009     {    
20010         var time = this.pop.select('tbody', true).first();
20011         
20012         time.dom.innerHTML = '';
20013         
20014         time.createChild({
20015             tag: 'tr',
20016             cn: [
20017                 {
20018                     tag: 'td',
20019                     cn: [
20020                         {
20021                             tag: 'a',
20022                             href: '#',
20023                             cls: 'btn',
20024                             cn: [
20025                                 {
20026                                     tag: 'span',
20027                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20028                                 }
20029                             ]
20030                         } 
20031                     ]
20032                 },
20033                 {
20034                     tag: 'td',
20035                     cls: 'separator'
20036                 },
20037                 {
20038                     tag: 'td',
20039                     cn: [
20040                         {
20041                             tag: 'a',
20042                             href: '#',
20043                             cls: 'btn',
20044                             cn: [
20045                                 {
20046                                     tag: 'span',
20047                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20048                                 }
20049                             ]
20050                         }
20051                     ]
20052                 },
20053                 {
20054                     tag: 'td',
20055                     cls: 'separator'
20056                 }
20057             ]
20058         });
20059         
20060         time.createChild({
20061             tag: 'tr',
20062             cn: [
20063                 {
20064                     tag: 'td',
20065                     cn: [
20066                         {
20067                             tag: 'span',
20068                             cls: 'timepicker-hour',
20069                             html: '00'
20070                         }  
20071                     ]
20072                 },
20073                 {
20074                     tag: 'td',
20075                     cls: 'separator',
20076                     html: ':'
20077                 },
20078                 {
20079                     tag: 'td',
20080                     cn: [
20081                         {
20082                             tag: 'span',
20083                             cls: 'timepicker-minute',
20084                             html: '00'
20085                         }  
20086                     ]
20087                 },
20088                 {
20089                     tag: 'td',
20090                     cls: 'separator'
20091                 },
20092                 {
20093                     tag: 'td',
20094                     cn: [
20095                         {
20096                             tag: 'button',
20097                             type: 'button',
20098                             cls: 'btn btn-primary period',
20099                             html: 'AM'
20100                             
20101                         }
20102                     ]
20103                 }
20104             ]
20105         });
20106         
20107         time.createChild({
20108             tag: 'tr',
20109             cn: [
20110                 {
20111                     tag: 'td',
20112                     cn: [
20113                         {
20114                             tag: 'a',
20115                             href: '#',
20116                             cls: 'btn',
20117                             cn: [
20118                                 {
20119                                     tag: 'span',
20120                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20121                                 }
20122                             ]
20123                         }
20124                     ]
20125                 },
20126                 {
20127                     tag: 'td',
20128                     cls: 'separator'
20129                 },
20130                 {
20131                     tag: 'td',
20132                     cn: [
20133                         {
20134                             tag: 'a',
20135                             href: '#',
20136                             cls: 'btn',
20137                             cn: [
20138                                 {
20139                                     tag: 'span',
20140                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20141                                 }
20142                             ]
20143                         }
20144                     ]
20145                 },
20146                 {
20147                     tag: 'td',
20148                     cls: 'separator'
20149                 }
20150             ]
20151         });
20152         
20153     },
20154     
20155     update: function()
20156     {
20157         
20158         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20159         
20160         this.fill();
20161     },
20162     
20163     fill: function() 
20164     {
20165         var hours = this.time.getHours();
20166         var minutes = this.time.getMinutes();
20167         var period = 'AM';
20168         
20169         if(hours > 11){
20170             period = 'PM';
20171         }
20172         
20173         if(hours == 0){
20174             hours = 12;
20175         }
20176         
20177         
20178         if(hours > 12){
20179             hours = hours - 12;
20180         }
20181         
20182         if(hours < 10){
20183             hours = '0' + hours;
20184         }
20185         
20186         if(minutes < 10){
20187             minutes = '0' + minutes;
20188         }
20189         
20190         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20191         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20192         this.pop.select('button', true).first().dom.innerHTML = period;
20193         
20194     },
20195     
20196     place: function()
20197     {   
20198         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20199         
20200         var cls = ['bottom'];
20201         
20202         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20203             cls.pop();
20204             cls.push('top');
20205         }
20206         
20207         cls.push('right');
20208         
20209         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20210             cls.pop();
20211             cls.push('left');
20212         }
20213         
20214         this.picker().addClass(cls.join('-'));
20215         
20216         var _this = this;
20217         
20218         Roo.each(cls, function(c){
20219             if(c == 'bottom'){
20220                 _this.picker().setTop(_this.inputEl().getHeight());
20221                 return;
20222             }
20223             if(c == 'top'){
20224                 _this.picker().setTop(0 - _this.picker().getHeight());
20225                 return;
20226             }
20227             
20228             if(c == 'left'){
20229                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20230                 return;
20231             }
20232             if(c == 'right'){
20233                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20234                 return;
20235             }
20236         });
20237         
20238     },
20239   
20240     onFocus : function()
20241     {
20242         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20243         this.show();
20244     },
20245     
20246     onBlur : function()
20247     {
20248         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20249         this.hide();
20250     },
20251     
20252     show : function()
20253     {
20254         this.picker().show();
20255         this.pop.show();
20256         this.update();
20257         this.place();
20258         
20259         this.fireEvent('show', this, this.date);
20260     },
20261     
20262     hide : function()
20263     {
20264         this.picker().hide();
20265         this.pop.hide();
20266         
20267         this.fireEvent('hide', this, this.date);
20268     },
20269     
20270     setTime : function()
20271     {
20272         this.hide();
20273         this.setValue(this.time.format(this.format));
20274         
20275         this.fireEvent('select', this, this.date);
20276         
20277         
20278     },
20279     
20280     onMousedown: function(e){
20281         e.stopPropagation();
20282         e.preventDefault();
20283     },
20284     
20285     onIncrementHours: function()
20286     {
20287         Roo.log('onIncrementHours');
20288         this.time = this.time.add(Date.HOUR, 1);
20289         this.update();
20290         
20291     },
20292     
20293     onDecrementHours: function()
20294     {
20295         Roo.log('onDecrementHours');
20296         this.time = this.time.add(Date.HOUR, -1);
20297         this.update();
20298     },
20299     
20300     onIncrementMinutes: function()
20301     {
20302         Roo.log('onIncrementMinutes');
20303         this.time = this.time.add(Date.MINUTE, 1);
20304         this.update();
20305     },
20306     
20307     onDecrementMinutes: function()
20308     {
20309         Roo.log('onDecrementMinutes');
20310         this.time = this.time.add(Date.MINUTE, -1);
20311         this.update();
20312     },
20313     
20314     onTogglePeriod: function()
20315     {
20316         Roo.log('onTogglePeriod');
20317         this.time = this.time.add(Date.HOUR, 12);
20318         this.update();
20319     }
20320     
20321    
20322 });
20323
20324 Roo.apply(Roo.bootstrap.TimeField,  {
20325     
20326     content : {
20327         tag: 'tbody',
20328         cn: [
20329             {
20330                 tag: 'tr',
20331                 cn: [
20332                 {
20333                     tag: 'td',
20334                     colspan: '7'
20335                 }
20336                 ]
20337             }
20338         ]
20339     },
20340     
20341     footer : {
20342         tag: 'tfoot',
20343         cn: [
20344             {
20345                 tag: 'tr',
20346                 cn: [
20347                 {
20348                     tag: 'th',
20349                     colspan: '7',
20350                     cls: '',
20351                     cn: [
20352                         {
20353                             tag: 'button',
20354                             cls: 'btn btn-info ok',
20355                             html: 'OK'
20356                         }
20357                     ]
20358                 }
20359
20360                 ]
20361             }
20362         ]
20363     }
20364 });
20365
20366 Roo.apply(Roo.bootstrap.TimeField,  {
20367   
20368     template : {
20369         tag: 'div',
20370         cls: 'datepicker dropdown-menu',
20371         cn: [
20372             {
20373                 tag: 'div',
20374                 cls: 'datepicker-time',
20375                 cn: [
20376                 {
20377                     tag: 'table',
20378                     cls: 'table-condensed',
20379                     cn:[
20380                     Roo.bootstrap.TimeField.content,
20381                     Roo.bootstrap.TimeField.footer
20382                     ]
20383                 }
20384                 ]
20385             }
20386         ]
20387     }
20388 });
20389
20390  
20391
20392  /*
20393  * - LGPL
20394  *
20395  * MonthField
20396  * 
20397  */
20398
20399 /**
20400  * @class Roo.bootstrap.MonthField
20401  * @extends Roo.bootstrap.Input
20402  * Bootstrap MonthField class
20403  * 
20404  * @cfg {String} language default en
20405  * 
20406  * @constructor
20407  * Create a new MonthField
20408  * @param {Object} config The config object
20409  */
20410
20411 Roo.bootstrap.MonthField = function(config){
20412     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20413     
20414     this.addEvents({
20415         /**
20416          * @event show
20417          * Fires when this field show.
20418          * @param {Roo.bootstrap.MonthField} this
20419          * @param {Mixed} date The date value
20420          */
20421         show : true,
20422         /**
20423          * @event show
20424          * Fires when this field hide.
20425          * @param {Roo.bootstrap.MonthField} this
20426          * @param {Mixed} date The date value
20427          */
20428         hide : true,
20429         /**
20430          * @event select
20431          * Fires when select a date.
20432          * @param {Roo.bootstrap.MonthField} this
20433          * @param {String} oldvalue The old value
20434          * @param {String} newvalue The new value
20435          */
20436         select : true
20437     });
20438 };
20439
20440 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20441     
20442     onRender: function(ct, position)
20443     {
20444         
20445         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20446         
20447         this.language = this.language || 'en';
20448         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20449         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20450         
20451         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20452         this.isInline = false;
20453         this.isInput = true;
20454         this.component = this.el.select('.add-on', true).first() || false;
20455         this.component = (this.component && this.component.length === 0) ? false : this.component;
20456         this.hasInput = this.component && this.inputEL().length;
20457         
20458         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20459         
20460         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20461         
20462         this.picker().on('mousedown', this.onMousedown, this);
20463         this.picker().on('click', this.onClick, this);
20464         
20465         this.picker().addClass('datepicker-dropdown');
20466         
20467         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20468             v.setStyle('width', '189px');
20469         });
20470         
20471         this.fillMonths();
20472         
20473         this.update();
20474         
20475         if(this.isInline) {
20476             this.show();
20477         }
20478         
20479     },
20480     
20481     setValue: function(v, suppressEvent)
20482     {   
20483         var o = this.getValue();
20484         
20485         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20486         
20487         this.update();
20488
20489         if(suppressEvent !== true){
20490             this.fireEvent('select', this, o, v);
20491         }
20492         
20493     },
20494     
20495     getValue: function()
20496     {
20497         return this.value;
20498     },
20499     
20500     onClick: function(e) 
20501     {
20502         e.stopPropagation();
20503         e.preventDefault();
20504         
20505         var target = e.getTarget();
20506         
20507         if(target.nodeName.toLowerCase() === 'i'){
20508             target = Roo.get(target).dom.parentNode;
20509         }
20510         
20511         var nodeName = target.nodeName;
20512         var className = target.className;
20513         var html = target.innerHTML;
20514         
20515         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20516             return;
20517         }
20518         
20519         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20520         
20521         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20522         
20523         this.hide();
20524                         
20525     },
20526     
20527     picker : function()
20528     {
20529         return this.pickerEl;
20530     },
20531     
20532     fillMonths: function()
20533     {    
20534         var i = 0;
20535         var months = this.picker().select('>.datepicker-months td', true).first();
20536         
20537         months.dom.innerHTML = '';
20538         
20539         while (i < 12) {
20540             var month = {
20541                 tag: 'span',
20542                 cls: 'month',
20543                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20544             };
20545             
20546             months.createChild(month);
20547         }
20548         
20549     },
20550     
20551     update: function()
20552     {
20553         var _this = this;
20554         
20555         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20556             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20557         }
20558         
20559         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20560             e.removeClass('active');
20561             
20562             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20563                 e.addClass('active');
20564             }
20565         })
20566     },
20567     
20568     place: function()
20569     {
20570         if(this.isInline) {
20571             return;
20572         }
20573         
20574         this.picker().removeClass(['bottom', 'top']);
20575         
20576         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20577             /*
20578              * place to the top of element!
20579              *
20580              */
20581             
20582             this.picker().addClass('top');
20583             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20584             
20585             return;
20586         }
20587         
20588         this.picker().addClass('bottom');
20589         
20590         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20591     },
20592     
20593     onFocus : function()
20594     {
20595         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20596         this.show();
20597     },
20598     
20599     onBlur : function()
20600     {
20601         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20602         
20603         var d = this.inputEl().getValue();
20604         
20605         this.setValue(d);
20606                 
20607         this.hide();
20608     },
20609     
20610     show : function()
20611     {
20612         this.picker().show();
20613         this.picker().select('>.datepicker-months', true).first().show();
20614         this.update();
20615         this.place();
20616         
20617         this.fireEvent('show', this, this.date);
20618     },
20619     
20620     hide : function()
20621     {
20622         if(this.isInline) {
20623             return;
20624         }
20625         this.picker().hide();
20626         this.fireEvent('hide', this, this.date);
20627         
20628     },
20629     
20630     onMousedown: function(e)
20631     {
20632         e.stopPropagation();
20633         e.preventDefault();
20634     },
20635     
20636     keyup: function(e)
20637     {
20638         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20639         this.update();
20640     },
20641
20642     fireKey: function(e)
20643     {
20644         if (!this.picker().isVisible()){
20645             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20646                 this.show();
20647             }
20648             return;
20649         }
20650         
20651         var dir;
20652         
20653         switch(e.keyCode){
20654             case 27: // escape
20655                 this.hide();
20656                 e.preventDefault();
20657                 break;
20658             case 37: // left
20659             case 39: // right
20660                 dir = e.keyCode == 37 ? -1 : 1;
20661                 
20662                 this.vIndex = this.vIndex + dir;
20663                 
20664                 if(this.vIndex < 0){
20665                     this.vIndex = 0;
20666                 }
20667                 
20668                 if(this.vIndex > 11){
20669                     this.vIndex = 11;
20670                 }
20671                 
20672                 if(isNaN(this.vIndex)){
20673                     this.vIndex = 0;
20674                 }
20675                 
20676                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20677                 
20678                 break;
20679             case 38: // up
20680             case 40: // down
20681                 
20682                 dir = e.keyCode == 38 ? -1 : 1;
20683                 
20684                 this.vIndex = this.vIndex + dir * 4;
20685                 
20686                 if(this.vIndex < 0){
20687                     this.vIndex = 0;
20688                 }
20689                 
20690                 if(this.vIndex > 11){
20691                     this.vIndex = 11;
20692                 }
20693                 
20694                 if(isNaN(this.vIndex)){
20695                     this.vIndex = 0;
20696                 }
20697                 
20698                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20699                 break;
20700                 
20701             case 13: // enter
20702                 
20703                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20704                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20705                 }
20706                 
20707                 this.hide();
20708                 e.preventDefault();
20709                 break;
20710             case 9: // tab
20711                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20712                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20713                 }
20714                 this.hide();
20715                 break;
20716             case 16: // shift
20717             case 17: // ctrl
20718             case 18: // alt
20719                 break;
20720             default :
20721                 this.hide();
20722                 
20723         }
20724     },
20725     
20726     remove: function() 
20727     {
20728         this.picker().remove();
20729     }
20730    
20731 });
20732
20733 Roo.apply(Roo.bootstrap.MonthField,  {
20734     
20735     content : {
20736         tag: 'tbody',
20737         cn: [
20738         {
20739             tag: 'tr',
20740             cn: [
20741             {
20742                 tag: 'td',
20743                 colspan: '7'
20744             }
20745             ]
20746         }
20747         ]
20748     },
20749     
20750     dates:{
20751         en: {
20752             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20753             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20754         }
20755     }
20756 });
20757
20758 Roo.apply(Roo.bootstrap.MonthField,  {
20759   
20760     template : {
20761         tag: 'div',
20762         cls: 'datepicker dropdown-menu roo-dynamic',
20763         cn: [
20764             {
20765                 tag: 'div',
20766                 cls: 'datepicker-months',
20767                 cn: [
20768                 {
20769                     tag: 'table',
20770                     cls: 'table-condensed',
20771                     cn:[
20772                         Roo.bootstrap.DateField.content
20773                     ]
20774                 }
20775                 ]
20776             }
20777         ]
20778     }
20779 });
20780
20781  
20782
20783  
20784  /*
20785  * - LGPL
20786  *
20787  * CheckBox
20788  * 
20789  */
20790
20791 /**
20792  * @class Roo.bootstrap.CheckBox
20793  * @extends Roo.bootstrap.Input
20794  * Bootstrap CheckBox class
20795  * 
20796  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20797  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20798  * @cfg {String} boxLabel The text that appears beside the checkbox
20799  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20800  * @cfg {Boolean} checked initnal the element
20801  * @cfg {Boolean} inline inline the element (default false)
20802  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20803  * @cfg {String} tooltip label tooltip
20804  * 
20805  * @constructor
20806  * Create a new CheckBox
20807  * @param {Object} config The config object
20808  */
20809
20810 Roo.bootstrap.CheckBox = function(config){
20811     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20812    
20813     this.addEvents({
20814         /**
20815         * @event check
20816         * Fires when the element is checked or unchecked.
20817         * @param {Roo.bootstrap.CheckBox} this This input
20818         * @param {Boolean} checked The new checked value
20819         */
20820        check : true,
20821        /**
20822         * @event click
20823         * Fires when the element is click.
20824         * @param {Roo.bootstrap.CheckBox} this This input
20825         */
20826        click : true
20827     });
20828     
20829 };
20830
20831 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20832   
20833     inputType: 'checkbox',
20834     inputValue: 1,
20835     valueOff: 0,
20836     boxLabel: false,
20837     checked: false,
20838     weight : false,
20839     inline: false,
20840     tooltip : '',
20841     
20842     getAutoCreate : function()
20843     {
20844         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20845         
20846         var id = Roo.id();
20847         
20848         var cfg = {};
20849         
20850         cfg.cls = 'form-group ' + this.inputType; //input-group
20851         
20852         if(this.inline){
20853             cfg.cls += ' ' + this.inputType + '-inline';
20854         }
20855         
20856         var input =  {
20857             tag: 'input',
20858             id : id,
20859             type : this.inputType,
20860             value : this.inputValue,
20861             cls : 'roo-' + this.inputType, //'form-box',
20862             placeholder : this.placeholder || ''
20863             
20864         };
20865         
20866         if(this.inputType != 'radio'){
20867             var hidden =  {
20868                 tag: 'input',
20869                 type : 'hidden',
20870                 cls : 'roo-hidden-value',
20871                 value : this.checked ? this.inputValue : this.valueOff
20872             };
20873         }
20874         
20875             
20876         if (this.weight) { // Validity check?
20877             cfg.cls += " " + this.inputType + "-" + this.weight;
20878         }
20879         
20880         if (this.disabled) {
20881             input.disabled=true;
20882         }
20883         
20884         if(this.checked){
20885             input.checked = this.checked;
20886         }
20887         
20888         if (this.name) {
20889             
20890             input.name = this.name;
20891             
20892             if(this.inputType != 'radio'){
20893                 hidden.name = this.name;
20894                 input.name = '_hidden_' + this.name;
20895             }
20896         }
20897         
20898         if (this.size) {
20899             input.cls += ' input-' + this.size;
20900         }
20901         
20902         var settings=this;
20903         
20904         ['xs','sm','md','lg'].map(function(size){
20905             if (settings[size]) {
20906                 cfg.cls += ' col-' + size + '-' + settings[size];
20907             }
20908         });
20909         
20910         var inputblock = input;
20911          
20912         if (this.before || this.after) {
20913             
20914             inputblock = {
20915                 cls : 'input-group',
20916                 cn :  [] 
20917             };
20918             
20919             if (this.before) {
20920                 inputblock.cn.push({
20921                     tag :'span',
20922                     cls : 'input-group-addon',
20923                     html : this.before
20924                 });
20925             }
20926             
20927             inputblock.cn.push(input);
20928             
20929             if(this.inputType != 'radio'){
20930                 inputblock.cn.push(hidden);
20931             }
20932             
20933             if (this.after) {
20934                 inputblock.cn.push({
20935                     tag :'span',
20936                     cls : 'input-group-addon',
20937                     html : this.after
20938                 });
20939             }
20940             
20941         }
20942         
20943         if (align ==='left' && this.fieldLabel.length) {
20944 //                Roo.log("left and has label");
20945             cfg.cn = [
20946                 {
20947                     tag: 'label',
20948                     'for' :  id,
20949                     cls : 'control-label',
20950                     html : this.fieldLabel
20951                 },
20952                 {
20953                     cls : "", 
20954                     cn: [
20955                         inputblock
20956                     ]
20957                 }
20958             ];
20959             
20960             if(this.labelWidth > 12){
20961                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20962             }
20963             
20964             if(this.labelWidth < 13 && this.labelmd == 0){
20965                 this.labelmd = this.labelWidth;
20966             }
20967             
20968             if(this.labellg > 0){
20969                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20970                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20971             }
20972             
20973             if(this.labelmd > 0){
20974                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20975                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20976             }
20977             
20978             if(this.labelsm > 0){
20979                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20980                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20981             }
20982             
20983             if(this.labelxs > 0){
20984                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20985                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20986             }
20987             
20988         } else if ( this.fieldLabel.length) {
20989 //                Roo.log(" label");
20990                 cfg.cn = [
20991                    
20992                     {
20993                         tag: this.boxLabel ? 'span' : 'label',
20994                         'for': id,
20995                         cls: 'control-label box-input-label',
20996                         //cls : 'input-group-addon',
20997                         html : this.fieldLabel
20998                     },
20999                     
21000                     inputblock
21001                     
21002                 ];
21003
21004         } else {
21005             
21006 //                Roo.log(" no label && no align");
21007                 cfg.cn = [  inputblock ] ;
21008                 
21009                 
21010         }
21011         
21012         if(this.boxLabel){
21013              var boxLabelCfg = {
21014                 tag: 'label',
21015                 //'for': id, // box label is handled by onclick - so no for...
21016                 cls: 'box-label',
21017                 html: this.boxLabel
21018             };
21019             
21020             if(this.tooltip){
21021                 boxLabelCfg.tooltip = this.tooltip;
21022             }
21023              
21024             cfg.cn.push(boxLabelCfg);
21025         }
21026         
21027         if(this.inputType != 'radio'){
21028             cfg.cn.push(hidden);
21029         }
21030         
21031         return cfg;
21032         
21033     },
21034     
21035     /**
21036      * return the real input element.
21037      */
21038     inputEl: function ()
21039     {
21040         return this.el.select('input.roo-' + this.inputType,true).first();
21041     },
21042     hiddenEl: function ()
21043     {
21044         return this.el.select('input.roo-hidden-value',true).first();
21045     },
21046     
21047     labelEl: function()
21048     {
21049         return this.el.select('label.control-label',true).first();
21050     },
21051     /* depricated... */
21052     
21053     label: function()
21054     {
21055         return this.labelEl();
21056     },
21057     
21058     boxLabelEl: function()
21059     {
21060         return this.el.select('label.box-label',true).first();
21061     },
21062     
21063     initEvents : function()
21064     {
21065 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21066         
21067         this.inputEl().on('click', this.onClick,  this);
21068         
21069         if (this.boxLabel) { 
21070             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21071         }
21072         
21073         this.startValue = this.getValue();
21074         
21075         if(this.groupId){
21076             Roo.bootstrap.CheckBox.register(this);
21077         }
21078     },
21079     
21080     onClick : function(e)
21081     {   
21082         if(this.fireEvent('click', this, e) !== false){
21083             this.setChecked(!this.checked);
21084         }
21085         
21086     },
21087     
21088     setChecked : function(state,suppressEvent)
21089     {
21090         this.startValue = this.getValue();
21091
21092         if(this.inputType == 'radio'){
21093             
21094             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21095                 e.dom.checked = false;
21096             });
21097             
21098             this.inputEl().dom.checked = true;
21099             
21100             this.inputEl().dom.value = this.inputValue;
21101             
21102             if(suppressEvent !== true){
21103                 this.fireEvent('check', this, true);
21104             }
21105             
21106             this.validate();
21107             
21108             return;
21109         }
21110         
21111         this.checked = state;
21112         
21113         this.inputEl().dom.checked = state;
21114         
21115         
21116         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21117         
21118         if(suppressEvent !== true){
21119             this.fireEvent('check', this, state);
21120         }
21121         
21122         this.validate();
21123     },
21124     
21125     getValue : function()
21126     {
21127         if(this.inputType == 'radio'){
21128             return this.getGroupValue();
21129         }
21130         
21131         return this.hiddenEl().dom.value;
21132         
21133     },
21134     
21135     getGroupValue : function()
21136     {
21137         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21138             return '';
21139         }
21140         
21141         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21142     },
21143     
21144     setValue : function(v,suppressEvent)
21145     {
21146         if(this.inputType == 'radio'){
21147             this.setGroupValue(v, suppressEvent);
21148             return;
21149         }
21150         
21151         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21152         
21153         this.validate();
21154     },
21155     
21156     setGroupValue : function(v, suppressEvent)
21157     {
21158         this.startValue = this.getValue();
21159         
21160         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21161             e.dom.checked = false;
21162             
21163             if(e.dom.value == v){
21164                 e.dom.checked = true;
21165             }
21166         });
21167         
21168         if(suppressEvent !== true){
21169             this.fireEvent('check', this, true);
21170         }
21171
21172         this.validate();
21173         
21174         return;
21175     },
21176     
21177     validate : function()
21178     {
21179         if(this.getVisibilityEl().hasClass('hidden')){
21180             return true;
21181         }
21182         
21183         if(
21184                 this.disabled || 
21185                 (this.inputType == 'radio' && this.validateRadio()) ||
21186                 (this.inputType == 'checkbox' && this.validateCheckbox())
21187         ){
21188             this.markValid();
21189             return true;
21190         }
21191         
21192         this.markInvalid();
21193         return false;
21194     },
21195     
21196     validateRadio : function()
21197     {
21198         if(this.getVisibilityEl().hasClass('hidden')){
21199             return true;
21200         }
21201         
21202         if(this.allowBlank){
21203             return true;
21204         }
21205         
21206         var valid = false;
21207         
21208         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21209             if(!e.dom.checked){
21210                 return;
21211             }
21212             
21213             valid = true;
21214             
21215             return false;
21216         });
21217         
21218         return valid;
21219     },
21220     
21221     validateCheckbox : function()
21222     {
21223         if(!this.groupId){
21224             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21225             //return (this.getValue() == this.inputValue) ? true : false;
21226         }
21227         
21228         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21229         
21230         if(!group){
21231             return false;
21232         }
21233         
21234         var r = false;
21235         
21236         for(var i in group){
21237             if(group[i].el.isVisible(true)){
21238                 r = false;
21239                 break;
21240             }
21241             
21242             r = true;
21243         }
21244         
21245         for(var i in group){
21246             if(r){
21247                 break;
21248             }
21249             
21250             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21251         }
21252         
21253         return r;
21254     },
21255     
21256     /**
21257      * Mark this field as valid
21258      */
21259     markValid : function()
21260     {
21261         var _this = this;
21262         
21263         this.fireEvent('valid', this);
21264         
21265         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21266         
21267         if(this.groupId){
21268             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21269         }
21270         
21271         if(label){
21272             label.markValid();
21273         }
21274
21275         if(this.inputType == 'radio'){
21276             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21277                 var fg = e.findParent('.form-group', false, true);
21278                 if (Roo.bootstrap.version == 3) {
21279                     fg.removeClass([_this.invalidClass, _this.validClass]);
21280                     fg.addClass(_this.validClass);
21281                 } else {
21282                     fg.removeClass(['is-valid', 'is-invalid']);
21283                     fg.addClass('is-valid');
21284                 }
21285             });
21286             
21287             return;
21288         }
21289
21290         if(!this.groupId){
21291             var fg = this.el.findParent('.form-group', false, true);
21292             if (Roo.bootstrap.version == 3) {
21293                 fg.removeClass([this.invalidClass, this.validClass]);
21294                 fg.addClass(this.validClass);
21295             } else {
21296                 fg.removeClass(['is-valid', 'is-invalid']);
21297                 fg.addClass('is-valid');
21298             }
21299             return;
21300         }
21301         
21302         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21303         
21304         if(!group){
21305             return;
21306         }
21307         
21308         for(var i in group){
21309             var fg = group[i].el.findParent('.form-group', false, true);
21310             if (Roo.bootstrap.version == 3) {
21311                 fg.removeClass([this.invalidClass, this.validClass]);
21312                 fg.addClass(this.validClass);
21313             } else {
21314                 fg.removeClass(['is-valid', 'is-invalid']);
21315                 fg.addClass('is-valid');
21316             }
21317         }
21318     },
21319     
21320      /**
21321      * Mark this field as invalid
21322      * @param {String} msg The validation message
21323      */
21324     markInvalid : function(msg)
21325     {
21326         if(this.allowBlank){
21327             return;
21328         }
21329         
21330         var _this = this;
21331         
21332         this.fireEvent('invalid', this, msg);
21333         
21334         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21335         
21336         if(this.groupId){
21337             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21338         }
21339         
21340         if(label){
21341             label.markInvalid();
21342         }
21343             
21344         if(this.inputType == 'radio'){
21345             
21346             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21347                 var fg = e.findParent('.form-group', false, true);
21348                 if (Roo.bootstrap.version == 3) {
21349                     fg.removeClass([_this.invalidClass, _this.validClass]);
21350                     fg.addClass(_this.invalidClass);
21351                 } else {
21352                     fg.removeClass(['is-invalid', 'is-valid']);
21353                     fg.addClass('is-invalid');
21354                 }
21355             });
21356             
21357             return;
21358         }
21359         
21360         if(!this.groupId){
21361             var fg = this.el.findParent('.form-group', false, true);
21362             if (Roo.bootstrap.version == 3) {
21363                 fg.removeClass([_this.invalidClass, _this.validClass]);
21364                 fg.addClass(_this.invalidClass);
21365             } else {
21366                 fg.removeClass(['is-invalid', 'is-valid']);
21367                 fg.addClass('is-invalid');
21368             }
21369             return;
21370         }
21371         
21372         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21373         
21374         if(!group){
21375             return;
21376         }
21377         
21378         for(var i in group){
21379             var fg = group[i].el.findParent('.form-group', false, true);
21380             if (Roo.bootstrap.version == 3) {
21381                 fg.removeClass([_this.invalidClass, _this.validClass]);
21382                 fg.addClass(_this.invalidClass);
21383             } else {
21384                 fg.removeClass(['is-invalid', 'is-valid']);
21385                 fg.addClass('is-invalid');
21386             }
21387         }
21388         
21389     },
21390     
21391     clearInvalid : function()
21392     {
21393         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21394         
21395         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21396         
21397         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21398         
21399         if (label && label.iconEl) {
21400             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21401             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21402         }
21403     },
21404     
21405     disable : function()
21406     {
21407         if(this.inputType != 'radio'){
21408             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21409             return;
21410         }
21411         
21412         var _this = this;
21413         
21414         if(this.rendered){
21415             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21416                 _this.getActionEl().addClass(this.disabledClass);
21417                 e.dom.disabled = true;
21418             });
21419         }
21420         
21421         this.disabled = true;
21422         this.fireEvent("disable", this);
21423         return this;
21424     },
21425
21426     enable : function()
21427     {
21428         if(this.inputType != 'radio'){
21429             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21430             return;
21431         }
21432         
21433         var _this = this;
21434         
21435         if(this.rendered){
21436             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21437                 _this.getActionEl().removeClass(this.disabledClass);
21438                 e.dom.disabled = false;
21439             });
21440         }
21441         
21442         this.disabled = false;
21443         this.fireEvent("enable", this);
21444         return this;
21445     },
21446     
21447     setBoxLabel : function(v)
21448     {
21449         this.boxLabel = v;
21450         
21451         if(this.rendered){
21452             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21453         }
21454     }
21455
21456 });
21457
21458 Roo.apply(Roo.bootstrap.CheckBox, {
21459     
21460     groups: {},
21461     
21462      /**
21463     * register a CheckBox Group
21464     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21465     */
21466     register : function(checkbox)
21467     {
21468         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21469             this.groups[checkbox.groupId] = {};
21470         }
21471         
21472         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21473             return;
21474         }
21475         
21476         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21477         
21478     },
21479     /**
21480     * fetch a CheckBox Group based on the group ID
21481     * @param {string} the group ID
21482     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21483     */
21484     get: function(groupId) {
21485         if (typeof(this.groups[groupId]) == 'undefined') {
21486             return false;
21487         }
21488         
21489         return this.groups[groupId] ;
21490     }
21491     
21492     
21493 });
21494 /*
21495  * - LGPL
21496  *
21497  * RadioItem
21498  * 
21499  */
21500
21501 /**
21502  * @class Roo.bootstrap.Radio
21503  * @extends Roo.bootstrap.Component
21504  * Bootstrap Radio class
21505  * @cfg {String} boxLabel - the label associated
21506  * @cfg {String} value - the value of radio
21507  * 
21508  * @constructor
21509  * Create a new Radio
21510  * @param {Object} config The config object
21511  */
21512 Roo.bootstrap.Radio = function(config){
21513     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21514     
21515 };
21516
21517 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21518     
21519     boxLabel : '',
21520     
21521     value : '',
21522     
21523     getAutoCreate : function()
21524     {
21525         var cfg = {
21526             tag : 'div',
21527             cls : 'form-group radio',
21528             cn : [
21529                 {
21530                     tag : 'label',
21531                     cls : 'box-label',
21532                     html : this.boxLabel
21533                 }
21534             ]
21535         };
21536         
21537         return cfg;
21538     },
21539     
21540     initEvents : function() 
21541     {
21542         this.parent().register(this);
21543         
21544         this.el.on('click', this.onClick, this);
21545         
21546     },
21547     
21548     onClick : function(e)
21549     {
21550         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21551             this.setChecked(true);
21552         }
21553     },
21554     
21555     setChecked : function(state, suppressEvent)
21556     {
21557         this.parent().setValue(this.value, suppressEvent);
21558         
21559     },
21560     
21561     setBoxLabel : function(v)
21562     {
21563         this.boxLabel = v;
21564         
21565         if(this.rendered){
21566             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21567         }
21568     }
21569     
21570 });
21571  
21572
21573  /*
21574  * - LGPL
21575  *
21576  * Input
21577  * 
21578  */
21579
21580 /**
21581  * @class Roo.bootstrap.SecurePass
21582  * @extends Roo.bootstrap.Input
21583  * Bootstrap SecurePass class
21584  *
21585  * 
21586  * @constructor
21587  * Create a new SecurePass
21588  * @param {Object} config The config object
21589  */
21590  
21591 Roo.bootstrap.SecurePass = function (config) {
21592     // these go here, so the translation tool can replace them..
21593     this.errors = {
21594         PwdEmpty: "Please type a password, and then retype it to confirm.",
21595         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21596         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21597         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21598         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21599         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21600         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21601         TooWeak: "Your password is Too Weak."
21602     },
21603     this.meterLabel = "Password strength:";
21604     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21605     this.meterClass = [
21606         "roo-password-meter-tooweak", 
21607         "roo-password-meter-weak", 
21608         "roo-password-meter-medium", 
21609         "roo-password-meter-strong", 
21610         "roo-password-meter-grey"
21611     ];
21612     
21613     this.errors = {};
21614     
21615     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21616 }
21617
21618 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21619     /**
21620      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21621      * {
21622      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21623      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21624      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21625      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21626      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21627      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21628      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21629      * })
21630      */
21631     // private
21632     
21633     meterWidth: 300,
21634     errorMsg :'',    
21635     errors: false,
21636     imageRoot: '/',
21637     /**
21638      * @cfg {String/Object} Label for the strength meter (defaults to
21639      * 'Password strength:')
21640      */
21641     // private
21642     meterLabel: '',
21643     /**
21644      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21645      * ['Weak', 'Medium', 'Strong'])
21646      */
21647     // private    
21648     pwdStrengths: false,    
21649     // private
21650     strength: 0,
21651     // private
21652     _lastPwd: null,
21653     // private
21654     kCapitalLetter: 0,
21655     kSmallLetter: 1,
21656     kDigit: 2,
21657     kPunctuation: 3,
21658     
21659     insecure: false,
21660     // private
21661     initEvents: function ()
21662     {
21663         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21664
21665         if (this.el.is('input[type=password]') && Roo.isSafari) {
21666             this.el.on('keydown', this.SafariOnKeyDown, this);
21667         }
21668
21669         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21670     },
21671     // private
21672     onRender: function (ct, position)
21673     {
21674         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21675         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21676         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21677
21678         this.trigger.createChild({
21679                    cn: [
21680                     {
21681                     //id: 'PwdMeter',
21682                     tag: 'div',
21683                     cls: 'roo-password-meter-grey col-xs-12',
21684                     style: {
21685                         //width: 0,
21686                         //width: this.meterWidth + 'px'                                                
21687                         }
21688                     },
21689                     {                            
21690                          cls: 'roo-password-meter-text'                          
21691                     }
21692                 ]            
21693         });
21694
21695          
21696         if (this.hideTrigger) {
21697             this.trigger.setDisplayed(false);
21698         }
21699         this.setSize(this.width || '', this.height || '');
21700     },
21701     // private
21702     onDestroy: function ()
21703     {
21704         if (this.trigger) {
21705             this.trigger.removeAllListeners();
21706             this.trigger.remove();
21707         }
21708         if (this.wrap) {
21709             this.wrap.remove();
21710         }
21711         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21712     },
21713     // private
21714     checkStrength: function ()
21715     {
21716         var pwd = this.inputEl().getValue();
21717         if (pwd == this._lastPwd) {
21718             return;
21719         }
21720
21721         var strength;
21722         if (this.ClientSideStrongPassword(pwd)) {
21723             strength = 3;
21724         } else if (this.ClientSideMediumPassword(pwd)) {
21725             strength = 2;
21726         } else if (this.ClientSideWeakPassword(pwd)) {
21727             strength = 1;
21728         } else {
21729             strength = 0;
21730         }
21731         
21732         Roo.log('strength1: ' + strength);
21733         
21734         //var pm = this.trigger.child('div/div/div').dom;
21735         var pm = this.trigger.child('div/div');
21736         pm.removeClass(this.meterClass);
21737         pm.addClass(this.meterClass[strength]);
21738                 
21739         
21740         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21741                 
21742         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21743         
21744         this._lastPwd = pwd;
21745     },
21746     reset: function ()
21747     {
21748         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21749         
21750         this._lastPwd = '';
21751         
21752         var pm = this.trigger.child('div/div');
21753         pm.removeClass(this.meterClass);
21754         pm.addClass('roo-password-meter-grey');        
21755         
21756         
21757         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21758         
21759         pt.innerHTML = '';
21760         this.inputEl().dom.type='password';
21761     },
21762     // private
21763     validateValue: function (value)
21764     {
21765         
21766         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21767             return false;
21768         }
21769         if (value.length == 0) {
21770             if (this.allowBlank) {
21771                 this.clearInvalid();
21772                 return true;
21773             }
21774
21775             this.markInvalid(this.errors.PwdEmpty);
21776             this.errorMsg = this.errors.PwdEmpty;
21777             return false;
21778         }
21779         
21780         if(this.insecure){
21781             return true;
21782         }
21783         
21784         if ('[\x21-\x7e]*'.match(value)) {
21785             this.markInvalid(this.errors.PwdBadChar);
21786             this.errorMsg = this.errors.PwdBadChar;
21787             return false;
21788         }
21789         if (value.length < 6) {
21790             this.markInvalid(this.errors.PwdShort);
21791             this.errorMsg = this.errors.PwdShort;
21792             return false;
21793         }
21794         if (value.length > 16) {
21795             this.markInvalid(this.errors.PwdLong);
21796             this.errorMsg = this.errors.PwdLong;
21797             return false;
21798         }
21799         var strength;
21800         if (this.ClientSideStrongPassword(value)) {
21801             strength = 3;
21802         } else if (this.ClientSideMediumPassword(value)) {
21803             strength = 2;
21804         } else if (this.ClientSideWeakPassword(value)) {
21805             strength = 1;
21806         } else {
21807             strength = 0;
21808         }
21809
21810         
21811         if (strength < 2) {
21812             //this.markInvalid(this.errors.TooWeak);
21813             this.errorMsg = this.errors.TooWeak;
21814             //return false;
21815         }
21816         
21817         
21818         console.log('strength2: ' + strength);
21819         
21820         //var pm = this.trigger.child('div/div/div').dom;
21821         
21822         var pm = this.trigger.child('div/div');
21823         pm.removeClass(this.meterClass);
21824         pm.addClass(this.meterClass[strength]);
21825                 
21826         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21827                 
21828         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21829         
21830         this.errorMsg = ''; 
21831         return true;
21832     },
21833     // private
21834     CharacterSetChecks: function (type)
21835     {
21836         this.type = type;
21837         this.fResult = false;
21838     },
21839     // private
21840     isctype: function (character, type)
21841     {
21842         switch (type) {  
21843             case this.kCapitalLetter:
21844                 if (character >= 'A' && character <= 'Z') {
21845                     return true;
21846                 }
21847                 break;
21848             
21849             case this.kSmallLetter:
21850                 if (character >= 'a' && character <= 'z') {
21851                     return true;
21852                 }
21853                 break;
21854             
21855             case this.kDigit:
21856                 if (character >= '0' && character <= '9') {
21857                     return true;
21858                 }
21859                 break;
21860             
21861             case this.kPunctuation:
21862                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21863                     return true;
21864                 }
21865                 break;
21866             
21867             default:
21868                 return false;
21869         }
21870
21871     },
21872     // private
21873     IsLongEnough: function (pwd, size)
21874     {
21875         return !(pwd == null || isNaN(size) || pwd.length < size);
21876     },
21877     // private
21878     SpansEnoughCharacterSets: function (word, nb)
21879     {
21880         if (!this.IsLongEnough(word, nb))
21881         {
21882             return false;
21883         }
21884
21885         var characterSetChecks = new Array(
21886             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21887             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21888         );
21889         
21890         for (var index = 0; index < word.length; ++index) {
21891             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21892                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21893                     characterSetChecks[nCharSet].fResult = true;
21894                     break;
21895                 }
21896             }
21897         }
21898
21899         var nCharSets = 0;
21900         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21901             if (characterSetChecks[nCharSet].fResult) {
21902                 ++nCharSets;
21903             }
21904         }
21905
21906         if (nCharSets < nb) {
21907             return false;
21908         }
21909         return true;
21910     },
21911     // private
21912     ClientSideStrongPassword: function (pwd)
21913     {
21914         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21915     },
21916     // private
21917     ClientSideMediumPassword: function (pwd)
21918     {
21919         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21920     },
21921     // private
21922     ClientSideWeakPassword: function (pwd)
21923     {
21924         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21925     }
21926           
21927 })//<script type="text/javascript">
21928
21929 /*
21930  * Based  Ext JS Library 1.1.1
21931  * Copyright(c) 2006-2007, Ext JS, LLC.
21932  * LGPL
21933  *
21934  */
21935  
21936 /**
21937  * @class Roo.HtmlEditorCore
21938  * @extends Roo.Component
21939  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21940  *
21941  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21942  */
21943
21944 Roo.HtmlEditorCore = function(config){
21945     
21946     
21947     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21948     
21949     
21950     this.addEvents({
21951         /**
21952          * @event initialize
21953          * Fires when the editor is fully initialized (including the iframe)
21954          * @param {Roo.HtmlEditorCore} this
21955          */
21956         initialize: true,
21957         /**
21958          * @event activate
21959          * Fires when the editor is first receives the focus. Any insertion must wait
21960          * until after this event.
21961          * @param {Roo.HtmlEditorCore} this
21962          */
21963         activate: true,
21964          /**
21965          * @event beforesync
21966          * Fires before the textarea is updated with content from the editor iframe. Return false
21967          * to cancel the sync.
21968          * @param {Roo.HtmlEditorCore} this
21969          * @param {String} html
21970          */
21971         beforesync: true,
21972          /**
21973          * @event beforepush
21974          * Fires before the iframe editor is updated with content from the textarea. Return false
21975          * to cancel the push.
21976          * @param {Roo.HtmlEditorCore} this
21977          * @param {String} html
21978          */
21979         beforepush: true,
21980          /**
21981          * @event sync
21982          * Fires when the textarea is updated with content from the editor iframe.
21983          * @param {Roo.HtmlEditorCore} this
21984          * @param {String} html
21985          */
21986         sync: true,
21987          /**
21988          * @event push
21989          * Fires when the iframe editor is updated with content from the textarea.
21990          * @param {Roo.HtmlEditorCore} this
21991          * @param {String} html
21992          */
21993         push: true,
21994         
21995         /**
21996          * @event editorevent
21997          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21998          * @param {Roo.HtmlEditorCore} this
21999          */
22000         editorevent: true
22001         
22002     });
22003     
22004     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22005     
22006     // defaults : white / black...
22007     this.applyBlacklists();
22008     
22009     
22010     
22011 };
22012
22013
22014 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22015
22016
22017      /**
22018      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22019      */
22020     
22021     owner : false,
22022     
22023      /**
22024      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22025      *                        Roo.resizable.
22026      */
22027     resizable : false,
22028      /**
22029      * @cfg {Number} height (in pixels)
22030      */   
22031     height: 300,
22032    /**
22033      * @cfg {Number} width (in pixels)
22034      */   
22035     width: 500,
22036     
22037     /**
22038      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22039      * 
22040      */
22041     stylesheets: false,
22042     
22043     // id of frame..
22044     frameId: false,
22045     
22046     // private properties
22047     validationEvent : false,
22048     deferHeight: true,
22049     initialized : false,
22050     activated : false,
22051     sourceEditMode : false,
22052     onFocus : Roo.emptyFn,
22053     iframePad:3,
22054     hideMode:'offsets',
22055     
22056     clearUp: true,
22057     
22058     // blacklist + whitelisted elements..
22059     black: false,
22060     white: false,
22061      
22062     bodyCls : '',
22063
22064     /**
22065      * Protected method that will not generally be called directly. It
22066      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22067      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22068      */
22069     getDocMarkup : function(){
22070         // body styles..
22071         var st = '';
22072         
22073         // inherit styels from page...?? 
22074         if (this.stylesheets === false) {
22075             
22076             Roo.get(document.head).select('style').each(function(node) {
22077                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22078             });
22079             
22080             Roo.get(document.head).select('link').each(function(node) { 
22081                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22082             });
22083             
22084         } else if (!this.stylesheets.length) {
22085                 // simple..
22086                 st = '<style type="text/css">' +
22087                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22088                    '</style>';
22089         } else { 
22090             st = '<style type="text/css">' +
22091                     this.stylesheets +
22092                 '</style>';
22093         }
22094         
22095         st +=  '<style type="text/css">' +
22096             'IMG { cursor: pointer } ' +
22097         '</style>';
22098
22099         var cls = 'roo-htmleditor-body';
22100         
22101         if(this.bodyCls.length){
22102             cls += ' ' + this.bodyCls;
22103         }
22104         
22105         return '<html><head>' + st  +
22106             //<style type="text/css">' +
22107             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22108             //'</style>' +
22109             ' </head><body class="' +  cls + '"></body></html>';
22110     },
22111
22112     // private
22113     onRender : function(ct, position)
22114     {
22115         var _t = this;
22116         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22117         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22118         
22119         
22120         this.el.dom.style.border = '0 none';
22121         this.el.dom.setAttribute('tabIndex', -1);
22122         this.el.addClass('x-hidden hide');
22123         
22124         
22125         
22126         if(Roo.isIE){ // fix IE 1px bogus margin
22127             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22128         }
22129        
22130         
22131         this.frameId = Roo.id();
22132         
22133          
22134         
22135         var iframe = this.owner.wrap.createChild({
22136             tag: 'iframe',
22137             cls: 'form-control', // bootstrap..
22138             id: this.frameId,
22139             name: this.frameId,
22140             frameBorder : 'no',
22141             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22142         }, this.el
22143         );
22144         
22145         
22146         this.iframe = iframe.dom;
22147
22148          this.assignDocWin();
22149         
22150         this.doc.designMode = 'on';
22151        
22152         this.doc.open();
22153         this.doc.write(this.getDocMarkup());
22154         this.doc.close();
22155
22156         
22157         var task = { // must defer to wait for browser to be ready
22158             run : function(){
22159                 //console.log("run task?" + this.doc.readyState);
22160                 this.assignDocWin();
22161                 if(this.doc.body || this.doc.readyState == 'complete'){
22162                     try {
22163                         this.doc.designMode="on";
22164                     } catch (e) {
22165                         return;
22166                     }
22167                     Roo.TaskMgr.stop(task);
22168                     this.initEditor.defer(10, this);
22169                 }
22170             },
22171             interval : 10,
22172             duration: 10000,
22173             scope: this
22174         };
22175         Roo.TaskMgr.start(task);
22176
22177     },
22178
22179     // private
22180     onResize : function(w, h)
22181     {
22182          Roo.log('resize: ' +w + ',' + h );
22183         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22184         if(!this.iframe){
22185             return;
22186         }
22187         if(typeof w == 'number'){
22188             
22189             this.iframe.style.width = w + 'px';
22190         }
22191         if(typeof h == 'number'){
22192             
22193             this.iframe.style.height = h + 'px';
22194             if(this.doc){
22195                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22196             }
22197         }
22198         
22199     },
22200
22201     /**
22202      * Toggles the editor between standard and source edit mode.
22203      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22204      */
22205     toggleSourceEdit : function(sourceEditMode){
22206         
22207         this.sourceEditMode = sourceEditMode === true;
22208         
22209         if(this.sourceEditMode){
22210  
22211             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22212             
22213         }else{
22214             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22215             //this.iframe.className = '';
22216             this.deferFocus();
22217         }
22218         //this.setSize(this.owner.wrap.getSize());
22219         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22220     },
22221
22222     
22223   
22224
22225     /**
22226      * Protected method that will not generally be called directly. If you need/want
22227      * custom HTML cleanup, this is the method you should override.
22228      * @param {String} html The HTML to be cleaned
22229      * return {String} The cleaned HTML
22230      */
22231     cleanHtml : function(html){
22232         html = String(html);
22233         if(html.length > 5){
22234             if(Roo.isSafari){ // strip safari nonsense
22235                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22236             }
22237         }
22238         if(html == '&nbsp;'){
22239             html = '';
22240         }
22241         return html;
22242     },
22243
22244     /**
22245      * HTML Editor -> Textarea
22246      * Protected method that will not generally be called directly. Syncs the contents
22247      * of the editor iframe with the textarea.
22248      */
22249     syncValue : function(){
22250         if(this.initialized){
22251             var bd = (this.doc.body || this.doc.documentElement);
22252             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22253             var html = bd.innerHTML;
22254             if(Roo.isSafari){
22255                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22256                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22257                 if(m && m[1]){
22258                     html = '<div style="'+m[0]+'">' + html + '</div>';
22259                 }
22260             }
22261             html = this.cleanHtml(html);
22262             // fix up the special chars.. normaly like back quotes in word...
22263             // however we do not want to do this with chinese..
22264             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22265                 var cc = b.charCodeAt();
22266                 if (
22267                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22268                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22269                     (cc >= 0xf900 && cc < 0xfb00 )
22270                 ) {
22271                         return b;
22272                 }
22273                 return "&#"+cc+";" 
22274             });
22275             if(this.owner.fireEvent('beforesync', this, html) !== false){
22276                 this.el.dom.value = html;
22277                 this.owner.fireEvent('sync', this, html);
22278             }
22279         }
22280     },
22281
22282     /**
22283      * Protected method that will not generally be called directly. Pushes the value of the textarea
22284      * into the iframe editor.
22285      */
22286     pushValue : function(){
22287         if(this.initialized){
22288             var v = this.el.dom.value.trim();
22289             
22290 //            if(v.length < 1){
22291 //                v = '&#160;';
22292 //            }
22293             
22294             if(this.owner.fireEvent('beforepush', this, v) !== false){
22295                 var d = (this.doc.body || this.doc.documentElement);
22296                 d.innerHTML = v;
22297                 this.cleanUpPaste();
22298                 this.el.dom.value = d.innerHTML;
22299                 this.owner.fireEvent('push', this, v);
22300             }
22301         }
22302     },
22303
22304     // private
22305     deferFocus : function(){
22306         this.focus.defer(10, this);
22307     },
22308
22309     // doc'ed in Field
22310     focus : function(){
22311         if(this.win && !this.sourceEditMode){
22312             this.win.focus();
22313         }else{
22314             this.el.focus();
22315         }
22316     },
22317     
22318     assignDocWin: function()
22319     {
22320         var iframe = this.iframe;
22321         
22322          if(Roo.isIE){
22323             this.doc = iframe.contentWindow.document;
22324             this.win = iframe.contentWindow;
22325         } else {
22326 //            if (!Roo.get(this.frameId)) {
22327 //                return;
22328 //            }
22329 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22330 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22331             
22332             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22333                 return;
22334             }
22335             
22336             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22337             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22338         }
22339     },
22340     
22341     // private
22342     initEditor : function(){
22343         //console.log("INIT EDITOR");
22344         this.assignDocWin();
22345         
22346         
22347         
22348         this.doc.designMode="on";
22349         this.doc.open();
22350         this.doc.write(this.getDocMarkup());
22351         this.doc.close();
22352         
22353         var dbody = (this.doc.body || this.doc.documentElement);
22354         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22355         // this copies styles from the containing element into thsi one..
22356         // not sure why we need all of this..
22357         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22358         
22359         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22360         //ss['background-attachment'] = 'fixed'; // w3c
22361         dbody.bgProperties = 'fixed'; // ie
22362         //Roo.DomHelper.applyStyles(dbody, ss);
22363         Roo.EventManager.on(this.doc, {
22364             //'mousedown': this.onEditorEvent,
22365             'mouseup': this.onEditorEvent,
22366             'dblclick': this.onEditorEvent,
22367             'click': this.onEditorEvent,
22368             'keyup': this.onEditorEvent,
22369             buffer:100,
22370             scope: this
22371         });
22372         if(Roo.isGecko){
22373             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22374         }
22375         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22376             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22377         }
22378         this.initialized = true;
22379
22380         this.owner.fireEvent('initialize', this);
22381         this.pushValue();
22382     },
22383
22384     // private
22385     onDestroy : function(){
22386         
22387         
22388         
22389         if(this.rendered){
22390             
22391             //for (var i =0; i < this.toolbars.length;i++) {
22392             //    // fixme - ask toolbars for heights?
22393             //    this.toolbars[i].onDestroy();
22394            // }
22395             
22396             //this.wrap.dom.innerHTML = '';
22397             //this.wrap.remove();
22398         }
22399     },
22400
22401     // private
22402     onFirstFocus : function(){
22403         
22404         this.assignDocWin();
22405         
22406         
22407         this.activated = true;
22408          
22409     
22410         if(Roo.isGecko){ // prevent silly gecko errors
22411             this.win.focus();
22412             var s = this.win.getSelection();
22413             if(!s.focusNode || s.focusNode.nodeType != 3){
22414                 var r = s.getRangeAt(0);
22415                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22416                 r.collapse(true);
22417                 this.deferFocus();
22418             }
22419             try{
22420                 this.execCmd('useCSS', true);
22421                 this.execCmd('styleWithCSS', false);
22422             }catch(e){}
22423         }
22424         this.owner.fireEvent('activate', this);
22425     },
22426
22427     // private
22428     adjustFont: function(btn){
22429         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22430         //if(Roo.isSafari){ // safari
22431         //    adjust *= 2;
22432        // }
22433         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22434         if(Roo.isSafari){ // safari
22435             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22436             v =  (v < 10) ? 10 : v;
22437             v =  (v > 48) ? 48 : v;
22438             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22439             
22440         }
22441         
22442         
22443         v = Math.max(1, v+adjust);
22444         
22445         this.execCmd('FontSize', v  );
22446     },
22447
22448     onEditorEvent : function(e)
22449     {
22450         this.owner.fireEvent('editorevent', this, e);
22451       //  this.updateToolbar();
22452         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22453     },
22454
22455     insertTag : function(tg)
22456     {
22457         // could be a bit smarter... -> wrap the current selected tRoo..
22458         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22459             
22460             range = this.createRange(this.getSelection());
22461             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22462             wrappingNode.appendChild(range.extractContents());
22463             range.insertNode(wrappingNode);
22464
22465             return;
22466             
22467             
22468             
22469         }
22470         this.execCmd("formatblock",   tg);
22471         
22472     },
22473     
22474     insertText : function(txt)
22475     {
22476         
22477         
22478         var range = this.createRange();
22479         range.deleteContents();
22480                //alert(Sender.getAttribute('label'));
22481                
22482         range.insertNode(this.doc.createTextNode(txt));
22483     } ,
22484     
22485      
22486
22487     /**
22488      * Executes a Midas editor command on the editor document and performs necessary focus and
22489      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22490      * @param {String} cmd The Midas command
22491      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22492      */
22493     relayCmd : function(cmd, value){
22494         this.win.focus();
22495         this.execCmd(cmd, value);
22496         this.owner.fireEvent('editorevent', this);
22497         //this.updateToolbar();
22498         this.owner.deferFocus();
22499     },
22500
22501     /**
22502      * Executes a Midas editor command directly on the editor document.
22503      * For visual commands, you should use {@link #relayCmd} instead.
22504      * <b>This should only be called after the editor is initialized.</b>
22505      * @param {String} cmd The Midas command
22506      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22507      */
22508     execCmd : function(cmd, value){
22509         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22510         this.syncValue();
22511     },
22512  
22513  
22514    
22515     /**
22516      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22517      * to insert tRoo.
22518      * @param {String} text | dom node.. 
22519      */
22520     insertAtCursor : function(text)
22521     {
22522         
22523         if(!this.activated){
22524             return;
22525         }
22526         /*
22527         if(Roo.isIE){
22528             this.win.focus();
22529             var r = this.doc.selection.createRange();
22530             if(r){
22531                 r.collapse(true);
22532                 r.pasteHTML(text);
22533                 this.syncValue();
22534                 this.deferFocus();
22535             
22536             }
22537             return;
22538         }
22539         */
22540         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22541             this.win.focus();
22542             
22543             
22544             // from jquery ui (MIT licenced)
22545             var range, node;
22546             var win = this.win;
22547             
22548             if (win.getSelection && win.getSelection().getRangeAt) {
22549                 range = win.getSelection().getRangeAt(0);
22550                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22551                 range.insertNode(node);
22552             } else if (win.document.selection && win.document.selection.createRange) {
22553                 // no firefox support
22554                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22555                 win.document.selection.createRange().pasteHTML(txt);
22556             } else {
22557                 // no firefox support
22558                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22559                 this.execCmd('InsertHTML', txt);
22560             } 
22561             
22562             this.syncValue();
22563             
22564             this.deferFocus();
22565         }
22566     },
22567  // private
22568     mozKeyPress : function(e){
22569         if(e.ctrlKey){
22570             var c = e.getCharCode(), cmd;
22571           
22572             if(c > 0){
22573                 c = String.fromCharCode(c).toLowerCase();
22574                 switch(c){
22575                     case 'b':
22576                         cmd = 'bold';
22577                         break;
22578                     case 'i':
22579                         cmd = 'italic';
22580                         break;
22581                     
22582                     case 'u':
22583                         cmd = 'underline';
22584                         break;
22585                     
22586                     case 'v':
22587                         this.cleanUpPaste.defer(100, this);
22588                         return;
22589                         
22590                 }
22591                 if(cmd){
22592                     this.win.focus();
22593                     this.execCmd(cmd);
22594                     this.deferFocus();
22595                     e.preventDefault();
22596                 }
22597                 
22598             }
22599         }
22600     },
22601
22602     // private
22603     fixKeys : function(){ // load time branching for fastest keydown performance
22604         if(Roo.isIE){
22605             return function(e){
22606                 var k = e.getKey(), r;
22607                 if(k == e.TAB){
22608                     e.stopEvent();
22609                     r = this.doc.selection.createRange();
22610                     if(r){
22611                         r.collapse(true);
22612                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22613                         this.deferFocus();
22614                     }
22615                     return;
22616                 }
22617                 
22618                 if(k == e.ENTER){
22619                     r = this.doc.selection.createRange();
22620                     if(r){
22621                         var target = r.parentElement();
22622                         if(!target || target.tagName.toLowerCase() != 'li'){
22623                             e.stopEvent();
22624                             r.pasteHTML('<br />');
22625                             r.collapse(false);
22626                             r.select();
22627                         }
22628                     }
22629                 }
22630                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22631                     this.cleanUpPaste.defer(100, this);
22632                     return;
22633                 }
22634                 
22635                 
22636             };
22637         }else if(Roo.isOpera){
22638             return function(e){
22639                 var k = e.getKey();
22640                 if(k == e.TAB){
22641                     e.stopEvent();
22642                     this.win.focus();
22643                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22644                     this.deferFocus();
22645                 }
22646                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22647                     this.cleanUpPaste.defer(100, this);
22648                     return;
22649                 }
22650                 
22651             };
22652         }else if(Roo.isSafari){
22653             return function(e){
22654                 var k = e.getKey();
22655                 
22656                 if(k == e.TAB){
22657                     e.stopEvent();
22658                     this.execCmd('InsertText','\t');
22659                     this.deferFocus();
22660                     return;
22661                 }
22662                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22663                     this.cleanUpPaste.defer(100, this);
22664                     return;
22665                 }
22666                 
22667              };
22668         }
22669     }(),
22670     
22671     getAllAncestors: function()
22672     {
22673         var p = this.getSelectedNode();
22674         var a = [];
22675         if (!p) {
22676             a.push(p); // push blank onto stack..
22677             p = this.getParentElement();
22678         }
22679         
22680         
22681         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22682             a.push(p);
22683             p = p.parentNode;
22684         }
22685         a.push(this.doc.body);
22686         return a;
22687     },
22688     lastSel : false,
22689     lastSelNode : false,
22690     
22691     
22692     getSelection : function() 
22693     {
22694         this.assignDocWin();
22695         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22696     },
22697     
22698     getSelectedNode: function() 
22699     {
22700         // this may only work on Gecko!!!
22701         
22702         // should we cache this!!!!
22703         
22704         
22705         
22706          
22707         var range = this.createRange(this.getSelection()).cloneRange();
22708         
22709         if (Roo.isIE) {
22710             var parent = range.parentElement();
22711             while (true) {
22712                 var testRange = range.duplicate();
22713                 testRange.moveToElementText(parent);
22714                 if (testRange.inRange(range)) {
22715                     break;
22716                 }
22717                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22718                     break;
22719                 }
22720                 parent = parent.parentElement;
22721             }
22722             return parent;
22723         }
22724         
22725         // is ancestor a text element.
22726         var ac =  range.commonAncestorContainer;
22727         if (ac.nodeType == 3) {
22728             ac = ac.parentNode;
22729         }
22730         
22731         var ar = ac.childNodes;
22732          
22733         var nodes = [];
22734         var other_nodes = [];
22735         var has_other_nodes = false;
22736         for (var i=0;i<ar.length;i++) {
22737             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22738                 continue;
22739             }
22740             // fullly contained node.
22741             
22742             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22743                 nodes.push(ar[i]);
22744                 continue;
22745             }
22746             
22747             // probably selected..
22748             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22749                 other_nodes.push(ar[i]);
22750                 continue;
22751             }
22752             // outer..
22753             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22754                 continue;
22755             }
22756             
22757             
22758             has_other_nodes = true;
22759         }
22760         if (!nodes.length && other_nodes.length) {
22761             nodes= other_nodes;
22762         }
22763         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22764             return false;
22765         }
22766         
22767         return nodes[0];
22768     },
22769     createRange: function(sel)
22770     {
22771         // this has strange effects when using with 
22772         // top toolbar - not sure if it's a great idea.
22773         //this.editor.contentWindow.focus();
22774         if (typeof sel != "undefined") {
22775             try {
22776                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22777             } catch(e) {
22778                 return this.doc.createRange();
22779             }
22780         } else {
22781             return this.doc.createRange();
22782         }
22783     },
22784     getParentElement: function()
22785     {
22786         
22787         this.assignDocWin();
22788         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22789         
22790         var range = this.createRange(sel);
22791          
22792         try {
22793             var p = range.commonAncestorContainer;
22794             while (p.nodeType == 3) { // text node
22795                 p = p.parentNode;
22796             }
22797             return p;
22798         } catch (e) {
22799             return null;
22800         }
22801     
22802     },
22803     /***
22804      *
22805      * Range intersection.. the hard stuff...
22806      *  '-1' = before
22807      *  '0' = hits..
22808      *  '1' = after.
22809      *         [ -- selected range --- ]
22810      *   [fail]                        [fail]
22811      *
22812      *    basically..
22813      *      if end is before start or  hits it. fail.
22814      *      if start is after end or hits it fail.
22815      *
22816      *   if either hits (but other is outside. - then it's not 
22817      *   
22818      *    
22819      **/
22820     
22821     
22822     // @see http://www.thismuchiknow.co.uk/?p=64.
22823     rangeIntersectsNode : function(range, node)
22824     {
22825         var nodeRange = node.ownerDocument.createRange();
22826         try {
22827             nodeRange.selectNode(node);
22828         } catch (e) {
22829             nodeRange.selectNodeContents(node);
22830         }
22831     
22832         var rangeStartRange = range.cloneRange();
22833         rangeStartRange.collapse(true);
22834     
22835         var rangeEndRange = range.cloneRange();
22836         rangeEndRange.collapse(false);
22837     
22838         var nodeStartRange = nodeRange.cloneRange();
22839         nodeStartRange.collapse(true);
22840     
22841         var nodeEndRange = nodeRange.cloneRange();
22842         nodeEndRange.collapse(false);
22843     
22844         return rangeStartRange.compareBoundaryPoints(
22845                  Range.START_TO_START, nodeEndRange) == -1 &&
22846                rangeEndRange.compareBoundaryPoints(
22847                  Range.START_TO_START, nodeStartRange) == 1;
22848         
22849          
22850     },
22851     rangeCompareNode : function(range, node)
22852     {
22853         var nodeRange = node.ownerDocument.createRange();
22854         try {
22855             nodeRange.selectNode(node);
22856         } catch (e) {
22857             nodeRange.selectNodeContents(node);
22858         }
22859         
22860         
22861         range.collapse(true);
22862     
22863         nodeRange.collapse(true);
22864      
22865         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22866         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22867          
22868         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22869         
22870         var nodeIsBefore   =  ss == 1;
22871         var nodeIsAfter    = ee == -1;
22872         
22873         if (nodeIsBefore && nodeIsAfter) {
22874             return 0; // outer
22875         }
22876         if (!nodeIsBefore && nodeIsAfter) {
22877             return 1; //right trailed.
22878         }
22879         
22880         if (nodeIsBefore && !nodeIsAfter) {
22881             return 2;  // left trailed.
22882         }
22883         // fully contined.
22884         return 3;
22885     },
22886
22887     // private? - in a new class?
22888     cleanUpPaste :  function()
22889     {
22890         // cleans up the whole document..
22891         Roo.log('cleanuppaste');
22892         
22893         this.cleanUpChildren(this.doc.body);
22894         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22895         if (clean != this.doc.body.innerHTML) {
22896             this.doc.body.innerHTML = clean;
22897         }
22898         
22899     },
22900     
22901     cleanWordChars : function(input) {// change the chars to hex code
22902         var he = Roo.HtmlEditorCore;
22903         
22904         var output = input;
22905         Roo.each(he.swapCodes, function(sw) { 
22906             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22907             
22908             output = output.replace(swapper, sw[1]);
22909         });
22910         
22911         return output;
22912     },
22913     
22914     
22915     cleanUpChildren : function (n)
22916     {
22917         if (!n.childNodes.length) {
22918             return;
22919         }
22920         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22921            this.cleanUpChild(n.childNodes[i]);
22922         }
22923     },
22924     
22925     
22926         
22927     
22928     cleanUpChild : function (node)
22929     {
22930         var ed = this;
22931         //console.log(node);
22932         if (node.nodeName == "#text") {
22933             // clean up silly Windows -- stuff?
22934             return; 
22935         }
22936         if (node.nodeName == "#comment") {
22937             node.parentNode.removeChild(node);
22938             // clean up silly Windows -- stuff?
22939             return; 
22940         }
22941         var lcname = node.tagName.toLowerCase();
22942         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22943         // whitelist of tags..
22944         
22945         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22946             // remove node.
22947             node.parentNode.removeChild(node);
22948             return;
22949             
22950         }
22951         
22952         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22953         
22954         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22955         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22956         
22957         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22958         //    remove_keep_children = true;
22959         //}
22960         
22961         if (remove_keep_children) {
22962             this.cleanUpChildren(node);
22963             // inserts everything just before this node...
22964             while (node.childNodes.length) {
22965                 var cn = node.childNodes[0];
22966                 node.removeChild(cn);
22967                 node.parentNode.insertBefore(cn, node);
22968             }
22969             node.parentNode.removeChild(node);
22970             return;
22971         }
22972         
22973         if (!node.attributes || !node.attributes.length) {
22974             this.cleanUpChildren(node);
22975             return;
22976         }
22977         
22978         function cleanAttr(n,v)
22979         {
22980             
22981             if (v.match(/^\./) || v.match(/^\//)) {
22982                 return;
22983             }
22984             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22985                 return;
22986             }
22987             if (v.match(/^#/)) {
22988                 return;
22989             }
22990 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22991             node.removeAttribute(n);
22992             
22993         }
22994         
22995         var cwhite = this.cwhite;
22996         var cblack = this.cblack;
22997             
22998         function cleanStyle(n,v)
22999         {
23000             if (v.match(/expression/)) { //XSS?? should we even bother..
23001                 node.removeAttribute(n);
23002                 return;
23003             }
23004             
23005             var parts = v.split(/;/);
23006             var clean = [];
23007             
23008             Roo.each(parts, function(p) {
23009                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23010                 if (!p.length) {
23011                     return true;
23012                 }
23013                 var l = p.split(':').shift().replace(/\s+/g,'');
23014                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23015                 
23016                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23017 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23018                     //node.removeAttribute(n);
23019                     return true;
23020                 }
23021                 //Roo.log()
23022                 // only allow 'c whitelisted system attributes'
23023                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23024 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23025                     //node.removeAttribute(n);
23026                     return true;
23027                 }
23028                 
23029                 
23030                  
23031                 
23032                 clean.push(p);
23033                 return true;
23034             });
23035             if (clean.length) { 
23036                 node.setAttribute(n, clean.join(';'));
23037             } else {
23038                 node.removeAttribute(n);
23039             }
23040             
23041         }
23042         
23043         
23044         for (var i = node.attributes.length-1; i > -1 ; i--) {
23045             var a = node.attributes[i];
23046             //console.log(a);
23047             
23048             if (a.name.toLowerCase().substr(0,2)=='on')  {
23049                 node.removeAttribute(a.name);
23050                 continue;
23051             }
23052             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23053                 node.removeAttribute(a.name);
23054                 continue;
23055             }
23056             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23057                 cleanAttr(a.name,a.value); // fixme..
23058                 continue;
23059             }
23060             if (a.name == 'style') {
23061                 cleanStyle(a.name,a.value);
23062                 continue;
23063             }
23064             /// clean up MS crap..
23065             // tecnically this should be a list of valid class'es..
23066             
23067             
23068             if (a.name == 'class') {
23069                 if (a.value.match(/^Mso/)) {
23070                     node.className = '';
23071                 }
23072                 
23073                 if (a.value.match(/^body$/)) {
23074                     node.className = '';
23075                 }
23076                 continue;
23077             }
23078             
23079             // style cleanup!?
23080             // class cleanup?
23081             
23082         }
23083         
23084         
23085         this.cleanUpChildren(node);
23086         
23087         
23088     },
23089     
23090     /**
23091      * Clean up MS wordisms...
23092      */
23093     cleanWord : function(node)
23094     {
23095         
23096         
23097         if (!node) {
23098             this.cleanWord(this.doc.body);
23099             return;
23100         }
23101         if (node.nodeName == "#text") {
23102             // clean up silly Windows -- stuff?
23103             return; 
23104         }
23105         if (node.nodeName == "#comment") {
23106             node.parentNode.removeChild(node);
23107             // clean up silly Windows -- stuff?
23108             return; 
23109         }
23110         
23111         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23112             node.parentNode.removeChild(node);
23113             return;
23114         }
23115         
23116         // remove - but keep children..
23117         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23118             while (node.childNodes.length) {
23119                 var cn = node.childNodes[0];
23120                 node.removeChild(cn);
23121                 node.parentNode.insertBefore(cn, node);
23122             }
23123             node.parentNode.removeChild(node);
23124             this.iterateChildren(node, this.cleanWord);
23125             return;
23126         }
23127         // clean styles
23128         if (node.className.length) {
23129             
23130             var cn = node.className.split(/\W+/);
23131             var cna = [];
23132             Roo.each(cn, function(cls) {
23133                 if (cls.match(/Mso[a-zA-Z]+/)) {
23134                     return;
23135                 }
23136                 cna.push(cls);
23137             });
23138             node.className = cna.length ? cna.join(' ') : '';
23139             if (!cna.length) {
23140                 node.removeAttribute("class");
23141             }
23142         }
23143         
23144         if (node.hasAttribute("lang")) {
23145             node.removeAttribute("lang");
23146         }
23147         
23148         if (node.hasAttribute("style")) {
23149             
23150             var styles = node.getAttribute("style").split(";");
23151             var nstyle = [];
23152             Roo.each(styles, function(s) {
23153                 if (!s.match(/:/)) {
23154                     return;
23155                 }
23156                 var kv = s.split(":");
23157                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23158                     return;
23159                 }
23160                 // what ever is left... we allow.
23161                 nstyle.push(s);
23162             });
23163             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23164             if (!nstyle.length) {
23165                 node.removeAttribute('style');
23166             }
23167         }
23168         this.iterateChildren(node, this.cleanWord);
23169         
23170         
23171         
23172     },
23173     /**
23174      * iterateChildren of a Node, calling fn each time, using this as the scole..
23175      * @param {DomNode} node node to iterate children of.
23176      * @param {Function} fn method of this class to call on each item.
23177      */
23178     iterateChildren : function(node, fn)
23179     {
23180         if (!node.childNodes.length) {
23181                 return;
23182         }
23183         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23184            fn.call(this, node.childNodes[i])
23185         }
23186     },
23187     
23188     
23189     /**
23190      * cleanTableWidths.
23191      *
23192      * Quite often pasting from word etc.. results in tables with column and widths.
23193      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23194      *
23195      */
23196     cleanTableWidths : function(node)
23197     {
23198          
23199          
23200         if (!node) {
23201             this.cleanTableWidths(this.doc.body);
23202             return;
23203         }
23204         
23205         // ignore list...
23206         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23207             return; 
23208         }
23209         Roo.log(node.tagName);
23210         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23211             this.iterateChildren(node, this.cleanTableWidths);
23212             return;
23213         }
23214         if (node.hasAttribute('width')) {
23215             node.removeAttribute('width');
23216         }
23217         
23218          
23219         if (node.hasAttribute("style")) {
23220             // pretty basic...
23221             
23222             var styles = node.getAttribute("style").split(";");
23223             var nstyle = [];
23224             Roo.each(styles, function(s) {
23225                 if (!s.match(/:/)) {
23226                     return;
23227                 }
23228                 var kv = s.split(":");
23229                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23230                     return;
23231                 }
23232                 // what ever is left... we allow.
23233                 nstyle.push(s);
23234             });
23235             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23236             if (!nstyle.length) {
23237                 node.removeAttribute('style');
23238             }
23239         }
23240         
23241         this.iterateChildren(node, this.cleanTableWidths);
23242         
23243         
23244     },
23245     
23246     
23247     
23248     
23249     domToHTML : function(currentElement, depth, nopadtext) {
23250         
23251         depth = depth || 0;
23252         nopadtext = nopadtext || false;
23253     
23254         if (!currentElement) {
23255             return this.domToHTML(this.doc.body);
23256         }
23257         
23258         //Roo.log(currentElement);
23259         var j;
23260         var allText = false;
23261         var nodeName = currentElement.nodeName;
23262         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23263         
23264         if  (nodeName == '#text') {
23265             
23266             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23267         }
23268         
23269         
23270         var ret = '';
23271         if (nodeName != 'BODY') {
23272              
23273             var i = 0;
23274             // Prints the node tagName, such as <A>, <IMG>, etc
23275             if (tagName) {
23276                 var attr = [];
23277                 for(i = 0; i < currentElement.attributes.length;i++) {
23278                     // quoting?
23279                     var aname = currentElement.attributes.item(i).name;
23280                     if (!currentElement.attributes.item(i).value.length) {
23281                         continue;
23282                     }
23283                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23284                 }
23285                 
23286                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23287             } 
23288             else {
23289                 
23290                 // eack
23291             }
23292         } else {
23293             tagName = false;
23294         }
23295         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23296             return ret;
23297         }
23298         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23299             nopadtext = true;
23300         }
23301         
23302         
23303         // Traverse the tree
23304         i = 0;
23305         var currentElementChild = currentElement.childNodes.item(i);
23306         var allText = true;
23307         var innerHTML  = '';
23308         lastnode = '';
23309         while (currentElementChild) {
23310             // Formatting code (indent the tree so it looks nice on the screen)
23311             var nopad = nopadtext;
23312             if (lastnode == 'SPAN') {
23313                 nopad  = true;
23314             }
23315             // text
23316             if  (currentElementChild.nodeName == '#text') {
23317                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23318                 toadd = nopadtext ? toadd : toadd.trim();
23319                 if (!nopad && toadd.length > 80) {
23320                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23321                 }
23322                 innerHTML  += toadd;
23323                 
23324                 i++;
23325                 currentElementChild = currentElement.childNodes.item(i);
23326                 lastNode = '';
23327                 continue;
23328             }
23329             allText = false;
23330             
23331             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23332                 
23333             // Recursively traverse the tree structure of the child node
23334             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23335             lastnode = currentElementChild.nodeName;
23336             i++;
23337             currentElementChild=currentElement.childNodes.item(i);
23338         }
23339         
23340         ret += innerHTML;
23341         
23342         if (!allText) {
23343                 // The remaining code is mostly for formatting the tree
23344             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23345         }
23346         
23347         
23348         if (tagName) {
23349             ret+= "</"+tagName+">";
23350         }
23351         return ret;
23352         
23353     },
23354         
23355     applyBlacklists : function()
23356     {
23357         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23358         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23359         
23360         this.white = [];
23361         this.black = [];
23362         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23363             if (b.indexOf(tag) > -1) {
23364                 return;
23365             }
23366             this.white.push(tag);
23367             
23368         }, this);
23369         
23370         Roo.each(w, function(tag) {
23371             if (b.indexOf(tag) > -1) {
23372                 return;
23373             }
23374             if (this.white.indexOf(tag) > -1) {
23375                 return;
23376             }
23377             this.white.push(tag);
23378             
23379         }, this);
23380         
23381         
23382         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23383             if (w.indexOf(tag) > -1) {
23384                 return;
23385             }
23386             this.black.push(tag);
23387             
23388         }, this);
23389         
23390         Roo.each(b, function(tag) {
23391             if (w.indexOf(tag) > -1) {
23392                 return;
23393             }
23394             if (this.black.indexOf(tag) > -1) {
23395                 return;
23396             }
23397             this.black.push(tag);
23398             
23399         }, this);
23400         
23401         
23402         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23403         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23404         
23405         this.cwhite = [];
23406         this.cblack = [];
23407         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23408             if (b.indexOf(tag) > -1) {
23409                 return;
23410             }
23411             this.cwhite.push(tag);
23412             
23413         }, this);
23414         
23415         Roo.each(w, function(tag) {
23416             if (b.indexOf(tag) > -1) {
23417                 return;
23418             }
23419             if (this.cwhite.indexOf(tag) > -1) {
23420                 return;
23421             }
23422             this.cwhite.push(tag);
23423             
23424         }, this);
23425         
23426         
23427         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23428             if (w.indexOf(tag) > -1) {
23429                 return;
23430             }
23431             this.cblack.push(tag);
23432             
23433         }, this);
23434         
23435         Roo.each(b, function(tag) {
23436             if (w.indexOf(tag) > -1) {
23437                 return;
23438             }
23439             if (this.cblack.indexOf(tag) > -1) {
23440                 return;
23441             }
23442             this.cblack.push(tag);
23443             
23444         }, this);
23445     },
23446     
23447     setStylesheets : function(stylesheets)
23448     {
23449         if(typeof(stylesheets) == 'string'){
23450             Roo.get(this.iframe.contentDocument.head).createChild({
23451                 tag : 'link',
23452                 rel : 'stylesheet',
23453                 type : 'text/css',
23454                 href : stylesheets
23455             });
23456             
23457             return;
23458         }
23459         var _this = this;
23460      
23461         Roo.each(stylesheets, function(s) {
23462             if(!s.length){
23463                 return;
23464             }
23465             
23466             Roo.get(_this.iframe.contentDocument.head).createChild({
23467                 tag : 'link',
23468                 rel : 'stylesheet',
23469                 type : 'text/css',
23470                 href : s
23471             });
23472         });
23473
23474         
23475     },
23476     
23477     removeStylesheets : function()
23478     {
23479         var _this = this;
23480         
23481         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23482             s.remove();
23483         });
23484     },
23485     
23486     setStyle : function(style)
23487     {
23488         Roo.get(this.iframe.contentDocument.head).createChild({
23489             tag : 'style',
23490             type : 'text/css',
23491             html : style
23492         });
23493
23494         return;
23495     }
23496     
23497     // hide stuff that is not compatible
23498     /**
23499      * @event blur
23500      * @hide
23501      */
23502     /**
23503      * @event change
23504      * @hide
23505      */
23506     /**
23507      * @event focus
23508      * @hide
23509      */
23510     /**
23511      * @event specialkey
23512      * @hide
23513      */
23514     /**
23515      * @cfg {String} fieldClass @hide
23516      */
23517     /**
23518      * @cfg {String} focusClass @hide
23519      */
23520     /**
23521      * @cfg {String} autoCreate @hide
23522      */
23523     /**
23524      * @cfg {String} inputType @hide
23525      */
23526     /**
23527      * @cfg {String} invalidClass @hide
23528      */
23529     /**
23530      * @cfg {String} invalidText @hide
23531      */
23532     /**
23533      * @cfg {String} msgFx @hide
23534      */
23535     /**
23536      * @cfg {String} validateOnBlur @hide
23537      */
23538 });
23539
23540 Roo.HtmlEditorCore.white = [
23541         'area', 'br', 'img', 'input', 'hr', 'wbr',
23542         
23543        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23544        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23545        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23546        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23547        'table',   'ul',         'xmp', 
23548        
23549        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23550       'thead',   'tr', 
23551      
23552       'dir', 'menu', 'ol', 'ul', 'dl',
23553        
23554       'embed',  'object'
23555 ];
23556
23557
23558 Roo.HtmlEditorCore.black = [
23559     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23560         'applet', // 
23561         'base',   'basefont', 'bgsound', 'blink',  'body', 
23562         'frame',  'frameset', 'head',    'html',   'ilayer', 
23563         'iframe', 'layer',  'link',     'meta',    'object',   
23564         'script', 'style' ,'title',  'xml' // clean later..
23565 ];
23566 Roo.HtmlEditorCore.clean = [
23567     'script', 'style', 'title', 'xml'
23568 ];
23569 Roo.HtmlEditorCore.remove = [
23570     'font'
23571 ];
23572 // attributes..
23573
23574 Roo.HtmlEditorCore.ablack = [
23575     'on'
23576 ];
23577     
23578 Roo.HtmlEditorCore.aclean = [ 
23579     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23580 ];
23581
23582 // protocols..
23583 Roo.HtmlEditorCore.pwhite= [
23584         'http',  'https',  'mailto'
23585 ];
23586
23587 // white listed style attributes.
23588 Roo.HtmlEditorCore.cwhite= [
23589       //  'text-align', /// default is to allow most things..
23590       
23591          
23592 //        'font-size'//??
23593 ];
23594
23595 // black listed style attributes.
23596 Roo.HtmlEditorCore.cblack= [
23597       //  'font-size' -- this can be set by the project 
23598 ];
23599
23600
23601 Roo.HtmlEditorCore.swapCodes   =[ 
23602     [    8211, "--" ], 
23603     [    8212, "--" ], 
23604     [    8216,  "'" ],  
23605     [    8217, "'" ],  
23606     [    8220, '"' ],  
23607     [    8221, '"' ],  
23608     [    8226, "*" ],  
23609     [    8230, "..." ]
23610 ]; 
23611
23612     /*
23613  * - LGPL
23614  *
23615  * HtmlEditor
23616  * 
23617  */
23618
23619 /**
23620  * @class Roo.bootstrap.HtmlEditor
23621  * @extends Roo.bootstrap.TextArea
23622  * Bootstrap HtmlEditor class
23623
23624  * @constructor
23625  * Create a new HtmlEditor
23626  * @param {Object} config The config object
23627  */
23628
23629 Roo.bootstrap.HtmlEditor = function(config){
23630     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23631     if (!this.toolbars) {
23632         this.toolbars = [];
23633     }
23634     
23635     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23636     this.addEvents({
23637             /**
23638              * @event initialize
23639              * Fires when the editor is fully initialized (including the iframe)
23640              * @param {HtmlEditor} this
23641              */
23642             initialize: true,
23643             /**
23644              * @event activate
23645              * Fires when the editor is first receives the focus. Any insertion must wait
23646              * until after this event.
23647              * @param {HtmlEditor} this
23648              */
23649             activate: true,
23650              /**
23651              * @event beforesync
23652              * Fires before the textarea is updated with content from the editor iframe. Return false
23653              * to cancel the sync.
23654              * @param {HtmlEditor} this
23655              * @param {String} html
23656              */
23657             beforesync: true,
23658              /**
23659              * @event beforepush
23660              * Fires before the iframe editor is updated with content from the textarea. Return false
23661              * to cancel the push.
23662              * @param {HtmlEditor} this
23663              * @param {String} html
23664              */
23665             beforepush: true,
23666              /**
23667              * @event sync
23668              * Fires when the textarea is updated with content from the editor iframe.
23669              * @param {HtmlEditor} this
23670              * @param {String} html
23671              */
23672             sync: true,
23673              /**
23674              * @event push
23675              * Fires when the iframe editor is updated with content from the textarea.
23676              * @param {HtmlEditor} this
23677              * @param {String} html
23678              */
23679             push: true,
23680              /**
23681              * @event editmodechange
23682              * Fires when the editor switches edit modes
23683              * @param {HtmlEditor} this
23684              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23685              */
23686             editmodechange: true,
23687             /**
23688              * @event editorevent
23689              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23690              * @param {HtmlEditor} this
23691              */
23692             editorevent: true,
23693             /**
23694              * @event firstfocus
23695              * Fires when on first focus - needed by toolbars..
23696              * @param {HtmlEditor} this
23697              */
23698             firstfocus: true,
23699             /**
23700              * @event autosave
23701              * Auto save the htmlEditor value as a file into Events
23702              * @param {HtmlEditor} this
23703              */
23704             autosave: true,
23705             /**
23706              * @event savedpreview
23707              * preview the saved version of htmlEditor
23708              * @param {HtmlEditor} this
23709              */
23710             savedpreview: true
23711         });
23712 };
23713
23714
23715 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23716     
23717     
23718       /**
23719      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23720      */
23721     toolbars : false,
23722     
23723      /**
23724     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23725     */
23726     btns : [],
23727    
23728      /**
23729      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23730      *                        Roo.resizable.
23731      */
23732     resizable : false,
23733      /**
23734      * @cfg {Number} height (in pixels)
23735      */   
23736     height: 300,
23737    /**
23738      * @cfg {Number} width (in pixels)
23739      */   
23740     width: false,
23741     
23742     /**
23743      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23744      * 
23745      */
23746     stylesheets: false,
23747     
23748     // id of frame..
23749     frameId: false,
23750     
23751     // private properties
23752     validationEvent : false,
23753     deferHeight: true,
23754     initialized : false,
23755     activated : false,
23756     
23757     onFocus : Roo.emptyFn,
23758     iframePad:3,
23759     hideMode:'offsets',
23760     
23761     tbContainer : false,
23762     
23763     bodyCls : '',
23764     
23765     toolbarContainer :function() {
23766         return this.wrap.select('.x-html-editor-tb',true).first();
23767     },
23768
23769     /**
23770      * Protected method that will not generally be called directly. It
23771      * is called when the editor creates its toolbar. Override this method if you need to
23772      * add custom toolbar buttons.
23773      * @param {HtmlEditor} editor
23774      */
23775     createToolbar : function(){
23776         Roo.log('renewing');
23777         Roo.log("create toolbars");
23778         
23779         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23780         this.toolbars[0].render(this.toolbarContainer());
23781         
23782         return;
23783         
23784 //        if (!editor.toolbars || !editor.toolbars.length) {
23785 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23786 //        }
23787 //        
23788 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23789 //            editor.toolbars[i] = Roo.factory(
23790 //                    typeof(editor.toolbars[i]) == 'string' ?
23791 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23792 //                Roo.bootstrap.HtmlEditor);
23793 //            editor.toolbars[i].init(editor);
23794 //        }
23795     },
23796
23797      
23798     // private
23799     onRender : function(ct, position)
23800     {
23801        // Roo.log("Call onRender: " + this.xtype);
23802         var _t = this;
23803         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23804       
23805         this.wrap = this.inputEl().wrap({
23806             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23807         });
23808         
23809         this.editorcore.onRender(ct, position);
23810          
23811         if (this.resizable) {
23812             this.resizeEl = new Roo.Resizable(this.wrap, {
23813                 pinned : true,
23814                 wrap: true,
23815                 dynamic : true,
23816                 minHeight : this.height,
23817                 height: this.height,
23818                 handles : this.resizable,
23819                 width: this.width,
23820                 listeners : {
23821                     resize : function(r, w, h) {
23822                         _t.onResize(w,h); // -something
23823                     }
23824                 }
23825             });
23826             
23827         }
23828         this.createToolbar(this);
23829        
23830         
23831         if(!this.width && this.resizable){
23832             this.setSize(this.wrap.getSize());
23833         }
23834         if (this.resizeEl) {
23835             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23836             // should trigger onReize..
23837         }
23838         
23839     },
23840
23841     // private
23842     onResize : function(w, h)
23843     {
23844         Roo.log('resize: ' +w + ',' + h );
23845         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23846         var ew = false;
23847         var eh = false;
23848         
23849         if(this.inputEl() ){
23850             if(typeof w == 'number'){
23851                 var aw = w - this.wrap.getFrameWidth('lr');
23852                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23853                 ew = aw;
23854             }
23855             if(typeof h == 'number'){
23856                  var tbh = -11;  // fixme it needs to tool bar size!
23857                 for (var i =0; i < this.toolbars.length;i++) {
23858                     // fixme - ask toolbars for heights?
23859                     tbh += this.toolbars[i].el.getHeight();
23860                     //if (this.toolbars[i].footer) {
23861                     //    tbh += this.toolbars[i].footer.el.getHeight();
23862                     //}
23863                 }
23864               
23865                 
23866                 
23867                 
23868                 
23869                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23870                 ah -= 5; // knock a few pixes off for look..
23871                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23872                 var eh = ah;
23873             }
23874         }
23875         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23876         this.editorcore.onResize(ew,eh);
23877         
23878     },
23879
23880     /**
23881      * Toggles the editor between standard and source edit mode.
23882      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23883      */
23884     toggleSourceEdit : function(sourceEditMode)
23885     {
23886         this.editorcore.toggleSourceEdit(sourceEditMode);
23887         
23888         if(this.editorcore.sourceEditMode){
23889             Roo.log('editor - showing textarea');
23890             
23891 //            Roo.log('in');
23892 //            Roo.log(this.syncValue());
23893             this.syncValue();
23894             this.inputEl().removeClass(['hide', 'x-hidden']);
23895             this.inputEl().dom.removeAttribute('tabIndex');
23896             this.inputEl().focus();
23897         }else{
23898             Roo.log('editor - hiding textarea');
23899 //            Roo.log('out')
23900 //            Roo.log(this.pushValue()); 
23901             this.pushValue();
23902             
23903             this.inputEl().addClass(['hide', 'x-hidden']);
23904             this.inputEl().dom.setAttribute('tabIndex', -1);
23905             //this.deferFocus();
23906         }
23907          
23908         if(this.resizable){
23909             this.setSize(this.wrap.getSize());
23910         }
23911         
23912         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23913     },
23914  
23915     // private (for BoxComponent)
23916     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23917
23918     // private (for BoxComponent)
23919     getResizeEl : function(){
23920         return this.wrap;
23921     },
23922
23923     // private (for BoxComponent)
23924     getPositionEl : function(){
23925         return this.wrap;
23926     },
23927
23928     // private
23929     initEvents : function(){
23930         this.originalValue = this.getValue();
23931     },
23932
23933 //    /**
23934 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23935 //     * @method
23936 //     */
23937 //    markInvalid : Roo.emptyFn,
23938 //    /**
23939 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23940 //     * @method
23941 //     */
23942 //    clearInvalid : Roo.emptyFn,
23943
23944     setValue : function(v){
23945         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23946         this.editorcore.pushValue();
23947     },
23948
23949      
23950     // private
23951     deferFocus : function(){
23952         this.focus.defer(10, this);
23953     },
23954
23955     // doc'ed in Field
23956     focus : function(){
23957         this.editorcore.focus();
23958         
23959     },
23960       
23961
23962     // private
23963     onDestroy : function(){
23964         
23965         
23966         
23967         if(this.rendered){
23968             
23969             for (var i =0; i < this.toolbars.length;i++) {
23970                 // fixme - ask toolbars for heights?
23971                 this.toolbars[i].onDestroy();
23972             }
23973             
23974             this.wrap.dom.innerHTML = '';
23975             this.wrap.remove();
23976         }
23977     },
23978
23979     // private
23980     onFirstFocus : function(){
23981         //Roo.log("onFirstFocus");
23982         this.editorcore.onFirstFocus();
23983          for (var i =0; i < this.toolbars.length;i++) {
23984             this.toolbars[i].onFirstFocus();
23985         }
23986         
23987     },
23988     
23989     // private
23990     syncValue : function()
23991     {   
23992         this.editorcore.syncValue();
23993     },
23994     
23995     pushValue : function()
23996     {   
23997         this.editorcore.pushValue();
23998     }
23999      
24000     
24001     // hide stuff that is not compatible
24002     /**
24003      * @event blur
24004      * @hide
24005      */
24006     /**
24007      * @event change
24008      * @hide
24009      */
24010     /**
24011      * @event focus
24012      * @hide
24013      */
24014     /**
24015      * @event specialkey
24016      * @hide
24017      */
24018     /**
24019      * @cfg {String} fieldClass @hide
24020      */
24021     /**
24022      * @cfg {String} focusClass @hide
24023      */
24024     /**
24025      * @cfg {String} autoCreate @hide
24026      */
24027     /**
24028      * @cfg {String} inputType @hide
24029      */
24030      
24031     /**
24032      * @cfg {String} invalidText @hide
24033      */
24034     /**
24035      * @cfg {String} msgFx @hide
24036      */
24037     /**
24038      * @cfg {String} validateOnBlur @hide
24039      */
24040 });
24041  
24042     
24043    
24044    
24045    
24046       
24047 Roo.namespace('Roo.bootstrap.htmleditor');
24048 /**
24049  * @class Roo.bootstrap.HtmlEditorToolbar1
24050  * Basic Toolbar
24051  * 
24052  * Usage:
24053  *
24054  new Roo.bootstrap.HtmlEditor({
24055     ....
24056     toolbars : [
24057         new Roo.bootstrap.HtmlEditorToolbar1({
24058             disable : { fonts: 1 , format: 1, ..., ... , ...],
24059             btns : [ .... ]
24060         })
24061     }
24062      
24063  * 
24064  * @cfg {Object} disable List of elements to disable..
24065  * @cfg {Array} btns List of additional buttons.
24066  * 
24067  * 
24068  * NEEDS Extra CSS? 
24069  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24070  */
24071  
24072 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24073 {
24074     
24075     Roo.apply(this, config);
24076     
24077     // default disabled, based on 'good practice'..
24078     this.disable = this.disable || {};
24079     Roo.applyIf(this.disable, {
24080         fontSize : true,
24081         colors : true,
24082         specialElements : true
24083     });
24084     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24085     
24086     this.editor = config.editor;
24087     this.editorcore = config.editor.editorcore;
24088     
24089     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24090     
24091     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24092     // dont call parent... till later.
24093 }
24094 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24095      
24096     bar : true,
24097     
24098     editor : false,
24099     editorcore : false,
24100     
24101     
24102     formats : [
24103         "p" ,  
24104         "h1","h2","h3","h4","h5","h6", 
24105         "pre", "code", 
24106         "abbr", "acronym", "address", "cite", "samp", "var",
24107         'div','span'
24108     ],
24109     
24110     onRender : function(ct, position)
24111     {
24112        // Roo.log("Call onRender: " + this.xtype);
24113         
24114        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24115        Roo.log(this.el);
24116        this.el.dom.style.marginBottom = '0';
24117        var _this = this;
24118        var editorcore = this.editorcore;
24119        var editor= this.editor;
24120        
24121        var children = [];
24122        var btn = function(id,cmd , toggle, handler, html){
24123        
24124             var  event = toggle ? 'toggle' : 'click';
24125        
24126             var a = {
24127                 size : 'sm',
24128                 xtype: 'Button',
24129                 xns: Roo.bootstrap,
24130                 //glyphicon : id,
24131                 fa: id,
24132                 cmd : id || cmd,
24133                 enableToggle:toggle !== false,
24134                 html : html || '',
24135                 pressed : toggle ? false : null,
24136                 listeners : {}
24137             };
24138             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24139                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24140             };
24141             children.push(a);
24142             return a;
24143        }
24144        
24145     //    var cb_box = function...
24146         
24147         var style = {
24148                 xtype: 'Button',
24149                 size : 'sm',
24150                 xns: Roo.bootstrap,
24151                 fa : 'font',
24152                 //html : 'submit'
24153                 menu : {
24154                     xtype: 'Menu',
24155                     xns: Roo.bootstrap,
24156                     items:  []
24157                 }
24158         };
24159         Roo.each(this.formats, function(f) {
24160             style.menu.items.push({
24161                 xtype :'MenuItem',
24162                 xns: Roo.bootstrap,
24163                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24164                 tagname : f,
24165                 listeners : {
24166                     click : function()
24167                     {
24168                         editorcore.insertTag(this.tagname);
24169                         editor.focus();
24170                     }
24171                 }
24172                 
24173             });
24174         });
24175         children.push(style);   
24176         
24177         btn('bold',false,true);
24178         btn('italic',false,true);
24179         btn('align-left', 'justifyleft',true);
24180         btn('align-center', 'justifycenter',true);
24181         btn('align-right' , 'justifyright',true);
24182         btn('link', false, false, function(btn) {
24183             //Roo.log("create link?");
24184             var url = prompt(this.createLinkText, this.defaultLinkValue);
24185             if(url && url != 'http:/'+'/'){
24186                 this.editorcore.relayCmd('createlink', url);
24187             }
24188         }),
24189         btn('list','insertunorderedlist',true);
24190         btn('pencil', false,true, function(btn){
24191                 Roo.log(this);
24192                 this.toggleSourceEdit(btn.pressed);
24193         });
24194         
24195         if (this.editor.btns.length > 0) {
24196             for (var i = 0; i<this.editor.btns.length; i++) {
24197                 children.push(this.editor.btns[i]);
24198             }
24199         }
24200         
24201         /*
24202         var cog = {
24203                 xtype: 'Button',
24204                 size : 'sm',
24205                 xns: Roo.bootstrap,
24206                 glyphicon : 'cog',
24207                 //html : 'submit'
24208                 menu : {
24209                     xtype: 'Menu',
24210                     xns: Roo.bootstrap,
24211                     items:  []
24212                 }
24213         };
24214         
24215         cog.menu.items.push({
24216             xtype :'MenuItem',
24217             xns: Roo.bootstrap,
24218             html : Clean styles,
24219             tagname : f,
24220             listeners : {
24221                 click : function()
24222                 {
24223                     editorcore.insertTag(this.tagname);
24224                     editor.focus();
24225                 }
24226             }
24227             
24228         });
24229        */
24230         
24231          
24232        this.xtype = 'NavSimplebar';
24233         
24234         for(var i=0;i< children.length;i++) {
24235             
24236             this.buttons.add(this.addxtypeChild(children[i]));
24237             
24238         }
24239         
24240         editor.on('editorevent', this.updateToolbar, this);
24241     },
24242     onBtnClick : function(id)
24243     {
24244        this.editorcore.relayCmd(id);
24245        this.editorcore.focus();
24246     },
24247     
24248     /**
24249      * Protected method that will not generally be called directly. It triggers
24250      * a toolbar update by reading the markup state of the current selection in the editor.
24251      */
24252     updateToolbar: function(){
24253
24254         if(!this.editorcore.activated){
24255             this.editor.onFirstFocus(); // is this neeed?
24256             return;
24257         }
24258
24259         var btns = this.buttons; 
24260         var doc = this.editorcore.doc;
24261         btns.get('bold').setActive(doc.queryCommandState('bold'));
24262         btns.get('italic').setActive(doc.queryCommandState('italic'));
24263         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24264         
24265         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24266         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24267         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24268         
24269         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24270         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24271          /*
24272         
24273         var ans = this.editorcore.getAllAncestors();
24274         if (this.formatCombo) {
24275             
24276             
24277             var store = this.formatCombo.store;
24278             this.formatCombo.setValue("");
24279             for (var i =0; i < ans.length;i++) {
24280                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24281                     // select it..
24282                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24283                     break;
24284                 }
24285             }
24286         }
24287         
24288         
24289         
24290         // hides menus... - so this cant be on a menu...
24291         Roo.bootstrap.MenuMgr.hideAll();
24292         */
24293         Roo.bootstrap.MenuMgr.hideAll();
24294         //this.editorsyncValue();
24295     },
24296     onFirstFocus: function() {
24297         this.buttons.each(function(item){
24298            item.enable();
24299         });
24300     },
24301     toggleSourceEdit : function(sourceEditMode){
24302         
24303           
24304         if(sourceEditMode){
24305             Roo.log("disabling buttons");
24306            this.buttons.each( function(item){
24307                 if(item.cmd != 'pencil'){
24308                     item.disable();
24309                 }
24310             });
24311           
24312         }else{
24313             Roo.log("enabling buttons");
24314             if(this.editorcore.initialized){
24315                 this.buttons.each( function(item){
24316                     item.enable();
24317                 });
24318             }
24319             
24320         }
24321         Roo.log("calling toggole on editor");
24322         // tell the editor that it's been pressed..
24323         this.editor.toggleSourceEdit(sourceEditMode);
24324        
24325     }
24326 });
24327
24328
24329
24330
24331
24332 /**
24333  * @class Roo.bootstrap.Table.AbstractSelectionModel
24334  * @extends Roo.util.Observable
24335  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24336  * implemented by descendant classes.  This class should not be directly instantiated.
24337  * @constructor
24338  */
24339 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24340     this.locked = false;
24341     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24342 };
24343
24344
24345 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24346     /** @ignore Called by the grid automatically. Do not call directly. */
24347     init : function(grid){
24348         this.grid = grid;
24349         this.initEvents();
24350     },
24351
24352     /**
24353      * Locks the selections.
24354      */
24355     lock : function(){
24356         this.locked = true;
24357     },
24358
24359     /**
24360      * Unlocks the selections.
24361      */
24362     unlock : function(){
24363         this.locked = false;
24364     },
24365
24366     /**
24367      * Returns true if the selections are locked.
24368      * @return {Boolean}
24369      */
24370     isLocked : function(){
24371         return this.locked;
24372     }
24373 });
24374 /**
24375  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24376  * @class Roo.bootstrap.Table.RowSelectionModel
24377  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24378  * It supports multiple selections and keyboard selection/navigation. 
24379  * @constructor
24380  * @param {Object} config
24381  */
24382
24383 Roo.bootstrap.Table.RowSelectionModel = function(config){
24384     Roo.apply(this, config);
24385     this.selections = new Roo.util.MixedCollection(false, function(o){
24386         return o.id;
24387     });
24388
24389     this.last = false;
24390     this.lastActive = false;
24391
24392     this.addEvents({
24393         /**
24394              * @event selectionchange
24395              * Fires when the selection changes
24396              * @param {SelectionModel} this
24397              */
24398             "selectionchange" : true,
24399         /**
24400              * @event afterselectionchange
24401              * Fires after the selection changes (eg. by key press or clicking)
24402              * @param {SelectionModel} this
24403              */
24404             "afterselectionchange" : true,
24405         /**
24406              * @event beforerowselect
24407              * Fires when a row is selected being selected, return false to cancel.
24408              * @param {SelectionModel} this
24409              * @param {Number} rowIndex The selected index
24410              * @param {Boolean} keepExisting False if other selections will be cleared
24411              */
24412             "beforerowselect" : true,
24413         /**
24414              * @event rowselect
24415              * Fires when a row is selected.
24416              * @param {SelectionModel} this
24417              * @param {Number} rowIndex The selected index
24418              * @param {Roo.data.Record} r The record
24419              */
24420             "rowselect" : true,
24421         /**
24422              * @event rowdeselect
24423              * Fires when a row is deselected.
24424              * @param {SelectionModel} this
24425              * @param {Number} rowIndex The selected index
24426              */
24427         "rowdeselect" : true
24428     });
24429     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24430     this.locked = false;
24431  };
24432
24433 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24434     /**
24435      * @cfg {Boolean} singleSelect
24436      * True to allow selection of only one row at a time (defaults to false)
24437      */
24438     singleSelect : false,
24439
24440     // private
24441     initEvents : function()
24442     {
24443
24444         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24445         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24446         //}else{ // allow click to work like normal
24447          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24448         //}
24449         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24450         this.grid.on("rowclick", this.handleMouseDown, this);
24451         
24452         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24453             "up" : function(e){
24454                 if(!e.shiftKey){
24455                     this.selectPrevious(e.shiftKey);
24456                 }else if(this.last !== false && this.lastActive !== false){
24457                     var last = this.last;
24458                     this.selectRange(this.last,  this.lastActive-1);
24459                     this.grid.getView().focusRow(this.lastActive);
24460                     if(last !== false){
24461                         this.last = last;
24462                     }
24463                 }else{
24464                     this.selectFirstRow();
24465                 }
24466                 this.fireEvent("afterselectionchange", this);
24467             },
24468             "down" : function(e){
24469                 if(!e.shiftKey){
24470                     this.selectNext(e.shiftKey);
24471                 }else if(this.last !== false && this.lastActive !== false){
24472                     var last = this.last;
24473                     this.selectRange(this.last,  this.lastActive+1);
24474                     this.grid.getView().focusRow(this.lastActive);
24475                     if(last !== false){
24476                         this.last = last;
24477                     }
24478                 }else{
24479                     this.selectFirstRow();
24480                 }
24481                 this.fireEvent("afterselectionchange", this);
24482             },
24483             scope: this
24484         });
24485         this.grid.store.on('load', function(){
24486             this.selections.clear();
24487         },this);
24488         /*
24489         var view = this.grid.view;
24490         view.on("refresh", this.onRefresh, this);
24491         view.on("rowupdated", this.onRowUpdated, this);
24492         view.on("rowremoved", this.onRemove, this);
24493         */
24494     },
24495
24496     // private
24497     onRefresh : function()
24498     {
24499         var ds = this.grid.store, i, v = this.grid.view;
24500         var s = this.selections;
24501         s.each(function(r){
24502             if((i = ds.indexOfId(r.id)) != -1){
24503                 v.onRowSelect(i);
24504             }else{
24505                 s.remove(r);
24506             }
24507         });
24508     },
24509
24510     // private
24511     onRemove : function(v, index, r){
24512         this.selections.remove(r);
24513     },
24514
24515     // private
24516     onRowUpdated : function(v, index, r){
24517         if(this.isSelected(r)){
24518             v.onRowSelect(index);
24519         }
24520     },
24521
24522     /**
24523      * Select records.
24524      * @param {Array} records The records to select
24525      * @param {Boolean} keepExisting (optional) True to keep existing selections
24526      */
24527     selectRecords : function(records, keepExisting)
24528     {
24529         if(!keepExisting){
24530             this.clearSelections();
24531         }
24532             var ds = this.grid.store;
24533         for(var i = 0, len = records.length; i < len; i++){
24534             this.selectRow(ds.indexOf(records[i]), true);
24535         }
24536     },
24537
24538     /**
24539      * Gets the number of selected rows.
24540      * @return {Number}
24541      */
24542     getCount : function(){
24543         return this.selections.length;
24544     },
24545
24546     /**
24547      * Selects the first row in the grid.
24548      */
24549     selectFirstRow : function(){
24550         this.selectRow(0);
24551     },
24552
24553     /**
24554      * Select the last row.
24555      * @param {Boolean} keepExisting (optional) True to keep existing selections
24556      */
24557     selectLastRow : function(keepExisting){
24558         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24559         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24560     },
24561
24562     /**
24563      * Selects the row immediately following the last selected row.
24564      * @param {Boolean} keepExisting (optional) True to keep existing selections
24565      */
24566     selectNext : function(keepExisting)
24567     {
24568             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24569             this.selectRow(this.last+1, keepExisting);
24570             this.grid.getView().focusRow(this.last);
24571         }
24572     },
24573
24574     /**
24575      * Selects the row that precedes the last selected row.
24576      * @param {Boolean} keepExisting (optional) True to keep existing selections
24577      */
24578     selectPrevious : function(keepExisting){
24579         if(this.last){
24580             this.selectRow(this.last-1, keepExisting);
24581             this.grid.getView().focusRow(this.last);
24582         }
24583     },
24584
24585     /**
24586      * Returns the selected records
24587      * @return {Array} Array of selected records
24588      */
24589     getSelections : function(){
24590         return [].concat(this.selections.items);
24591     },
24592
24593     /**
24594      * Returns the first selected record.
24595      * @return {Record}
24596      */
24597     getSelected : function(){
24598         return this.selections.itemAt(0);
24599     },
24600
24601
24602     /**
24603      * Clears all selections.
24604      */
24605     clearSelections : function(fast)
24606     {
24607         if(this.locked) {
24608             return;
24609         }
24610         if(fast !== true){
24611                 var ds = this.grid.store;
24612             var s = this.selections;
24613             s.each(function(r){
24614                 this.deselectRow(ds.indexOfId(r.id));
24615             }, this);
24616             s.clear();
24617         }else{
24618             this.selections.clear();
24619         }
24620         this.last = false;
24621     },
24622
24623
24624     /**
24625      * Selects all rows.
24626      */
24627     selectAll : function(){
24628         if(this.locked) {
24629             return;
24630         }
24631         this.selections.clear();
24632         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24633             this.selectRow(i, true);
24634         }
24635     },
24636
24637     /**
24638      * Returns True if there is a selection.
24639      * @return {Boolean}
24640      */
24641     hasSelection : function(){
24642         return this.selections.length > 0;
24643     },
24644
24645     /**
24646      * Returns True if the specified row is selected.
24647      * @param {Number/Record} record The record or index of the record to check
24648      * @return {Boolean}
24649      */
24650     isSelected : function(index){
24651             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24652         return (r && this.selections.key(r.id) ? true : false);
24653     },
24654
24655     /**
24656      * Returns True if the specified record id is selected.
24657      * @param {String} id The id of record to check
24658      * @return {Boolean}
24659      */
24660     isIdSelected : function(id){
24661         return (this.selections.key(id) ? true : false);
24662     },
24663
24664
24665     // private
24666     handleMouseDBClick : function(e, t){
24667         
24668     },
24669     // private
24670     handleMouseDown : function(e, t)
24671     {
24672             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24673         if(this.isLocked() || rowIndex < 0 ){
24674             return;
24675         };
24676         if(e.shiftKey && this.last !== false){
24677             var last = this.last;
24678             this.selectRange(last, rowIndex, e.ctrlKey);
24679             this.last = last; // reset the last
24680             t.focus();
24681     
24682         }else{
24683             var isSelected = this.isSelected(rowIndex);
24684             //Roo.log("select row:" + rowIndex);
24685             if(isSelected){
24686                 this.deselectRow(rowIndex);
24687             } else {
24688                         this.selectRow(rowIndex, true);
24689             }
24690     
24691             /*
24692                 if(e.button !== 0 && isSelected){
24693                 alert('rowIndex 2: ' + rowIndex);
24694                     view.focusRow(rowIndex);
24695                 }else if(e.ctrlKey && isSelected){
24696                     this.deselectRow(rowIndex);
24697                 }else if(!isSelected){
24698                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24699                     view.focusRow(rowIndex);
24700                 }
24701             */
24702         }
24703         this.fireEvent("afterselectionchange", this);
24704     },
24705     // private
24706     handleDragableRowClick :  function(grid, rowIndex, e) 
24707     {
24708         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24709             this.selectRow(rowIndex, false);
24710             grid.view.focusRow(rowIndex);
24711              this.fireEvent("afterselectionchange", this);
24712         }
24713     },
24714     
24715     /**
24716      * Selects multiple rows.
24717      * @param {Array} rows Array of the indexes of the row to select
24718      * @param {Boolean} keepExisting (optional) True to keep existing selections
24719      */
24720     selectRows : function(rows, keepExisting){
24721         if(!keepExisting){
24722             this.clearSelections();
24723         }
24724         for(var i = 0, len = rows.length; i < len; i++){
24725             this.selectRow(rows[i], true);
24726         }
24727     },
24728
24729     /**
24730      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24731      * @param {Number} startRow The index of the first row in the range
24732      * @param {Number} endRow The index of the last row in the range
24733      * @param {Boolean} keepExisting (optional) True to retain existing selections
24734      */
24735     selectRange : function(startRow, endRow, keepExisting){
24736         if(this.locked) {
24737             return;
24738         }
24739         if(!keepExisting){
24740             this.clearSelections();
24741         }
24742         if(startRow <= endRow){
24743             for(var i = startRow; i <= endRow; i++){
24744                 this.selectRow(i, true);
24745             }
24746         }else{
24747             for(var i = startRow; i >= endRow; i--){
24748                 this.selectRow(i, true);
24749             }
24750         }
24751     },
24752
24753     /**
24754      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24755      * @param {Number} startRow The index of the first row in the range
24756      * @param {Number} endRow The index of the last row in the range
24757      */
24758     deselectRange : function(startRow, endRow, preventViewNotify){
24759         if(this.locked) {
24760             return;
24761         }
24762         for(var i = startRow; i <= endRow; i++){
24763             this.deselectRow(i, preventViewNotify);
24764         }
24765     },
24766
24767     /**
24768      * Selects a row.
24769      * @param {Number} row The index of the row to select
24770      * @param {Boolean} keepExisting (optional) True to keep existing selections
24771      */
24772     selectRow : function(index, keepExisting, preventViewNotify)
24773     {
24774             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24775             return;
24776         }
24777         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24778             if(!keepExisting || this.singleSelect){
24779                 this.clearSelections();
24780             }
24781             
24782             var r = this.grid.store.getAt(index);
24783             //console.log('selectRow - record id :' + r.id);
24784             
24785             this.selections.add(r);
24786             this.last = this.lastActive = index;
24787             if(!preventViewNotify){
24788                 var proxy = new Roo.Element(
24789                                 this.grid.getRowDom(index)
24790                 );
24791                 proxy.addClass('bg-info info');
24792             }
24793             this.fireEvent("rowselect", this, index, r);
24794             this.fireEvent("selectionchange", this);
24795         }
24796     },
24797
24798     /**
24799      * Deselects a row.
24800      * @param {Number} row The index of the row to deselect
24801      */
24802     deselectRow : function(index, preventViewNotify)
24803     {
24804         if(this.locked) {
24805             return;
24806         }
24807         if(this.last == index){
24808             this.last = false;
24809         }
24810         if(this.lastActive == index){
24811             this.lastActive = false;
24812         }
24813         
24814         var r = this.grid.store.getAt(index);
24815         if (!r) {
24816             return;
24817         }
24818         
24819         this.selections.remove(r);
24820         //.console.log('deselectRow - record id :' + r.id);
24821         if(!preventViewNotify){
24822         
24823             var proxy = new Roo.Element(
24824                 this.grid.getRowDom(index)
24825             );
24826             proxy.removeClass('bg-info info');
24827         }
24828         this.fireEvent("rowdeselect", this, index);
24829         this.fireEvent("selectionchange", this);
24830     },
24831
24832     // private
24833     restoreLast : function(){
24834         if(this._last){
24835             this.last = this._last;
24836         }
24837     },
24838
24839     // private
24840     acceptsNav : function(row, col, cm){
24841         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24842     },
24843
24844     // private
24845     onEditorKey : function(field, e){
24846         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24847         if(k == e.TAB){
24848             e.stopEvent();
24849             ed.completeEdit();
24850             if(e.shiftKey){
24851                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24852             }else{
24853                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24854             }
24855         }else if(k == e.ENTER && !e.ctrlKey){
24856             e.stopEvent();
24857             ed.completeEdit();
24858             if(e.shiftKey){
24859                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24860             }else{
24861                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24862             }
24863         }else if(k == e.ESC){
24864             ed.cancelEdit();
24865         }
24866         if(newCell){
24867             g.startEditing(newCell[0], newCell[1]);
24868         }
24869     }
24870 });
24871 /*
24872  * Based on:
24873  * Ext JS Library 1.1.1
24874  * Copyright(c) 2006-2007, Ext JS, LLC.
24875  *
24876  * Originally Released Under LGPL - original licence link has changed is not relivant.
24877  *
24878  * Fork - LGPL
24879  * <script type="text/javascript">
24880  */
24881  
24882 /**
24883  * @class Roo.bootstrap.PagingToolbar
24884  * @extends Roo.bootstrap.NavSimplebar
24885  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24886  * @constructor
24887  * Create a new PagingToolbar
24888  * @param {Object} config The config object
24889  * @param {Roo.data.Store} store
24890  */
24891 Roo.bootstrap.PagingToolbar = function(config)
24892 {
24893     // old args format still supported... - xtype is prefered..
24894         // created from xtype...
24895     
24896     this.ds = config.dataSource;
24897     
24898     if (config.store && !this.ds) {
24899         this.store= Roo.factory(config.store, Roo.data);
24900         this.ds = this.store;
24901         this.ds.xmodule = this.xmodule || false;
24902     }
24903     
24904     this.toolbarItems = [];
24905     if (config.items) {
24906         this.toolbarItems = config.items;
24907     }
24908     
24909     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24910     
24911     this.cursor = 0;
24912     
24913     if (this.ds) { 
24914         this.bind(this.ds);
24915     }
24916     
24917     if (Roo.bootstrap.version == 4) {
24918         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24919     } else {
24920         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24921     }
24922     
24923 };
24924
24925 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24926     /**
24927      * @cfg {Roo.data.Store} dataSource
24928      * The underlying data store providing the paged data
24929      */
24930     /**
24931      * @cfg {String/HTMLElement/Element} container
24932      * container The id or element that will contain the toolbar
24933      */
24934     /**
24935      * @cfg {Boolean} displayInfo
24936      * True to display the displayMsg (defaults to false)
24937      */
24938     /**
24939      * @cfg {Number} pageSize
24940      * The number of records to display per page (defaults to 20)
24941      */
24942     pageSize: 20,
24943     /**
24944      * @cfg {String} displayMsg
24945      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24946      */
24947     displayMsg : 'Displaying {0} - {1} of {2}',
24948     /**
24949      * @cfg {String} emptyMsg
24950      * The message to display when no records are found (defaults to "No data to display")
24951      */
24952     emptyMsg : 'No data to display',
24953     /**
24954      * Customizable piece of the default paging text (defaults to "Page")
24955      * @type String
24956      */
24957     beforePageText : "Page",
24958     /**
24959      * Customizable piece of the default paging text (defaults to "of %0")
24960      * @type String
24961      */
24962     afterPageText : "of {0}",
24963     /**
24964      * Customizable piece of the default paging text (defaults to "First Page")
24965      * @type String
24966      */
24967     firstText : "First Page",
24968     /**
24969      * Customizable piece of the default paging text (defaults to "Previous Page")
24970      * @type String
24971      */
24972     prevText : "Previous Page",
24973     /**
24974      * Customizable piece of the default paging text (defaults to "Next Page")
24975      * @type String
24976      */
24977     nextText : "Next Page",
24978     /**
24979      * Customizable piece of the default paging text (defaults to "Last Page")
24980      * @type String
24981      */
24982     lastText : "Last Page",
24983     /**
24984      * Customizable piece of the default paging text (defaults to "Refresh")
24985      * @type String
24986      */
24987     refreshText : "Refresh",
24988
24989     buttons : false,
24990     // private
24991     onRender : function(ct, position) 
24992     {
24993         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24994         this.navgroup.parentId = this.id;
24995         this.navgroup.onRender(this.el, null);
24996         // add the buttons to the navgroup
24997         
24998         if(this.displayInfo){
24999             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25000             this.displayEl = this.el.select('.x-paging-info', true).first();
25001 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25002 //            this.displayEl = navel.el.select('span',true).first();
25003         }
25004         
25005         var _this = this;
25006         
25007         if(this.buttons){
25008             Roo.each(_this.buttons, function(e){ // this might need to use render????
25009                Roo.factory(e).render(_this.el);
25010             });
25011         }
25012             
25013         Roo.each(_this.toolbarItems, function(e) {
25014             _this.navgroup.addItem(e);
25015         });
25016         
25017         
25018         this.first = this.navgroup.addItem({
25019             tooltip: this.firstText,
25020             cls: "prev btn-outline-secondary",
25021             html : ' <i class="fa fa-step-backward"></i>',
25022             disabled: true,
25023             preventDefault: true,
25024             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25025         });
25026         
25027         this.prev =  this.navgroup.addItem({
25028             tooltip: this.prevText,
25029             cls: "prev btn-outline-secondary",
25030             html : ' <i class="fa fa-backward"></i>',
25031             disabled: true,
25032             preventDefault: true,
25033             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25034         });
25035     //this.addSeparator();
25036         
25037         
25038         var field = this.navgroup.addItem( {
25039             tagtype : 'span',
25040             cls : 'x-paging-position  btn-outline-secondary',
25041              disabled: true,
25042             html : this.beforePageText  +
25043                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25044                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25045          } ); //?? escaped?
25046         
25047         this.field = field.el.select('input', true).first();
25048         this.field.on("keydown", this.onPagingKeydown, this);
25049         this.field.on("focus", function(){this.dom.select();});
25050     
25051     
25052         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25053         //this.field.setHeight(18);
25054         //this.addSeparator();
25055         this.next = this.navgroup.addItem({
25056             tooltip: this.nextText,
25057             cls: "next btn-outline-secondary",
25058             html : ' <i class="fa fa-forward"></i>',
25059             disabled: true,
25060             preventDefault: true,
25061             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25062         });
25063         this.last = this.navgroup.addItem({
25064             tooltip: this.lastText,
25065             html : ' <i class="fa fa-step-forward"></i>',
25066             cls: "next btn-outline-secondary",
25067             disabled: true,
25068             preventDefault: true,
25069             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25070         });
25071     //this.addSeparator();
25072         this.loading = this.navgroup.addItem({
25073             tooltip: this.refreshText,
25074             cls: "btn-outline-secondary",
25075             html : ' <i class="fa fa-refresh"></i>',
25076             preventDefault: true,
25077             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25078         });
25079         
25080     },
25081
25082     // private
25083     updateInfo : function(){
25084         if(this.displayEl){
25085             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25086             var msg = count == 0 ?
25087                 this.emptyMsg :
25088                 String.format(
25089                     this.displayMsg,
25090                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25091                 );
25092             this.displayEl.update(msg);
25093         }
25094     },
25095
25096     // private
25097     onLoad : function(ds, r, o)
25098     {
25099         this.cursor = o.params.start ? o.params.start : 0;
25100         
25101         var d = this.getPageData(),
25102             ap = d.activePage,
25103             ps = d.pages;
25104         
25105         
25106         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25107         this.field.dom.value = ap;
25108         this.first.setDisabled(ap == 1);
25109         this.prev.setDisabled(ap == 1);
25110         this.next.setDisabled(ap == ps);
25111         this.last.setDisabled(ap == ps);
25112         this.loading.enable();
25113         this.updateInfo();
25114     },
25115
25116     // private
25117     getPageData : function(){
25118         var total = this.ds.getTotalCount();
25119         return {
25120             total : total,
25121             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25122             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25123         };
25124     },
25125
25126     // private
25127     onLoadError : function(){
25128         this.loading.enable();
25129     },
25130
25131     // private
25132     onPagingKeydown : function(e){
25133         var k = e.getKey();
25134         var d = this.getPageData();
25135         if(k == e.RETURN){
25136             var v = this.field.dom.value, pageNum;
25137             if(!v || isNaN(pageNum = parseInt(v, 10))){
25138                 this.field.dom.value = d.activePage;
25139                 return;
25140             }
25141             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25142             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25143             e.stopEvent();
25144         }
25145         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))
25146         {
25147           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25148           this.field.dom.value = pageNum;
25149           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25150           e.stopEvent();
25151         }
25152         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25153         {
25154           var v = this.field.dom.value, pageNum; 
25155           var increment = (e.shiftKey) ? 10 : 1;
25156           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25157                 increment *= -1;
25158           }
25159           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25160             this.field.dom.value = d.activePage;
25161             return;
25162           }
25163           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25164           {
25165             this.field.dom.value = parseInt(v, 10) + increment;
25166             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25167             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25168           }
25169           e.stopEvent();
25170         }
25171     },
25172
25173     // private
25174     beforeLoad : function(){
25175         if(this.loading){
25176             this.loading.disable();
25177         }
25178     },
25179
25180     // private
25181     onClick : function(which){
25182         
25183         var ds = this.ds;
25184         if (!ds) {
25185             return;
25186         }
25187         
25188         switch(which){
25189             case "first":
25190                 ds.load({params:{start: 0, limit: this.pageSize}});
25191             break;
25192             case "prev":
25193                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25194             break;
25195             case "next":
25196                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25197             break;
25198             case "last":
25199                 var total = ds.getTotalCount();
25200                 var extra = total % this.pageSize;
25201                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25202                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25203             break;
25204             case "refresh":
25205                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25206             break;
25207         }
25208     },
25209
25210     /**
25211      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25212      * @param {Roo.data.Store} store The data store to unbind
25213      */
25214     unbind : function(ds){
25215         ds.un("beforeload", this.beforeLoad, this);
25216         ds.un("load", this.onLoad, this);
25217         ds.un("loadexception", this.onLoadError, this);
25218         ds.un("remove", this.updateInfo, this);
25219         ds.un("add", this.updateInfo, this);
25220         this.ds = undefined;
25221     },
25222
25223     /**
25224      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25225      * @param {Roo.data.Store} store The data store to bind
25226      */
25227     bind : function(ds){
25228         ds.on("beforeload", this.beforeLoad, this);
25229         ds.on("load", this.onLoad, this);
25230         ds.on("loadexception", this.onLoadError, this);
25231         ds.on("remove", this.updateInfo, this);
25232         ds.on("add", this.updateInfo, this);
25233         this.ds = ds;
25234     }
25235 });/*
25236  * - LGPL
25237  *
25238  * element
25239  * 
25240  */
25241
25242 /**
25243  * @class Roo.bootstrap.MessageBar
25244  * @extends Roo.bootstrap.Component
25245  * Bootstrap MessageBar class
25246  * @cfg {String} html contents of the MessageBar
25247  * @cfg {String} weight (info | success | warning | danger) default info
25248  * @cfg {String} beforeClass insert the bar before the given class
25249  * @cfg {Boolean} closable (true | false) default false
25250  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25251  * 
25252  * @constructor
25253  * Create a new Element
25254  * @param {Object} config The config object
25255  */
25256
25257 Roo.bootstrap.MessageBar = function(config){
25258     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25259 };
25260
25261 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25262     
25263     html: '',
25264     weight: 'info',
25265     closable: false,
25266     fixed: false,
25267     beforeClass: 'bootstrap-sticky-wrap',
25268     
25269     getAutoCreate : function(){
25270         
25271         var cfg = {
25272             tag: 'div',
25273             cls: 'alert alert-dismissable alert-' + this.weight,
25274             cn: [
25275                 {
25276                     tag: 'span',
25277                     cls: 'message',
25278                     html: this.html || ''
25279                 }
25280             ]
25281         };
25282         
25283         if(this.fixed){
25284             cfg.cls += ' alert-messages-fixed';
25285         }
25286         
25287         if(this.closable){
25288             cfg.cn.push({
25289                 tag: 'button',
25290                 cls: 'close',
25291                 html: 'x'
25292             });
25293         }
25294         
25295         return cfg;
25296     },
25297     
25298     onRender : function(ct, position)
25299     {
25300         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25301         
25302         if(!this.el){
25303             var cfg = Roo.apply({},  this.getAutoCreate());
25304             cfg.id = Roo.id();
25305             
25306             if (this.cls) {
25307                 cfg.cls += ' ' + this.cls;
25308             }
25309             if (this.style) {
25310                 cfg.style = this.style;
25311             }
25312             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25313             
25314             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25315         }
25316         
25317         this.el.select('>button.close').on('click', this.hide, this);
25318         
25319     },
25320     
25321     show : function()
25322     {
25323         if (!this.rendered) {
25324             this.render();
25325         }
25326         
25327         this.el.show();
25328         
25329         this.fireEvent('show', this);
25330         
25331     },
25332     
25333     hide : function()
25334     {
25335         if (!this.rendered) {
25336             this.render();
25337         }
25338         
25339         this.el.hide();
25340         
25341         this.fireEvent('hide', this);
25342     },
25343     
25344     update : function()
25345     {
25346 //        var e = this.el.dom.firstChild;
25347 //        
25348 //        if(this.closable){
25349 //            e = e.nextSibling;
25350 //        }
25351 //        
25352 //        e.data = this.html || '';
25353
25354         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25355     }
25356    
25357 });
25358
25359  
25360
25361      /*
25362  * - LGPL
25363  *
25364  * Graph
25365  * 
25366  */
25367
25368
25369 /**
25370  * @class Roo.bootstrap.Graph
25371  * @extends Roo.bootstrap.Component
25372  * Bootstrap Graph class
25373 > Prameters
25374  -sm {number} sm 4
25375  -md {number} md 5
25376  @cfg {String} graphtype  bar | vbar | pie
25377  @cfg {number} g_x coodinator | centre x (pie)
25378  @cfg {number} g_y coodinator | centre y (pie)
25379  @cfg {number} g_r radius (pie)
25380  @cfg {number} g_height height of the chart (respected by all elements in the set)
25381  @cfg {number} g_width width of the chart (respected by all elements in the set)
25382  @cfg {Object} title The title of the chart
25383     
25384  -{Array}  values
25385  -opts (object) options for the chart 
25386      o {
25387      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25388      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25389      o vgutter (number)
25390      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.
25391      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25392      o to
25393      o stretch (boolean)
25394      o }
25395  -opts (object) options for the pie
25396      o{
25397      o cut
25398      o startAngle (number)
25399      o endAngle (number)
25400      } 
25401  *
25402  * @constructor
25403  * Create a new Input
25404  * @param {Object} config The config object
25405  */
25406
25407 Roo.bootstrap.Graph = function(config){
25408     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25409     
25410     this.addEvents({
25411         // img events
25412         /**
25413          * @event click
25414          * The img click event for the img.
25415          * @param {Roo.EventObject} e
25416          */
25417         "click" : true
25418     });
25419 };
25420
25421 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25422     
25423     sm: 4,
25424     md: 5,
25425     graphtype: 'bar',
25426     g_height: 250,
25427     g_width: 400,
25428     g_x: 50,
25429     g_y: 50,
25430     g_r: 30,
25431     opts:{
25432         //g_colors: this.colors,
25433         g_type: 'soft',
25434         g_gutter: '20%'
25435
25436     },
25437     title : false,
25438
25439     getAutoCreate : function(){
25440         
25441         var cfg = {
25442             tag: 'div',
25443             html : null
25444         };
25445         
25446         
25447         return  cfg;
25448     },
25449
25450     onRender : function(ct,position){
25451         
25452         
25453         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25454         
25455         if (typeof(Raphael) == 'undefined') {
25456             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25457             return;
25458         }
25459         
25460         this.raphael = Raphael(this.el.dom);
25461         
25462                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25463                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25464                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25465                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25466                 /*
25467                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25468                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25469                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25470                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25471                 
25472                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25473                 r.barchart(330, 10, 300, 220, data1);
25474                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25475                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25476                 */
25477                 
25478                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25479                 // r.barchart(30, 30, 560, 250,  xdata, {
25480                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25481                 //     axis : "0 0 1 1",
25482                 //     axisxlabels :  xdata
25483                 //     //yvalues : cols,
25484                    
25485                 // });
25486 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25487 //        
25488 //        this.load(null,xdata,{
25489 //                axis : "0 0 1 1",
25490 //                axisxlabels :  xdata
25491 //                });
25492
25493     },
25494
25495     load : function(graphtype,xdata,opts)
25496     {
25497         this.raphael.clear();
25498         if(!graphtype) {
25499             graphtype = this.graphtype;
25500         }
25501         if(!opts){
25502             opts = this.opts;
25503         }
25504         var r = this.raphael,
25505             fin = function () {
25506                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25507             },
25508             fout = function () {
25509                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25510             },
25511             pfin = function() {
25512                 this.sector.stop();
25513                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25514
25515                 if (this.label) {
25516                     this.label[0].stop();
25517                     this.label[0].attr({ r: 7.5 });
25518                     this.label[1].attr({ "font-weight": 800 });
25519                 }
25520             },
25521             pfout = function() {
25522                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25523
25524                 if (this.label) {
25525                     this.label[0].animate({ r: 5 }, 500, "bounce");
25526                     this.label[1].attr({ "font-weight": 400 });
25527                 }
25528             };
25529
25530         switch(graphtype){
25531             case 'bar':
25532                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25533                 break;
25534             case 'hbar':
25535                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25536                 break;
25537             case 'pie':
25538 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25539 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25540 //            
25541                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25542                 
25543                 break;
25544
25545         }
25546         
25547         if(this.title){
25548             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25549         }
25550         
25551     },
25552     
25553     setTitle: function(o)
25554     {
25555         this.title = o;
25556     },
25557     
25558     initEvents: function() {
25559         
25560         if(!this.href){
25561             this.el.on('click', this.onClick, this);
25562         }
25563     },
25564     
25565     onClick : function(e)
25566     {
25567         Roo.log('img onclick');
25568         this.fireEvent('click', this, e);
25569     }
25570    
25571 });
25572
25573  
25574 /*
25575  * - LGPL
25576  *
25577  * numberBox
25578  * 
25579  */
25580 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25581
25582 /**
25583  * @class Roo.bootstrap.dash.NumberBox
25584  * @extends Roo.bootstrap.Component
25585  * Bootstrap NumberBox class
25586  * @cfg {String} headline Box headline
25587  * @cfg {String} content Box content
25588  * @cfg {String} icon Box icon
25589  * @cfg {String} footer Footer text
25590  * @cfg {String} fhref Footer href
25591  * 
25592  * @constructor
25593  * Create a new NumberBox
25594  * @param {Object} config The config object
25595  */
25596
25597
25598 Roo.bootstrap.dash.NumberBox = function(config){
25599     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25600     
25601 };
25602
25603 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25604     
25605     headline : '',
25606     content : '',
25607     icon : '',
25608     footer : '',
25609     fhref : '',
25610     ficon : '',
25611     
25612     getAutoCreate : function(){
25613         
25614         var cfg = {
25615             tag : 'div',
25616             cls : 'small-box ',
25617             cn : [
25618                 {
25619                     tag : 'div',
25620                     cls : 'inner',
25621                     cn :[
25622                         {
25623                             tag : 'h3',
25624                             cls : 'roo-headline',
25625                             html : this.headline
25626                         },
25627                         {
25628                             tag : 'p',
25629                             cls : 'roo-content',
25630                             html : this.content
25631                         }
25632                     ]
25633                 }
25634             ]
25635         };
25636         
25637         if(this.icon){
25638             cfg.cn.push({
25639                 tag : 'div',
25640                 cls : 'icon',
25641                 cn :[
25642                     {
25643                         tag : 'i',
25644                         cls : 'ion ' + this.icon
25645                     }
25646                 ]
25647             });
25648         }
25649         
25650         if(this.footer){
25651             var footer = {
25652                 tag : 'a',
25653                 cls : 'small-box-footer',
25654                 href : this.fhref || '#',
25655                 html : this.footer
25656             };
25657             
25658             cfg.cn.push(footer);
25659             
25660         }
25661         
25662         return  cfg;
25663     },
25664
25665     onRender : function(ct,position){
25666         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25667
25668
25669        
25670                 
25671     },
25672
25673     setHeadline: function (value)
25674     {
25675         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25676     },
25677     
25678     setFooter: function (value, href)
25679     {
25680         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25681         
25682         if(href){
25683             this.el.select('a.small-box-footer',true).first().attr('href', href);
25684         }
25685         
25686     },
25687
25688     setContent: function (value)
25689     {
25690         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25691     },
25692
25693     initEvents: function() 
25694     {   
25695         
25696     }
25697     
25698 });
25699
25700  
25701 /*
25702  * - LGPL
25703  *
25704  * TabBox
25705  * 
25706  */
25707 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25708
25709 /**
25710  * @class Roo.bootstrap.dash.TabBox
25711  * @extends Roo.bootstrap.Component
25712  * Bootstrap TabBox class
25713  * @cfg {String} title Title of the TabBox
25714  * @cfg {String} icon Icon of the TabBox
25715  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25716  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25717  * 
25718  * @constructor
25719  * Create a new TabBox
25720  * @param {Object} config The config object
25721  */
25722
25723
25724 Roo.bootstrap.dash.TabBox = function(config){
25725     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25726     this.addEvents({
25727         // raw events
25728         /**
25729          * @event addpane
25730          * When a pane is added
25731          * @param {Roo.bootstrap.dash.TabPane} pane
25732          */
25733         "addpane" : true,
25734         /**
25735          * @event activatepane
25736          * When a pane is activated
25737          * @param {Roo.bootstrap.dash.TabPane} pane
25738          */
25739         "activatepane" : true
25740         
25741          
25742     });
25743     
25744     this.panes = [];
25745 };
25746
25747 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25748
25749     title : '',
25750     icon : false,
25751     showtabs : true,
25752     tabScrollable : false,
25753     
25754     getChildContainer : function()
25755     {
25756         return this.el.select('.tab-content', true).first();
25757     },
25758     
25759     getAutoCreate : function(){
25760         
25761         var header = {
25762             tag: 'li',
25763             cls: 'pull-left header',
25764             html: this.title,
25765             cn : []
25766         };
25767         
25768         if(this.icon){
25769             header.cn.push({
25770                 tag: 'i',
25771                 cls: 'fa ' + this.icon
25772             });
25773         }
25774         
25775         var h = {
25776             tag: 'ul',
25777             cls: 'nav nav-tabs pull-right',
25778             cn: [
25779                 header
25780             ]
25781         };
25782         
25783         if(this.tabScrollable){
25784             h = {
25785                 tag: 'div',
25786                 cls: 'tab-header',
25787                 cn: [
25788                     {
25789                         tag: 'ul',
25790                         cls: 'nav nav-tabs pull-right',
25791                         cn: [
25792                             header
25793                         ]
25794                     }
25795                 ]
25796             };
25797         }
25798         
25799         var cfg = {
25800             tag: 'div',
25801             cls: 'nav-tabs-custom',
25802             cn: [
25803                 h,
25804                 {
25805                     tag: 'div',
25806                     cls: 'tab-content no-padding',
25807                     cn: []
25808                 }
25809             ]
25810         };
25811
25812         return  cfg;
25813     },
25814     initEvents : function()
25815     {
25816         //Roo.log('add add pane handler');
25817         this.on('addpane', this.onAddPane, this);
25818     },
25819      /**
25820      * Updates the box title
25821      * @param {String} html to set the title to.
25822      */
25823     setTitle : function(value)
25824     {
25825         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25826     },
25827     onAddPane : function(pane)
25828     {
25829         this.panes.push(pane);
25830         //Roo.log('addpane');
25831         //Roo.log(pane);
25832         // tabs are rendere left to right..
25833         if(!this.showtabs){
25834             return;
25835         }
25836         
25837         var ctr = this.el.select('.nav-tabs', true).first();
25838          
25839          
25840         var existing = ctr.select('.nav-tab',true);
25841         var qty = existing.getCount();;
25842         
25843         
25844         var tab = ctr.createChild({
25845             tag : 'li',
25846             cls : 'nav-tab' + (qty ? '' : ' active'),
25847             cn : [
25848                 {
25849                     tag : 'a',
25850                     href:'#',
25851                     html : pane.title
25852                 }
25853             ]
25854         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25855         pane.tab = tab;
25856         
25857         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25858         if (!qty) {
25859             pane.el.addClass('active');
25860         }
25861         
25862                 
25863     },
25864     onTabClick : function(ev,un,ob,pane)
25865     {
25866         //Roo.log('tab - prev default');
25867         ev.preventDefault();
25868         
25869         
25870         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25871         pane.tab.addClass('active');
25872         //Roo.log(pane.title);
25873         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25874         // technically we should have a deactivate event.. but maybe add later.
25875         // and it should not de-activate the selected tab...
25876         this.fireEvent('activatepane', pane);
25877         pane.el.addClass('active');
25878         pane.fireEvent('activate');
25879         
25880         
25881     },
25882     
25883     getActivePane : function()
25884     {
25885         var r = false;
25886         Roo.each(this.panes, function(p) {
25887             if(p.el.hasClass('active')){
25888                 r = p;
25889                 return false;
25890             }
25891             
25892             return;
25893         });
25894         
25895         return r;
25896     }
25897     
25898     
25899 });
25900
25901  
25902 /*
25903  * - LGPL
25904  *
25905  * Tab pane
25906  * 
25907  */
25908 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25909 /**
25910  * @class Roo.bootstrap.TabPane
25911  * @extends Roo.bootstrap.Component
25912  * Bootstrap TabPane class
25913  * @cfg {Boolean} active (false | true) Default false
25914  * @cfg {String} title title of panel
25915
25916  * 
25917  * @constructor
25918  * Create a new TabPane
25919  * @param {Object} config The config object
25920  */
25921
25922 Roo.bootstrap.dash.TabPane = function(config){
25923     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25924     
25925     this.addEvents({
25926         // raw events
25927         /**
25928          * @event activate
25929          * When a pane is activated
25930          * @param {Roo.bootstrap.dash.TabPane} pane
25931          */
25932         "activate" : true
25933          
25934     });
25935 };
25936
25937 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25938     
25939     active : false,
25940     title : '',
25941     
25942     // the tabBox that this is attached to.
25943     tab : false,
25944      
25945     getAutoCreate : function() 
25946     {
25947         var cfg = {
25948             tag: 'div',
25949             cls: 'tab-pane'
25950         };
25951         
25952         if(this.active){
25953             cfg.cls += ' active';
25954         }
25955         
25956         return cfg;
25957     },
25958     initEvents  : function()
25959     {
25960         //Roo.log('trigger add pane handler');
25961         this.parent().fireEvent('addpane', this)
25962     },
25963     
25964      /**
25965      * Updates the tab title 
25966      * @param {String} html to set the title to.
25967      */
25968     setTitle: function(str)
25969     {
25970         if (!this.tab) {
25971             return;
25972         }
25973         this.title = str;
25974         this.tab.select('a', true).first().dom.innerHTML = str;
25975         
25976     }
25977     
25978     
25979     
25980 });
25981
25982  
25983
25984
25985  /*
25986  * - LGPL
25987  *
25988  * menu
25989  * 
25990  */
25991 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25992
25993 /**
25994  * @class Roo.bootstrap.menu.Menu
25995  * @extends Roo.bootstrap.Component
25996  * Bootstrap Menu class - container for Menu
25997  * @cfg {String} html Text of the menu
25998  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25999  * @cfg {String} icon Font awesome icon
26000  * @cfg {String} pos Menu align to (top | bottom) default bottom
26001  * 
26002  * 
26003  * @constructor
26004  * Create a new Menu
26005  * @param {Object} config The config object
26006  */
26007
26008
26009 Roo.bootstrap.menu.Menu = function(config){
26010     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26011     
26012     this.addEvents({
26013         /**
26014          * @event beforeshow
26015          * Fires before this menu is displayed
26016          * @param {Roo.bootstrap.menu.Menu} this
26017          */
26018         beforeshow : true,
26019         /**
26020          * @event beforehide
26021          * Fires before this menu is hidden
26022          * @param {Roo.bootstrap.menu.Menu} this
26023          */
26024         beforehide : true,
26025         /**
26026          * @event show
26027          * Fires after this menu is displayed
26028          * @param {Roo.bootstrap.menu.Menu} this
26029          */
26030         show : true,
26031         /**
26032          * @event hide
26033          * Fires after this menu is hidden
26034          * @param {Roo.bootstrap.menu.Menu} this
26035          */
26036         hide : true,
26037         /**
26038          * @event click
26039          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26040          * @param {Roo.bootstrap.menu.Menu} this
26041          * @param {Roo.EventObject} e
26042          */
26043         click : true
26044     });
26045     
26046 };
26047
26048 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26049     
26050     submenu : false,
26051     html : '',
26052     weight : 'default',
26053     icon : false,
26054     pos : 'bottom',
26055     
26056     
26057     getChildContainer : function() {
26058         if(this.isSubMenu){
26059             return this.el;
26060         }
26061         
26062         return this.el.select('ul.dropdown-menu', true).first();  
26063     },
26064     
26065     getAutoCreate : function()
26066     {
26067         var text = [
26068             {
26069                 tag : 'span',
26070                 cls : 'roo-menu-text',
26071                 html : this.html
26072             }
26073         ];
26074         
26075         if(this.icon){
26076             text.unshift({
26077                 tag : 'i',
26078                 cls : 'fa ' + this.icon
26079             })
26080         }
26081         
26082         
26083         var cfg = {
26084             tag : 'div',
26085             cls : 'btn-group',
26086             cn : [
26087                 {
26088                     tag : 'button',
26089                     cls : 'dropdown-button btn btn-' + this.weight,
26090                     cn : text
26091                 },
26092                 {
26093                     tag : 'button',
26094                     cls : 'dropdown-toggle btn btn-' + this.weight,
26095                     cn : [
26096                         {
26097                             tag : 'span',
26098                             cls : 'caret'
26099                         }
26100                     ]
26101                 },
26102                 {
26103                     tag : 'ul',
26104                     cls : 'dropdown-menu'
26105                 }
26106             ]
26107             
26108         };
26109         
26110         if(this.pos == 'top'){
26111             cfg.cls += ' dropup';
26112         }
26113         
26114         if(this.isSubMenu){
26115             cfg = {
26116                 tag : 'ul',
26117                 cls : 'dropdown-menu'
26118             }
26119         }
26120         
26121         return cfg;
26122     },
26123     
26124     onRender : function(ct, position)
26125     {
26126         this.isSubMenu = ct.hasClass('dropdown-submenu');
26127         
26128         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26129     },
26130     
26131     initEvents : function() 
26132     {
26133         if(this.isSubMenu){
26134             return;
26135         }
26136         
26137         this.hidden = true;
26138         
26139         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26140         this.triggerEl.on('click', this.onTriggerPress, this);
26141         
26142         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26143         this.buttonEl.on('click', this.onClick, this);
26144         
26145     },
26146     
26147     list : function()
26148     {
26149         if(this.isSubMenu){
26150             return this.el;
26151         }
26152         
26153         return this.el.select('ul.dropdown-menu', true).first();
26154     },
26155     
26156     onClick : function(e)
26157     {
26158         this.fireEvent("click", this, e);
26159     },
26160     
26161     onTriggerPress  : function(e)
26162     {   
26163         if (this.isVisible()) {
26164             this.hide();
26165         } else {
26166             this.show();
26167         }
26168     },
26169     
26170     isVisible : function(){
26171         return !this.hidden;
26172     },
26173     
26174     show : function()
26175     {
26176         this.fireEvent("beforeshow", this);
26177         
26178         this.hidden = false;
26179         this.el.addClass('open');
26180         
26181         Roo.get(document).on("mouseup", this.onMouseUp, this);
26182         
26183         this.fireEvent("show", this);
26184         
26185         
26186     },
26187     
26188     hide : function()
26189     {
26190         this.fireEvent("beforehide", this);
26191         
26192         this.hidden = true;
26193         this.el.removeClass('open');
26194         
26195         Roo.get(document).un("mouseup", this.onMouseUp);
26196         
26197         this.fireEvent("hide", this);
26198     },
26199     
26200     onMouseUp : function()
26201     {
26202         this.hide();
26203     }
26204     
26205 });
26206
26207  
26208  /*
26209  * - LGPL
26210  *
26211  * menu item
26212  * 
26213  */
26214 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26215
26216 /**
26217  * @class Roo.bootstrap.menu.Item
26218  * @extends Roo.bootstrap.Component
26219  * Bootstrap MenuItem class
26220  * @cfg {Boolean} submenu (true | false) default false
26221  * @cfg {String} html text of the item
26222  * @cfg {String} href the link
26223  * @cfg {Boolean} disable (true | false) default false
26224  * @cfg {Boolean} preventDefault (true | false) default true
26225  * @cfg {String} icon Font awesome icon
26226  * @cfg {String} pos Submenu align to (left | right) default right 
26227  * 
26228  * 
26229  * @constructor
26230  * Create a new Item
26231  * @param {Object} config The config object
26232  */
26233
26234
26235 Roo.bootstrap.menu.Item = function(config){
26236     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26237     this.addEvents({
26238         /**
26239          * @event mouseover
26240          * Fires when the mouse is hovering over this menu
26241          * @param {Roo.bootstrap.menu.Item} this
26242          * @param {Roo.EventObject} e
26243          */
26244         mouseover : true,
26245         /**
26246          * @event mouseout
26247          * Fires when the mouse exits this menu
26248          * @param {Roo.bootstrap.menu.Item} this
26249          * @param {Roo.EventObject} e
26250          */
26251         mouseout : true,
26252         // raw events
26253         /**
26254          * @event click
26255          * The raw click event for the entire grid.
26256          * @param {Roo.EventObject} e
26257          */
26258         click : true
26259     });
26260 };
26261
26262 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26263     
26264     submenu : false,
26265     href : '',
26266     html : '',
26267     preventDefault: true,
26268     disable : false,
26269     icon : false,
26270     pos : 'right',
26271     
26272     getAutoCreate : function()
26273     {
26274         var text = [
26275             {
26276                 tag : 'span',
26277                 cls : 'roo-menu-item-text',
26278                 html : this.html
26279             }
26280         ];
26281         
26282         if(this.icon){
26283             text.unshift({
26284                 tag : 'i',
26285                 cls : 'fa ' + this.icon
26286             })
26287         }
26288         
26289         var cfg = {
26290             tag : 'li',
26291             cn : [
26292                 {
26293                     tag : 'a',
26294                     href : this.href || '#',
26295                     cn : text
26296                 }
26297             ]
26298         };
26299         
26300         if(this.disable){
26301             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26302         }
26303         
26304         if(this.submenu){
26305             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26306             
26307             if(this.pos == 'left'){
26308                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26309             }
26310         }
26311         
26312         return cfg;
26313     },
26314     
26315     initEvents : function() 
26316     {
26317         this.el.on('mouseover', this.onMouseOver, this);
26318         this.el.on('mouseout', this.onMouseOut, this);
26319         
26320         this.el.select('a', true).first().on('click', this.onClick, this);
26321         
26322     },
26323     
26324     onClick : function(e)
26325     {
26326         if(this.preventDefault){
26327             e.preventDefault();
26328         }
26329         
26330         this.fireEvent("click", this, e);
26331     },
26332     
26333     onMouseOver : function(e)
26334     {
26335         if(this.submenu && this.pos == 'left'){
26336             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26337         }
26338         
26339         this.fireEvent("mouseover", this, e);
26340     },
26341     
26342     onMouseOut : function(e)
26343     {
26344         this.fireEvent("mouseout", this, e);
26345     }
26346 });
26347
26348  
26349
26350  /*
26351  * - LGPL
26352  *
26353  * menu separator
26354  * 
26355  */
26356 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26357
26358 /**
26359  * @class Roo.bootstrap.menu.Separator
26360  * @extends Roo.bootstrap.Component
26361  * Bootstrap Separator class
26362  * 
26363  * @constructor
26364  * Create a new Separator
26365  * @param {Object} config The config object
26366  */
26367
26368
26369 Roo.bootstrap.menu.Separator = function(config){
26370     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26371 };
26372
26373 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26374     
26375     getAutoCreate : function(){
26376         var cfg = {
26377             tag : 'li',
26378             cls: 'divider'
26379         };
26380         
26381         return cfg;
26382     }
26383    
26384 });
26385
26386  
26387
26388  /*
26389  * - LGPL
26390  *
26391  * Tooltip
26392  * 
26393  */
26394
26395 /**
26396  * @class Roo.bootstrap.Tooltip
26397  * Bootstrap Tooltip class
26398  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26399  * to determine which dom element triggers the tooltip.
26400  * 
26401  * It needs to add support for additional attributes like tooltip-position
26402  * 
26403  * @constructor
26404  * Create a new Toolti
26405  * @param {Object} config The config object
26406  */
26407
26408 Roo.bootstrap.Tooltip = function(config){
26409     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26410     
26411     this.alignment = Roo.bootstrap.Tooltip.alignment;
26412     
26413     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26414         this.alignment = config.alignment;
26415     }
26416     
26417 };
26418
26419 Roo.apply(Roo.bootstrap.Tooltip, {
26420     /**
26421      * @function init initialize tooltip monitoring.
26422      * @static
26423      */
26424     currentEl : false,
26425     currentTip : false,
26426     currentRegion : false,
26427     
26428     //  init : delay?
26429     
26430     init : function()
26431     {
26432         Roo.get(document).on('mouseover', this.enter ,this);
26433         Roo.get(document).on('mouseout', this.leave, this);
26434          
26435         
26436         this.currentTip = new Roo.bootstrap.Tooltip();
26437     },
26438     
26439     enter : function(ev)
26440     {
26441         var dom = ev.getTarget();
26442         
26443         //Roo.log(['enter',dom]);
26444         var el = Roo.fly(dom);
26445         if (this.currentEl) {
26446             //Roo.log(dom);
26447             //Roo.log(this.currentEl);
26448             //Roo.log(this.currentEl.contains(dom));
26449             if (this.currentEl == el) {
26450                 return;
26451             }
26452             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26453                 return;
26454             }
26455
26456         }
26457         
26458         if (this.currentTip.el) {
26459             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26460         }    
26461         //Roo.log(ev);
26462         
26463         if(!el || el.dom == document){
26464             return;
26465         }
26466         
26467         var bindEl = el;
26468         
26469         // you can not look for children, as if el is the body.. then everythign is the child..
26470         if (!el.attr('tooltip')) { //
26471             if (!el.select("[tooltip]").elements.length) {
26472                 return;
26473             }
26474             // is the mouse over this child...?
26475             bindEl = el.select("[tooltip]").first();
26476             var xy = ev.getXY();
26477             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26478                 //Roo.log("not in region.");
26479                 return;
26480             }
26481             //Roo.log("child element over..");
26482             
26483         }
26484         this.currentEl = bindEl;
26485         this.currentTip.bind(bindEl);
26486         this.currentRegion = Roo.lib.Region.getRegion(dom);
26487         this.currentTip.enter();
26488         
26489     },
26490     leave : function(ev)
26491     {
26492         var dom = ev.getTarget();
26493         //Roo.log(['leave',dom]);
26494         if (!this.currentEl) {
26495             return;
26496         }
26497         
26498         
26499         if (dom != this.currentEl.dom) {
26500             return;
26501         }
26502         var xy = ev.getXY();
26503         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26504             return;
26505         }
26506         // only activate leave if mouse cursor is outside... bounding box..
26507         
26508         
26509         
26510         
26511         if (this.currentTip) {
26512             this.currentTip.leave();
26513         }
26514         //Roo.log('clear currentEl');
26515         this.currentEl = false;
26516         
26517         
26518     },
26519     alignment : {
26520         'left' : ['r-l', [-2,0], 'right'],
26521         'right' : ['l-r', [2,0], 'left'],
26522         'bottom' : ['t-b', [0,2], 'top'],
26523         'top' : [ 'b-t', [0,-2], 'bottom']
26524     }
26525     
26526 });
26527
26528
26529 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26530     
26531     
26532     bindEl : false,
26533     
26534     delay : null, // can be { show : 300 , hide: 500}
26535     
26536     timeout : null,
26537     
26538     hoverState : null, //???
26539     
26540     placement : 'bottom', 
26541     
26542     alignment : false,
26543     
26544     getAutoCreate : function(){
26545     
26546         var cfg = {
26547            cls : 'tooltip',
26548            role : 'tooltip',
26549            cn : [
26550                 {
26551                     cls : 'tooltip-arrow'
26552                 },
26553                 {
26554                     cls : 'tooltip-inner'
26555                 }
26556            ]
26557         };
26558         
26559         return cfg;
26560     },
26561     bind : function(el)
26562     {
26563         this.bindEl = el;
26564     },
26565       
26566     
26567     enter : function () {
26568        
26569         if (this.timeout != null) {
26570             clearTimeout(this.timeout);
26571         }
26572         
26573         this.hoverState = 'in';
26574          //Roo.log("enter - show");
26575         if (!this.delay || !this.delay.show) {
26576             this.show();
26577             return;
26578         }
26579         var _t = this;
26580         this.timeout = setTimeout(function () {
26581             if (_t.hoverState == 'in') {
26582                 _t.show();
26583             }
26584         }, this.delay.show);
26585     },
26586     leave : function()
26587     {
26588         clearTimeout(this.timeout);
26589     
26590         this.hoverState = 'out';
26591          if (!this.delay || !this.delay.hide) {
26592             this.hide();
26593             return;
26594         }
26595        
26596         var _t = this;
26597         this.timeout = setTimeout(function () {
26598             //Roo.log("leave - timeout");
26599             
26600             if (_t.hoverState == 'out') {
26601                 _t.hide();
26602                 Roo.bootstrap.Tooltip.currentEl = false;
26603             }
26604         }, delay);
26605     },
26606     
26607     show : function (msg)
26608     {
26609         if (!this.el) {
26610             this.render(document.body);
26611         }
26612         // set content.
26613         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26614         
26615         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26616         
26617         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26618         
26619         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26620         
26621         var placement = typeof this.placement == 'function' ?
26622             this.placement.call(this, this.el, on_el) :
26623             this.placement;
26624             
26625         var autoToken = /\s?auto?\s?/i;
26626         var autoPlace = autoToken.test(placement);
26627         if (autoPlace) {
26628             placement = placement.replace(autoToken, '') || 'top';
26629         }
26630         
26631         //this.el.detach()
26632         //this.el.setXY([0,0]);
26633         this.el.show();
26634         //this.el.dom.style.display='block';
26635         
26636         //this.el.appendTo(on_el);
26637         
26638         var p = this.getPosition();
26639         var box = this.el.getBox();
26640         
26641         if (autoPlace) {
26642             // fixme..
26643         }
26644         
26645         var align = this.alignment[placement];
26646         
26647         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26648         
26649         if(placement == 'top' || placement == 'bottom'){
26650             if(xy[0] < 0){
26651                 placement = 'right';
26652             }
26653             
26654             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26655                 placement = 'left';
26656             }
26657             
26658             var scroll = Roo.select('body', true).first().getScroll();
26659             
26660             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26661                 placement = 'top';
26662             }
26663             
26664             align = this.alignment[placement];
26665         }
26666         
26667         this.el.alignTo(this.bindEl, align[0],align[1]);
26668         //var arrow = this.el.select('.arrow',true).first();
26669         //arrow.set(align[2], 
26670         
26671         this.el.addClass(placement);
26672         
26673         this.el.addClass('in fade');
26674         
26675         this.hoverState = null;
26676         
26677         if (this.el.hasClass('fade')) {
26678             // fade it?
26679         }
26680         
26681     },
26682     hide : function()
26683     {
26684          
26685         if (!this.el) {
26686             return;
26687         }
26688         //this.el.setXY([0,0]);
26689         this.el.removeClass('in');
26690         //this.el.hide();
26691         
26692     }
26693     
26694 });
26695  
26696
26697  /*
26698  * - LGPL
26699  *
26700  * Location Picker
26701  * 
26702  */
26703
26704 /**
26705  * @class Roo.bootstrap.LocationPicker
26706  * @extends Roo.bootstrap.Component
26707  * Bootstrap LocationPicker class
26708  * @cfg {Number} latitude Position when init default 0
26709  * @cfg {Number} longitude Position when init default 0
26710  * @cfg {Number} zoom default 15
26711  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26712  * @cfg {Boolean} mapTypeControl default false
26713  * @cfg {Boolean} disableDoubleClickZoom default false
26714  * @cfg {Boolean} scrollwheel default true
26715  * @cfg {Boolean} streetViewControl default false
26716  * @cfg {Number} radius default 0
26717  * @cfg {String} locationName
26718  * @cfg {Boolean} draggable default true
26719  * @cfg {Boolean} enableAutocomplete default false
26720  * @cfg {Boolean} enableReverseGeocode default true
26721  * @cfg {String} markerTitle
26722  * 
26723  * @constructor
26724  * Create a new LocationPicker
26725  * @param {Object} config The config object
26726  */
26727
26728
26729 Roo.bootstrap.LocationPicker = function(config){
26730     
26731     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26732     
26733     this.addEvents({
26734         /**
26735          * @event initial
26736          * Fires when the picker initialized.
26737          * @param {Roo.bootstrap.LocationPicker} this
26738          * @param {Google Location} location
26739          */
26740         initial : true,
26741         /**
26742          * @event positionchanged
26743          * Fires when the picker position changed.
26744          * @param {Roo.bootstrap.LocationPicker} this
26745          * @param {Google Location} location
26746          */
26747         positionchanged : true,
26748         /**
26749          * @event resize
26750          * Fires when the map resize.
26751          * @param {Roo.bootstrap.LocationPicker} this
26752          */
26753         resize : true,
26754         /**
26755          * @event show
26756          * Fires when the map show.
26757          * @param {Roo.bootstrap.LocationPicker} this
26758          */
26759         show : true,
26760         /**
26761          * @event hide
26762          * Fires when the map hide.
26763          * @param {Roo.bootstrap.LocationPicker} this
26764          */
26765         hide : true,
26766         /**
26767          * @event mapClick
26768          * Fires when click the map.
26769          * @param {Roo.bootstrap.LocationPicker} this
26770          * @param {Map event} e
26771          */
26772         mapClick : true,
26773         /**
26774          * @event mapRightClick
26775          * Fires when right click the map.
26776          * @param {Roo.bootstrap.LocationPicker} this
26777          * @param {Map event} e
26778          */
26779         mapRightClick : true,
26780         /**
26781          * @event markerClick
26782          * Fires when click the marker.
26783          * @param {Roo.bootstrap.LocationPicker} this
26784          * @param {Map event} e
26785          */
26786         markerClick : true,
26787         /**
26788          * @event markerRightClick
26789          * Fires when right click the marker.
26790          * @param {Roo.bootstrap.LocationPicker} this
26791          * @param {Map event} e
26792          */
26793         markerRightClick : true,
26794         /**
26795          * @event OverlayViewDraw
26796          * Fires when OverlayView Draw
26797          * @param {Roo.bootstrap.LocationPicker} this
26798          */
26799         OverlayViewDraw : true,
26800         /**
26801          * @event OverlayViewOnAdd
26802          * Fires when OverlayView Draw
26803          * @param {Roo.bootstrap.LocationPicker} this
26804          */
26805         OverlayViewOnAdd : true,
26806         /**
26807          * @event OverlayViewOnRemove
26808          * Fires when OverlayView Draw
26809          * @param {Roo.bootstrap.LocationPicker} this
26810          */
26811         OverlayViewOnRemove : true,
26812         /**
26813          * @event OverlayViewShow
26814          * Fires when OverlayView Draw
26815          * @param {Roo.bootstrap.LocationPicker} this
26816          * @param {Pixel} cpx
26817          */
26818         OverlayViewShow : true,
26819         /**
26820          * @event OverlayViewHide
26821          * Fires when OverlayView Draw
26822          * @param {Roo.bootstrap.LocationPicker} this
26823          */
26824         OverlayViewHide : true,
26825         /**
26826          * @event loadexception
26827          * Fires when load google lib failed.
26828          * @param {Roo.bootstrap.LocationPicker} this
26829          */
26830         loadexception : true
26831     });
26832         
26833 };
26834
26835 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26836     
26837     gMapContext: false,
26838     
26839     latitude: 0,
26840     longitude: 0,
26841     zoom: 15,
26842     mapTypeId: false,
26843     mapTypeControl: false,
26844     disableDoubleClickZoom: false,
26845     scrollwheel: true,
26846     streetViewControl: false,
26847     radius: 0,
26848     locationName: '',
26849     draggable: true,
26850     enableAutocomplete: false,
26851     enableReverseGeocode: true,
26852     markerTitle: '',
26853     
26854     getAutoCreate: function()
26855     {
26856
26857         var cfg = {
26858             tag: 'div',
26859             cls: 'roo-location-picker'
26860         };
26861         
26862         return cfg
26863     },
26864     
26865     initEvents: function(ct, position)
26866     {       
26867         if(!this.el.getWidth() || this.isApplied()){
26868             return;
26869         }
26870         
26871         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26872         
26873         this.initial();
26874     },
26875     
26876     initial: function()
26877     {
26878         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26879             this.fireEvent('loadexception', this);
26880             return;
26881         }
26882         
26883         if(!this.mapTypeId){
26884             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26885         }
26886         
26887         this.gMapContext = this.GMapContext();
26888         
26889         this.initOverlayView();
26890         
26891         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26892         
26893         var _this = this;
26894                 
26895         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26896             _this.setPosition(_this.gMapContext.marker.position);
26897         });
26898         
26899         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26900             _this.fireEvent('mapClick', this, event);
26901             
26902         });
26903
26904         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26905             _this.fireEvent('mapRightClick', this, event);
26906             
26907         });
26908         
26909         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26910             _this.fireEvent('markerClick', this, event);
26911             
26912         });
26913
26914         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26915             _this.fireEvent('markerRightClick', this, event);
26916             
26917         });
26918         
26919         this.setPosition(this.gMapContext.location);
26920         
26921         this.fireEvent('initial', this, this.gMapContext.location);
26922     },
26923     
26924     initOverlayView: function()
26925     {
26926         var _this = this;
26927         
26928         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26929             
26930             draw: function()
26931             {
26932                 _this.fireEvent('OverlayViewDraw', _this);
26933             },
26934             
26935             onAdd: function()
26936             {
26937                 _this.fireEvent('OverlayViewOnAdd', _this);
26938             },
26939             
26940             onRemove: function()
26941             {
26942                 _this.fireEvent('OverlayViewOnRemove', _this);
26943             },
26944             
26945             show: function(cpx)
26946             {
26947                 _this.fireEvent('OverlayViewShow', _this, cpx);
26948             },
26949             
26950             hide: function()
26951             {
26952                 _this.fireEvent('OverlayViewHide', _this);
26953             }
26954             
26955         });
26956     },
26957     
26958     fromLatLngToContainerPixel: function(event)
26959     {
26960         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26961     },
26962     
26963     isApplied: function() 
26964     {
26965         return this.getGmapContext() == false ? false : true;
26966     },
26967     
26968     getGmapContext: function() 
26969     {
26970         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26971     },
26972     
26973     GMapContext: function() 
26974     {
26975         var position = new google.maps.LatLng(this.latitude, this.longitude);
26976         
26977         var _map = new google.maps.Map(this.el.dom, {
26978             center: position,
26979             zoom: this.zoom,
26980             mapTypeId: this.mapTypeId,
26981             mapTypeControl: this.mapTypeControl,
26982             disableDoubleClickZoom: this.disableDoubleClickZoom,
26983             scrollwheel: this.scrollwheel,
26984             streetViewControl: this.streetViewControl,
26985             locationName: this.locationName,
26986             draggable: this.draggable,
26987             enableAutocomplete: this.enableAutocomplete,
26988             enableReverseGeocode: this.enableReverseGeocode
26989         });
26990         
26991         var _marker = new google.maps.Marker({
26992             position: position,
26993             map: _map,
26994             title: this.markerTitle,
26995             draggable: this.draggable
26996         });
26997         
26998         return {
26999             map: _map,
27000             marker: _marker,
27001             circle: null,
27002             location: position,
27003             radius: this.radius,
27004             locationName: this.locationName,
27005             addressComponents: {
27006                 formatted_address: null,
27007                 addressLine1: null,
27008                 addressLine2: null,
27009                 streetName: null,
27010                 streetNumber: null,
27011                 city: null,
27012                 district: null,
27013                 state: null,
27014                 stateOrProvince: null
27015             },
27016             settings: this,
27017             domContainer: this.el.dom,
27018             geodecoder: new google.maps.Geocoder()
27019         };
27020     },
27021     
27022     drawCircle: function(center, radius, options) 
27023     {
27024         if (this.gMapContext.circle != null) {
27025             this.gMapContext.circle.setMap(null);
27026         }
27027         if (radius > 0) {
27028             radius *= 1;
27029             options = Roo.apply({}, options, {
27030                 strokeColor: "#0000FF",
27031                 strokeOpacity: .35,
27032                 strokeWeight: 2,
27033                 fillColor: "#0000FF",
27034                 fillOpacity: .2
27035             });
27036             
27037             options.map = this.gMapContext.map;
27038             options.radius = radius;
27039             options.center = center;
27040             this.gMapContext.circle = new google.maps.Circle(options);
27041             return this.gMapContext.circle;
27042         }
27043         
27044         return null;
27045     },
27046     
27047     setPosition: function(location) 
27048     {
27049         this.gMapContext.location = location;
27050         this.gMapContext.marker.setPosition(location);
27051         this.gMapContext.map.panTo(location);
27052         this.drawCircle(location, this.gMapContext.radius, {});
27053         
27054         var _this = this;
27055         
27056         if (this.gMapContext.settings.enableReverseGeocode) {
27057             this.gMapContext.geodecoder.geocode({
27058                 latLng: this.gMapContext.location
27059             }, function(results, status) {
27060                 
27061                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27062                     _this.gMapContext.locationName = results[0].formatted_address;
27063                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27064                     
27065                     _this.fireEvent('positionchanged', this, location);
27066                 }
27067             });
27068             
27069             return;
27070         }
27071         
27072         this.fireEvent('positionchanged', this, location);
27073     },
27074     
27075     resize: function()
27076     {
27077         google.maps.event.trigger(this.gMapContext.map, "resize");
27078         
27079         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27080         
27081         this.fireEvent('resize', this);
27082     },
27083     
27084     setPositionByLatLng: function(latitude, longitude)
27085     {
27086         this.setPosition(new google.maps.LatLng(latitude, longitude));
27087     },
27088     
27089     getCurrentPosition: function() 
27090     {
27091         return {
27092             latitude: this.gMapContext.location.lat(),
27093             longitude: this.gMapContext.location.lng()
27094         };
27095     },
27096     
27097     getAddressName: function() 
27098     {
27099         return this.gMapContext.locationName;
27100     },
27101     
27102     getAddressComponents: function() 
27103     {
27104         return this.gMapContext.addressComponents;
27105     },
27106     
27107     address_component_from_google_geocode: function(address_components) 
27108     {
27109         var result = {};
27110         
27111         for (var i = 0; i < address_components.length; i++) {
27112             var component = address_components[i];
27113             if (component.types.indexOf("postal_code") >= 0) {
27114                 result.postalCode = component.short_name;
27115             } else if (component.types.indexOf("street_number") >= 0) {
27116                 result.streetNumber = component.short_name;
27117             } else if (component.types.indexOf("route") >= 0) {
27118                 result.streetName = component.short_name;
27119             } else if (component.types.indexOf("neighborhood") >= 0) {
27120                 result.city = component.short_name;
27121             } else if (component.types.indexOf("locality") >= 0) {
27122                 result.city = component.short_name;
27123             } else if (component.types.indexOf("sublocality") >= 0) {
27124                 result.district = component.short_name;
27125             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27126                 result.stateOrProvince = component.short_name;
27127             } else if (component.types.indexOf("country") >= 0) {
27128                 result.country = component.short_name;
27129             }
27130         }
27131         
27132         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27133         result.addressLine2 = "";
27134         return result;
27135     },
27136     
27137     setZoomLevel: function(zoom)
27138     {
27139         this.gMapContext.map.setZoom(zoom);
27140     },
27141     
27142     show: function()
27143     {
27144         if(!this.el){
27145             return;
27146         }
27147         
27148         this.el.show();
27149         
27150         this.resize();
27151         
27152         this.fireEvent('show', this);
27153     },
27154     
27155     hide: function()
27156     {
27157         if(!this.el){
27158             return;
27159         }
27160         
27161         this.el.hide();
27162         
27163         this.fireEvent('hide', this);
27164     }
27165     
27166 });
27167
27168 Roo.apply(Roo.bootstrap.LocationPicker, {
27169     
27170     OverlayView : function(map, options)
27171     {
27172         options = options || {};
27173         
27174         this.setMap(map);
27175     }
27176     
27177     
27178 });/*
27179  * - LGPL
27180  *
27181  * Alert
27182  * 
27183  */
27184
27185 /**
27186  * @class Roo.bootstrap.Alert
27187  * @extends Roo.bootstrap.Component
27188  * Bootstrap Alert class
27189  * @cfg {String} title The title of alert
27190  * @cfg {String} html The content of alert
27191  * @cfg {String} weight (  success | info | warning | danger )
27192  * @cfg {String} faicon font-awesomeicon
27193  * 
27194  * @constructor
27195  * Create a new alert
27196  * @param {Object} config The config object
27197  */
27198
27199
27200 Roo.bootstrap.Alert = function(config){
27201     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27202     
27203 };
27204
27205 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27206     
27207     title: '',
27208     html: '',
27209     weight: false,
27210     faicon: false,
27211     
27212     getAutoCreate : function()
27213     {
27214         
27215         var cfg = {
27216             tag : 'div',
27217             cls : 'alert',
27218             cn : [
27219                 {
27220                     tag : 'i',
27221                     cls : 'roo-alert-icon'
27222                     
27223                 },
27224                 {
27225                     tag : 'b',
27226                     cls : 'roo-alert-title',
27227                     html : this.title
27228                 },
27229                 {
27230                     tag : 'span',
27231                     cls : 'roo-alert-text',
27232                     html : this.html
27233                 }
27234             ]
27235         };
27236         
27237         if(this.faicon){
27238             cfg.cn[0].cls += ' fa ' + this.faicon;
27239         }
27240         
27241         if(this.weight){
27242             cfg.cls += ' alert-' + this.weight;
27243         }
27244         
27245         return cfg;
27246     },
27247     
27248     initEvents: function() 
27249     {
27250         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27251     },
27252     
27253     setTitle : function(str)
27254     {
27255         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27256     },
27257     
27258     setText : function(str)
27259     {
27260         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27261     },
27262     
27263     setWeight : function(weight)
27264     {
27265         if(this.weight){
27266             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27267         }
27268         
27269         this.weight = weight;
27270         
27271         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27272     },
27273     
27274     setIcon : function(icon)
27275     {
27276         if(this.faicon){
27277             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27278         }
27279         
27280         this.faicon = icon;
27281         
27282         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27283     },
27284     
27285     hide: function() 
27286     {
27287         this.el.hide();   
27288     },
27289     
27290     show: function() 
27291     {  
27292         this.el.show();   
27293     }
27294     
27295 });
27296
27297  
27298 /*
27299 * Licence: LGPL
27300 */
27301
27302 /**
27303  * @class Roo.bootstrap.UploadCropbox
27304  * @extends Roo.bootstrap.Component
27305  * Bootstrap UploadCropbox class
27306  * @cfg {String} emptyText show when image has been loaded
27307  * @cfg {String} rotateNotify show when image too small to rotate
27308  * @cfg {Number} errorTimeout default 3000
27309  * @cfg {Number} minWidth default 300
27310  * @cfg {Number} minHeight default 300
27311  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27312  * @cfg {Boolean} isDocument (true|false) default false
27313  * @cfg {String} url action url
27314  * @cfg {String} paramName default 'imageUpload'
27315  * @cfg {String} method default POST
27316  * @cfg {Boolean} loadMask (true|false) default true
27317  * @cfg {Boolean} loadingText default 'Loading...'
27318  * 
27319  * @constructor
27320  * Create a new UploadCropbox
27321  * @param {Object} config The config object
27322  */
27323
27324 Roo.bootstrap.UploadCropbox = function(config){
27325     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27326     
27327     this.addEvents({
27328         /**
27329          * @event beforeselectfile
27330          * Fire before select file
27331          * @param {Roo.bootstrap.UploadCropbox} this
27332          */
27333         "beforeselectfile" : true,
27334         /**
27335          * @event initial
27336          * Fire after initEvent
27337          * @param {Roo.bootstrap.UploadCropbox} this
27338          */
27339         "initial" : true,
27340         /**
27341          * @event crop
27342          * Fire after initEvent
27343          * @param {Roo.bootstrap.UploadCropbox} this
27344          * @param {String} data
27345          */
27346         "crop" : true,
27347         /**
27348          * @event prepare
27349          * Fire when preparing the file data
27350          * @param {Roo.bootstrap.UploadCropbox} this
27351          * @param {Object} file
27352          */
27353         "prepare" : true,
27354         /**
27355          * @event exception
27356          * Fire when get exception
27357          * @param {Roo.bootstrap.UploadCropbox} this
27358          * @param {XMLHttpRequest} xhr
27359          */
27360         "exception" : true,
27361         /**
27362          * @event beforeloadcanvas
27363          * Fire before load the canvas
27364          * @param {Roo.bootstrap.UploadCropbox} this
27365          * @param {String} src
27366          */
27367         "beforeloadcanvas" : true,
27368         /**
27369          * @event trash
27370          * Fire when trash image
27371          * @param {Roo.bootstrap.UploadCropbox} this
27372          */
27373         "trash" : true,
27374         /**
27375          * @event download
27376          * Fire when download the image
27377          * @param {Roo.bootstrap.UploadCropbox} this
27378          */
27379         "download" : true,
27380         /**
27381          * @event footerbuttonclick
27382          * Fire when footerbuttonclick
27383          * @param {Roo.bootstrap.UploadCropbox} this
27384          * @param {String} type
27385          */
27386         "footerbuttonclick" : true,
27387         /**
27388          * @event resize
27389          * Fire when resize
27390          * @param {Roo.bootstrap.UploadCropbox} this
27391          */
27392         "resize" : true,
27393         /**
27394          * @event rotate
27395          * Fire when rotate the image
27396          * @param {Roo.bootstrap.UploadCropbox} this
27397          * @param {String} pos
27398          */
27399         "rotate" : true,
27400         /**
27401          * @event inspect
27402          * Fire when inspect the file
27403          * @param {Roo.bootstrap.UploadCropbox} this
27404          * @param {Object} file
27405          */
27406         "inspect" : true,
27407         /**
27408          * @event upload
27409          * Fire when xhr upload the file
27410          * @param {Roo.bootstrap.UploadCropbox} this
27411          * @param {Object} data
27412          */
27413         "upload" : true,
27414         /**
27415          * @event arrange
27416          * Fire when arrange the file data
27417          * @param {Roo.bootstrap.UploadCropbox} this
27418          * @param {Object} formData
27419          */
27420         "arrange" : true
27421     });
27422     
27423     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27424 };
27425
27426 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27427     
27428     emptyText : 'Click to upload image',
27429     rotateNotify : 'Image is too small to rotate',
27430     errorTimeout : 3000,
27431     scale : 0,
27432     baseScale : 1,
27433     rotate : 0,
27434     dragable : false,
27435     pinching : false,
27436     mouseX : 0,
27437     mouseY : 0,
27438     cropData : false,
27439     minWidth : 300,
27440     minHeight : 300,
27441     file : false,
27442     exif : {},
27443     baseRotate : 1,
27444     cropType : 'image/jpeg',
27445     buttons : false,
27446     canvasLoaded : false,
27447     isDocument : false,
27448     method : 'POST',
27449     paramName : 'imageUpload',
27450     loadMask : true,
27451     loadingText : 'Loading...',
27452     maskEl : false,
27453     
27454     getAutoCreate : function()
27455     {
27456         var cfg = {
27457             tag : 'div',
27458             cls : 'roo-upload-cropbox',
27459             cn : [
27460                 {
27461                     tag : 'input',
27462                     cls : 'roo-upload-cropbox-selector',
27463                     type : 'file'
27464                 },
27465                 {
27466                     tag : 'div',
27467                     cls : 'roo-upload-cropbox-body',
27468                     style : 'cursor:pointer',
27469                     cn : [
27470                         {
27471                             tag : 'div',
27472                             cls : 'roo-upload-cropbox-preview'
27473                         },
27474                         {
27475                             tag : 'div',
27476                             cls : 'roo-upload-cropbox-thumb'
27477                         },
27478                         {
27479                             tag : 'div',
27480                             cls : 'roo-upload-cropbox-empty-notify',
27481                             html : this.emptyText
27482                         },
27483                         {
27484                             tag : 'div',
27485                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27486                             html : this.rotateNotify
27487                         }
27488                     ]
27489                 },
27490                 {
27491                     tag : 'div',
27492                     cls : 'roo-upload-cropbox-footer',
27493                     cn : {
27494                         tag : 'div',
27495                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27496                         cn : []
27497                     }
27498                 }
27499             ]
27500         };
27501         
27502         return cfg;
27503     },
27504     
27505     onRender : function(ct, position)
27506     {
27507         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27508         
27509         if (this.buttons.length) {
27510             
27511             Roo.each(this.buttons, function(bb) {
27512                 
27513                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27514                 
27515                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27516                 
27517             }, this);
27518         }
27519         
27520         if(this.loadMask){
27521             this.maskEl = this.el;
27522         }
27523     },
27524     
27525     initEvents : function()
27526     {
27527         this.urlAPI = (window.createObjectURL && window) || 
27528                                 (window.URL && URL.revokeObjectURL && URL) || 
27529                                 (window.webkitURL && webkitURL);
27530                         
27531         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27532         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27533         
27534         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27535         this.selectorEl.hide();
27536         
27537         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27538         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27539         
27540         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27541         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27542         this.thumbEl.hide();
27543         
27544         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27545         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27546         
27547         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27548         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27549         this.errorEl.hide();
27550         
27551         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27552         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27553         this.footerEl.hide();
27554         
27555         this.setThumbBoxSize();
27556         
27557         this.bind();
27558         
27559         this.resize();
27560         
27561         this.fireEvent('initial', this);
27562     },
27563
27564     bind : function()
27565     {
27566         var _this = this;
27567         
27568         window.addEventListener("resize", function() { _this.resize(); } );
27569         
27570         this.bodyEl.on('click', this.beforeSelectFile, this);
27571         
27572         if(Roo.isTouch){
27573             this.bodyEl.on('touchstart', this.onTouchStart, this);
27574             this.bodyEl.on('touchmove', this.onTouchMove, this);
27575             this.bodyEl.on('touchend', this.onTouchEnd, this);
27576         }
27577         
27578         if(!Roo.isTouch){
27579             this.bodyEl.on('mousedown', this.onMouseDown, this);
27580             this.bodyEl.on('mousemove', this.onMouseMove, this);
27581             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27582             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27583             Roo.get(document).on('mouseup', this.onMouseUp, this);
27584         }
27585         
27586         this.selectorEl.on('change', this.onFileSelected, this);
27587     },
27588     
27589     reset : function()
27590     {    
27591         this.scale = 0;
27592         this.baseScale = 1;
27593         this.rotate = 0;
27594         this.baseRotate = 1;
27595         this.dragable = false;
27596         this.pinching = false;
27597         this.mouseX = 0;
27598         this.mouseY = 0;
27599         this.cropData = false;
27600         this.notifyEl.dom.innerHTML = this.emptyText;
27601         
27602         this.selectorEl.dom.value = '';
27603         
27604     },
27605     
27606     resize : function()
27607     {
27608         if(this.fireEvent('resize', this) != false){
27609             this.setThumbBoxPosition();
27610             this.setCanvasPosition();
27611         }
27612     },
27613     
27614     onFooterButtonClick : function(e, el, o, type)
27615     {
27616         switch (type) {
27617             case 'rotate-left' :
27618                 this.onRotateLeft(e);
27619                 break;
27620             case 'rotate-right' :
27621                 this.onRotateRight(e);
27622                 break;
27623             case 'picture' :
27624                 this.beforeSelectFile(e);
27625                 break;
27626             case 'trash' :
27627                 this.trash(e);
27628                 break;
27629             case 'crop' :
27630                 this.crop(e);
27631                 break;
27632             case 'download' :
27633                 this.download(e);
27634                 break;
27635             default :
27636                 break;
27637         }
27638         
27639         this.fireEvent('footerbuttonclick', this, type);
27640     },
27641     
27642     beforeSelectFile : function(e)
27643     {
27644         e.preventDefault();
27645         
27646         if(this.fireEvent('beforeselectfile', this) != false){
27647             this.selectorEl.dom.click();
27648         }
27649     },
27650     
27651     onFileSelected : function(e)
27652     {
27653         e.preventDefault();
27654         
27655         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27656             return;
27657         }
27658         
27659         var file = this.selectorEl.dom.files[0];
27660         
27661         if(this.fireEvent('inspect', this, file) != false){
27662             this.prepare(file);
27663         }
27664         
27665     },
27666     
27667     trash : function(e)
27668     {
27669         this.fireEvent('trash', this);
27670     },
27671     
27672     download : function(e)
27673     {
27674         this.fireEvent('download', this);
27675     },
27676     
27677     loadCanvas : function(src)
27678     {   
27679         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27680             
27681             this.reset();
27682             
27683             this.imageEl = document.createElement('img');
27684             
27685             var _this = this;
27686             
27687             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27688             
27689             this.imageEl.src = src;
27690         }
27691     },
27692     
27693     onLoadCanvas : function()
27694     {   
27695         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27696         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27697         
27698         this.bodyEl.un('click', this.beforeSelectFile, this);
27699         
27700         this.notifyEl.hide();
27701         this.thumbEl.show();
27702         this.footerEl.show();
27703         
27704         this.baseRotateLevel();
27705         
27706         if(this.isDocument){
27707             this.setThumbBoxSize();
27708         }
27709         
27710         this.setThumbBoxPosition();
27711         
27712         this.baseScaleLevel();
27713         
27714         this.draw();
27715         
27716         this.resize();
27717         
27718         this.canvasLoaded = true;
27719         
27720         if(this.loadMask){
27721             this.maskEl.unmask();
27722         }
27723         
27724     },
27725     
27726     setCanvasPosition : function()
27727     {   
27728         if(!this.canvasEl){
27729             return;
27730         }
27731         
27732         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27733         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27734         
27735         this.previewEl.setLeft(pw);
27736         this.previewEl.setTop(ph);
27737         
27738     },
27739     
27740     onMouseDown : function(e)
27741     {   
27742         e.stopEvent();
27743         
27744         this.dragable = true;
27745         this.pinching = false;
27746         
27747         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27748             this.dragable = false;
27749             return;
27750         }
27751         
27752         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27753         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27754         
27755     },
27756     
27757     onMouseMove : function(e)
27758     {   
27759         e.stopEvent();
27760         
27761         if(!this.canvasLoaded){
27762             return;
27763         }
27764         
27765         if (!this.dragable){
27766             return;
27767         }
27768         
27769         var minX = Math.ceil(this.thumbEl.getLeft(true));
27770         var minY = Math.ceil(this.thumbEl.getTop(true));
27771         
27772         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27773         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27774         
27775         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27776         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27777         
27778         x = x - this.mouseX;
27779         y = y - this.mouseY;
27780         
27781         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27782         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27783         
27784         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27785         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27786         
27787         this.previewEl.setLeft(bgX);
27788         this.previewEl.setTop(bgY);
27789         
27790         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27791         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27792     },
27793     
27794     onMouseUp : function(e)
27795     {   
27796         e.stopEvent();
27797         
27798         this.dragable = false;
27799     },
27800     
27801     onMouseWheel : function(e)
27802     {   
27803         e.stopEvent();
27804         
27805         this.startScale = this.scale;
27806         
27807         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27808         
27809         if(!this.zoomable()){
27810             this.scale = this.startScale;
27811             return;
27812         }
27813         
27814         this.draw();
27815         
27816         return;
27817     },
27818     
27819     zoomable : function()
27820     {
27821         var minScale = this.thumbEl.getWidth() / this.minWidth;
27822         
27823         if(this.minWidth < this.minHeight){
27824             minScale = this.thumbEl.getHeight() / this.minHeight;
27825         }
27826         
27827         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27828         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27829         
27830         if(
27831                 this.isDocument &&
27832                 (this.rotate == 0 || this.rotate == 180) && 
27833                 (
27834                     width > this.imageEl.OriginWidth || 
27835                     height > this.imageEl.OriginHeight ||
27836                     (width < this.minWidth && height < this.minHeight)
27837                 )
27838         ){
27839             return false;
27840         }
27841         
27842         if(
27843                 this.isDocument &&
27844                 (this.rotate == 90 || this.rotate == 270) && 
27845                 (
27846                     width > this.imageEl.OriginWidth || 
27847                     height > this.imageEl.OriginHeight ||
27848                     (width < this.minHeight && height < this.minWidth)
27849                 )
27850         ){
27851             return false;
27852         }
27853         
27854         if(
27855                 !this.isDocument &&
27856                 (this.rotate == 0 || this.rotate == 180) && 
27857                 (
27858                     width < this.minWidth || 
27859                     width > this.imageEl.OriginWidth || 
27860                     height < this.minHeight || 
27861                     height > this.imageEl.OriginHeight
27862                 )
27863         ){
27864             return false;
27865         }
27866         
27867         if(
27868                 !this.isDocument &&
27869                 (this.rotate == 90 || this.rotate == 270) && 
27870                 (
27871                     width < this.minHeight || 
27872                     width > this.imageEl.OriginWidth || 
27873                     height < this.minWidth || 
27874                     height > this.imageEl.OriginHeight
27875                 )
27876         ){
27877             return false;
27878         }
27879         
27880         return true;
27881         
27882     },
27883     
27884     onRotateLeft : function(e)
27885     {   
27886         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27887             
27888             var minScale = this.thumbEl.getWidth() / this.minWidth;
27889             
27890             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27891             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27892             
27893             this.startScale = this.scale;
27894             
27895             while (this.getScaleLevel() < minScale){
27896             
27897                 this.scale = this.scale + 1;
27898                 
27899                 if(!this.zoomable()){
27900                     break;
27901                 }
27902                 
27903                 if(
27904                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27905                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27906                 ){
27907                     continue;
27908                 }
27909                 
27910                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27911
27912                 this.draw();
27913                 
27914                 return;
27915             }
27916             
27917             this.scale = this.startScale;
27918             
27919             this.onRotateFail();
27920             
27921             return false;
27922         }
27923         
27924         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27925
27926         if(this.isDocument){
27927             this.setThumbBoxSize();
27928             this.setThumbBoxPosition();
27929             this.setCanvasPosition();
27930         }
27931         
27932         this.draw();
27933         
27934         this.fireEvent('rotate', this, 'left');
27935         
27936     },
27937     
27938     onRotateRight : function(e)
27939     {
27940         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27941             
27942             var minScale = this.thumbEl.getWidth() / this.minWidth;
27943         
27944             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27945             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27946             
27947             this.startScale = this.scale;
27948             
27949             while (this.getScaleLevel() < minScale){
27950             
27951                 this.scale = this.scale + 1;
27952                 
27953                 if(!this.zoomable()){
27954                     break;
27955                 }
27956                 
27957                 if(
27958                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27959                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27960                 ){
27961                     continue;
27962                 }
27963                 
27964                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27965
27966                 this.draw();
27967                 
27968                 return;
27969             }
27970             
27971             this.scale = this.startScale;
27972             
27973             this.onRotateFail();
27974             
27975             return false;
27976         }
27977         
27978         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27979
27980         if(this.isDocument){
27981             this.setThumbBoxSize();
27982             this.setThumbBoxPosition();
27983             this.setCanvasPosition();
27984         }
27985         
27986         this.draw();
27987         
27988         this.fireEvent('rotate', this, 'right');
27989     },
27990     
27991     onRotateFail : function()
27992     {
27993         this.errorEl.show(true);
27994         
27995         var _this = this;
27996         
27997         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27998     },
27999     
28000     draw : function()
28001     {
28002         this.previewEl.dom.innerHTML = '';
28003         
28004         var canvasEl = document.createElement("canvas");
28005         
28006         var contextEl = canvasEl.getContext("2d");
28007         
28008         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28009         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28010         var center = this.imageEl.OriginWidth / 2;
28011         
28012         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28013             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28014             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28015             center = this.imageEl.OriginHeight / 2;
28016         }
28017         
28018         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28019         
28020         contextEl.translate(center, center);
28021         contextEl.rotate(this.rotate * Math.PI / 180);
28022
28023         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28024         
28025         this.canvasEl = document.createElement("canvas");
28026         
28027         this.contextEl = this.canvasEl.getContext("2d");
28028         
28029         switch (this.rotate) {
28030             case 0 :
28031                 
28032                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28033                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28034                 
28035                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28036                 
28037                 break;
28038             case 90 : 
28039                 
28040                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28041                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28042                 
28043                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28044                     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);
28045                     break;
28046                 }
28047                 
28048                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28049                 
28050                 break;
28051             case 180 :
28052                 
28053                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28054                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28055                 
28056                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28057                     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);
28058                     break;
28059                 }
28060                 
28061                 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);
28062                 
28063                 break;
28064             case 270 :
28065                 
28066                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28067                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28068         
28069                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28070                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28071                     break;
28072                 }
28073                 
28074                 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);
28075                 
28076                 break;
28077             default : 
28078                 break;
28079         }
28080         
28081         this.previewEl.appendChild(this.canvasEl);
28082         
28083         this.setCanvasPosition();
28084     },
28085     
28086     crop : function()
28087     {
28088         if(!this.canvasLoaded){
28089             return;
28090         }
28091         
28092         var imageCanvas = document.createElement("canvas");
28093         
28094         var imageContext = imageCanvas.getContext("2d");
28095         
28096         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28097         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28098         
28099         var center = imageCanvas.width / 2;
28100         
28101         imageContext.translate(center, center);
28102         
28103         imageContext.rotate(this.rotate * Math.PI / 180);
28104         
28105         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28106         
28107         var canvas = document.createElement("canvas");
28108         
28109         var context = canvas.getContext("2d");
28110                 
28111         canvas.width = this.minWidth;
28112         canvas.height = this.minHeight;
28113
28114         switch (this.rotate) {
28115             case 0 :
28116                 
28117                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28118                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28119                 
28120                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28121                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28122                 
28123                 var targetWidth = this.minWidth - 2 * x;
28124                 var targetHeight = this.minHeight - 2 * y;
28125                 
28126                 var scale = 1;
28127                 
28128                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28129                     scale = targetWidth / width;
28130                 }
28131                 
28132                 if(x > 0 && y == 0){
28133                     scale = targetHeight / height;
28134                 }
28135                 
28136                 if(x > 0 && y > 0){
28137                     scale = targetWidth / width;
28138                     
28139                     if(width < height){
28140                         scale = targetHeight / height;
28141                     }
28142                 }
28143                 
28144                 context.scale(scale, scale);
28145                 
28146                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28147                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28148
28149                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28150                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28151
28152                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28153                 
28154                 break;
28155             case 90 : 
28156                 
28157                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28158                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28159                 
28160                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28161                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28162                 
28163                 var targetWidth = this.minWidth - 2 * x;
28164                 var targetHeight = this.minHeight - 2 * y;
28165                 
28166                 var scale = 1;
28167                 
28168                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28169                     scale = targetWidth / width;
28170                 }
28171                 
28172                 if(x > 0 && y == 0){
28173                     scale = targetHeight / height;
28174                 }
28175                 
28176                 if(x > 0 && y > 0){
28177                     scale = targetWidth / width;
28178                     
28179                     if(width < height){
28180                         scale = targetHeight / height;
28181                     }
28182                 }
28183                 
28184                 context.scale(scale, scale);
28185                 
28186                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28187                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28188
28189                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28190                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28191                 
28192                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28193                 
28194                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28195                 
28196                 break;
28197             case 180 :
28198                 
28199                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28200                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28201                 
28202                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28203                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28204                 
28205                 var targetWidth = this.minWidth - 2 * x;
28206                 var targetHeight = this.minHeight - 2 * y;
28207                 
28208                 var scale = 1;
28209                 
28210                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28211                     scale = targetWidth / width;
28212                 }
28213                 
28214                 if(x > 0 && y == 0){
28215                     scale = targetHeight / height;
28216                 }
28217                 
28218                 if(x > 0 && y > 0){
28219                     scale = targetWidth / width;
28220                     
28221                     if(width < height){
28222                         scale = targetHeight / height;
28223                     }
28224                 }
28225                 
28226                 context.scale(scale, scale);
28227                 
28228                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28229                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28230
28231                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28232                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28233
28234                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28235                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28236                 
28237                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28238                 
28239                 break;
28240             case 270 :
28241                 
28242                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28243                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28244                 
28245                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28246                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28247                 
28248                 var targetWidth = this.minWidth - 2 * x;
28249                 var targetHeight = this.minHeight - 2 * y;
28250                 
28251                 var scale = 1;
28252                 
28253                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28254                     scale = targetWidth / width;
28255                 }
28256                 
28257                 if(x > 0 && y == 0){
28258                     scale = targetHeight / height;
28259                 }
28260                 
28261                 if(x > 0 && y > 0){
28262                     scale = targetWidth / width;
28263                     
28264                     if(width < height){
28265                         scale = targetHeight / height;
28266                     }
28267                 }
28268                 
28269                 context.scale(scale, scale);
28270                 
28271                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28272                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28273
28274                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28275                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28276                 
28277                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28278                 
28279                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28280                 
28281                 break;
28282             default : 
28283                 break;
28284         }
28285         
28286         this.cropData = canvas.toDataURL(this.cropType);
28287         
28288         if(this.fireEvent('crop', this, this.cropData) !== false){
28289             this.process(this.file, this.cropData);
28290         }
28291         
28292         return;
28293         
28294     },
28295     
28296     setThumbBoxSize : function()
28297     {
28298         var width, height;
28299         
28300         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28301             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28302             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28303             
28304             this.minWidth = width;
28305             this.minHeight = height;
28306             
28307             if(this.rotate == 90 || this.rotate == 270){
28308                 this.minWidth = height;
28309                 this.minHeight = width;
28310             }
28311         }
28312         
28313         height = 300;
28314         width = Math.ceil(this.minWidth * height / this.minHeight);
28315         
28316         if(this.minWidth > this.minHeight){
28317             width = 300;
28318             height = Math.ceil(this.minHeight * width / this.minWidth);
28319         }
28320         
28321         this.thumbEl.setStyle({
28322             width : width + 'px',
28323             height : height + 'px'
28324         });
28325
28326         return;
28327             
28328     },
28329     
28330     setThumbBoxPosition : function()
28331     {
28332         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28333         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28334         
28335         this.thumbEl.setLeft(x);
28336         this.thumbEl.setTop(y);
28337         
28338     },
28339     
28340     baseRotateLevel : function()
28341     {
28342         this.baseRotate = 1;
28343         
28344         if(
28345                 typeof(this.exif) != 'undefined' &&
28346                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28347                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28348         ){
28349             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28350         }
28351         
28352         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28353         
28354     },
28355     
28356     baseScaleLevel : function()
28357     {
28358         var width, height;
28359         
28360         if(this.isDocument){
28361             
28362             if(this.baseRotate == 6 || this.baseRotate == 8){
28363             
28364                 height = this.thumbEl.getHeight();
28365                 this.baseScale = height / this.imageEl.OriginWidth;
28366
28367                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28368                     width = this.thumbEl.getWidth();
28369                     this.baseScale = width / this.imageEl.OriginHeight;
28370                 }
28371
28372                 return;
28373             }
28374
28375             height = this.thumbEl.getHeight();
28376             this.baseScale = height / this.imageEl.OriginHeight;
28377
28378             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28379                 width = this.thumbEl.getWidth();
28380                 this.baseScale = width / this.imageEl.OriginWidth;
28381             }
28382
28383             return;
28384         }
28385         
28386         if(this.baseRotate == 6 || this.baseRotate == 8){
28387             
28388             width = this.thumbEl.getHeight();
28389             this.baseScale = width / this.imageEl.OriginHeight;
28390             
28391             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28392                 height = this.thumbEl.getWidth();
28393                 this.baseScale = height / this.imageEl.OriginHeight;
28394             }
28395             
28396             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28397                 height = this.thumbEl.getWidth();
28398                 this.baseScale = height / this.imageEl.OriginHeight;
28399                 
28400                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28401                     width = this.thumbEl.getHeight();
28402                     this.baseScale = width / this.imageEl.OriginWidth;
28403                 }
28404             }
28405             
28406             return;
28407         }
28408         
28409         width = this.thumbEl.getWidth();
28410         this.baseScale = width / this.imageEl.OriginWidth;
28411         
28412         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28413             height = this.thumbEl.getHeight();
28414             this.baseScale = height / this.imageEl.OriginHeight;
28415         }
28416         
28417         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28418             
28419             height = this.thumbEl.getHeight();
28420             this.baseScale = height / this.imageEl.OriginHeight;
28421             
28422             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28423                 width = this.thumbEl.getWidth();
28424                 this.baseScale = width / this.imageEl.OriginWidth;
28425             }
28426             
28427         }
28428         
28429         return;
28430     },
28431     
28432     getScaleLevel : function()
28433     {
28434         return this.baseScale * Math.pow(1.1, this.scale);
28435     },
28436     
28437     onTouchStart : function(e)
28438     {
28439         if(!this.canvasLoaded){
28440             this.beforeSelectFile(e);
28441             return;
28442         }
28443         
28444         var touches = e.browserEvent.touches;
28445         
28446         if(!touches){
28447             return;
28448         }
28449         
28450         if(touches.length == 1){
28451             this.onMouseDown(e);
28452             return;
28453         }
28454         
28455         if(touches.length != 2){
28456             return;
28457         }
28458         
28459         var coords = [];
28460         
28461         for(var i = 0, finger; finger = touches[i]; i++){
28462             coords.push(finger.pageX, finger.pageY);
28463         }
28464         
28465         var x = Math.pow(coords[0] - coords[2], 2);
28466         var y = Math.pow(coords[1] - coords[3], 2);
28467         
28468         this.startDistance = Math.sqrt(x + y);
28469         
28470         this.startScale = this.scale;
28471         
28472         this.pinching = true;
28473         this.dragable = false;
28474         
28475     },
28476     
28477     onTouchMove : function(e)
28478     {
28479         if(!this.pinching && !this.dragable){
28480             return;
28481         }
28482         
28483         var touches = e.browserEvent.touches;
28484         
28485         if(!touches){
28486             return;
28487         }
28488         
28489         if(this.dragable){
28490             this.onMouseMove(e);
28491             return;
28492         }
28493         
28494         var coords = [];
28495         
28496         for(var i = 0, finger; finger = touches[i]; i++){
28497             coords.push(finger.pageX, finger.pageY);
28498         }
28499         
28500         var x = Math.pow(coords[0] - coords[2], 2);
28501         var y = Math.pow(coords[1] - coords[3], 2);
28502         
28503         this.endDistance = Math.sqrt(x + y);
28504         
28505         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28506         
28507         if(!this.zoomable()){
28508             this.scale = this.startScale;
28509             return;
28510         }
28511         
28512         this.draw();
28513         
28514     },
28515     
28516     onTouchEnd : function(e)
28517     {
28518         this.pinching = false;
28519         this.dragable = false;
28520         
28521     },
28522     
28523     process : function(file, crop)
28524     {
28525         if(this.loadMask){
28526             this.maskEl.mask(this.loadingText);
28527         }
28528         
28529         this.xhr = new XMLHttpRequest();
28530         
28531         file.xhr = this.xhr;
28532
28533         this.xhr.open(this.method, this.url, true);
28534         
28535         var headers = {
28536             "Accept": "application/json",
28537             "Cache-Control": "no-cache",
28538             "X-Requested-With": "XMLHttpRequest"
28539         };
28540         
28541         for (var headerName in headers) {
28542             var headerValue = headers[headerName];
28543             if (headerValue) {
28544                 this.xhr.setRequestHeader(headerName, headerValue);
28545             }
28546         }
28547         
28548         var _this = this;
28549         
28550         this.xhr.onload = function()
28551         {
28552             _this.xhrOnLoad(_this.xhr);
28553         }
28554         
28555         this.xhr.onerror = function()
28556         {
28557             _this.xhrOnError(_this.xhr);
28558         }
28559         
28560         var formData = new FormData();
28561
28562         formData.append('returnHTML', 'NO');
28563         
28564         if(crop){
28565             formData.append('crop', crop);
28566         }
28567         
28568         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28569             formData.append(this.paramName, file, file.name);
28570         }
28571         
28572         if(typeof(file.filename) != 'undefined'){
28573             formData.append('filename', file.filename);
28574         }
28575         
28576         if(typeof(file.mimetype) != 'undefined'){
28577             formData.append('mimetype', file.mimetype);
28578         }
28579         
28580         if(this.fireEvent('arrange', this, formData) != false){
28581             this.xhr.send(formData);
28582         };
28583     },
28584     
28585     xhrOnLoad : function(xhr)
28586     {
28587         if(this.loadMask){
28588             this.maskEl.unmask();
28589         }
28590         
28591         if (xhr.readyState !== 4) {
28592             this.fireEvent('exception', this, xhr);
28593             return;
28594         }
28595
28596         var response = Roo.decode(xhr.responseText);
28597         
28598         if(!response.success){
28599             this.fireEvent('exception', this, xhr);
28600             return;
28601         }
28602         
28603         var response = Roo.decode(xhr.responseText);
28604         
28605         this.fireEvent('upload', this, response);
28606         
28607     },
28608     
28609     xhrOnError : function()
28610     {
28611         if(this.loadMask){
28612             this.maskEl.unmask();
28613         }
28614         
28615         Roo.log('xhr on error');
28616         
28617         var response = Roo.decode(xhr.responseText);
28618           
28619         Roo.log(response);
28620         
28621     },
28622     
28623     prepare : function(file)
28624     {   
28625         if(this.loadMask){
28626             this.maskEl.mask(this.loadingText);
28627         }
28628         
28629         this.file = false;
28630         this.exif = {};
28631         
28632         if(typeof(file) === 'string'){
28633             this.loadCanvas(file);
28634             return;
28635         }
28636         
28637         if(!file || !this.urlAPI){
28638             return;
28639         }
28640         
28641         this.file = file;
28642         this.cropType = file.type;
28643         
28644         var _this = this;
28645         
28646         if(this.fireEvent('prepare', this, this.file) != false){
28647             
28648             var reader = new FileReader();
28649             
28650             reader.onload = function (e) {
28651                 if (e.target.error) {
28652                     Roo.log(e.target.error);
28653                     return;
28654                 }
28655                 
28656                 var buffer = e.target.result,
28657                     dataView = new DataView(buffer),
28658                     offset = 2,
28659                     maxOffset = dataView.byteLength - 4,
28660                     markerBytes,
28661                     markerLength;
28662                 
28663                 if (dataView.getUint16(0) === 0xffd8) {
28664                     while (offset < maxOffset) {
28665                         markerBytes = dataView.getUint16(offset);
28666                         
28667                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28668                             markerLength = dataView.getUint16(offset + 2) + 2;
28669                             if (offset + markerLength > dataView.byteLength) {
28670                                 Roo.log('Invalid meta data: Invalid segment size.');
28671                                 break;
28672                             }
28673                             
28674                             if(markerBytes == 0xffe1){
28675                                 _this.parseExifData(
28676                                     dataView,
28677                                     offset,
28678                                     markerLength
28679                                 );
28680                             }
28681                             
28682                             offset += markerLength;
28683                             
28684                             continue;
28685                         }
28686                         
28687                         break;
28688                     }
28689                     
28690                 }
28691                 
28692                 var url = _this.urlAPI.createObjectURL(_this.file);
28693                 
28694                 _this.loadCanvas(url);
28695                 
28696                 return;
28697             }
28698             
28699             reader.readAsArrayBuffer(this.file);
28700             
28701         }
28702         
28703     },
28704     
28705     parseExifData : function(dataView, offset, length)
28706     {
28707         var tiffOffset = offset + 10,
28708             littleEndian,
28709             dirOffset;
28710     
28711         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28712             // No Exif data, might be XMP data instead
28713             return;
28714         }
28715         
28716         // Check for the ASCII code for "Exif" (0x45786966):
28717         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28718             // No Exif data, might be XMP data instead
28719             return;
28720         }
28721         if (tiffOffset + 8 > dataView.byteLength) {
28722             Roo.log('Invalid Exif data: Invalid segment size.');
28723             return;
28724         }
28725         // Check for the two null bytes:
28726         if (dataView.getUint16(offset + 8) !== 0x0000) {
28727             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28728             return;
28729         }
28730         // Check the byte alignment:
28731         switch (dataView.getUint16(tiffOffset)) {
28732         case 0x4949:
28733             littleEndian = true;
28734             break;
28735         case 0x4D4D:
28736             littleEndian = false;
28737             break;
28738         default:
28739             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28740             return;
28741         }
28742         // Check for the TIFF tag marker (0x002A):
28743         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28744             Roo.log('Invalid Exif data: Missing TIFF marker.');
28745             return;
28746         }
28747         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28748         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28749         
28750         this.parseExifTags(
28751             dataView,
28752             tiffOffset,
28753             tiffOffset + dirOffset,
28754             littleEndian
28755         );
28756     },
28757     
28758     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28759     {
28760         var tagsNumber,
28761             dirEndOffset,
28762             i;
28763         if (dirOffset + 6 > dataView.byteLength) {
28764             Roo.log('Invalid Exif data: Invalid directory offset.');
28765             return;
28766         }
28767         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28768         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28769         if (dirEndOffset + 4 > dataView.byteLength) {
28770             Roo.log('Invalid Exif data: Invalid directory size.');
28771             return;
28772         }
28773         for (i = 0; i < tagsNumber; i += 1) {
28774             this.parseExifTag(
28775                 dataView,
28776                 tiffOffset,
28777                 dirOffset + 2 + 12 * i, // tag offset
28778                 littleEndian
28779             );
28780         }
28781         // Return the offset to the next directory:
28782         return dataView.getUint32(dirEndOffset, littleEndian);
28783     },
28784     
28785     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28786     {
28787         var tag = dataView.getUint16(offset, littleEndian);
28788         
28789         this.exif[tag] = this.getExifValue(
28790             dataView,
28791             tiffOffset,
28792             offset,
28793             dataView.getUint16(offset + 2, littleEndian), // tag type
28794             dataView.getUint32(offset + 4, littleEndian), // tag length
28795             littleEndian
28796         );
28797     },
28798     
28799     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28800     {
28801         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28802             tagSize,
28803             dataOffset,
28804             values,
28805             i,
28806             str,
28807             c;
28808     
28809         if (!tagType) {
28810             Roo.log('Invalid Exif data: Invalid tag type.');
28811             return;
28812         }
28813         
28814         tagSize = tagType.size * length;
28815         // Determine if the value is contained in the dataOffset bytes,
28816         // or if the value at the dataOffset is a pointer to the actual data:
28817         dataOffset = tagSize > 4 ?
28818                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28819         if (dataOffset + tagSize > dataView.byteLength) {
28820             Roo.log('Invalid Exif data: Invalid data offset.');
28821             return;
28822         }
28823         if (length === 1) {
28824             return tagType.getValue(dataView, dataOffset, littleEndian);
28825         }
28826         values = [];
28827         for (i = 0; i < length; i += 1) {
28828             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28829         }
28830         
28831         if (tagType.ascii) {
28832             str = '';
28833             // Concatenate the chars:
28834             for (i = 0; i < values.length; i += 1) {
28835                 c = values[i];
28836                 // Ignore the terminating NULL byte(s):
28837                 if (c === '\u0000') {
28838                     break;
28839                 }
28840                 str += c;
28841             }
28842             return str;
28843         }
28844         return values;
28845     }
28846     
28847 });
28848
28849 Roo.apply(Roo.bootstrap.UploadCropbox, {
28850     tags : {
28851         'Orientation': 0x0112
28852     },
28853     
28854     Orientation: {
28855             1: 0, //'top-left',
28856 //            2: 'top-right',
28857             3: 180, //'bottom-right',
28858 //            4: 'bottom-left',
28859 //            5: 'left-top',
28860             6: 90, //'right-top',
28861 //            7: 'right-bottom',
28862             8: 270 //'left-bottom'
28863     },
28864     
28865     exifTagTypes : {
28866         // byte, 8-bit unsigned int:
28867         1: {
28868             getValue: function (dataView, dataOffset) {
28869                 return dataView.getUint8(dataOffset);
28870             },
28871             size: 1
28872         },
28873         // ascii, 8-bit byte:
28874         2: {
28875             getValue: function (dataView, dataOffset) {
28876                 return String.fromCharCode(dataView.getUint8(dataOffset));
28877             },
28878             size: 1,
28879             ascii: true
28880         },
28881         // short, 16 bit int:
28882         3: {
28883             getValue: function (dataView, dataOffset, littleEndian) {
28884                 return dataView.getUint16(dataOffset, littleEndian);
28885             },
28886             size: 2
28887         },
28888         // long, 32 bit int:
28889         4: {
28890             getValue: function (dataView, dataOffset, littleEndian) {
28891                 return dataView.getUint32(dataOffset, littleEndian);
28892             },
28893             size: 4
28894         },
28895         // rational = two long values, first is numerator, second is denominator:
28896         5: {
28897             getValue: function (dataView, dataOffset, littleEndian) {
28898                 return dataView.getUint32(dataOffset, littleEndian) /
28899                     dataView.getUint32(dataOffset + 4, littleEndian);
28900             },
28901             size: 8
28902         },
28903         // slong, 32 bit signed int:
28904         9: {
28905             getValue: function (dataView, dataOffset, littleEndian) {
28906                 return dataView.getInt32(dataOffset, littleEndian);
28907             },
28908             size: 4
28909         },
28910         // srational, two slongs, first is numerator, second is denominator:
28911         10: {
28912             getValue: function (dataView, dataOffset, littleEndian) {
28913                 return dataView.getInt32(dataOffset, littleEndian) /
28914                     dataView.getInt32(dataOffset + 4, littleEndian);
28915             },
28916             size: 8
28917         }
28918     },
28919     
28920     footer : {
28921         STANDARD : [
28922             {
28923                 tag : 'div',
28924                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28925                 action : 'rotate-left',
28926                 cn : [
28927                     {
28928                         tag : 'button',
28929                         cls : 'btn btn-default',
28930                         html : '<i class="fa fa-undo"></i>'
28931                     }
28932                 ]
28933             },
28934             {
28935                 tag : 'div',
28936                 cls : 'btn-group roo-upload-cropbox-picture',
28937                 action : 'picture',
28938                 cn : [
28939                     {
28940                         tag : 'button',
28941                         cls : 'btn btn-default',
28942                         html : '<i class="fa fa-picture-o"></i>'
28943                     }
28944                 ]
28945             },
28946             {
28947                 tag : 'div',
28948                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28949                 action : 'rotate-right',
28950                 cn : [
28951                     {
28952                         tag : 'button',
28953                         cls : 'btn btn-default',
28954                         html : '<i class="fa fa-repeat"></i>'
28955                     }
28956                 ]
28957             }
28958         ],
28959         DOCUMENT : [
28960             {
28961                 tag : 'div',
28962                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28963                 action : 'rotate-left',
28964                 cn : [
28965                     {
28966                         tag : 'button',
28967                         cls : 'btn btn-default',
28968                         html : '<i class="fa fa-undo"></i>'
28969                     }
28970                 ]
28971             },
28972             {
28973                 tag : 'div',
28974                 cls : 'btn-group roo-upload-cropbox-download',
28975                 action : 'download',
28976                 cn : [
28977                     {
28978                         tag : 'button',
28979                         cls : 'btn btn-default',
28980                         html : '<i class="fa fa-download"></i>'
28981                     }
28982                 ]
28983             },
28984             {
28985                 tag : 'div',
28986                 cls : 'btn-group roo-upload-cropbox-crop',
28987                 action : 'crop',
28988                 cn : [
28989                     {
28990                         tag : 'button',
28991                         cls : 'btn btn-default',
28992                         html : '<i class="fa fa-crop"></i>'
28993                     }
28994                 ]
28995             },
28996             {
28997                 tag : 'div',
28998                 cls : 'btn-group roo-upload-cropbox-trash',
28999                 action : 'trash',
29000                 cn : [
29001                     {
29002                         tag : 'button',
29003                         cls : 'btn btn-default',
29004                         html : '<i class="fa fa-trash"></i>'
29005                     }
29006                 ]
29007             },
29008             {
29009                 tag : 'div',
29010                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29011                 action : 'rotate-right',
29012                 cn : [
29013                     {
29014                         tag : 'button',
29015                         cls : 'btn btn-default',
29016                         html : '<i class="fa fa-repeat"></i>'
29017                     }
29018                 ]
29019             }
29020         ],
29021         ROTATOR : [
29022             {
29023                 tag : 'div',
29024                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29025                 action : 'rotate-left',
29026                 cn : [
29027                     {
29028                         tag : 'button',
29029                         cls : 'btn btn-default',
29030                         html : '<i class="fa fa-undo"></i>'
29031                     }
29032                 ]
29033             },
29034             {
29035                 tag : 'div',
29036                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29037                 action : 'rotate-right',
29038                 cn : [
29039                     {
29040                         tag : 'button',
29041                         cls : 'btn btn-default',
29042                         html : '<i class="fa fa-repeat"></i>'
29043                     }
29044                 ]
29045             }
29046         ]
29047     }
29048 });
29049
29050 /*
29051 * Licence: LGPL
29052 */
29053
29054 /**
29055  * @class Roo.bootstrap.DocumentManager
29056  * @extends Roo.bootstrap.Component
29057  * Bootstrap DocumentManager class
29058  * @cfg {String} paramName default 'imageUpload'
29059  * @cfg {String} toolTipName default 'filename'
29060  * @cfg {String} method default POST
29061  * @cfg {String} url action url
29062  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29063  * @cfg {Boolean} multiple multiple upload default true
29064  * @cfg {Number} thumbSize default 300
29065  * @cfg {String} fieldLabel
29066  * @cfg {Number} labelWidth default 4
29067  * @cfg {String} labelAlign (left|top) default left
29068  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29069 * @cfg {Number} labellg set the width of label (1-12)
29070  * @cfg {Number} labelmd set the width of label (1-12)
29071  * @cfg {Number} labelsm set the width of label (1-12)
29072  * @cfg {Number} labelxs set the width of label (1-12)
29073  * 
29074  * @constructor
29075  * Create a new DocumentManager
29076  * @param {Object} config The config object
29077  */
29078
29079 Roo.bootstrap.DocumentManager = function(config){
29080     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29081     
29082     this.files = [];
29083     this.delegates = [];
29084     
29085     this.addEvents({
29086         /**
29087          * @event initial
29088          * Fire when initial the DocumentManager
29089          * @param {Roo.bootstrap.DocumentManager} this
29090          */
29091         "initial" : true,
29092         /**
29093          * @event inspect
29094          * inspect selected file
29095          * @param {Roo.bootstrap.DocumentManager} this
29096          * @param {File} file
29097          */
29098         "inspect" : true,
29099         /**
29100          * @event exception
29101          * Fire when xhr load exception
29102          * @param {Roo.bootstrap.DocumentManager} this
29103          * @param {XMLHttpRequest} xhr
29104          */
29105         "exception" : true,
29106         /**
29107          * @event afterupload
29108          * Fire when xhr load exception
29109          * @param {Roo.bootstrap.DocumentManager} this
29110          * @param {XMLHttpRequest} xhr
29111          */
29112         "afterupload" : true,
29113         /**
29114          * @event prepare
29115          * prepare the form data
29116          * @param {Roo.bootstrap.DocumentManager} this
29117          * @param {Object} formData
29118          */
29119         "prepare" : true,
29120         /**
29121          * @event remove
29122          * Fire when remove the file
29123          * @param {Roo.bootstrap.DocumentManager} this
29124          * @param {Object} file
29125          */
29126         "remove" : true,
29127         /**
29128          * @event refresh
29129          * Fire after refresh the file
29130          * @param {Roo.bootstrap.DocumentManager} this
29131          */
29132         "refresh" : true,
29133         /**
29134          * @event click
29135          * Fire after click the image
29136          * @param {Roo.bootstrap.DocumentManager} this
29137          * @param {Object} file
29138          */
29139         "click" : true,
29140         /**
29141          * @event edit
29142          * Fire when upload a image and editable set to true
29143          * @param {Roo.bootstrap.DocumentManager} this
29144          * @param {Object} file
29145          */
29146         "edit" : true,
29147         /**
29148          * @event beforeselectfile
29149          * Fire before select file
29150          * @param {Roo.bootstrap.DocumentManager} this
29151          */
29152         "beforeselectfile" : true,
29153         /**
29154          * @event process
29155          * Fire before process file
29156          * @param {Roo.bootstrap.DocumentManager} this
29157          * @param {Object} file
29158          */
29159         "process" : true,
29160         /**
29161          * @event previewrendered
29162          * Fire when preview rendered
29163          * @param {Roo.bootstrap.DocumentManager} this
29164          * @param {Object} file
29165          */
29166         "previewrendered" : true,
29167         /**
29168          */
29169         "previewResize" : true
29170         
29171     });
29172 };
29173
29174 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29175     
29176     boxes : 0,
29177     inputName : '',
29178     thumbSize : 300,
29179     multiple : true,
29180     files : false,
29181     method : 'POST',
29182     url : '',
29183     paramName : 'imageUpload',
29184     toolTipName : 'filename',
29185     fieldLabel : '',
29186     labelWidth : 4,
29187     labelAlign : 'left',
29188     editable : true,
29189     delegates : false,
29190     xhr : false, 
29191     
29192     labellg : 0,
29193     labelmd : 0,
29194     labelsm : 0,
29195     labelxs : 0,
29196     
29197     getAutoCreate : function()
29198     {   
29199         var managerWidget = {
29200             tag : 'div',
29201             cls : 'roo-document-manager',
29202             cn : [
29203                 {
29204                     tag : 'input',
29205                     cls : 'roo-document-manager-selector',
29206                     type : 'file'
29207                 },
29208                 {
29209                     tag : 'div',
29210                     cls : 'roo-document-manager-uploader',
29211                     cn : [
29212                         {
29213                             tag : 'div',
29214                             cls : 'roo-document-manager-upload-btn',
29215                             html : '<i class="fa fa-plus"></i>'
29216                         }
29217                     ]
29218                     
29219                 }
29220             ]
29221         };
29222         
29223         var content = [
29224             {
29225                 tag : 'div',
29226                 cls : 'column col-md-12',
29227                 cn : managerWidget
29228             }
29229         ];
29230         
29231         if(this.fieldLabel.length){
29232             
29233             content = [
29234                 {
29235                     tag : 'div',
29236                     cls : 'column col-md-12',
29237                     html : this.fieldLabel
29238                 },
29239                 {
29240                     tag : 'div',
29241                     cls : 'column col-md-12',
29242                     cn : managerWidget
29243                 }
29244             ];
29245
29246             if(this.labelAlign == 'left'){
29247                 content = [
29248                     {
29249                         tag : 'div',
29250                         cls : 'column',
29251                         html : this.fieldLabel
29252                     },
29253                     {
29254                         tag : 'div',
29255                         cls : 'column',
29256                         cn : managerWidget
29257                     }
29258                 ];
29259                 
29260                 if(this.labelWidth > 12){
29261                     content[0].style = "width: " + this.labelWidth + 'px';
29262                 }
29263
29264                 if(this.labelWidth < 13 && this.labelmd == 0){
29265                     this.labelmd = this.labelWidth;
29266                 }
29267
29268                 if(this.labellg > 0){
29269                     content[0].cls += ' col-lg-' + this.labellg;
29270                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29271                 }
29272
29273                 if(this.labelmd > 0){
29274                     content[0].cls += ' col-md-' + this.labelmd;
29275                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29276                 }
29277
29278                 if(this.labelsm > 0){
29279                     content[0].cls += ' col-sm-' + this.labelsm;
29280                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29281                 }
29282
29283                 if(this.labelxs > 0){
29284                     content[0].cls += ' col-xs-' + this.labelxs;
29285                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29286                 }
29287                 
29288             }
29289         }
29290         
29291         var cfg = {
29292             tag : 'div',
29293             cls : 'row clearfix',
29294             cn : content
29295         };
29296         
29297         return cfg;
29298         
29299     },
29300     
29301     initEvents : function()
29302     {
29303         this.managerEl = this.el.select('.roo-document-manager', true).first();
29304         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29305         
29306         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29307         this.selectorEl.hide();
29308         
29309         if(this.multiple){
29310             this.selectorEl.attr('multiple', 'multiple');
29311         }
29312         
29313         this.selectorEl.on('change', this.onFileSelected, this);
29314         
29315         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29316         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29317         
29318         this.uploader.on('click', this.onUploaderClick, this);
29319         
29320         this.renderProgressDialog();
29321         
29322         var _this = this;
29323         
29324         window.addEventListener("resize", function() { _this.refresh(); } );
29325         
29326         this.fireEvent('initial', this);
29327     },
29328     
29329     renderProgressDialog : function()
29330     {
29331         var _this = this;
29332         
29333         this.progressDialog = new Roo.bootstrap.Modal({
29334             cls : 'roo-document-manager-progress-dialog',
29335             allow_close : false,
29336             animate : false,
29337             title : '',
29338             buttons : [
29339                 {
29340                     name  :'cancel',
29341                     weight : 'danger',
29342                     html : 'Cancel'
29343                 }
29344             ], 
29345             listeners : { 
29346                 btnclick : function() {
29347                     _this.uploadCancel();
29348                     this.hide();
29349                 }
29350             }
29351         });
29352          
29353         this.progressDialog.render(Roo.get(document.body));
29354          
29355         this.progress = new Roo.bootstrap.Progress({
29356             cls : 'roo-document-manager-progress',
29357             active : true,
29358             striped : true
29359         });
29360         
29361         this.progress.render(this.progressDialog.getChildContainer());
29362         
29363         this.progressBar = new Roo.bootstrap.ProgressBar({
29364             cls : 'roo-document-manager-progress-bar',
29365             aria_valuenow : 0,
29366             aria_valuemin : 0,
29367             aria_valuemax : 12,
29368             panel : 'success'
29369         });
29370         
29371         this.progressBar.render(this.progress.getChildContainer());
29372     },
29373     
29374     onUploaderClick : function(e)
29375     {
29376         e.preventDefault();
29377      
29378         if(this.fireEvent('beforeselectfile', this) != false){
29379             this.selectorEl.dom.click();
29380         }
29381         
29382     },
29383     
29384     onFileSelected : function(e)
29385     {
29386         e.preventDefault();
29387         
29388         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29389             return;
29390         }
29391         
29392         Roo.each(this.selectorEl.dom.files, function(file){
29393             if(this.fireEvent('inspect', this, file) != false){
29394                 this.files.push(file);
29395             }
29396         }, this);
29397         
29398         this.queue();
29399         
29400     },
29401     
29402     queue : function()
29403     {
29404         this.selectorEl.dom.value = '';
29405         
29406         if(!this.files || !this.files.length){
29407             return;
29408         }
29409         
29410         if(this.boxes > 0 && this.files.length > this.boxes){
29411             this.files = this.files.slice(0, this.boxes);
29412         }
29413         
29414         this.uploader.show();
29415         
29416         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29417             this.uploader.hide();
29418         }
29419         
29420         var _this = this;
29421         
29422         var files = [];
29423         
29424         var docs = [];
29425         
29426         Roo.each(this.files, function(file){
29427             
29428             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29429                 var f = this.renderPreview(file);
29430                 files.push(f);
29431                 return;
29432             }
29433             
29434             if(file.type.indexOf('image') != -1){
29435                 this.delegates.push(
29436                     (function(){
29437                         _this.process(file);
29438                     }).createDelegate(this)
29439                 );
29440         
29441                 return;
29442             }
29443             
29444             docs.push(
29445                 (function(){
29446                     _this.process(file);
29447                 }).createDelegate(this)
29448             );
29449             
29450         }, this);
29451         
29452         this.files = files;
29453         
29454         this.delegates = this.delegates.concat(docs);
29455         
29456         if(!this.delegates.length){
29457             this.refresh();
29458             return;
29459         }
29460         
29461         this.progressBar.aria_valuemax = this.delegates.length;
29462         
29463         this.arrange();
29464         
29465         return;
29466     },
29467     
29468     arrange : function()
29469     {
29470         if(!this.delegates.length){
29471             this.progressDialog.hide();
29472             this.refresh();
29473             return;
29474         }
29475         
29476         var delegate = this.delegates.shift();
29477         
29478         this.progressDialog.show();
29479         
29480         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29481         
29482         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29483         
29484         delegate();
29485     },
29486     
29487     refresh : function()
29488     {
29489         this.uploader.show();
29490         
29491         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29492             this.uploader.hide();
29493         }
29494         
29495         Roo.isTouch ? this.closable(false) : this.closable(true);
29496         
29497         this.fireEvent('refresh', this);
29498     },
29499     
29500     onRemove : function(e, el, o)
29501     {
29502         e.preventDefault();
29503         
29504         this.fireEvent('remove', this, o);
29505         
29506     },
29507     
29508     remove : function(o)
29509     {
29510         var files = [];
29511         
29512         Roo.each(this.files, function(file){
29513             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29514                 files.push(file);
29515                 return;
29516             }
29517
29518             o.target.remove();
29519
29520         }, this);
29521         
29522         this.files = files;
29523         
29524         this.refresh();
29525     },
29526     
29527     clear : function()
29528     {
29529         Roo.each(this.files, function(file){
29530             if(!file.target){
29531                 return;
29532             }
29533             
29534             file.target.remove();
29535
29536         }, this);
29537         
29538         this.files = [];
29539         
29540         this.refresh();
29541     },
29542     
29543     onClick : function(e, el, o)
29544     {
29545         e.preventDefault();
29546         
29547         this.fireEvent('click', this, o);
29548         
29549     },
29550     
29551     closable : function(closable)
29552     {
29553         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29554             
29555             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29556             
29557             if(closable){
29558                 el.show();
29559                 return;
29560             }
29561             
29562             el.hide();
29563             
29564         }, this);
29565     },
29566     
29567     xhrOnLoad : function(xhr)
29568     {
29569         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29570             el.remove();
29571         }, this);
29572         
29573         if (xhr.readyState !== 4) {
29574             this.arrange();
29575             this.fireEvent('exception', this, xhr);
29576             return;
29577         }
29578
29579         var response = Roo.decode(xhr.responseText);
29580         
29581         if(!response.success){
29582             this.arrange();
29583             this.fireEvent('exception', this, xhr);
29584             return;
29585         }
29586         
29587         var file = this.renderPreview(response.data);
29588         
29589         this.files.push(file);
29590         
29591         this.arrange();
29592         
29593         this.fireEvent('afterupload', this, xhr);
29594         
29595     },
29596     
29597     xhrOnError : function(xhr)
29598     {
29599         Roo.log('xhr on error');
29600         
29601         var response = Roo.decode(xhr.responseText);
29602           
29603         Roo.log(response);
29604         
29605         this.arrange();
29606     },
29607     
29608     process : function(file)
29609     {
29610         if(this.fireEvent('process', this, file) !== false){
29611             if(this.editable && file.type.indexOf('image') != -1){
29612                 this.fireEvent('edit', this, file);
29613                 return;
29614             }
29615
29616             this.uploadStart(file, false);
29617
29618             return;
29619         }
29620         
29621     },
29622     
29623     uploadStart : function(file, crop)
29624     {
29625         this.xhr = new XMLHttpRequest();
29626         
29627         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29628             this.arrange();
29629             return;
29630         }
29631         
29632         file.xhr = this.xhr;
29633             
29634         this.managerEl.createChild({
29635             tag : 'div',
29636             cls : 'roo-document-manager-loading',
29637             cn : [
29638                 {
29639                     tag : 'div',
29640                     tooltip : file.name,
29641                     cls : 'roo-document-manager-thumb',
29642                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29643                 }
29644             ]
29645
29646         });
29647
29648         this.xhr.open(this.method, this.url, true);
29649         
29650         var headers = {
29651             "Accept": "application/json",
29652             "Cache-Control": "no-cache",
29653             "X-Requested-With": "XMLHttpRequest"
29654         };
29655         
29656         for (var headerName in headers) {
29657             var headerValue = headers[headerName];
29658             if (headerValue) {
29659                 this.xhr.setRequestHeader(headerName, headerValue);
29660             }
29661         }
29662         
29663         var _this = this;
29664         
29665         this.xhr.onload = function()
29666         {
29667             _this.xhrOnLoad(_this.xhr);
29668         }
29669         
29670         this.xhr.onerror = function()
29671         {
29672             _this.xhrOnError(_this.xhr);
29673         }
29674         
29675         var formData = new FormData();
29676
29677         formData.append('returnHTML', 'NO');
29678         
29679         if(crop){
29680             formData.append('crop', crop);
29681         }
29682         
29683         formData.append(this.paramName, file, file.name);
29684         
29685         var options = {
29686             file : file, 
29687             manually : false
29688         };
29689         
29690         if(this.fireEvent('prepare', this, formData, options) != false){
29691             
29692             if(options.manually){
29693                 return;
29694             }
29695             
29696             this.xhr.send(formData);
29697             return;
29698         };
29699         
29700         this.uploadCancel();
29701     },
29702     
29703     uploadCancel : function()
29704     {
29705         if (this.xhr) {
29706             this.xhr.abort();
29707         }
29708         
29709         this.delegates = [];
29710         
29711         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29712             el.remove();
29713         }, this);
29714         
29715         this.arrange();
29716     },
29717     
29718     renderPreview : function(file)
29719     {
29720         if(typeof(file.target) != 'undefined' && file.target){
29721             return file;
29722         }
29723         
29724         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29725         
29726         var previewEl = this.managerEl.createChild({
29727             tag : 'div',
29728             cls : 'roo-document-manager-preview',
29729             cn : [
29730                 {
29731                     tag : 'div',
29732                     tooltip : file[this.toolTipName],
29733                     cls : 'roo-document-manager-thumb',
29734                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29735                 },
29736                 {
29737                     tag : 'button',
29738                     cls : 'close',
29739                     html : '<i class="fa fa-times-circle"></i>'
29740                 }
29741             ]
29742         });
29743
29744         var close = previewEl.select('button.close', true).first();
29745
29746         close.on('click', this.onRemove, this, file);
29747
29748         file.target = previewEl;
29749
29750         var image = previewEl.select('img', true).first();
29751         
29752         var _this = this;
29753         
29754         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29755         
29756         image.on('click', this.onClick, this, file);
29757         
29758         this.fireEvent('previewrendered', this, file);
29759         
29760         return file;
29761         
29762     },
29763     
29764     onPreviewLoad : function(file, image)
29765     {
29766         if(typeof(file.target) == 'undefined' || !file.target){
29767             return;
29768         }
29769         
29770         var width = image.dom.naturalWidth || image.dom.width;
29771         var height = image.dom.naturalHeight || image.dom.height;
29772         
29773         if(!this.previewResize) {
29774             return;
29775         }
29776         
29777         if(width > height){
29778             file.target.addClass('wide');
29779             return;
29780         }
29781         
29782         file.target.addClass('tall');
29783         return;
29784         
29785     },
29786     
29787     uploadFromSource : function(file, crop)
29788     {
29789         this.xhr = new XMLHttpRequest();
29790         
29791         this.managerEl.createChild({
29792             tag : 'div',
29793             cls : 'roo-document-manager-loading',
29794             cn : [
29795                 {
29796                     tag : 'div',
29797                     tooltip : file.name,
29798                     cls : 'roo-document-manager-thumb',
29799                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29800                 }
29801             ]
29802
29803         });
29804
29805         this.xhr.open(this.method, this.url, true);
29806         
29807         var headers = {
29808             "Accept": "application/json",
29809             "Cache-Control": "no-cache",
29810             "X-Requested-With": "XMLHttpRequest"
29811         };
29812         
29813         for (var headerName in headers) {
29814             var headerValue = headers[headerName];
29815             if (headerValue) {
29816                 this.xhr.setRequestHeader(headerName, headerValue);
29817             }
29818         }
29819         
29820         var _this = this;
29821         
29822         this.xhr.onload = function()
29823         {
29824             _this.xhrOnLoad(_this.xhr);
29825         }
29826         
29827         this.xhr.onerror = function()
29828         {
29829             _this.xhrOnError(_this.xhr);
29830         }
29831         
29832         var formData = new FormData();
29833
29834         formData.append('returnHTML', 'NO');
29835         
29836         formData.append('crop', crop);
29837         
29838         if(typeof(file.filename) != 'undefined'){
29839             formData.append('filename', file.filename);
29840         }
29841         
29842         if(typeof(file.mimetype) != 'undefined'){
29843             formData.append('mimetype', file.mimetype);
29844         }
29845         
29846         Roo.log(formData);
29847         
29848         if(this.fireEvent('prepare', this, formData) != false){
29849             this.xhr.send(formData);
29850         };
29851     }
29852 });
29853
29854 /*
29855 * Licence: LGPL
29856 */
29857
29858 /**
29859  * @class Roo.bootstrap.DocumentViewer
29860  * @extends Roo.bootstrap.Component
29861  * Bootstrap DocumentViewer class
29862  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29863  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29864  * 
29865  * @constructor
29866  * Create a new DocumentViewer
29867  * @param {Object} config The config object
29868  */
29869
29870 Roo.bootstrap.DocumentViewer = function(config){
29871     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29872     
29873     this.addEvents({
29874         /**
29875          * @event initial
29876          * Fire after initEvent
29877          * @param {Roo.bootstrap.DocumentViewer} this
29878          */
29879         "initial" : true,
29880         /**
29881          * @event click
29882          * Fire after click
29883          * @param {Roo.bootstrap.DocumentViewer} this
29884          */
29885         "click" : true,
29886         /**
29887          * @event download
29888          * Fire after download button
29889          * @param {Roo.bootstrap.DocumentViewer} this
29890          */
29891         "download" : true,
29892         /**
29893          * @event trash
29894          * Fire after trash button
29895          * @param {Roo.bootstrap.DocumentViewer} this
29896          */
29897         "trash" : true
29898         
29899     });
29900 };
29901
29902 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29903     
29904     showDownload : true,
29905     
29906     showTrash : true,
29907     
29908     getAutoCreate : function()
29909     {
29910         var cfg = {
29911             tag : 'div',
29912             cls : 'roo-document-viewer',
29913             cn : [
29914                 {
29915                     tag : 'div',
29916                     cls : 'roo-document-viewer-body',
29917                     cn : [
29918                         {
29919                             tag : 'div',
29920                             cls : 'roo-document-viewer-thumb',
29921                             cn : [
29922                                 {
29923                                     tag : 'img',
29924                                     cls : 'roo-document-viewer-image'
29925                                 }
29926                             ]
29927                         }
29928                     ]
29929                 },
29930                 {
29931                     tag : 'div',
29932                     cls : 'roo-document-viewer-footer',
29933                     cn : {
29934                         tag : 'div',
29935                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29936                         cn : [
29937                             {
29938                                 tag : 'div',
29939                                 cls : 'btn-group roo-document-viewer-download',
29940                                 cn : [
29941                                     {
29942                                         tag : 'button',
29943                                         cls : 'btn btn-default',
29944                                         html : '<i class="fa fa-download"></i>'
29945                                     }
29946                                 ]
29947                             },
29948                             {
29949                                 tag : 'div',
29950                                 cls : 'btn-group roo-document-viewer-trash',
29951                                 cn : [
29952                                     {
29953                                         tag : 'button',
29954                                         cls : 'btn btn-default',
29955                                         html : '<i class="fa fa-trash"></i>'
29956                                     }
29957                                 ]
29958                             }
29959                         ]
29960                     }
29961                 }
29962             ]
29963         };
29964         
29965         return cfg;
29966     },
29967     
29968     initEvents : function()
29969     {
29970         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29971         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29972         
29973         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29974         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29975         
29976         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29977         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29978         
29979         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29980         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29981         
29982         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29983         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29984         
29985         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29986         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29987         
29988         this.bodyEl.on('click', this.onClick, this);
29989         this.downloadBtn.on('click', this.onDownload, this);
29990         this.trashBtn.on('click', this.onTrash, this);
29991         
29992         this.downloadBtn.hide();
29993         this.trashBtn.hide();
29994         
29995         if(this.showDownload){
29996             this.downloadBtn.show();
29997         }
29998         
29999         if(this.showTrash){
30000             this.trashBtn.show();
30001         }
30002         
30003         if(!this.showDownload && !this.showTrash) {
30004             this.footerEl.hide();
30005         }
30006         
30007     },
30008     
30009     initial : function()
30010     {
30011         this.fireEvent('initial', this);
30012         
30013     },
30014     
30015     onClick : function(e)
30016     {
30017         e.preventDefault();
30018         
30019         this.fireEvent('click', this);
30020     },
30021     
30022     onDownload : function(e)
30023     {
30024         e.preventDefault();
30025         
30026         this.fireEvent('download', this);
30027     },
30028     
30029     onTrash : function(e)
30030     {
30031         e.preventDefault();
30032         
30033         this.fireEvent('trash', this);
30034     }
30035     
30036 });
30037 /*
30038  * - LGPL
30039  *
30040  * nav progress bar
30041  * 
30042  */
30043
30044 /**
30045  * @class Roo.bootstrap.NavProgressBar
30046  * @extends Roo.bootstrap.Component
30047  * Bootstrap NavProgressBar class
30048  * 
30049  * @constructor
30050  * Create a new nav progress bar
30051  * @param {Object} config The config object
30052  */
30053
30054 Roo.bootstrap.NavProgressBar = function(config){
30055     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30056
30057     this.bullets = this.bullets || [];
30058    
30059 //    Roo.bootstrap.NavProgressBar.register(this);
30060      this.addEvents({
30061         /**
30062              * @event changed
30063              * Fires when the active item changes
30064              * @param {Roo.bootstrap.NavProgressBar} this
30065              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30066              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30067          */
30068         'changed': true
30069      });
30070     
30071 };
30072
30073 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30074     
30075     bullets : [],
30076     barItems : [],
30077     
30078     getAutoCreate : function()
30079     {
30080         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30081         
30082         cfg = {
30083             tag : 'div',
30084             cls : 'roo-navigation-bar-group',
30085             cn : [
30086                 {
30087                     tag : 'div',
30088                     cls : 'roo-navigation-top-bar'
30089                 },
30090                 {
30091                     tag : 'div',
30092                     cls : 'roo-navigation-bullets-bar',
30093                     cn : [
30094                         {
30095                             tag : 'ul',
30096                             cls : 'roo-navigation-bar'
30097                         }
30098                     ]
30099                 },
30100                 
30101                 {
30102                     tag : 'div',
30103                     cls : 'roo-navigation-bottom-bar'
30104                 }
30105             ]
30106             
30107         };
30108         
30109         return cfg;
30110         
30111     },
30112     
30113     initEvents: function() 
30114     {
30115         
30116     },
30117     
30118     onRender : function(ct, position) 
30119     {
30120         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30121         
30122         if(this.bullets.length){
30123             Roo.each(this.bullets, function(b){
30124                this.addItem(b);
30125             }, this);
30126         }
30127         
30128         this.format();
30129         
30130     },
30131     
30132     addItem : function(cfg)
30133     {
30134         var item = new Roo.bootstrap.NavProgressItem(cfg);
30135         
30136         item.parentId = this.id;
30137         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30138         
30139         if(cfg.html){
30140             var top = new Roo.bootstrap.Element({
30141                 tag : 'div',
30142                 cls : 'roo-navigation-bar-text'
30143             });
30144             
30145             var bottom = new Roo.bootstrap.Element({
30146                 tag : 'div',
30147                 cls : 'roo-navigation-bar-text'
30148             });
30149             
30150             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30151             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30152             
30153             var topText = new Roo.bootstrap.Element({
30154                 tag : 'span',
30155                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30156             });
30157             
30158             var bottomText = new Roo.bootstrap.Element({
30159                 tag : 'span',
30160                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30161             });
30162             
30163             topText.onRender(top.el, null);
30164             bottomText.onRender(bottom.el, null);
30165             
30166             item.topEl = top;
30167             item.bottomEl = bottom;
30168         }
30169         
30170         this.barItems.push(item);
30171         
30172         return item;
30173     },
30174     
30175     getActive : function()
30176     {
30177         var active = false;
30178         
30179         Roo.each(this.barItems, function(v){
30180             
30181             if (!v.isActive()) {
30182                 return;
30183             }
30184             
30185             active = v;
30186             return false;
30187             
30188         });
30189         
30190         return active;
30191     },
30192     
30193     setActiveItem : function(item)
30194     {
30195         var prev = false;
30196         
30197         Roo.each(this.barItems, function(v){
30198             if (v.rid == item.rid) {
30199                 return ;
30200             }
30201             
30202             if (v.isActive()) {
30203                 v.setActive(false);
30204                 prev = v;
30205             }
30206         });
30207
30208         item.setActive(true);
30209         
30210         this.fireEvent('changed', this, item, prev);
30211     },
30212     
30213     getBarItem: function(rid)
30214     {
30215         var ret = false;
30216         
30217         Roo.each(this.barItems, function(e) {
30218             if (e.rid != rid) {
30219                 return;
30220             }
30221             
30222             ret =  e;
30223             return false;
30224         });
30225         
30226         return ret;
30227     },
30228     
30229     indexOfItem : function(item)
30230     {
30231         var index = false;
30232         
30233         Roo.each(this.barItems, function(v, i){
30234             
30235             if (v.rid != item.rid) {
30236                 return;
30237             }
30238             
30239             index = i;
30240             return false
30241         });
30242         
30243         return index;
30244     },
30245     
30246     setActiveNext : function()
30247     {
30248         var i = this.indexOfItem(this.getActive());
30249         
30250         if (i > this.barItems.length) {
30251             return;
30252         }
30253         
30254         this.setActiveItem(this.barItems[i+1]);
30255     },
30256     
30257     setActivePrev : function()
30258     {
30259         var i = this.indexOfItem(this.getActive());
30260         
30261         if (i  < 1) {
30262             return;
30263         }
30264         
30265         this.setActiveItem(this.barItems[i-1]);
30266     },
30267     
30268     format : function()
30269     {
30270         if(!this.barItems.length){
30271             return;
30272         }
30273      
30274         var width = 100 / this.barItems.length;
30275         
30276         Roo.each(this.barItems, function(i){
30277             i.el.setStyle('width', width + '%');
30278             i.topEl.el.setStyle('width', width + '%');
30279             i.bottomEl.el.setStyle('width', width + '%');
30280         }, this);
30281         
30282     }
30283     
30284 });
30285 /*
30286  * - LGPL
30287  *
30288  * Nav Progress Item
30289  * 
30290  */
30291
30292 /**
30293  * @class Roo.bootstrap.NavProgressItem
30294  * @extends Roo.bootstrap.Component
30295  * Bootstrap NavProgressItem class
30296  * @cfg {String} rid the reference id
30297  * @cfg {Boolean} active (true|false) Is item active default false
30298  * @cfg {Boolean} disabled (true|false) Is item active default false
30299  * @cfg {String} html
30300  * @cfg {String} position (top|bottom) text position default bottom
30301  * @cfg {String} icon show icon instead of number
30302  * 
30303  * @constructor
30304  * Create a new NavProgressItem
30305  * @param {Object} config The config object
30306  */
30307 Roo.bootstrap.NavProgressItem = function(config){
30308     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30309     this.addEvents({
30310         // raw events
30311         /**
30312          * @event click
30313          * The raw click event for the entire grid.
30314          * @param {Roo.bootstrap.NavProgressItem} this
30315          * @param {Roo.EventObject} e
30316          */
30317         "click" : true
30318     });
30319    
30320 };
30321
30322 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30323     
30324     rid : '',
30325     active : false,
30326     disabled : false,
30327     html : '',
30328     position : 'bottom',
30329     icon : false,
30330     
30331     getAutoCreate : function()
30332     {
30333         var iconCls = 'roo-navigation-bar-item-icon';
30334         
30335         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30336         
30337         var cfg = {
30338             tag: 'li',
30339             cls: 'roo-navigation-bar-item',
30340             cn : [
30341                 {
30342                     tag : 'i',
30343                     cls : iconCls
30344                 }
30345             ]
30346         };
30347         
30348         if(this.active){
30349             cfg.cls += ' active';
30350         }
30351         if(this.disabled){
30352             cfg.cls += ' disabled';
30353         }
30354         
30355         return cfg;
30356     },
30357     
30358     disable : function()
30359     {
30360         this.setDisabled(true);
30361     },
30362     
30363     enable : function()
30364     {
30365         this.setDisabled(false);
30366     },
30367     
30368     initEvents: function() 
30369     {
30370         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30371         
30372         this.iconEl.on('click', this.onClick, this);
30373     },
30374     
30375     onClick : function(e)
30376     {
30377         e.preventDefault();
30378         
30379         if(this.disabled){
30380             return;
30381         }
30382         
30383         if(this.fireEvent('click', this, e) === false){
30384             return;
30385         };
30386         
30387         this.parent().setActiveItem(this);
30388     },
30389     
30390     isActive: function () 
30391     {
30392         return this.active;
30393     },
30394     
30395     setActive : function(state)
30396     {
30397         if(this.active == state){
30398             return;
30399         }
30400         
30401         this.active = state;
30402         
30403         if (state) {
30404             this.el.addClass('active');
30405             return;
30406         }
30407         
30408         this.el.removeClass('active');
30409         
30410         return;
30411     },
30412     
30413     setDisabled : function(state)
30414     {
30415         if(this.disabled == state){
30416             return;
30417         }
30418         
30419         this.disabled = state;
30420         
30421         if (state) {
30422             this.el.addClass('disabled');
30423             return;
30424         }
30425         
30426         this.el.removeClass('disabled');
30427     },
30428     
30429     tooltipEl : function()
30430     {
30431         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30432     }
30433 });
30434  
30435
30436  /*
30437  * - LGPL
30438  *
30439  * FieldLabel
30440  * 
30441  */
30442
30443 /**
30444  * @class Roo.bootstrap.FieldLabel
30445  * @extends Roo.bootstrap.Component
30446  * Bootstrap FieldLabel class
30447  * @cfg {String} html contents of the element
30448  * @cfg {String} tag tag of the element default label
30449  * @cfg {String} cls class of the element
30450  * @cfg {String} target label target 
30451  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30452  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30453  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30454  * @cfg {String} iconTooltip default "This field is required"
30455  * @cfg {String} indicatorpos (left|right) default left
30456  * 
30457  * @constructor
30458  * Create a new FieldLabel
30459  * @param {Object} config The config object
30460  */
30461
30462 Roo.bootstrap.FieldLabel = function(config){
30463     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30464     
30465     this.addEvents({
30466             /**
30467              * @event invalid
30468              * Fires after the field has been marked as invalid.
30469              * @param {Roo.form.FieldLabel} this
30470              * @param {String} msg The validation message
30471              */
30472             invalid : true,
30473             /**
30474              * @event valid
30475              * Fires after the field has been validated with no errors.
30476              * @param {Roo.form.FieldLabel} this
30477              */
30478             valid : true
30479         });
30480 };
30481
30482 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30483     
30484     tag: 'label',
30485     cls: '',
30486     html: '',
30487     target: '',
30488     allowBlank : true,
30489     invalidClass : 'has-warning',
30490     validClass : 'has-success',
30491     iconTooltip : 'This field is required',
30492     indicatorpos : 'left',
30493     
30494     getAutoCreate : function(){
30495         
30496         var cls = "";
30497         if (!this.allowBlank) {
30498             cls  = "visible";
30499         }
30500         
30501         var cfg = {
30502             tag : this.tag,
30503             cls : 'roo-bootstrap-field-label ' + this.cls,
30504             for : this.target,
30505             cn : [
30506                 {
30507                     tag : 'i',
30508                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30509                     tooltip : this.iconTooltip
30510                 },
30511                 {
30512                     tag : 'span',
30513                     html : this.html
30514                 }
30515             ] 
30516         };
30517         
30518         if(this.indicatorpos == 'right'){
30519             var cfg = {
30520                 tag : this.tag,
30521                 cls : 'roo-bootstrap-field-label ' + this.cls,
30522                 for : this.target,
30523                 cn : [
30524                     {
30525                         tag : 'span',
30526                         html : this.html
30527                     },
30528                     {
30529                         tag : 'i',
30530                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30531                         tooltip : this.iconTooltip
30532                     }
30533                 ] 
30534             };
30535         }
30536         
30537         return cfg;
30538     },
30539     
30540     initEvents: function() 
30541     {
30542         Roo.bootstrap.Element.superclass.initEvents.call(this);
30543         
30544         this.indicator = this.indicatorEl();
30545         
30546         if(this.indicator){
30547             this.indicator.removeClass('visible');
30548             this.indicator.addClass('invisible');
30549         }
30550         
30551         Roo.bootstrap.FieldLabel.register(this);
30552     },
30553     
30554     indicatorEl : function()
30555     {
30556         var indicator = this.el.select('i.roo-required-indicator',true).first();
30557         
30558         if(!indicator){
30559             return false;
30560         }
30561         
30562         return indicator;
30563         
30564     },
30565     
30566     /**
30567      * Mark this field as valid
30568      */
30569     markValid : function()
30570     {
30571         if(this.indicator){
30572             this.indicator.removeClass('visible');
30573             this.indicator.addClass('invisible');
30574         }
30575         if (Roo.bootstrap.version == 3) {
30576             this.el.removeClass(this.invalidClass);
30577             this.el.addClass(this.validClass);
30578         } else {
30579             this.el.removeClass('is-invalid');
30580             this.el.addClass('is-valid');
30581         }
30582         
30583         
30584         this.fireEvent('valid', this);
30585     },
30586     
30587     /**
30588      * Mark this field as invalid
30589      * @param {String} msg The validation message
30590      */
30591     markInvalid : function(msg)
30592     {
30593         if(this.indicator){
30594             this.indicator.removeClass('invisible');
30595             this.indicator.addClass('visible');
30596         }
30597           if (Roo.bootstrap.version == 3) {
30598             this.el.removeClass(this.validClass);
30599             this.el.addClass(this.invalidClass);
30600         } else {
30601             this.el.removeClass('is-valid');
30602             this.el.addClass('is-invalid');
30603         }
30604         
30605         
30606         this.fireEvent('invalid', this, msg);
30607     }
30608     
30609    
30610 });
30611
30612 Roo.apply(Roo.bootstrap.FieldLabel, {
30613     
30614     groups: {},
30615     
30616      /**
30617     * register a FieldLabel Group
30618     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30619     */
30620     register : function(label)
30621     {
30622         if(this.groups.hasOwnProperty(label.target)){
30623             return;
30624         }
30625      
30626         this.groups[label.target] = label;
30627         
30628     },
30629     /**
30630     * fetch a FieldLabel Group based on the target
30631     * @param {string} target
30632     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30633     */
30634     get: function(target) {
30635         if (typeof(this.groups[target]) == 'undefined') {
30636             return false;
30637         }
30638         
30639         return this.groups[target] ;
30640     }
30641 });
30642
30643  
30644
30645  /*
30646  * - LGPL
30647  *
30648  * page DateSplitField.
30649  * 
30650  */
30651
30652
30653 /**
30654  * @class Roo.bootstrap.DateSplitField
30655  * @extends Roo.bootstrap.Component
30656  * Bootstrap DateSplitField class
30657  * @cfg {string} fieldLabel - the label associated
30658  * @cfg {Number} labelWidth set the width of label (0-12)
30659  * @cfg {String} labelAlign (top|left)
30660  * @cfg {Boolean} dayAllowBlank (true|false) default false
30661  * @cfg {Boolean} monthAllowBlank (true|false) default false
30662  * @cfg {Boolean} yearAllowBlank (true|false) default false
30663  * @cfg {string} dayPlaceholder 
30664  * @cfg {string} monthPlaceholder
30665  * @cfg {string} yearPlaceholder
30666  * @cfg {string} dayFormat default 'd'
30667  * @cfg {string} monthFormat default 'm'
30668  * @cfg {string} yearFormat default 'Y'
30669  * @cfg {Number} labellg set the width of label (1-12)
30670  * @cfg {Number} labelmd set the width of label (1-12)
30671  * @cfg {Number} labelsm set the width of label (1-12)
30672  * @cfg {Number} labelxs set the width of label (1-12)
30673
30674  *     
30675  * @constructor
30676  * Create a new DateSplitField
30677  * @param {Object} config The config object
30678  */
30679
30680 Roo.bootstrap.DateSplitField = function(config){
30681     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30682     
30683     this.addEvents({
30684         // raw events
30685          /**
30686          * @event years
30687          * getting the data of years
30688          * @param {Roo.bootstrap.DateSplitField} this
30689          * @param {Object} years
30690          */
30691         "years" : true,
30692         /**
30693          * @event days
30694          * getting the data of days
30695          * @param {Roo.bootstrap.DateSplitField} this
30696          * @param {Object} days
30697          */
30698         "days" : true,
30699         /**
30700          * @event invalid
30701          * Fires after the field has been marked as invalid.
30702          * @param {Roo.form.Field} this
30703          * @param {String} msg The validation message
30704          */
30705         invalid : true,
30706        /**
30707          * @event valid
30708          * Fires after the field has been validated with no errors.
30709          * @param {Roo.form.Field} this
30710          */
30711         valid : true
30712     });
30713 };
30714
30715 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30716     
30717     fieldLabel : '',
30718     labelAlign : 'top',
30719     labelWidth : 3,
30720     dayAllowBlank : false,
30721     monthAllowBlank : false,
30722     yearAllowBlank : false,
30723     dayPlaceholder : '',
30724     monthPlaceholder : '',
30725     yearPlaceholder : '',
30726     dayFormat : 'd',
30727     monthFormat : 'm',
30728     yearFormat : 'Y',
30729     isFormField : true,
30730     labellg : 0,
30731     labelmd : 0,
30732     labelsm : 0,
30733     labelxs : 0,
30734     
30735     getAutoCreate : function()
30736     {
30737         var cfg = {
30738             tag : 'div',
30739             cls : 'row roo-date-split-field-group',
30740             cn : [
30741                 {
30742                     tag : 'input',
30743                     type : 'hidden',
30744                     cls : 'form-hidden-field roo-date-split-field-group-value',
30745                     name : this.name
30746                 }
30747             ]
30748         };
30749         
30750         var labelCls = 'col-md-12';
30751         var contentCls = 'col-md-4';
30752         
30753         if(this.fieldLabel){
30754             
30755             var label = {
30756                 tag : 'div',
30757                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30758                 cn : [
30759                     {
30760                         tag : 'label',
30761                         html : this.fieldLabel
30762                     }
30763                 ]
30764             };
30765             
30766             if(this.labelAlign == 'left'){
30767             
30768                 if(this.labelWidth > 12){
30769                     label.style = "width: " + this.labelWidth + 'px';
30770                 }
30771
30772                 if(this.labelWidth < 13 && this.labelmd == 0){
30773                     this.labelmd = this.labelWidth;
30774                 }
30775
30776                 if(this.labellg > 0){
30777                     labelCls = ' col-lg-' + this.labellg;
30778                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30779                 }
30780
30781                 if(this.labelmd > 0){
30782                     labelCls = ' col-md-' + this.labelmd;
30783                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30784                 }
30785
30786                 if(this.labelsm > 0){
30787                     labelCls = ' col-sm-' + this.labelsm;
30788                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30789                 }
30790
30791                 if(this.labelxs > 0){
30792                     labelCls = ' col-xs-' + this.labelxs;
30793                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30794                 }
30795             }
30796             
30797             label.cls += ' ' + labelCls;
30798             
30799             cfg.cn.push(label);
30800         }
30801         
30802         Roo.each(['day', 'month', 'year'], function(t){
30803             cfg.cn.push({
30804                 tag : 'div',
30805                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30806             });
30807         }, this);
30808         
30809         return cfg;
30810     },
30811     
30812     inputEl: function ()
30813     {
30814         return this.el.select('.roo-date-split-field-group-value', true).first();
30815     },
30816     
30817     onRender : function(ct, position) 
30818     {
30819         var _this = this;
30820         
30821         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30822         
30823         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30824         
30825         this.dayField = new Roo.bootstrap.ComboBox({
30826             allowBlank : this.dayAllowBlank,
30827             alwaysQuery : true,
30828             displayField : 'value',
30829             editable : false,
30830             fieldLabel : '',
30831             forceSelection : true,
30832             mode : 'local',
30833             placeholder : this.dayPlaceholder,
30834             selectOnFocus : true,
30835             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30836             triggerAction : 'all',
30837             typeAhead : true,
30838             valueField : 'value',
30839             store : new Roo.data.SimpleStore({
30840                 data : (function() {    
30841                     var days = [];
30842                     _this.fireEvent('days', _this, days);
30843                     return days;
30844                 })(),
30845                 fields : [ 'value' ]
30846             }),
30847             listeners : {
30848                 select : function (_self, record, index)
30849                 {
30850                     _this.setValue(_this.getValue());
30851                 }
30852             }
30853         });
30854
30855         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30856         
30857         this.monthField = new Roo.bootstrap.MonthField({
30858             after : '<i class=\"fa fa-calendar\"></i>',
30859             allowBlank : this.monthAllowBlank,
30860             placeholder : this.monthPlaceholder,
30861             readOnly : true,
30862             listeners : {
30863                 render : function (_self)
30864                 {
30865                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30866                         e.preventDefault();
30867                         _self.focus();
30868                     });
30869                 },
30870                 select : function (_self, oldvalue, newvalue)
30871                 {
30872                     _this.setValue(_this.getValue());
30873                 }
30874             }
30875         });
30876         
30877         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30878         
30879         this.yearField = new Roo.bootstrap.ComboBox({
30880             allowBlank : this.yearAllowBlank,
30881             alwaysQuery : true,
30882             displayField : 'value',
30883             editable : false,
30884             fieldLabel : '',
30885             forceSelection : true,
30886             mode : 'local',
30887             placeholder : this.yearPlaceholder,
30888             selectOnFocus : true,
30889             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30890             triggerAction : 'all',
30891             typeAhead : true,
30892             valueField : 'value',
30893             store : new Roo.data.SimpleStore({
30894                 data : (function() {
30895                     var years = [];
30896                     _this.fireEvent('years', _this, years);
30897                     return years;
30898                 })(),
30899                 fields : [ 'value' ]
30900             }),
30901             listeners : {
30902                 select : function (_self, record, index)
30903                 {
30904                     _this.setValue(_this.getValue());
30905                 }
30906             }
30907         });
30908
30909         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30910     },
30911     
30912     setValue : function(v, format)
30913     {
30914         this.inputEl.dom.value = v;
30915         
30916         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30917         
30918         var d = Date.parseDate(v, f);
30919         
30920         if(!d){
30921             this.validate();
30922             return;
30923         }
30924         
30925         this.setDay(d.format(this.dayFormat));
30926         this.setMonth(d.format(this.monthFormat));
30927         this.setYear(d.format(this.yearFormat));
30928         
30929         this.validate();
30930         
30931         return;
30932     },
30933     
30934     setDay : function(v)
30935     {
30936         this.dayField.setValue(v);
30937         this.inputEl.dom.value = this.getValue();
30938         this.validate();
30939         return;
30940     },
30941     
30942     setMonth : function(v)
30943     {
30944         this.monthField.setValue(v, true);
30945         this.inputEl.dom.value = this.getValue();
30946         this.validate();
30947         return;
30948     },
30949     
30950     setYear : function(v)
30951     {
30952         this.yearField.setValue(v);
30953         this.inputEl.dom.value = this.getValue();
30954         this.validate();
30955         return;
30956     },
30957     
30958     getDay : function()
30959     {
30960         return this.dayField.getValue();
30961     },
30962     
30963     getMonth : function()
30964     {
30965         return this.monthField.getValue();
30966     },
30967     
30968     getYear : function()
30969     {
30970         return this.yearField.getValue();
30971     },
30972     
30973     getValue : function()
30974     {
30975         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30976         
30977         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30978         
30979         return date;
30980     },
30981     
30982     reset : function()
30983     {
30984         this.setDay('');
30985         this.setMonth('');
30986         this.setYear('');
30987         this.inputEl.dom.value = '';
30988         this.validate();
30989         return;
30990     },
30991     
30992     validate : function()
30993     {
30994         var d = this.dayField.validate();
30995         var m = this.monthField.validate();
30996         var y = this.yearField.validate();
30997         
30998         var valid = true;
30999         
31000         if(
31001                 (!this.dayAllowBlank && !d) ||
31002                 (!this.monthAllowBlank && !m) ||
31003                 (!this.yearAllowBlank && !y)
31004         ){
31005             valid = false;
31006         }
31007         
31008         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31009             return valid;
31010         }
31011         
31012         if(valid){
31013             this.markValid();
31014             return valid;
31015         }
31016         
31017         this.markInvalid();
31018         
31019         return valid;
31020     },
31021     
31022     markValid : function()
31023     {
31024         
31025         var label = this.el.select('label', true).first();
31026         var icon = this.el.select('i.fa-star', true).first();
31027
31028         if(label && icon){
31029             icon.remove();
31030         }
31031         
31032         this.fireEvent('valid', this);
31033     },
31034     
31035      /**
31036      * Mark this field as invalid
31037      * @param {String} msg The validation message
31038      */
31039     markInvalid : function(msg)
31040     {
31041         
31042         var label = this.el.select('label', true).first();
31043         var icon = this.el.select('i.fa-star', true).first();
31044
31045         if(label && !icon){
31046             this.el.select('.roo-date-split-field-label', true).createChild({
31047                 tag : 'i',
31048                 cls : 'text-danger fa fa-lg fa-star',
31049                 tooltip : 'This field is required',
31050                 style : 'margin-right:5px;'
31051             }, label, true);
31052         }
31053         
31054         this.fireEvent('invalid', this, msg);
31055     },
31056     
31057     clearInvalid : function()
31058     {
31059         var label = this.el.select('label', true).first();
31060         var icon = this.el.select('i.fa-star', true).first();
31061
31062         if(label && icon){
31063             icon.remove();
31064         }
31065         
31066         this.fireEvent('valid', this);
31067     },
31068     
31069     getName: function()
31070     {
31071         return this.name;
31072     }
31073     
31074 });
31075
31076  /**
31077  *
31078  * This is based on 
31079  * http://masonry.desandro.com
31080  *
31081  * The idea is to render all the bricks based on vertical width...
31082  *
31083  * The original code extends 'outlayer' - we might need to use that....
31084  * 
31085  */
31086
31087
31088 /**
31089  * @class Roo.bootstrap.LayoutMasonry
31090  * @extends Roo.bootstrap.Component
31091  * Bootstrap Layout Masonry class
31092  * 
31093  * @constructor
31094  * Create a new Element
31095  * @param {Object} config The config object
31096  */
31097
31098 Roo.bootstrap.LayoutMasonry = function(config){
31099     
31100     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31101     
31102     this.bricks = [];
31103     
31104     Roo.bootstrap.LayoutMasonry.register(this);
31105     
31106     this.addEvents({
31107         // raw events
31108         /**
31109          * @event layout
31110          * Fire after layout the items
31111          * @param {Roo.bootstrap.LayoutMasonry} this
31112          * @param {Roo.EventObject} e
31113          */
31114         "layout" : true
31115     });
31116     
31117 };
31118
31119 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31120     
31121     /**
31122      * @cfg {Boolean} isLayoutInstant = no animation?
31123      */   
31124     isLayoutInstant : false, // needed?
31125    
31126     /**
31127      * @cfg {Number} boxWidth  width of the columns
31128      */   
31129     boxWidth : 450,
31130     
31131       /**
31132      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31133      */   
31134     boxHeight : 0,
31135     
31136     /**
31137      * @cfg {Number} padWidth padding below box..
31138      */   
31139     padWidth : 10, 
31140     
31141     /**
31142      * @cfg {Number} gutter gutter width..
31143      */   
31144     gutter : 10,
31145     
31146      /**
31147      * @cfg {Number} maxCols maximum number of columns
31148      */   
31149     
31150     maxCols: 0,
31151     
31152     /**
31153      * @cfg {Boolean} isAutoInitial defalut true
31154      */   
31155     isAutoInitial : true, 
31156     
31157     containerWidth: 0,
31158     
31159     /**
31160      * @cfg {Boolean} isHorizontal defalut false
31161      */   
31162     isHorizontal : false, 
31163
31164     currentSize : null,
31165     
31166     tag: 'div',
31167     
31168     cls: '',
31169     
31170     bricks: null, //CompositeElement
31171     
31172     cols : 1,
31173     
31174     _isLayoutInited : false,
31175     
31176 //    isAlternative : false, // only use for vertical layout...
31177     
31178     /**
31179      * @cfg {Number} alternativePadWidth padding below box..
31180      */   
31181     alternativePadWidth : 50,
31182     
31183     selectedBrick : [],
31184     
31185     getAutoCreate : function(){
31186         
31187         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31188         
31189         var cfg = {
31190             tag: this.tag,
31191             cls: 'blog-masonary-wrapper ' + this.cls,
31192             cn : {
31193                 cls : 'mas-boxes masonary'
31194             }
31195         };
31196         
31197         return cfg;
31198     },
31199     
31200     getChildContainer: function( )
31201     {
31202         if (this.boxesEl) {
31203             return this.boxesEl;
31204         }
31205         
31206         this.boxesEl = this.el.select('.mas-boxes').first();
31207         
31208         return this.boxesEl;
31209     },
31210     
31211     
31212     initEvents : function()
31213     {
31214         var _this = this;
31215         
31216         if(this.isAutoInitial){
31217             Roo.log('hook children rendered');
31218             this.on('childrenrendered', function() {
31219                 Roo.log('children rendered');
31220                 _this.initial();
31221             } ,this);
31222         }
31223     },
31224     
31225     initial : function()
31226     {
31227         this.selectedBrick = [];
31228         
31229         this.currentSize = this.el.getBox(true);
31230         
31231         Roo.EventManager.onWindowResize(this.resize, this); 
31232
31233         if(!this.isAutoInitial){
31234             this.layout();
31235             return;
31236         }
31237         
31238         this.layout();
31239         
31240         return;
31241         //this.layout.defer(500,this);
31242         
31243     },
31244     
31245     resize : function()
31246     {
31247         var cs = this.el.getBox(true);
31248         
31249         if (
31250                 this.currentSize.width == cs.width && 
31251                 this.currentSize.x == cs.x && 
31252                 this.currentSize.height == cs.height && 
31253                 this.currentSize.y == cs.y 
31254         ) {
31255             Roo.log("no change in with or X or Y");
31256             return;
31257         }
31258         
31259         this.currentSize = cs;
31260         
31261         this.layout();
31262         
31263     },
31264     
31265     layout : function()
31266     {   
31267         this._resetLayout();
31268         
31269         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31270         
31271         this.layoutItems( isInstant );
31272       
31273         this._isLayoutInited = true;
31274         
31275         this.fireEvent('layout', this);
31276         
31277     },
31278     
31279     _resetLayout : function()
31280     {
31281         if(this.isHorizontal){
31282             this.horizontalMeasureColumns();
31283             return;
31284         }
31285         
31286         this.verticalMeasureColumns();
31287         
31288     },
31289     
31290     verticalMeasureColumns : function()
31291     {
31292         this.getContainerWidth();
31293         
31294 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31295 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31296 //            return;
31297 //        }
31298         
31299         var boxWidth = this.boxWidth + this.padWidth;
31300         
31301         if(this.containerWidth < this.boxWidth){
31302             boxWidth = this.containerWidth
31303         }
31304         
31305         var containerWidth = this.containerWidth;
31306         
31307         var cols = Math.floor(containerWidth / boxWidth);
31308         
31309         this.cols = Math.max( cols, 1 );
31310         
31311         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31312         
31313         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31314         
31315         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31316         
31317         this.colWidth = boxWidth + avail - this.padWidth;
31318         
31319         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31320         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31321     },
31322     
31323     horizontalMeasureColumns : function()
31324     {
31325         this.getContainerWidth();
31326         
31327         var boxWidth = this.boxWidth;
31328         
31329         if(this.containerWidth < boxWidth){
31330             boxWidth = this.containerWidth;
31331         }
31332         
31333         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31334         
31335         this.el.setHeight(boxWidth);
31336         
31337     },
31338     
31339     getContainerWidth : function()
31340     {
31341         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31342     },
31343     
31344     layoutItems : function( isInstant )
31345     {
31346         Roo.log(this.bricks);
31347         
31348         var items = Roo.apply([], this.bricks);
31349         
31350         if(this.isHorizontal){
31351             this._horizontalLayoutItems( items , isInstant );
31352             return;
31353         }
31354         
31355 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31356 //            this._verticalAlternativeLayoutItems( items , isInstant );
31357 //            return;
31358 //        }
31359         
31360         this._verticalLayoutItems( items , isInstant );
31361         
31362     },
31363     
31364     _verticalLayoutItems : function ( items , isInstant)
31365     {
31366         if ( !items || !items.length ) {
31367             return;
31368         }
31369         
31370         var standard = [
31371             ['xs', 'xs', 'xs', 'tall'],
31372             ['xs', 'xs', 'tall'],
31373             ['xs', 'xs', 'sm'],
31374             ['xs', 'xs', 'xs'],
31375             ['xs', 'tall'],
31376             ['xs', 'sm'],
31377             ['xs', 'xs'],
31378             ['xs'],
31379             
31380             ['sm', 'xs', 'xs'],
31381             ['sm', 'xs'],
31382             ['sm'],
31383             
31384             ['tall', 'xs', 'xs', 'xs'],
31385             ['tall', 'xs', 'xs'],
31386             ['tall', 'xs'],
31387             ['tall']
31388             
31389         ];
31390         
31391         var queue = [];
31392         
31393         var boxes = [];
31394         
31395         var box = [];
31396         
31397         Roo.each(items, function(item, k){
31398             
31399             switch (item.size) {
31400                 // these layouts take up a full box,
31401                 case 'md' :
31402                 case 'md-left' :
31403                 case 'md-right' :
31404                 case 'wide' :
31405                     
31406                     if(box.length){
31407                         boxes.push(box);
31408                         box = [];
31409                     }
31410                     
31411                     boxes.push([item]);
31412                     
31413                     break;
31414                     
31415                 case 'xs' :
31416                 case 'sm' :
31417                 case 'tall' :
31418                     
31419                     box.push(item);
31420                     
31421                     break;
31422                 default :
31423                     break;
31424                     
31425             }
31426             
31427         }, this);
31428         
31429         if(box.length){
31430             boxes.push(box);
31431             box = [];
31432         }
31433         
31434         var filterPattern = function(box, length)
31435         {
31436             if(!box.length){
31437                 return;
31438             }
31439             
31440             var match = false;
31441             
31442             var pattern = box.slice(0, length);
31443             
31444             var format = [];
31445             
31446             Roo.each(pattern, function(i){
31447                 format.push(i.size);
31448             }, this);
31449             
31450             Roo.each(standard, function(s){
31451                 
31452                 if(String(s) != String(format)){
31453                     return;
31454                 }
31455                 
31456                 match = true;
31457                 return false;
31458                 
31459             }, this);
31460             
31461             if(!match && length == 1){
31462                 return;
31463             }
31464             
31465             if(!match){
31466                 filterPattern(box, length - 1);
31467                 return;
31468             }
31469                 
31470             queue.push(pattern);
31471
31472             box = box.slice(length, box.length);
31473
31474             filterPattern(box, 4);
31475
31476             return;
31477             
31478         }
31479         
31480         Roo.each(boxes, function(box, k){
31481             
31482             if(!box.length){
31483                 return;
31484             }
31485             
31486             if(box.length == 1){
31487                 queue.push(box);
31488                 return;
31489             }
31490             
31491             filterPattern(box, 4);
31492             
31493         }, this);
31494         
31495         this._processVerticalLayoutQueue( queue, isInstant );
31496         
31497     },
31498     
31499 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31500 //    {
31501 //        if ( !items || !items.length ) {
31502 //            return;
31503 //        }
31504 //
31505 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31506 //        
31507 //    },
31508     
31509     _horizontalLayoutItems : function ( items , isInstant)
31510     {
31511         if ( !items || !items.length || items.length < 3) {
31512             return;
31513         }
31514         
31515         items.reverse();
31516         
31517         var eItems = items.slice(0, 3);
31518         
31519         items = items.slice(3, items.length);
31520         
31521         var standard = [
31522             ['xs', 'xs', 'xs', 'wide'],
31523             ['xs', 'xs', 'wide'],
31524             ['xs', 'xs', 'sm'],
31525             ['xs', 'xs', 'xs'],
31526             ['xs', 'wide'],
31527             ['xs', 'sm'],
31528             ['xs', 'xs'],
31529             ['xs'],
31530             
31531             ['sm', 'xs', 'xs'],
31532             ['sm', 'xs'],
31533             ['sm'],
31534             
31535             ['wide', 'xs', 'xs', 'xs'],
31536             ['wide', 'xs', 'xs'],
31537             ['wide', 'xs'],
31538             ['wide'],
31539             
31540             ['wide-thin']
31541         ];
31542         
31543         var queue = [];
31544         
31545         var boxes = [];
31546         
31547         var box = [];
31548         
31549         Roo.each(items, function(item, k){
31550             
31551             switch (item.size) {
31552                 case 'md' :
31553                 case 'md-left' :
31554                 case 'md-right' :
31555                 case 'tall' :
31556                     
31557                     if(box.length){
31558                         boxes.push(box);
31559                         box = [];
31560                     }
31561                     
31562                     boxes.push([item]);
31563                     
31564                     break;
31565                     
31566                 case 'xs' :
31567                 case 'sm' :
31568                 case 'wide' :
31569                 case 'wide-thin' :
31570                     
31571                     box.push(item);
31572                     
31573                     break;
31574                 default :
31575                     break;
31576                     
31577             }
31578             
31579         }, this);
31580         
31581         if(box.length){
31582             boxes.push(box);
31583             box = [];
31584         }
31585         
31586         var filterPattern = function(box, length)
31587         {
31588             if(!box.length){
31589                 return;
31590             }
31591             
31592             var match = false;
31593             
31594             var pattern = box.slice(0, length);
31595             
31596             var format = [];
31597             
31598             Roo.each(pattern, function(i){
31599                 format.push(i.size);
31600             }, this);
31601             
31602             Roo.each(standard, function(s){
31603                 
31604                 if(String(s) != String(format)){
31605                     return;
31606                 }
31607                 
31608                 match = true;
31609                 return false;
31610                 
31611             }, this);
31612             
31613             if(!match && length == 1){
31614                 return;
31615             }
31616             
31617             if(!match){
31618                 filterPattern(box, length - 1);
31619                 return;
31620             }
31621                 
31622             queue.push(pattern);
31623
31624             box = box.slice(length, box.length);
31625
31626             filterPattern(box, 4);
31627
31628             return;
31629             
31630         }
31631         
31632         Roo.each(boxes, function(box, k){
31633             
31634             if(!box.length){
31635                 return;
31636             }
31637             
31638             if(box.length == 1){
31639                 queue.push(box);
31640                 return;
31641             }
31642             
31643             filterPattern(box, 4);
31644             
31645         }, this);
31646         
31647         
31648         var prune = [];
31649         
31650         var pos = this.el.getBox(true);
31651         
31652         var minX = pos.x;
31653         
31654         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31655         
31656         var hit_end = false;
31657         
31658         Roo.each(queue, function(box){
31659             
31660             if(hit_end){
31661                 
31662                 Roo.each(box, function(b){
31663                 
31664                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31665                     b.el.hide();
31666
31667                 }, this);
31668
31669                 return;
31670             }
31671             
31672             var mx = 0;
31673             
31674             Roo.each(box, function(b){
31675                 
31676                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31677                 b.el.show();
31678
31679                 mx = Math.max(mx, b.x);
31680                 
31681             }, this);
31682             
31683             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31684             
31685             if(maxX < minX){
31686                 
31687                 Roo.each(box, function(b){
31688                 
31689                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31690                     b.el.hide();
31691                     
31692                 }, this);
31693                 
31694                 hit_end = true;
31695                 
31696                 return;
31697             }
31698             
31699             prune.push(box);
31700             
31701         }, this);
31702         
31703         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31704     },
31705     
31706     /** Sets position of item in DOM
31707     * @param {Element} item
31708     * @param {Number} x - horizontal position
31709     * @param {Number} y - vertical position
31710     * @param {Boolean} isInstant - disables transitions
31711     */
31712     _processVerticalLayoutQueue : function( queue, isInstant )
31713     {
31714         var pos = this.el.getBox(true);
31715         var x = pos.x;
31716         var y = pos.y;
31717         var maxY = [];
31718         
31719         for (var i = 0; i < this.cols; i++){
31720             maxY[i] = pos.y;
31721         }
31722         
31723         Roo.each(queue, function(box, k){
31724             
31725             var col = k % this.cols;
31726             
31727             Roo.each(box, function(b,kk){
31728                 
31729                 b.el.position('absolute');
31730                 
31731                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31732                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31733                 
31734                 if(b.size == 'md-left' || b.size == 'md-right'){
31735                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31736                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31737                 }
31738                 
31739                 b.el.setWidth(width);
31740                 b.el.setHeight(height);
31741                 // iframe?
31742                 b.el.select('iframe',true).setSize(width,height);
31743                 
31744             }, this);
31745             
31746             for (var i = 0; i < this.cols; i++){
31747                 
31748                 if(maxY[i] < maxY[col]){
31749                     col = i;
31750                     continue;
31751                 }
31752                 
31753                 col = Math.min(col, i);
31754                 
31755             }
31756             
31757             x = pos.x + col * (this.colWidth + this.padWidth);
31758             
31759             y = maxY[col];
31760             
31761             var positions = [];
31762             
31763             switch (box.length){
31764                 case 1 :
31765                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31766                     break;
31767                 case 2 :
31768                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31769                     break;
31770                 case 3 :
31771                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31772                     break;
31773                 case 4 :
31774                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31775                     break;
31776                 default :
31777                     break;
31778             }
31779             
31780             Roo.each(box, function(b,kk){
31781                 
31782                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31783                 
31784                 var sz = b.el.getSize();
31785                 
31786                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31787                 
31788             }, this);
31789             
31790         }, this);
31791         
31792         var mY = 0;
31793         
31794         for (var i = 0; i < this.cols; i++){
31795             mY = Math.max(mY, maxY[i]);
31796         }
31797         
31798         this.el.setHeight(mY - pos.y);
31799         
31800     },
31801     
31802 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31803 //    {
31804 //        var pos = this.el.getBox(true);
31805 //        var x = pos.x;
31806 //        var y = pos.y;
31807 //        var maxX = pos.right;
31808 //        
31809 //        var maxHeight = 0;
31810 //        
31811 //        Roo.each(items, function(item, k){
31812 //            
31813 //            var c = k % 2;
31814 //            
31815 //            item.el.position('absolute');
31816 //                
31817 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31818 //
31819 //            item.el.setWidth(width);
31820 //
31821 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31822 //
31823 //            item.el.setHeight(height);
31824 //            
31825 //            if(c == 0){
31826 //                item.el.setXY([x, y], isInstant ? false : true);
31827 //            } else {
31828 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31829 //            }
31830 //            
31831 //            y = y + height + this.alternativePadWidth;
31832 //            
31833 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31834 //            
31835 //        }, this);
31836 //        
31837 //        this.el.setHeight(maxHeight);
31838 //        
31839 //    },
31840     
31841     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31842     {
31843         var pos = this.el.getBox(true);
31844         
31845         var minX = pos.x;
31846         var minY = pos.y;
31847         
31848         var maxX = pos.right;
31849         
31850         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31851         
31852         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31853         
31854         Roo.each(queue, function(box, k){
31855             
31856             Roo.each(box, function(b, kk){
31857                 
31858                 b.el.position('absolute');
31859                 
31860                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31861                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31862                 
31863                 if(b.size == 'md-left' || b.size == 'md-right'){
31864                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31865                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31866                 }
31867                 
31868                 b.el.setWidth(width);
31869                 b.el.setHeight(height);
31870                 
31871             }, this);
31872             
31873             if(!box.length){
31874                 return;
31875             }
31876             
31877             var positions = [];
31878             
31879             switch (box.length){
31880                 case 1 :
31881                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31882                     break;
31883                 case 2 :
31884                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31885                     break;
31886                 case 3 :
31887                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31888                     break;
31889                 case 4 :
31890                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31891                     break;
31892                 default :
31893                     break;
31894             }
31895             
31896             Roo.each(box, function(b,kk){
31897                 
31898                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31899                 
31900                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31901                 
31902             }, this);
31903             
31904         }, this);
31905         
31906     },
31907     
31908     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31909     {
31910         Roo.each(eItems, function(b,k){
31911             
31912             b.size = (k == 0) ? 'sm' : 'xs';
31913             b.x = (k == 0) ? 2 : 1;
31914             b.y = (k == 0) ? 2 : 1;
31915             
31916             b.el.position('absolute');
31917             
31918             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31919                 
31920             b.el.setWidth(width);
31921             
31922             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31923             
31924             b.el.setHeight(height);
31925             
31926         }, this);
31927
31928         var positions = [];
31929         
31930         positions.push({
31931             x : maxX - this.unitWidth * 2 - this.gutter,
31932             y : minY
31933         });
31934         
31935         positions.push({
31936             x : maxX - this.unitWidth,
31937             y : minY + (this.unitWidth + this.gutter) * 2
31938         });
31939         
31940         positions.push({
31941             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31942             y : minY
31943         });
31944         
31945         Roo.each(eItems, function(b,k){
31946             
31947             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31948
31949         }, this);
31950         
31951     },
31952     
31953     getVerticalOneBoxColPositions : function(x, y, box)
31954     {
31955         var pos = [];
31956         
31957         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31958         
31959         if(box[0].size == 'md-left'){
31960             rand = 0;
31961         }
31962         
31963         if(box[0].size == 'md-right'){
31964             rand = 1;
31965         }
31966         
31967         pos.push({
31968             x : x + (this.unitWidth + this.gutter) * rand,
31969             y : y
31970         });
31971         
31972         return pos;
31973     },
31974     
31975     getVerticalTwoBoxColPositions : function(x, y, box)
31976     {
31977         var pos = [];
31978         
31979         if(box[0].size == 'xs'){
31980             
31981             pos.push({
31982                 x : x,
31983                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31984             });
31985
31986             pos.push({
31987                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31988                 y : y
31989             });
31990             
31991             return pos;
31992             
31993         }
31994         
31995         pos.push({
31996             x : x,
31997             y : y
31998         });
31999
32000         pos.push({
32001             x : x + (this.unitWidth + this.gutter) * 2,
32002             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32003         });
32004         
32005         return pos;
32006         
32007     },
32008     
32009     getVerticalThreeBoxColPositions : function(x, y, box)
32010     {
32011         var pos = [];
32012         
32013         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32014             
32015             pos.push({
32016                 x : x,
32017                 y : y
32018             });
32019
32020             pos.push({
32021                 x : x + (this.unitWidth + this.gutter) * 1,
32022                 y : y
32023             });
32024             
32025             pos.push({
32026                 x : x + (this.unitWidth + this.gutter) * 2,
32027                 y : y
32028             });
32029             
32030             return pos;
32031             
32032         }
32033         
32034         if(box[0].size == 'xs' && box[1].size == 'xs'){
32035             
32036             pos.push({
32037                 x : x,
32038                 y : y
32039             });
32040
32041             pos.push({
32042                 x : x,
32043                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32044             });
32045             
32046             pos.push({
32047                 x : x + (this.unitWidth + this.gutter) * 1,
32048                 y : y
32049             });
32050             
32051             return pos;
32052             
32053         }
32054         
32055         pos.push({
32056             x : x,
32057             y : y
32058         });
32059
32060         pos.push({
32061             x : x + (this.unitWidth + this.gutter) * 2,
32062             y : y
32063         });
32064
32065         pos.push({
32066             x : x + (this.unitWidth + this.gutter) * 2,
32067             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32068         });
32069             
32070         return pos;
32071         
32072     },
32073     
32074     getVerticalFourBoxColPositions : function(x, y, box)
32075     {
32076         var pos = [];
32077         
32078         if(box[0].size == 'xs'){
32079             
32080             pos.push({
32081                 x : x,
32082                 y : y
32083             });
32084
32085             pos.push({
32086                 x : x,
32087                 y : y + (this.unitHeight + this.gutter) * 1
32088             });
32089             
32090             pos.push({
32091                 x : x,
32092                 y : y + (this.unitHeight + this.gutter) * 2
32093             });
32094             
32095             pos.push({
32096                 x : x + (this.unitWidth + this.gutter) * 1,
32097                 y : y
32098             });
32099             
32100             return pos;
32101             
32102         }
32103         
32104         pos.push({
32105             x : x,
32106             y : y
32107         });
32108
32109         pos.push({
32110             x : x + (this.unitWidth + this.gutter) * 2,
32111             y : y
32112         });
32113
32114         pos.push({
32115             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32116             y : y + (this.unitHeight + this.gutter) * 1
32117         });
32118
32119         pos.push({
32120             x : x + (this.unitWidth + this.gutter) * 2,
32121             y : y + (this.unitWidth + this.gutter) * 2
32122         });
32123
32124         return pos;
32125         
32126     },
32127     
32128     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32129     {
32130         var pos = [];
32131         
32132         if(box[0].size == 'md-left'){
32133             pos.push({
32134                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32135                 y : minY
32136             });
32137             
32138             return pos;
32139         }
32140         
32141         if(box[0].size == 'md-right'){
32142             pos.push({
32143                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32144                 y : minY + (this.unitWidth + this.gutter) * 1
32145             });
32146             
32147             return pos;
32148         }
32149         
32150         var rand = Math.floor(Math.random() * (4 - box[0].y));
32151         
32152         pos.push({
32153             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32154             y : minY + (this.unitWidth + this.gutter) * rand
32155         });
32156         
32157         return pos;
32158         
32159     },
32160     
32161     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32162     {
32163         var pos = [];
32164         
32165         if(box[0].size == 'xs'){
32166             
32167             pos.push({
32168                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32169                 y : minY
32170             });
32171
32172             pos.push({
32173                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32174                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32175             });
32176             
32177             return pos;
32178             
32179         }
32180         
32181         pos.push({
32182             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32183             y : minY
32184         });
32185
32186         pos.push({
32187             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32188             y : minY + (this.unitWidth + this.gutter) * 2
32189         });
32190         
32191         return pos;
32192         
32193     },
32194     
32195     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32196     {
32197         var pos = [];
32198         
32199         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32200             
32201             pos.push({
32202                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32203                 y : minY
32204             });
32205
32206             pos.push({
32207                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32208                 y : minY + (this.unitWidth + this.gutter) * 1
32209             });
32210             
32211             pos.push({
32212                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32213                 y : minY + (this.unitWidth + this.gutter) * 2
32214             });
32215             
32216             return pos;
32217             
32218         }
32219         
32220         if(box[0].size == 'xs' && box[1].size == 'xs'){
32221             
32222             pos.push({
32223                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32224                 y : minY
32225             });
32226
32227             pos.push({
32228                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32229                 y : minY
32230             });
32231             
32232             pos.push({
32233                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32234                 y : minY + (this.unitWidth + this.gutter) * 1
32235             });
32236             
32237             return pos;
32238             
32239         }
32240         
32241         pos.push({
32242             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32243             y : minY
32244         });
32245
32246         pos.push({
32247             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32248             y : minY + (this.unitWidth + this.gutter) * 2
32249         });
32250
32251         pos.push({
32252             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32253             y : minY + (this.unitWidth + this.gutter) * 2
32254         });
32255             
32256         return pos;
32257         
32258     },
32259     
32260     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32261     {
32262         var pos = [];
32263         
32264         if(box[0].size == 'xs'){
32265             
32266             pos.push({
32267                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32268                 y : minY
32269             });
32270
32271             pos.push({
32272                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32273                 y : minY
32274             });
32275             
32276             pos.push({
32277                 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),
32278                 y : minY
32279             });
32280             
32281             pos.push({
32282                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32283                 y : minY + (this.unitWidth + this.gutter) * 1
32284             });
32285             
32286             return pos;
32287             
32288         }
32289         
32290         pos.push({
32291             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32292             y : minY
32293         });
32294         
32295         pos.push({
32296             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32297             y : minY + (this.unitWidth + this.gutter) * 2
32298         });
32299         
32300         pos.push({
32301             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32302             y : minY + (this.unitWidth + this.gutter) * 2
32303         });
32304         
32305         pos.push({
32306             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),
32307             y : minY + (this.unitWidth + this.gutter) * 2
32308         });
32309
32310         return pos;
32311         
32312     },
32313     
32314     /**
32315     * remove a Masonry Brick
32316     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32317     */
32318     removeBrick : function(brick_id)
32319     {
32320         if (!brick_id) {
32321             return;
32322         }
32323         
32324         for (var i = 0; i<this.bricks.length; i++) {
32325             if (this.bricks[i].id == brick_id) {
32326                 this.bricks.splice(i,1);
32327                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32328                 this.initial();
32329             }
32330         }
32331     },
32332     
32333     /**
32334     * adds a Masonry Brick
32335     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32336     */
32337     addBrick : function(cfg)
32338     {
32339         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32340         //this.register(cn);
32341         cn.parentId = this.id;
32342         cn.render(this.el);
32343         return cn;
32344     },
32345     
32346     /**
32347     * register a Masonry Brick
32348     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32349     */
32350     
32351     register : function(brick)
32352     {
32353         this.bricks.push(brick);
32354         brick.masonryId = this.id;
32355     },
32356     
32357     /**
32358     * clear all the Masonry Brick
32359     */
32360     clearAll : function()
32361     {
32362         this.bricks = [];
32363         //this.getChildContainer().dom.innerHTML = "";
32364         this.el.dom.innerHTML = '';
32365     },
32366     
32367     getSelected : function()
32368     {
32369         if (!this.selectedBrick) {
32370             return false;
32371         }
32372         
32373         return this.selectedBrick;
32374     }
32375 });
32376
32377 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32378     
32379     groups: {},
32380      /**
32381     * register a Masonry Layout
32382     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32383     */
32384     
32385     register : function(layout)
32386     {
32387         this.groups[layout.id] = layout;
32388     },
32389     /**
32390     * fetch a  Masonry Layout based on the masonry layout ID
32391     * @param {string} the masonry layout to add
32392     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32393     */
32394     
32395     get: function(layout_id) {
32396         if (typeof(this.groups[layout_id]) == 'undefined') {
32397             return false;
32398         }
32399         return this.groups[layout_id] ;
32400     }
32401     
32402     
32403     
32404 });
32405
32406  
32407
32408  /**
32409  *
32410  * This is based on 
32411  * http://masonry.desandro.com
32412  *
32413  * The idea is to render all the bricks based on vertical width...
32414  *
32415  * The original code extends 'outlayer' - we might need to use that....
32416  * 
32417  */
32418
32419
32420 /**
32421  * @class Roo.bootstrap.LayoutMasonryAuto
32422  * @extends Roo.bootstrap.Component
32423  * Bootstrap Layout Masonry class
32424  * 
32425  * @constructor
32426  * Create a new Element
32427  * @param {Object} config The config object
32428  */
32429
32430 Roo.bootstrap.LayoutMasonryAuto = function(config){
32431     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32432 };
32433
32434 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32435     
32436       /**
32437      * @cfg {Boolean} isFitWidth  - resize the width..
32438      */   
32439     isFitWidth : false,  // options..
32440     /**
32441      * @cfg {Boolean} isOriginLeft = left align?
32442      */   
32443     isOriginLeft : true,
32444     /**
32445      * @cfg {Boolean} isOriginTop = top align?
32446      */   
32447     isOriginTop : false,
32448     /**
32449      * @cfg {Boolean} isLayoutInstant = no animation?
32450      */   
32451     isLayoutInstant : false, // needed?
32452     /**
32453      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32454      */   
32455     isResizingContainer : true,
32456     /**
32457      * @cfg {Number} columnWidth  width of the columns 
32458      */   
32459     
32460     columnWidth : 0,
32461     
32462     /**
32463      * @cfg {Number} maxCols maximum number of columns
32464      */   
32465     
32466     maxCols: 0,
32467     /**
32468      * @cfg {Number} padHeight padding below box..
32469      */   
32470     
32471     padHeight : 10, 
32472     
32473     /**
32474      * @cfg {Boolean} isAutoInitial defalut true
32475      */   
32476     
32477     isAutoInitial : true, 
32478     
32479     // private?
32480     gutter : 0,
32481     
32482     containerWidth: 0,
32483     initialColumnWidth : 0,
32484     currentSize : null,
32485     
32486     colYs : null, // array.
32487     maxY : 0,
32488     padWidth: 10,
32489     
32490     
32491     tag: 'div',
32492     cls: '',
32493     bricks: null, //CompositeElement
32494     cols : 0, // array?
32495     // element : null, // wrapped now this.el
32496     _isLayoutInited : null, 
32497     
32498     
32499     getAutoCreate : function(){
32500         
32501         var cfg = {
32502             tag: this.tag,
32503             cls: 'blog-masonary-wrapper ' + this.cls,
32504             cn : {
32505                 cls : 'mas-boxes masonary'
32506             }
32507         };
32508         
32509         return cfg;
32510     },
32511     
32512     getChildContainer: function( )
32513     {
32514         if (this.boxesEl) {
32515             return this.boxesEl;
32516         }
32517         
32518         this.boxesEl = this.el.select('.mas-boxes').first();
32519         
32520         return this.boxesEl;
32521     },
32522     
32523     
32524     initEvents : function()
32525     {
32526         var _this = this;
32527         
32528         if(this.isAutoInitial){
32529             Roo.log('hook children rendered');
32530             this.on('childrenrendered', function() {
32531                 Roo.log('children rendered');
32532                 _this.initial();
32533             } ,this);
32534         }
32535         
32536     },
32537     
32538     initial : function()
32539     {
32540         this.reloadItems();
32541
32542         this.currentSize = this.el.getBox(true);
32543
32544         /// was window resize... - let's see if this works..
32545         Roo.EventManager.onWindowResize(this.resize, this); 
32546
32547         if(!this.isAutoInitial){
32548             this.layout();
32549             return;
32550         }
32551         
32552         this.layout.defer(500,this);
32553     },
32554     
32555     reloadItems: function()
32556     {
32557         this.bricks = this.el.select('.masonry-brick', true);
32558         
32559         this.bricks.each(function(b) {
32560             //Roo.log(b.getSize());
32561             if (!b.attr('originalwidth')) {
32562                 b.attr('originalwidth',  b.getSize().width);
32563             }
32564             
32565         });
32566         
32567         Roo.log(this.bricks.elements.length);
32568     },
32569     
32570     resize : function()
32571     {
32572         Roo.log('resize');
32573         var cs = this.el.getBox(true);
32574         
32575         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32576             Roo.log("no change in with or X");
32577             return;
32578         }
32579         this.currentSize = cs;
32580         this.layout();
32581     },
32582     
32583     layout : function()
32584     {
32585          Roo.log('layout');
32586         this._resetLayout();
32587         //this._manageStamps();
32588       
32589         // don't animate first layout
32590         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32591         this.layoutItems( isInstant );
32592       
32593         // flag for initalized
32594         this._isLayoutInited = true;
32595     },
32596     
32597     layoutItems : function( isInstant )
32598     {
32599         //var items = this._getItemsForLayout( this.items );
32600         // original code supports filtering layout items.. we just ignore it..
32601         
32602         this._layoutItems( this.bricks , isInstant );
32603       
32604         this._postLayout();
32605     },
32606     _layoutItems : function ( items , isInstant)
32607     {
32608        //this.fireEvent( 'layout', this, items );
32609     
32610
32611         if ( !items || !items.elements.length ) {
32612           // no items, emit event with empty array
32613             return;
32614         }
32615
32616         var queue = [];
32617         items.each(function(item) {
32618             Roo.log("layout item");
32619             Roo.log(item);
32620             // get x/y object from method
32621             var position = this._getItemLayoutPosition( item );
32622             // enqueue
32623             position.item = item;
32624             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32625             queue.push( position );
32626         }, this);
32627       
32628         this._processLayoutQueue( queue );
32629     },
32630     /** Sets position of item in DOM
32631     * @param {Element} item
32632     * @param {Number} x - horizontal position
32633     * @param {Number} y - vertical position
32634     * @param {Boolean} isInstant - disables transitions
32635     */
32636     _processLayoutQueue : function( queue )
32637     {
32638         for ( var i=0, len = queue.length; i < len; i++ ) {
32639             var obj = queue[i];
32640             obj.item.position('absolute');
32641             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32642         }
32643     },
32644       
32645     
32646     /**
32647     * Any logic you want to do after each layout,
32648     * i.e. size the container
32649     */
32650     _postLayout : function()
32651     {
32652         this.resizeContainer();
32653     },
32654     
32655     resizeContainer : function()
32656     {
32657         if ( !this.isResizingContainer ) {
32658             return;
32659         }
32660         var size = this._getContainerSize();
32661         if ( size ) {
32662             this.el.setSize(size.width,size.height);
32663             this.boxesEl.setSize(size.width,size.height);
32664         }
32665     },
32666     
32667     
32668     
32669     _resetLayout : function()
32670     {
32671         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32672         this.colWidth = this.el.getWidth();
32673         //this.gutter = this.el.getWidth(); 
32674         
32675         this.measureColumns();
32676
32677         // reset column Y
32678         var i = this.cols;
32679         this.colYs = [];
32680         while (i--) {
32681             this.colYs.push( 0 );
32682         }
32683     
32684         this.maxY = 0;
32685     },
32686
32687     measureColumns : function()
32688     {
32689         this.getContainerWidth();
32690       // if columnWidth is 0, default to outerWidth of first item
32691         if ( !this.columnWidth ) {
32692             var firstItem = this.bricks.first();
32693             Roo.log(firstItem);
32694             this.columnWidth  = this.containerWidth;
32695             if (firstItem && firstItem.attr('originalwidth') ) {
32696                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32697             }
32698             // columnWidth fall back to item of first element
32699             Roo.log("set column width?");
32700                         this.initialColumnWidth = this.columnWidth  ;
32701
32702             // if first elem has no width, default to size of container
32703             
32704         }
32705         
32706         
32707         if (this.initialColumnWidth) {
32708             this.columnWidth = this.initialColumnWidth;
32709         }
32710         
32711         
32712             
32713         // column width is fixed at the top - however if container width get's smaller we should
32714         // reduce it...
32715         
32716         // this bit calcs how man columns..
32717             
32718         var columnWidth = this.columnWidth += this.gutter;
32719       
32720         // calculate columns
32721         var containerWidth = this.containerWidth + this.gutter;
32722         
32723         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32724         // fix rounding errors, typically with gutters
32725         var excess = columnWidth - containerWidth % columnWidth;
32726         
32727         
32728         // if overshoot is less than a pixel, round up, otherwise floor it
32729         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32730         cols = Math[ mathMethod ]( cols );
32731         this.cols = Math.max( cols, 1 );
32732         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32733         
32734          // padding positioning..
32735         var totalColWidth = this.cols * this.columnWidth;
32736         var padavail = this.containerWidth - totalColWidth;
32737         // so for 2 columns - we need 3 'pads'
32738         
32739         var padNeeded = (1+this.cols) * this.padWidth;
32740         
32741         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32742         
32743         this.columnWidth += padExtra
32744         //this.padWidth = Math.floor(padavail /  ( this.cols));
32745         
32746         // adjust colum width so that padding is fixed??
32747         
32748         // we have 3 columns ... total = width * 3
32749         // we have X left over... that should be used by 
32750         
32751         //if (this.expandC) {
32752             
32753         //}
32754         
32755         
32756         
32757     },
32758     
32759     getContainerWidth : function()
32760     {
32761        /* // container is parent if fit width
32762         var container = this.isFitWidth ? this.element.parentNode : this.element;
32763         // check that this.size and size are there
32764         // IE8 triggers resize on body size change, so they might not be
32765         
32766         var size = getSize( container );  //FIXME
32767         this.containerWidth = size && size.innerWidth; //FIXME
32768         */
32769          
32770         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32771         
32772     },
32773     
32774     _getItemLayoutPosition : function( item )  // what is item?
32775     {
32776         // we resize the item to our columnWidth..
32777       
32778         item.setWidth(this.columnWidth);
32779         item.autoBoxAdjust  = false;
32780         
32781         var sz = item.getSize();
32782  
32783         // how many columns does this brick span
32784         var remainder = this.containerWidth % this.columnWidth;
32785         
32786         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32787         // round if off by 1 pixel, otherwise use ceil
32788         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32789         colSpan = Math.min( colSpan, this.cols );
32790         
32791         // normally this should be '1' as we dont' currently allow multi width columns..
32792         
32793         var colGroup = this._getColGroup( colSpan );
32794         // get the minimum Y value from the columns
32795         var minimumY = Math.min.apply( Math, colGroup );
32796         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32797         
32798         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32799          
32800         // position the brick
32801         var position = {
32802             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32803             y: this.currentSize.y + minimumY + this.padHeight
32804         };
32805         
32806         Roo.log(position);
32807         // apply setHeight to necessary columns
32808         var setHeight = minimumY + sz.height + this.padHeight;
32809         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32810         
32811         var setSpan = this.cols + 1 - colGroup.length;
32812         for ( var i = 0; i < setSpan; i++ ) {
32813           this.colYs[ shortColIndex + i ] = setHeight ;
32814         }
32815       
32816         return position;
32817     },
32818     
32819     /**
32820      * @param {Number} colSpan - number of columns the element spans
32821      * @returns {Array} colGroup
32822      */
32823     _getColGroup : function( colSpan )
32824     {
32825         if ( colSpan < 2 ) {
32826           // if brick spans only one column, use all the column Ys
32827           return this.colYs;
32828         }
32829       
32830         var colGroup = [];
32831         // how many different places could this brick fit horizontally
32832         var groupCount = this.cols + 1 - colSpan;
32833         // for each group potential horizontal position
32834         for ( var i = 0; i < groupCount; i++ ) {
32835           // make an array of colY values for that one group
32836           var groupColYs = this.colYs.slice( i, i + colSpan );
32837           // and get the max value of the array
32838           colGroup[i] = Math.max.apply( Math, groupColYs );
32839         }
32840         return colGroup;
32841     },
32842     /*
32843     _manageStamp : function( stamp )
32844     {
32845         var stampSize =  stamp.getSize();
32846         var offset = stamp.getBox();
32847         // get the columns that this stamp affects
32848         var firstX = this.isOriginLeft ? offset.x : offset.right;
32849         var lastX = firstX + stampSize.width;
32850         var firstCol = Math.floor( firstX / this.columnWidth );
32851         firstCol = Math.max( 0, firstCol );
32852         
32853         var lastCol = Math.floor( lastX / this.columnWidth );
32854         // lastCol should not go over if multiple of columnWidth #425
32855         lastCol -= lastX % this.columnWidth ? 0 : 1;
32856         lastCol = Math.min( this.cols - 1, lastCol );
32857         
32858         // set colYs to bottom of the stamp
32859         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32860             stampSize.height;
32861             
32862         for ( var i = firstCol; i <= lastCol; i++ ) {
32863           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32864         }
32865     },
32866     */
32867     
32868     _getContainerSize : function()
32869     {
32870         this.maxY = Math.max.apply( Math, this.colYs );
32871         var size = {
32872             height: this.maxY
32873         };
32874       
32875         if ( this.isFitWidth ) {
32876             size.width = this._getContainerFitWidth();
32877         }
32878       
32879         return size;
32880     },
32881     
32882     _getContainerFitWidth : function()
32883     {
32884         var unusedCols = 0;
32885         // count unused columns
32886         var i = this.cols;
32887         while ( --i ) {
32888           if ( this.colYs[i] !== 0 ) {
32889             break;
32890           }
32891           unusedCols++;
32892         }
32893         // fit container to columns that have been used
32894         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32895     },
32896     
32897     needsResizeLayout : function()
32898     {
32899         var previousWidth = this.containerWidth;
32900         this.getContainerWidth();
32901         return previousWidth !== this.containerWidth;
32902     }
32903  
32904 });
32905
32906  
32907
32908  /*
32909  * - LGPL
32910  *
32911  * element
32912  * 
32913  */
32914
32915 /**
32916  * @class Roo.bootstrap.MasonryBrick
32917  * @extends Roo.bootstrap.Component
32918  * Bootstrap MasonryBrick class
32919  * 
32920  * @constructor
32921  * Create a new MasonryBrick
32922  * @param {Object} config The config object
32923  */
32924
32925 Roo.bootstrap.MasonryBrick = function(config){
32926     
32927     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32928     
32929     Roo.bootstrap.MasonryBrick.register(this);
32930     
32931     this.addEvents({
32932         // raw events
32933         /**
32934          * @event click
32935          * When a MasonryBrick is clcik
32936          * @param {Roo.bootstrap.MasonryBrick} this
32937          * @param {Roo.EventObject} e
32938          */
32939         "click" : true
32940     });
32941 };
32942
32943 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32944     
32945     /**
32946      * @cfg {String} title
32947      */   
32948     title : '',
32949     /**
32950      * @cfg {String} html
32951      */   
32952     html : '',
32953     /**
32954      * @cfg {String} bgimage
32955      */   
32956     bgimage : '',
32957     /**
32958      * @cfg {String} videourl
32959      */   
32960     videourl : '',
32961     /**
32962      * @cfg {String} cls
32963      */   
32964     cls : '',
32965     /**
32966      * @cfg {String} href
32967      */   
32968     href : '',
32969     /**
32970      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32971      */   
32972     size : 'xs',
32973     
32974     /**
32975      * @cfg {String} placetitle (center|bottom)
32976      */   
32977     placetitle : '',
32978     
32979     /**
32980      * @cfg {Boolean} isFitContainer defalut true
32981      */   
32982     isFitContainer : true, 
32983     
32984     /**
32985      * @cfg {Boolean} preventDefault defalut false
32986      */   
32987     preventDefault : false, 
32988     
32989     /**
32990      * @cfg {Boolean} inverse defalut false
32991      */   
32992     maskInverse : false, 
32993     
32994     getAutoCreate : function()
32995     {
32996         if(!this.isFitContainer){
32997             return this.getSplitAutoCreate();
32998         }
32999         
33000         var cls = 'masonry-brick masonry-brick-full';
33001         
33002         if(this.href.length){
33003             cls += ' masonry-brick-link';
33004         }
33005         
33006         if(this.bgimage.length){
33007             cls += ' masonry-brick-image';
33008         }
33009         
33010         if(this.maskInverse){
33011             cls += ' mask-inverse';
33012         }
33013         
33014         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33015             cls += ' enable-mask';
33016         }
33017         
33018         if(this.size){
33019             cls += ' masonry-' + this.size + '-brick';
33020         }
33021         
33022         if(this.placetitle.length){
33023             
33024             switch (this.placetitle) {
33025                 case 'center' :
33026                     cls += ' masonry-center-title';
33027                     break;
33028                 case 'bottom' :
33029                     cls += ' masonry-bottom-title';
33030                     break;
33031                 default:
33032                     break;
33033             }
33034             
33035         } else {
33036             if(!this.html.length && !this.bgimage.length){
33037                 cls += ' masonry-center-title';
33038             }
33039
33040             if(!this.html.length && this.bgimage.length){
33041                 cls += ' masonry-bottom-title';
33042             }
33043         }
33044         
33045         if(this.cls){
33046             cls += ' ' + this.cls;
33047         }
33048         
33049         var cfg = {
33050             tag: (this.href.length) ? 'a' : 'div',
33051             cls: cls,
33052             cn: [
33053                 {
33054                     tag: 'div',
33055                     cls: 'masonry-brick-mask'
33056                 },
33057                 {
33058                     tag: 'div',
33059                     cls: 'masonry-brick-paragraph',
33060                     cn: []
33061                 }
33062             ]
33063         };
33064         
33065         if(this.href.length){
33066             cfg.href = this.href;
33067         }
33068         
33069         var cn = cfg.cn[1].cn;
33070         
33071         if(this.title.length){
33072             cn.push({
33073                 tag: 'h4',
33074                 cls: 'masonry-brick-title',
33075                 html: this.title
33076             });
33077         }
33078         
33079         if(this.html.length){
33080             cn.push({
33081                 tag: 'p',
33082                 cls: 'masonry-brick-text',
33083                 html: this.html
33084             });
33085         }
33086         
33087         if (!this.title.length && !this.html.length) {
33088             cfg.cn[1].cls += ' hide';
33089         }
33090         
33091         if(this.bgimage.length){
33092             cfg.cn.push({
33093                 tag: 'img',
33094                 cls: 'masonry-brick-image-view',
33095                 src: this.bgimage
33096             });
33097         }
33098         
33099         if(this.videourl.length){
33100             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33101             // youtube support only?
33102             cfg.cn.push({
33103                 tag: 'iframe',
33104                 cls: 'masonry-brick-image-view',
33105                 src: vurl,
33106                 frameborder : 0,
33107                 allowfullscreen : true
33108             });
33109         }
33110         
33111         return cfg;
33112         
33113     },
33114     
33115     getSplitAutoCreate : function()
33116     {
33117         var cls = 'masonry-brick masonry-brick-split';
33118         
33119         if(this.href.length){
33120             cls += ' masonry-brick-link';
33121         }
33122         
33123         if(this.bgimage.length){
33124             cls += ' masonry-brick-image';
33125         }
33126         
33127         if(this.size){
33128             cls += ' masonry-' + this.size + '-brick';
33129         }
33130         
33131         switch (this.placetitle) {
33132             case 'center' :
33133                 cls += ' masonry-center-title';
33134                 break;
33135             case 'bottom' :
33136                 cls += ' masonry-bottom-title';
33137                 break;
33138             default:
33139                 if(!this.bgimage.length){
33140                     cls += ' masonry-center-title';
33141                 }
33142
33143                 if(this.bgimage.length){
33144                     cls += ' masonry-bottom-title';
33145                 }
33146                 break;
33147         }
33148         
33149         if(this.cls){
33150             cls += ' ' + this.cls;
33151         }
33152         
33153         var cfg = {
33154             tag: (this.href.length) ? 'a' : 'div',
33155             cls: cls,
33156             cn: [
33157                 {
33158                     tag: 'div',
33159                     cls: 'masonry-brick-split-head',
33160                     cn: [
33161                         {
33162                             tag: 'div',
33163                             cls: 'masonry-brick-paragraph',
33164                             cn: []
33165                         }
33166                     ]
33167                 },
33168                 {
33169                     tag: 'div',
33170                     cls: 'masonry-brick-split-body',
33171                     cn: []
33172                 }
33173             ]
33174         };
33175         
33176         if(this.href.length){
33177             cfg.href = this.href;
33178         }
33179         
33180         if(this.title.length){
33181             cfg.cn[0].cn[0].cn.push({
33182                 tag: 'h4',
33183                 cls: 'masonry-brick-title',
33184                 html: this.title
33185             });
33186         }
33187         
33188         if(this.html.length){
33189             cfg.cn[1].cn.push({
33190                 tag: 'p',
33191                 cls: 'masonry-brick-text',
33192                 html: this.html
33193             });
33194         }
33195
33196         if(this.bgimage.length){
33197             cfg.cn[0].cn.push({
33198                 tag: 'img',
33199                 cls: 'masonry-brick-image-view',
33200                 src: this.bgimage
33201             });
33202         }
33203         
33204         if(this.videourl.length){
33205             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33206             // youtube support only?
33207             cfg.cn[0].cn.cn.push({
33208                 tag: 'iframe',
33209                 cls: 'masonry-brick-image-view',
33210                 src: vurl,
33211                 frameborder : 0,
33212                 allowfullscreen : true
33213             });
33214         }
33215         
33216         return cfg;
33217     },
33218     
33219     initEvents: function() 
33220     {
33221         switch (this.size) {
33222             case 'xs' :
33223                 this.x = 1;
33224                 this.y = 1;
33225                 break;
33226             case 'sm' :
33227                 this.x = 2;
33228                 this.y = 2;
33229                 break;
33230             case 'md' :
33231             case 'md-left' :
33232             case 'md-right' :
33233                 this.x = 3;
33234                 this.y = 3;
33235                 break;
33236             case 'tall' :
33237                 this.x = 2;
33238                 this.y = 3;
33239                 break;
33240             case 'wide' :
33241                 this.x = 3;
33242                 this.y = 2;
33243                 break;
33244             case 'wide-thin' :
33245                 this.x = 3;
33246                 this.y = 1;
33247                 break;
33248                         
33249             default :
33250                 break;
33251         }
33252         
33253         if(Roo.isTouch){
33254             this.el.on('touchstart', this.onTouchStart, this);
33255             this.el.on('touchmove', this.onTouchMove, this);
33256             this.el.on('touchend', this.onTouchEnd, this);
33257             this.el.on('contextmenu', this.onContextMenu, this);
33258         } else {
33259             this.el.on('mouseenter'  ,this.enter, this);
33260             this.el.on('mouseleave', this.leave, this);
33261             this.el.on('click', this.onClick, this);
33262         }
33263         
33264         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33265             this.parent().bricks.push(this);   
33266         }
33267         
33268     },
33269     
33270     onClick: function(e, el)
33271     {
33272         var time = this.endTimer - this.startTimer;
33273         // Roo.log(e.preventDefault());
33274         if(Roo.isTouch){
33275             if(time > 1000){
33276                 e.preventDefault();
33277                 return;
33278             }
33279         }
33280         
33281         if(!this.preventDefault){
33282             return;
33283         }
33284         
33285         e.preventDefault();
33286         
33287         if (this.activeClass != '') {
33288             this.selectBrick();
33289         }
33290         
33291         this.fireEvent('click', this, e);
33292     },
33293     
33294     enter: function(e, el)
33295     {
33296         e.preventDefault();
33297         
33298         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33299             return;
33300         }
33301         
33302         if(this.bgimage.length && this.html.length){
33303             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33304         }
33305     },
33306     
33307     leave: function(e, el)
33308     {
33309         e.preventDefault();
33310         
33311         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33312             return;
33313         }
33314         
33315         if(this.bgimage.length && this.html.length){
33316             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33317         }
33318     },
33319     
33320     onTouchStart: function(e, el)
33321     {
33322 //        e.preventDefault();
33323         
33324         this.touchmoved = false;
33325         
33326         if(!this.isFitContainer){
33327             return;
33328         }
33329         
33330         if(!this.bgimage.length || !this.html.length){
33331             return;
33332         }
33333         
33334         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33335         
33336         this.timer = new Date().getTime();
33337         
33338     },
33339     
33340     onTouchMove: function(e, el)
33341     {
33342         this.touchmoved = true;
33343     },
33344     
33345     onContextMenu : function(e,el)
33346     {
33347         e.preventDefault();
33348         e.stopPropagation();
33349         return false;
33350     },
33351     
33352     onTouchEnd: function(e, el)
33353     {
33354 //        e.preventDefault();
33355         
33356         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33357         
33358             this.leave(e,el);
33359             
33360             return;
33361         }
33362         
33363         if(!this.bgimage.length || !this.html.length){
33364             
33365             if(this.href.length){
33366                 window.location.href = this.href;
33367             }
33368             
33369             return;
33370         }
33371         
33372         if(!this.isFitContainer){
33373             return;
33374         }
33375         
33376         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33377         
33378         window.location.href = this.href;
33379     },
33380     
33381     //selection on single brick only
33382     selectBrick : function() {
33383         
33384         if (!this.parentId) {
33385             return;
33386         }
33387         
33388         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33389         var index = m.selectedBrick.indexOf(this.id);
33390         
33391         if ( index > -1) {
33392             m.selectedBrick.splice(index,1);
33393             this.el.removeClass(this.activeClass);
33394             return;
33395         }
33396         
33397         for(var i = 0; i < m.selectedBrick.length; i++) {
33398             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33399             b.el.removeClass(b.activeClass);
33400         }
33401         
33402         m.selectedBrick = [];
33403         
33404         m.selectedBrick.push(this.id);
33405         this.el.addClass(this.activeClass);
33406         return;
33407     },
33408     
33409     isSelected : function(){
33410         return this.el.hasClass(this.activeClass);
33411         
33412     }
33413 });
33414
33415 Roo.apply(Roo.bootstrap.MasonryBrick, {
33416     
33417     //groups: {},
33418     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33419      /**
33420     * register a Masonry Brick
33421     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33422     */
33423     
33424     register : function(brick)
33425     {
33426         //this.groups[brick.id] = brick;
33427         this.groups.add(brick.id, brick);
33428     },
33429     /**
33430     * fetch a  masonry brick based on the masonry brick ID
33431     * @param {string} the masonry brick to add
33432     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33433     */
33434     
33435     get: function(brick_id) 
33436     {
33437         // if (typeof(this.groups[brick_id]) == 'undefined') {
33438         //     return false;
33439         // }
33440         // return this.groups[brick_id] ;
33441         
33442         if(this.groups.key(brick_id)) {
33443             return this.groups.key(brick_id);
33444         }
33445         
33446         return false;
33447     }
33448     
33449     
33450     
33451 });
33452
33453  /*
33454  * - LGPL
33455  *
33456  * element
33457  * 
33458  */
33459
33460 /**
33461  * @class Roo.bootstrap.Brick
33462  * @extends Roo.bootstrap.Component
33463  * Bootstrap Brick class
33464  * 
33465  * @constructor
33466  * Create a new Brick
33467  * @param {Object} config The config object
33468  */
33469
33470 Roo.bootstrap.Brick = function(config){
33471     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33472     
33473     this.addEvents({
33474         // raw events
33475         /**
33476          * @event click
33477          * When a Brick is click
33478          * @param {Roo.bootstrap.Brick} this
33479          * @param {Roo.EventObject} e
33480          */
33481         "click" : true
33482     });
33483 };
33484
33485 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33486     
33487     /**
33488      * @cfg {String} title
33489      */   
33490     title : '',
33491     /**
33492      * @cfg {String} html
33493      */   
33494     html : '',
33495     /**
33496      * @cfg {String} bgimage
33497      */   
33498     bgimage : '',
33499     /**
33500      * @cfg {String} cls
33501      */   
33502     cls : '',
33503     /**
33504      * @cfg {String} href
33505      */   
33506     href : '',
33507     /**
33508      * @cfg {String} video
33509      */   
33510     video : '',
33511     /**
33512      * @cfg {Boolean} square
33513      */   
33514     square : true,
33515     
33516     getAutoCreate : function()
33517     {
33518         var cls = 'roo-brick';
33519         
33520         if(this.href.length){
33521             cls += ' roo-brick-link';
33522         }
33523         
33524         if(this.bgimage.length){
33525             cls += ' roo-brick-image';
33526         }
33527         
33528         if(!this.html.length && !this.bgimage.length){
33529             cls += ' roo-brick-center-title';
33530         }
33531         
33532         if(!this.html.length && this.bgimage.length){
33533             cls += ' roo-brick-bottom-title';
33534         }
33535         
33536         if(this.cls){
33537             cls += ' ' + this.cls;
33538         }
33539         
33540         var cfg = {
33541             tag: (this.href.length) ? 'a' : 'div',
33542             cls: cls,
33543             cn: [
33544                 {
33545                     tag: 'div',
33546                     cls: 'roo-brick-paragraph',
33547                     cn: []
33548                 }
33549             ]
33550         };
33551         
33552         if(this.href.length){
33553             cfg.href = this.href;
33554         }
33555         
33556         var cn = cfg.cn[0].cn;
33557         
33558         if(this.title.length){
33559             cn.push({
33560                 tag: 'h4',
33561                 cls: 'roo-brick-title',
33562                 html: this.title
33563             });
33564         }
33565         
33566         if(this.html.length){
33567             cn.push({
33568                 tag: 'p',
33569                 cls: 'roo-brick-text',
33570                 html: this.html
33571             });
33572         } else {
33573             cn.cls += ' hide';
33574         }
33575         
33576         if(this.bgimage.length){
33577             cfg.cn.push({
33578                 tag: 'img',
33579                 cls: 'roo-brick-image-view',
33580                 src: this.bgimage
33581             });
33582         }
33583         
33584         return cfg;
33585     },
33586     
33587     initEvents: function() 
33588     {
33589         if(this.title.length || this.html.length){
33590             this.el.on('mouseenter'  ,this.enter, this);
33591             this.el.on('mouseleave', this.leave, this);
33592         }
33593         
33594         Roo.EventManager.onWindowResize(this.resize, this); 
33595         
33596         if(this.bgimage.length){
33597             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33598             this.imageEl.on('load', this.onImageLoad, this);
33599             return;
33600         }
33601         
33602         this.resize();
33603     },
33604     
33605     onImageLoad : function()
33606     {
33607         this.resize();
33608     },
33609     
33610     resize : function()
33611     {
33612         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33613         
33614         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33615         
33616         if(this.bgimage.length){
33617             var image = this.el.select('.roo-brick-image-view', true).first();
33618             
33619             image.setWidth(paragraph.getWidth());
33620             
33621             if(this.square){
33622                 image.setHeight(paragraph.getWidth());
33623             }
33624             
33625             this.el.setHeight(image.getHeight());
33626             paragraph.setHeight(image.getHeight());
33627             
33628         }
33629         
33630     },
33631     
33632     enter: function(e, el)
33633     {
33634         e.preventDefault();
33635         
33636         if(this.bgimage.length){
33637             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33638             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33639         }
33640     },
33641     
33642     leave: function(e, el)
33643     {
33644         e.preventDefault();
33645         
33646         if(this.bgimage.length){
33647             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33648             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33649         }
33650     }
33651     
33652 });
33653
33654  
33655
33656  /*
33657  * - LGPL
33658  *
33659  * Number field 
33660  */
33661
33662 /**
33663  * @class Roo.bootstrap.NumberField
33664  * @extends Roo.bootstrap.Input
33665  * Bootstrap NumberField class
33666  * 
33667  * 
33668  * 
33669  * 
33670  * @constructor
33671  * Create a new NumberField
33672  * @param {Object} config The config object
33673  */
33674
33675 Roo.bootstrap.NumberField = function(config){
33676     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33677 };
33678
33679 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33680     
33681     /**
33682      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33683      */
33684     allowDecimals : true,
33685     /**
33686      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33687      */
33688     decimalSeparator : ".",
33689     /**
33690      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33691      */
33692     decimalPrecision : 2,
33693     /**
33694      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33695      */
33696     allowNegative : true,
33697     
33698     /**
33699      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33700      */
33701     allowZero: true,
33702     /**
33703      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33704      */
33705     minValue : Number.NEGATIVE_INFINITY,
33706     /**
33707      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33708      */
33709     maxValue : Number.MAX_VALUE,
33710     /**
33711      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33712      */
33713     minText : "The minimum value for this field is {0}",
33714     /**
33715      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33716      */
33717     maxText : "The maximum value for this field is {0}",
33718     /**
33719      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33720      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33721      */
33722     nanText : "{0} is not a valid number",
33723     /**
33724      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33725      */
33726     thousandsDelimiter : false,
33727     /**
33728      * @cfg {String} valueAlign alignment of value
33729      */
33730     valueAlign : "left",
33731
33732     getAutoCreate : function()
33733     {
33734         var hiddenInput = {
33735             tag: 'input',
33736             type: 'hidden',
33737             id: Roo.id(),
33738             cls: 'hidden-number-input'
33739         };
33740         
33741         if (this.name) {
33742             hiddenInput.name = this.name;
33743         }
33744         
33745         this.name = '';
33746         
33747         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33748         
33749         this.name = hiddenInput.name;
33750         
33751         if(cfg.cn.length > 0) {
33752             cfg.cn.push(hiddenInput);
33753         }
33754         
33755         return cfg;
33756     },
33757
33758     // private
33759     initEvents : function()
33760     {   
33761         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33762         
33763         var allowed = "0123456789";
33764         
33765         if(this.allowDecimals){
33766             allowed += this.decimalSeparator;
33767         }
33768         
33769         if(this.allowNegative){
33770             allowed += "-";
33771         }
33772         
33773         if(this.thousandsDelimiter) {
33774             allowed += ",";
33775         }
33776         
33777         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33778         
33779         var keyPress = function(e){
33780             
33781             var k = e.getKey();
33782             
33783             var c = e.getCharCode();
33784             
33785             if(
33786                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33787                     allowed.indexOf(String.fromCharCode(c)) === -1
33788             ){
33789                 e.stopEvent();
33790                 return;
33791             }
33792             
33793             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33794                 return;
33795             }
33796             
33797             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33798                 e.stopEvent();
33799             }
33800         };
33801         
33802         this.el.on("keypress", keyPress, this);
33803     },
33804     
33805     validateValue : function(value)
33806     {
33807         
33808         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33809             return false;
33810         }
33811         
33812         var num = this.parseValue(value);
33813         
33814         if(isNaN(num)){
33815             this.markInvalid(String.format(this.nanText, value));
33816             return false;
33817         }
33818         
33819         if(num < this.minValue){
33820             this.markInvalid(String.format(this.minText, this.minValue));
33821             return false;
33822         }
33823         
33824         if(num > this.maxValue){
33825             this.markInvalid(String.format(this.maxText, this.maxValue));
33826             return false;
33827         }
33828         
33829         return true;
33830     },
33831
33832     getValue : function()
33833     {
33834         var v = this.hiddenEl().getValue();
33835         
33836         return this.fixPrecision(this.parseValue(v));
33837     },
33838
33839     parseValue : function(value)
33840     {
33841         if(this.thousandsDelimiter) {
33842             value += "";
33843             r = new RegExp(",", "g");
33844             value = value.replace(r, "");
33845         }
33846         
33847         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33848         return isNaN(value) ? '' : value;
33849     },
33850
33851     fixPrecision : function(value)
33852     {
33853         if(this.thousandsDelimiter) {
33854             value += "";
33855             r = new RegExp(",", "g");
33856             value = value.replace(r, "");
33857         }
33858         
33859         var nan = isNaN(value);
33860         
33861         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33862             return nan ? '' : value;
33863         }
33864         return parseFloat(value).toFixed(this.decimalPrecision);
33865     },
33866
33867     setValue : function(v)
33868     {
33869         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33870         
33871         this.value = v;
33872         
33873         if(this.rendered){
33874             
33875             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33876             
33877             this.inputEl().dom.value = (v == '') ? '' :
33878                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33879             
33880             if(!this.allowZero && v === '0') {
33881                 this.hiddenEl().dom.value = '';
33882                 this.inputEl().dom.value = '';
33883             }
33884             
33885             this.validate();
33886         }
33887     },
33888
33889     decimalPrecisionFcn : function(v)
33890     {
33891         return Math.floor(v);
33892     },
33893
33894     beforeBlur : function()
33895     {
33896         var v = this.parseValue(this.getRawValue());
33897         
33898         if(v || v === 0 || v === ''){
33899             this.setValue(v);
33900         }
33901     },
33902     
33903     hiddenEl : function()
33904     {
33905         return this.el.select('input.hidden-number-input',true).first();
33906     }
33907     
33908 });
33909
33910  
33911
33912 /*
33913 * Licence: LGPL
33914 */
33915
33916 /**
33917  * @class Roo.bootstrap.DocumentSlider
33918  * @extends Roo.bootstrap.Component
33919  * Bootstrap DocumentSlider class
33920  * 
33921  * @constructor
33922  * Create a new DocumentViewer
33923  * @param {Object} config The config object
33924  */
33925
33926 Roo.bootstrap.DocumentSlider = function(config){
33927     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33928     
33929     this.files = [];
33930     
33931     this.addEvents({
33932         /**
33933          * @event initial
33934          * Fire after initEvent
33935          * @param {Roo.bootstrap.DocumentSlider} this
33936          */
33937         "initial" : true,
33938         /**
33939          * @event update
33940          * Fire after update
33941          * @param {Roo.bootstrap.DocumentSlider} this
33942          */
33943         "update" : true,
33944         /**
33945          * @event click
33946          * Fire after click
33947          * @param {Roo.bootstrap.DocumentSlider} this
33948          */
33949         "click" : true
33950     });
33951 };
33952
33953 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33954     
33955     files : false,
33956     
33957     indicator : 0,
33958     
33959     getAutoCreate : function()
33960     {
33961         var cfg = {
33962             tag : 'div',
33963             cls : 'roo-document-slider',
33964             cn : [
33965                 {
33966                     tag : 'div',
33967                     cls : 'roo-document-slider-header',
33968                     cn : [
33969                         {
33970                             tag : 'div',
33971                             cls : 'roo-document-slider-header-title'
33972                         }
33973                     ]
33974                 },
33975                 {
33976                     tag : 'div',
33977                     cls : 'roo-document-slider-body',
33978                     cn : [
33979                         {
33980                             tag : 'div',
33981                             cls : 'roo-document-slider-prev',
33982                             cn : [
33983                                 {
33984                                     tag : 'i',
33985                                     cls : 'fa fa-chevron-left'
33986                                 }
33987                             ]
33988                         },
33989                         {
33990                             tag : 'div',
33991                             cls : 'roo-document-slider-thumb',
33992                             cn : [
33993                                 {
33994                                     tag : 'img',
33995                                     cls : 'roo-document-slider-image'
33996                                 }
33997                             ]
33998                         },
33999                         {
34000                             tag : 'div',
34001                             cls : 'roo-document-slider-next',
34002                             cn : [
34003                                 {
34004                                     tag : 'i',
34005                                     cls : 'fa fa-chevron-right'
34006                                 }
34007                             ]
34008                         }
34009                     ]
34010                 }
34011             ]
34012         };
34013         
34014         return cfg;
34015     },
34016     
34017     initEvents : function()
34018     {
34019         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34020         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34021         
34022         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34023         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34024         
34025         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34026         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34027         
34028         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34029         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34030         
34031         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34032         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34033         
34034         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34035         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34036         
34037         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34038         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34039         
34040         this.thumbEl.on('click', this.onClick, this);
34041         
34042         this.prevIndicator.on('click', this.prev, this);
34043         
34044         this.nextIndicator.on('click', this.next, this);
34045         
34046     },
34047     
34048     initial : function()
34049     {
34050         if(this.files.length){
34051             this.indicator = 1;
34052             this.update()
34053         }
34054         
34055         this.fireEvent('initial', this);
34056     },
34057     
34058     update : function()
34059     {
34060         this.imageEl.attr('src', this.files[this.indicator - 1]);
34061         
34062         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34063         
34064         this.prevIndicator.show();
34065         
34066         if(this.indicator == 1){
34067             this.prevIndicator.hide();
34068         }
34069         
34070         this.nextIndicator.show();
34071         
34072         if(this.indicator == this.files.length){
34073             this.nextIndicator.hide();
34074         }
34075         
34076         this.thumbEl.scrollTo('top');
34077         
34078         this.fireEvent('update', this);
34079     },
34080     
34081     onClick : function(e)
34082     {
34083         e.preventDefault();
34084         
34085         this.fireEvent('click', this);
34086     },
34087     
34088     prev : function(e)
34089     {
34090         e.preventDefault();
34091         
34092         this.indicator = Math.max(1, this.indicator - 1);
34093         
34094         this.update();
34095     },
34096     
34097     next : function(e)
34098     {
34099         e.preventDefault();
34100         
34101         this.indicator = Math.min(this.files.length, this.indicator + 1);
34102         
34103         this.update();
34104     }
34105 });
34106 /*
34107  * - LGPL
34108  *
34109  * RadioSet
34110  *
34111  *
34112  */
34113
34114 /**
34115  * @class Roo.bootstrap.RadioSet
34116  * @extends Roo.bootstrap.Input
34117  * Bootstrap RadioSet class
34118  * @cfg {String} indicatorpos (left|right) default left
34119  * @cfg {Boolean} inline (true|false) inline the element (default true)
34120  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34121  * @constructor
34122  * Create a new RadioSet
34123  * @param {Object} config The config object
34124  */
34125
34126 Roo.bootstrap.RadioSet = function(config){
34127     
34128     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34129     
34130     this.radioes = [];
34131     
34132     Roo.bootstrap.RadioSet.register(this);
34133     
34134     this.addEvents({
34135         /**
34136         * @event check
34137         * Fires when the element is checked or unchecked.
34138         * @param {Roo.bootstrap.RadioSet} this This radio
34139         * @param {Roo.bootstrap.Radio} item The checked item
34140         */
34141        check : true,
34142        /**
34143         * @event click
34144         * Fires when the element is click.
34145         * @param {Roo.bootstrap.RadioSet} this This radio set
34146         * @param {Roo.bootstrap.Radio} item The checked item
34147         * @param {Roo.EventObject} e The event object
34148         */
34149        click : true
34150     });
34151     
34152 };
34153
34154 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34155
34156     radioes : false,
34157     
34158     inline : true,
34159     
34160     weight : '',
34161     
34162     indicatorpos : 'left',
34163     
34164     getAutoCreate : function()
34165     {
34166         var label = {
34167             tag : 'label',
34168             cls : 'roo-radio-set-label',
34169             cn : [
34170                 {
34171                     tag : 'span',
34172                     html : this.fieldLabel
34173                 }
34174             ]
34175         };
34176         if (Roo.bootstrap.version == 3) {
34177             
34178             
34179             if(this.indicatorpos == 'left'){
34180                 label.cn.unshift({
34181                     tag : 'i',
34182                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34183                     tooltip : 'This field is required'
34184                 });
34185             } else {
34186                 label.cn.push({
34187                     tag : 'i',
34188                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34189                     tooltip : 'This field is required'
34190                 });
34191             }
34192         }
34193         var items = {
34194             tag : 'div',
34195             cls : 'roo-radio-set-items'
34196         };
34197         
34198         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34199         
34200         if (align === 'left' && this.fieldLabel.length) {
34201             
34202             items = {
34203                 cls : "roo-radio-set-right", 
34204                 cn: [
34205                     items
34206                 ]
34207             };
34208             
34209             if(this.labelWidth > 12){
34210                 label.style = "width: " + this.labelWidth + 'px';
34211             }
34212             
34213             if(this.labelWidth < 13 && this.labelmd == 0){
34214                 this.labelmd = this.labelWidth;
34215             }
34216             
34217             if(this.labellg > 0){
34218                 label.cls += ' col-lg-' + this.labellg;
34219                 items.cls += ' col-lg-' + (12 - this.labellg);
34220             }
34221             
34222             if(this.labelmd > 0){
34223                 label.cls += ' col-md-' + this.labelmd;
34224                 items.cls += ' col-md-' + (12 - this.labelmd);
34225             }
34226             
34227             if(this.labelsm > 0){
34228                 label.cls += ' col-sm-' + this.labelsm;
34229                 items.cls += ' col-sm-' + (12 - this.labelsm);
34230             }
34231             
34232             if(this.labelxs > 0){
34233                 label.cls += ' col-xs-' + this.labelxs;
34234                 items.cls += ' col-xs-' + (12 - this.labelxs);
34235             }
34236         }
34237         
34238         var cfg = {
34239             tag : 'div',
34240             cls : 'roo-radio-set',
34241             cn : [
34242                 {
34243                     tag : 'input',
34244                     cls : 'roo-radio-set-input',
34245                     type : 'hidden',
34246                     name : this.name,
34247                     value : this.value ? this.value :  ''
34248                 },
34249                 label,
34250                 items
34251             ]
34252         };
34253         
34254         if(this.weight.length){
34255             cfg.cls += ' roo-radio-' + this.weight;
34256         }
34257         
34258         if(this.inline) {
34259             cfg.cls += ' roo-radio-set-inline';
34260         }
34261         
34262         var settings=this;
34263         ['xs','sm','md','lg'].map(function(size){
34264             if (settings[size]) {
34265                 cfg.cls += ' col-' + size + '-' + settings[size];
34266             }
34267         });
34268         
34269         return cfg;
34270         
34271     },
34272
34273     initEvents : function()
34274     {
34275         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34276         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34277         
34278         if(!this.fieldLabel.length){
34279             this.labelEl.hide();
34280         }
34281         
34282         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34283         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34284         
34285         this.indicator = this.indicatorEl();
34286         
34287         if(this.indicator){
34288             this.indicator.addClass('invisible');
34289         }
34290         
34291         this.originalValue = this.getValue();
34292         
34293     },
34294     
34295     inputEl: function ()
34296     {
34297         return this.el.select('.roo-radio-set-input', true).first();
34298     },
34299     
34300     getChildContainer : function()
34301     {
34302         return this.itemsEl;
34303     },
34304     
34305     register : function(item)
34306     {
34307         this.radioes.push(item);
34308         
34309     },
34310     
34311     validate : function()
34312     {   
34313         if(this.getVisibilityEl().hasClass('hidden')){
34314             return true;
34315         }
34316         
34317         var valid = false;
34318         
34319         Roo.each(this.radioes, function(i){
34320             if(!i.checked){
34321                 return;
34322             }
34323             
34324             valid = true;
34325             return false;
34326         });
34327         
34328         if(this.allowBlank) {
34329             return true;
34330         }
34331         
34332         if(this.disabled || valid){
34333             this.markValid();
34334             return true;
34335         }
34336         
34337         this.markInvalid();
34338         return false;
34339         
34340     },
34341     
34342     markValid : function()
34343     {
34344         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34345             this.indicatorEl().removeClass('visible');
34346             this.indicatorEl().addClass('invisible');
34347         }
34348         
34349         
34350         if (Roo.bootstrap.version == 3) {
34351             this.el.removeClass([this.invalidClass, this.validClass]);
34352             this.el.addClass(this.validClass);
34353         } else {
34354             this.el.removeClass(['is-invalid','is-valid']);
34355             this.el.addClass(['is-valid']);
34356         }
34357         this.fireEvent('valid', this);
34358     },
34359     
34360     markInvalid : function(msg)
34361     {
34362         if(this.allowBlank || this.disabled){
34363             return;
34364         }
34365         
34366         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34367             this.indicatorEl().removeClass('invisible');
34368             this.indicatorEl().addClass('visible');
34369         }
34370         if (Roo.bootstrap.version == 3) {
34371             this.el.removeClass([this.invalidClass, this.validClass]);
34372             this.el.addClass(this.invalidClass);
34373         } else {
34374             this.el.removeClass(['is-invalid','is-valid']);
34375             this.el.addClass(['is-invalid']);
34376         }
34377         
34378         this.fireEvent('invalid', this, msg);
34379         
34380     },
34381     
34382     setValue : function(v, suppressEvent)
34383     {   
34384         if(this.value === v){
34385             return;
34386         }
34387         
34388         this.value = v;
34389         
34390         if(this.rendered){
34391             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34392         }
34393         
34394         Roo.each(this.radioes, function(i){
34395             i.checked = false;
34396             i.el.removeClass('checked');
34397         });
34398         
34399         Roo.each(this.radioes, function(i){
34400             
34401             if(i.value === v || i.value.toString() === v.toString()){
34402                 i.checked = true;
34403                 i.el.addClass('checked');
34404                 
34405                 if(suppressEvent !== true){
34406                     this.fireEvent('check', this, i);
34407                 }
34408                 
34409                 return false;
34410             }
34411             
34412         }, this);
34413         
34414         this.validate();
34415     },
34416     
34417     clearInvalid : function(){
34418         
34419         if(!this.el || this.preventMark){
34420             return;
34421         }
34422         
34423         this.el.removeClass([this.invalidClass]);
34424         
34425         this.fireEvent('valid', this);
34426     }
34427     
34428 });
34429
34430 Roo.apply(Roo.bootstrap.RadioSet, {
34431     
34432     groups: {},
34433     
34434     register : function(set)
34435     {
34436         this.groups[set.name] = set;
34437     },
34438     
34439     get: function(name) 
34440     {
34441         if (typeof(this.groups[name]) == 'undefined') {
34442             return false;
34443         }
34444         
34445         return this.groups[name] ;
34446     }
34447     
34448 });
34449 /*
34450  * Based on:
34451  * Ext JS Library 1.1.1
34452  * Copyright(c) 2006-2007, Ext JS, LLC.
34453  *
34454  * Originally Released Under LGPL - original licence link has changed is not relivant.
34455  *
34456  * Fork - LGPL
34457  * <script type="text/javascript">
34458  */
34459
34460
34461 /**
34462  * @class Roo.bootstrap.SplitBar
34463  * @extends Roo.util.Observable
34464  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34465  * <br><br>
34466  * Usage:
34467  * <pre><code>
34468 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34469                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34470 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34471 split.minSize = 100;
34472 split.maxSize = 600;
34473 split.animate = true;
34474 split.on('moved', splitterMoved);
34475 </code></pre>
34476  * @constructor
34477  * Create a new SplitBar
34478  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34479  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34480  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34481  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34482                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34483                         position of the SplitBar).
34484  */
34485 Roo.bootstrap.SplitBar = function(cfg){
34486     
34487     /** @private */
34488     
34489     //{
34490     //  dragElement : elm
34491     //  resizingElement: el,
34492         // optional..
34493     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34494     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34495         // existingProxy ???
34496     //}
34497     
34498     this.el = Roo.get(cfg.dragElement, true);
34499     this.el.dom.unselectable = "on";
34500     /** @private */
34501     this.resizingEl = Roo.get(cfg.resizingElement, true);
34502
34503     /**
34504      * @private
34505      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34506      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34507      * @type Number
34508      */
34509     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34510     
34511     /**
34512      * The minimum size of the resizing element. (Defaults to 0)
34513      * @type Number
34514      */
34515     this.minSize = 0;
34516     
34517     /**
34518      * The maximum size of the resizing element. (Defaults to 2000)
34519      * @type Number
34520      */
34521     this.maxSize = 2000;
34522     
34523     /**
34524      * Whether to animate the transition to the new size
34525      * @type Boolean
34526      */
34527     this.animate = false;
34528     
34529     /**
34530      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34531      * @type Boolean
34532      */
34533     this.useShim = false;
34534     
34535     /** @private */
34536     this.shim = null;
34537     
34538     if(!cfg.existingProxy){
34539         /** @private */
34540         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34541     }else{
34542         this.proxy = Roo.get(cfg.existingProxy).dom;
34543     }
34544     /** @private */
34545     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34546     
34547     /** @private */
34548     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34549     
34550     /** @private */
34551     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34552     
34553     /** @private */
34554     this.dragSpecs = {};
34555     
34556     /**
34557      * @private The adapter to use to positon and resize elements
34558      */
34559     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34560     this.adapter.init(this);
34561     
34562     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34563         /** @private */
34564         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34565         this.el.addClass("roo-splitbar-h");
34566     }else{
34567         /** @private */
34568         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34569         this.el.addClass("roo-splitbar-v");
34570     }
34571     
34572     this.addEvents({
34573         /**
34574          * @event resize
34575          * Fires when the splitter is moved (alias for {@link #event-moved})
34576          * @param {Roo.bootstrap.SplitBar} this
34577          * @param {Number} newSize the new width or height
34578          */
34579         "resize" : true,
34580         /**
34581          * @event moved
34582          * Fires when the splitter is moved
34583          * @param {Roo.bootstrap.SplitBar} this
34584          * @param {Number} newSize the new width or height
34585          */
34586         "moved" : true,
34587         /**
34588          * @event beforeresize
34589          * Fires before the splitter is dragged
34590          * @param {Roo.bootstrap.SplitBar} this
34591          */
34592         "beforeresize" : true,
34593
34594         "beforeapply" : true
34595     });
34596
34597     Roo.util.Observable.call(this);
34598 };
34599
34600 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34601     onStartProxyDrag : function(x, y){
34602         this.fireEvent("beforeresize", this);
34603         if(!this.overlay){
34604             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34605             o.unselectable();
34606             o.enableDisplayMode("block");
34607             // all splitbars share the same overlay
34608             Roo.bootstrap.SplitBar.prototype.overlay = o;
34609         }
34610         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34611         this.overlay.show();
34612         Roo.get(this.proxy).setDisplayed("block");
34613         var size = this.adapter.getElementSize(this);
34614         this.activeMinSize = this.getMinimumSize();;
34615         this.activeMaxSize = this.getMaximumSize();;
34616         var c1 = size - this.activeMinSize;
34617         var c2 = Math.max(this.activeMaxSize - size, 0);
34618         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34619             this.dd.resetConstraints();
34620             this.dd.setXConstraint(
34621                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34622                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34623             );
34624             this.dd.setYConstraint(0, 0);
34625         }else{
34626             this.dd.resetConstraints();
34627             this.dd.setXConstraint(0, 0);
34628             this.dd.setYConstraint(
34629                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34630                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34631             );
34632          }
34633         this.dragSpecs.startSize = size;
34634         this.dragSpecs.startPoint = [x, y];
34635         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34636     },
34637     
34638     /** 
34639      * @private Called after the drag operation by the DDProxy
34640      */
34641     onEndProxyDrag : function(e){
34642         Roo.get(this.proxy).setDisplayed(false);
34643         var endPoint = Roo.lib.Event.getXY(e);
34644         if(this.overlay){
34645             this.overlay.hide();
34646         }
34647         var newSize;
34648         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34649             newSize = this.dragSpecs.startSize + 
34650                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34651                     endPoint[0] - this.dragSpecs.startPoint[0] :
34652                     this.dragSpecs.startPoint[0] - endPoint[0]
34653                 );
34654         }else{
34655             newSize = this.dragSpecs.startSize + 
34656                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34657                     endPoint[1] - this.dragSpecs.startPoint[1] :
34658                     this.dragSpecs.startPoint[1] - endPoint[1]
34659                 );
34660         }
34661         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34662         if(newSize != this.dragSpecs.startSize){
34663             if(this.fireEvent('beforeapply', this, newSize) !== false){
34664                 this.adapter.setElementSize(this, newSize);
34665                 this.fireEvent("moved", this, newSize);
34666                 this.fireEvent("resize", this, newSize);
34667             }
34668         }
34669     },
34670     
34671     /**
34672      * Get the adapter this SplitBar uses
34673      * @return The adapter object
34674      */
34675     getAdapter : function(){
34676         return this.adapter;
34677     },
34678     
34679     /**
34680      * Set the adapter this SplitBar uses
34681      * @param {Object} adapter A SplitBar adapter object
34682      */
34683     setAdapter : function(adapter){
34684         this.adapter = adapter;
34685         this.adapter.init(this);
34686     },
34687     
34688     /**
34689      * Gets the minimum size for the resizing element
34690      * @return {Number} The minimum size
34691      */
34692     getMinimumSize : function(){
34693         return this.minSize;
34694     },
34695     
34696     /**
34697      * Sets the minimum size for the resizing element
34698      * @param {Number} minSize The minimum size
34699      */
34700     setMinimumSize : function(minSize){
34701         this.minSize = minSize;
34702     },
34703     
34704     /**
34705      * Gets the maximum size for the resizing element
34706      * @return {Number} The maximum size
34707      */
34708     getMaximumSize : function(){
34709         return this.maxSize;
34710     },
34711     
34712     /**
34713      * Sets the maximum size for the resizing element
34714      * @param {Number} maxSize The maximum size
34715      */
34716     setMaximumSize : function(maxSize){
34717         this.maxSize = maxSize;
34718     },
34719     
34720     /**
34721      * Sets the initialize size for the resizing element
34722      * @param {Number} size The initial size
34723      */
34724     setCurrentSize : function(size){
34725         var oldAnimate = this.animate;
34726         this.animate = false;
34727         this.adapter.setElementSize(this, size);
34728         this.animate = oldAnimate;
34729     },
34730     
34731     /**
34732      * Destroy this splitbar. 
34733      * @param {Boolean} removeEl True to remove the element
34734      */
34735     destroy : function(removeEl){
34736         if(this.shim){
34737             this.shim.remove();
34738         }
34739         this.dd.unreg();
34740         this.proxy.parentNode.removeChild(this.proxy);
34741         if(removeEl){
34742             this.el.remove();
34743         }
34744     }
34745 });
34746
34747 /**
34748  * @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.
34749  */
34750 Roo.bootstrap.SplitBar.createProxy = function(dir){
34751     var proxy = new Roo.Element(document.createElement("div"));
34752     proxy.unselectable();
34753     var cls = 'roo-splitbar-proxy';
34754     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34755     document.body.appendChild(proxy.dom);
34756     return proxy.dom;
34757 };
34758
34759 /** 
34760  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34761  * Default Adapter. It assumes the splitter and resizing element are not positioned
34762  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34763  */
34764 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34765 };
34766
34767 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34768     // do nothing for now
34769     init : function(s){
34770     
34771     },
34772     /**
34773      * Called before drag operations to get the current size of the resizing element. 
34774      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34775      */
34776      getElementSize : function(s){
34777         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34778             return s.resizingEl.getWidth();
34779         }else{
34780             return s.resizingEl.getHeight();
34781         }
34782     },
34783     
34784     /**
34785      * Called after drag operations to set the size of the resizing element.
34786      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34787      * @param {Number} newSize The new size to set
34788      * @param {Function} onComplete A function to be invoked when resizing is complete
34789      */
34790     setElementSize : function(s, newSize, onComplete){
34791         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34792             if(!s.animate){
34793                 s.resizingEl.setWidth(newSize);
34794                 if(onComplete){
34795                     onComplete(s, newSize);
34796                 }
34797             }else{
34798                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34799             }
34800         }else{
34801             
34802             if(!s.animate){
34803                 s.resizingEl.setHeight(newSize);
34804                 if(onComplete){
34805                     onComplete(s, newSize);
34806                 }
34807             }else{
34808                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34809             }
34810         }
34811     }
34812 };
34813
34814 /** 
34815  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34816  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34817  * Adapter that  moves the splitter element to align with the resized sizing element. 
34818  * Used with an absolute positioned SplitBar.
34819  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34820  * document.body, make sure you assign an id to the body element.
34821  */
34822 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34823     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34824     this.container = Roo.get(container);
34825 };
34826
34827 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34828     init : function(s){
34829         this.basic.init(s);
34830     },
34831     
34832     getElementSize : function(s){
34833         return this.basic.getElementSize(s);
34834     },
34835     
34836     setElementSize : function(s, newSize, onComplete){
34837         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34838     },
34839     
34840     moveSplitter : function(s){
34841         var yes = Roo.bootstrap.SplitBar;
34842         switch(s.placement){
34843             case yes.LEFT:
34844                 s.el.setX(s.resizingEl.getRight());
34845                 break;
34846             case yes.RIGHT:
34847                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34848                 break;
34849             case yes.TOP:
34850                 s.el.setY(s.resizingEl.getBottom());
34851                 break;
34852             case yes.BOTTOM:
34853                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34854                 break;
34855         }
34856     }
34857 };
34858
34859 /**
34860  * Orientation constant - Create a vertical SplitBar
34861  * @static
34862  * @type Number
34863  */
34864 Roo.bootstrap.SplitBar.VERTICAL = 1;
34865
34866 /**
34867  * Orientation constant - Create a horizontal SplitBar
34868  * @static
34869  * @type Number
34870  */
34871 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34872
34873 /**
34874  * Placement constant - The resizing element is to the left of the splitter element
34875  * @static
34876  * @type Number
34877  */
34878 Roo.bootstrap.SplitBar.LEFT = 1;
34879
34880 /**
34881  * Placement constant - The resizing element is to the right of the splitter element
34882  * @static
34883  * @type Number
34884  */
34885 Roo.bootstrap.SplitBar.RIGHT = 2;
34886
34887 /**
34888  * Placement constant - The resizing element is positioned above the splitter element
34889  * @static
34890  * @type Number
34891  */
34892 Roo.bootstrap.SplitBar.TOP = 3;
34893
34894 /**
34895  * Placement constant - The resizing element is positioned under splitter element
34896  * @static
34897  * @type Number
34898  */
34899 Roo.bootstrap.SplitBar.BOTTOM = 4;
34900 Roo.namespace("Roo.bootstrap.layout");/*
34901  * Based on:
34902  * Ext JS Library 1.1.1
34903  * Copyright(c) 2006-2007, Ext JS, LLC.
34904  *
34905  * Originally Released Under LGPL - original licence link has changed is not relivant.
34906  *
34907  * Fork - LGPL
34908  * <script type="text/javascript">
34909  */
34910
34911 /**
34912  * @class Roo.bootstrap.layout.Manager
34913  * @extends Roo.bootstrap.Component
34914  * Base class for layout managers.
34915  */
34916 Roo.bootstrap.layout.Manager = function(config)
34917 {
34918     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34919
34920
34921
34922
34923
34924     /** false to disable window resize monitoring @type Boolean */
34925     this.monitorWindowResize = true;
34926     this.regions = {};
34927     this.addEvents({
34928         /**
34929          * @event layout
34930          * Fires when a layout is performed.
34931          * @param {Roo.LayoutManager} this
34932          */
34933         "layout" : true,
34934         /**
34935          * @event regionresized
34936          * Fires when the user resizes a region.
34937          * @param {Roo.LayoutRegion} region The resized region
34938          * @param {Number} newSize The new size (width for east/west, height for north/south)
34939          */
34940         "regionresized" : true,
34941         /**
34942          * @event regioncollapsed
34943          * Fires when a region is collapsed.
34944          * @param {Roo.LayoutRegion} region The collapsed region
34945          */
34946         "regioncollapsed" : true,
34947         /**
34948          * @event regionexpanded
34949          * Fires when a region is expanded.
34950          * @param {Roo.LayoutRegion} region The expanded region
34951          */
34952         "regionexpanded" : true
34953     });
34954     this.updating = false;
34955
34956     if (config.el) {
34957         this.el = Roo.get(config.el);
34958         this.initEvents();
34959     }
34960
34961 };
34962
34963 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34964
34965
34966     regions : null,
34967
34968     monitorWindowResize : true,
34969
34970
34971     updating : false,
34972
34973
34974     onRender : function(ct, position)
34975     {
34976         if(!this.el){
34977             this.el = Roo.get(ct);
34978             this.initEvents();
34979         }
34980         //this.fireEvent('render',this);
34981     },
34982
34983
34984     initEvents: function()
34985     {
34986
34987
34988         // ie scrollbar fix
34989         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34990             document.body.scroll = "no";
34991         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34992             this.el.position('relative');
34993         }
34994         this.id = this.el.id;
34995         this.el.addClass("roo-layout-container");
34996         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34997         if(this.el.dom != document.body ) {
34998             this.el.on('resize', this.layout,this);
34999             this.el.on('show', this.layout,this);
35000         }
35001
35002     },
35003
35004     /**
35005      * Returns true if this layout is currently being updated
35006      * @return {Boolean}
35007      */
35008     isUpdating : function(){
35009         return this.updating;
35010     },
35011
35012     /**
35013      * Suspend the LayoutManager from doing auto-layouts while
35014      * making multiple add or remove calls
35015      */
35016     beginUpdate : function(){
35017         this.updating = true;
35018     },
35019
35020     /**
35021      * Restore auto-layouts and optionally disable the manager from performing a layout
35022      * @param {Boolean} noLayout true to disable a layout update
35023      */
35024     endUpdate : function(noLayout){
35025         this.updating = false;
35026         if(!noLayout){
35027             this.layout();
35028         }
35029     },
35030
35031     layout: function(){
35032         // abstract...
35033     },
35034
35035     onRegionResized : function(region, newSize){
35036         this.fireEvent("regionresized", region, newSize);
35037         this.layout();
35038     },
35039
35040     onRegionCollapsed : function(region){
35041         this.fireEvent("regioncollapsed", region);
35042     },
35043
35044     onRegionExpanded : function(region){
35045         this.fireEvent("regionexpanded", region);
35046     },
35047
35048     /**
35049      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35050      * performs box-model adjustments.
35051      * @return {Object} The size as an object {width: (the width), height: (the height)}
35052      */
35053     getViewSize : function()
35054     {
35055         var size;
35056         if(this.el.dom != document.body){
35057             size = this.el.getSize();
35058         }else{
35059             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35060         }
35061         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35062         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35063         return size;
35064     },
35065
35066     /**
35067      * Returns the Element this layout is bound to.
35068      * @return {Roo.Element}
35069      */
35070     getEl : function(){
35071         return this.el;
35072     },
35073
35074     /**
35075      * Returns the specified region.
35076      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35077      * @return {Roo.LayoutRegion}
35078      */
35079     getRegion : function(target){
35080         return this.regions[target.toLowerCase()];
35081     },
35082
35083     onWindowResize : function(){
35084         if(this.monitorWindowResize){
35085             this.layout();
35086         }
35087     }
35088 });
35089 /*
35090  * Based on:
35091  * Ext JS Library 1.1.1
35092  * Copyright(c) 2006-2007, Ext JS, LLC.
35093  *
35094  * Originally Released Under LGPL - original licence link has changed is not relivant.
35095  *
35096  * Fork - LGPL
35097  * <script type="text/javascript">
35098  */
35099 /**
35100  * @class Roo.bootstrap.layout.Border
35101  * @extends Roo.bootstrap.layout.Manager
35102  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35103  * please see: examples/bootstrap/nested.html<br><br>
35104  
35105 <b>The container the layout is rendered into can be either the body element or any other element.
35106 If it is not the body element, the container needs to either be an absolute positioned element,
35107 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35108 the container size if it is not the body element.</b>
35109
35110 * @constructor
35111 * Create a new Border
35112 * @param {Object} config Configuration options
35113  */
35114 Roo.bootstrap.layout.Border = function(config){
35115     config = config || {};
35116     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35117     
35118     
35119     
35120     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35121         if(config[region]){
35122             config[region].region = region;
35123             this.addRegion(config[region]);
35124         }
35125     },this);
35126     
35127 };
35128
35129 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35130
35131 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35132     
35133     parent : false, // this might point to a 'nest' or a ???
35134     
35135     /**
35136      * Creates and adds a new region if it doesn't already exist.
35137      * @param {String} target The target region key (north, south, east, west or center).
35138      * @param {Object} config The regions config object
35139      * @return {BorderLayoutRegion} The new region
35140      */
35141     addRegion : function(config)
35142     {
35143         if(!this.regions[config.region]){
35144             var r = this.factory(config);
35145             this.bindRegion(r);
35146         }
35147         return this.regions[config.region];
35148     },
35149
35150     // private (kinda)
35151     bindRegion : function(r){
35152         this.regions[r.config.region] = r;
35153         
35154         r.on("visibilitychange",    this.layout, this);
35155         r.on("paneladded",          this.layout, this);
35156         r.on("panelremoved",        this.layout, this);
35157         r.on("invalidated",         this.layout, this);
35158         r.on("resized",             this.onRegionResized, this);
35159         r.on("collapsed",           this.onRegionCollapsed, this);
35160         r.on("expanded",            this.onRegionExpanded, this);
35161     },
35162
35163     /**
35164      * Performs a layout update.
35165      */
35166     layout : function()
35167     {
35168         if(this.updating) {
35169             return;
35170         }
35171         
35172         // render all the rebions if they have not been done alreayd?
35173         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35174             if(this.regions[region] && !this.regions[region].bodyEl){
35175                 this.regions[region].onRender(this.el)
35176             }
35177         },this);
35178         
35179         var size = this.getViewSize();
35180         var w = size.width;
35181         var h = size.height;
35182         var centerW = w;
35183         var centerH = h;
35184         var centerY = 0;
35185         var centerX = 0;
35186         //var x = 0, y = 0;
35187
35188         var rs = this.regions;
35189         var north = rs["north"];
35190         var south = rs["south"]; 
35191         var west = rs["west"];
35192         var east = rs["east"];
35193         var center = rs["center"];
35194         //if(this.hideOnLayout){ // not supported anymore
35195             //c.el.setStyle("display", "none");
35196         //}
35197         if(north && north.isVisible()){
35198             var b = north.getBox();
35199             var m = north.getMargins();
35200             b.width = w - (m.left+m.right);
35201             b.x = m.left;
35202             b.y = m.top;
35203             centerY = b.height + b.y + m.bottom;
35204             centerH -= centerY;
35205             north.updateBox(this.safeBox(b));
35206         }
35207         if(south && south.isVisible()){
35208             var b = south.getBox();
35209             var m = south.getMargins();
35210             b.width = w - (m.left+m.right);
35211             b.x = m.left;
35212             var totalHeight = (b.height + m.top + m.bottom);
35213             b.y = h - totalHeight + m.top;
35214             centerH -= totalHeight;
35215             south.updateBox(this.safeBox(b));
35216         }
35217         if(west && west.isVisible()){
35218             var b = west.getBox();
35219             var m = west.getMargins();
35220             b.height = centerH - (m.top+m.bottom);
35221             b.x = m.left;
35222             b.y = centerY + m.top;
35223             var totalWidth = (b.width + m.left + m.right);
35224             centerX += totalWidth;
35225             centerW -= totalWidth;
35226             west.updateBox(this.safeBox(b));
35227         }
35228         if(east && east.isVisible()){
35229             var b = east.getBox();
35230             var m = east.getMargins();
35231             b.height = centerH - (m.top+m.bottom);
35232             var totalWidth = (b.width + m.left + m.right);
35233             b.x = w - totalWidth + m.left;
35234             b.y = centerY + m.top;
35235             centerW -= totalWidth;
35236             east.updateBox(this.safeBox(b));
35237         }
35238         if(center){
35239             var m = center.getMargins();
35240             var centerBox = {
35241                 x: centerX + m.left,
35242                 y: centerY + m.top,
35243                 width: centerW - (m.left+m.right),
35244                 height: centerH - (m.top+m.bottom)
35245             };
35246             //if(this.hideOnLayout){
35247                 //center.el.setStyle("display", "block");
35248             //}
35249             center.updateBox(this.safeBox(centerBox));
35250         }
35251         this.el.repaint();
35252         this.fireEvent("layout", this);
35253     },
35254
35255     // private
35256     safeBox : function(box){
35257         box.width = Math.max(0, box.width);
35258         box.height = Math.max(0, box.height);
35259         return box;
35260     },
35261
35262     /**
35263      * Adds a ContentPanel (or subclass) to this layout.
35264      * @param {String} target The target region key (north, south, east, west or center).
35265      * @param {Roo.ContentPanel} panel The panel to add
35266      * @return {Roo.ContentPanel} The added panel
35267      */
35268     add : function(target, panel){
35269          
35270         target = target.toLowerCase();
35271         return this.regions[target].add(panel);
35272     },
35273
35274     /**
35275      * Remove a ContentPanel (or subclass) to this layout.
35276      * @param {String} target The target region key (north, south, east, west or center).
35277      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35278      * @return {Roo.ContentPanel} The removed panel
35279      */
35280     remove : function(target, panel){
35281         target = target.toLowerCase();
35282         return this.regions[target].remove(panel);
35283     },
35284
35285     /**
35286      * Searches all regions for a panel with the specified id
35287      * @param {String} panelId
35288      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35289      */
35290     findPanel : function(panelId){
35291         var rs = this.regions;
35292         for(var target in rs){
35293             if(typeof rs[target] != "function"){
35294                 var p = rs[target].getPanel(panelId);
35295                 if(p){
35296                     return p;
35297                 }
35298             }
35299         }
35300         return null;
35301     },
35302
35303     /**
35304      * Searches all regions for a panel with the specified id and activates (shows) it.
35305      * @param {String/ContentPanel} panelId The panels id or the panel itself
35306      * @return {Roo.ContentPanel} The shown panel or null
35307      */
35308     showPanel : function(panelId) {
35309       var rs = this.regions;
35310       for(var target in rs){
35311          var r = rs[target];
35312          if(typeof r != "function"){
35313             if(r.hasPanel(panelId)){
35314                return r.showPanel(panelId);
35315             }
35316          }
35317       }
35318       return null;
35319    },
35320
35321    /**
35322      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35323      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35324      */
35325    /*
35326     restoreState : function(provider){
35327         if(!provider){
35328             provider = Roo.state.Manager;
35329         }
35330         var sm = new Roo.LayoutStateManager();
35331         sm.init(this, provider);
35332     },
35333 */
35334  
35335  
35336     /**
35337      * Adds a xtype elements to the layout.
35338      * <pre><code>
35339
35340 layout.addxtype({
35341        xtype : 'ContentPanel',
35342        region: 'west',
35343        items: [ .... ]
35344    }
35345 );
35346
35347 layout.addxtype({
35348         xtype : 'NestedLayoutPanel',
35349         region: 'west',
35350         layout: {
35351            center: { },
35352            west: { }   
35353         },
35354         items : [ ... list of content panels or nested layout panels.. ]
35355    }
35356 );
35357 </code></pre>
35358      * @param {Object} cfg Xtype definition of item to add.
35359      */
35360     addxtype : function(cfg)
35361     {
35362         // basically accepts a pannel...
35363         // can accept a layout region..!?!?
35364         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35365         
35366         
35367         // theory?  children can only be panels??
35368         
35369         //if (!cfg.xtype.match(/Panel$/)) {
35370         //    return false;
35371         //}
35372         var ret = false;
35373         
35374         if (typeof(cfg.region) == 'undefined') {
35375             Roo.log("Failed to add Panel, region was not set");
35376             Roo.log(cfg);
35377             return false;
35378         }
35379         var region = cfg.region;
35380         delete cfg.region;
35381         
35382           
35383         var xitems = [];
35384         if (cfg.items) {
35385             xitems = cfg.items;
35386             delete cfg.items;
35387         }
35388         var nb = false;
35389         
35390         if ( region == 'center') {
35391             Roo.log("Center: " + cfg.title);
35392         }
35393         
35394         
35395         switch(cfg.xtype) 
35396         {
35397             case 'Content':  // ContentPanel (el, cfg)
35398             case 'Scroll':  // ContentPanel (el, cfg)
35399             case 'View': 
35400                 cfg.autoCreate = cfg.autoCreate || true;
35401                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35402                 //} else {
35403                 //    var el = this.el.createChild();
35404                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35405                 //}
35406                 
35407                 this.add(region, ret);
35408                 break;
35409             
35410             /*
35411             case 'TreePanel': // our new panel!
35412                 cfg.el = this.el.createChild();
35413                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35414                 this.add(region, ret);
35415                 break;
35416             */
35417             
35418             case 'Nest': 
35419                 // create a new Layout (which is  a Border Layout...
35420                 
35421                 var clayout = cfg.layout;
35422                 clayout.el  = this.el.createChild();
35423                 clayout.items   = clayout.items  || [];
35424                 
35425                 delete cfg.layout;
35426                 
35427                 // replace this exitems with the clayout ones..
35428                 xitems = clayout.items;
35429                  
35430                 // force background off if it's in center...
35431                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35432                     cfg.background = false;
35433                 }
35434                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35435                 
35436                 
35437                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35438                 //console.log('adding nested layout panel '  + cfg.toSource());
35439                 this.add(region, ret);
35440                 nb = {}; /// find first...
35441                 break;
35442             
35443             case 'Grid':
35444                 
35445                 // needs grid and region
35446                 
35447                 //var el = this.getRegion(region).el.createChild();
35448                 /*
35449                  *var el = this.el.createChild();
35450                 // create the grid first...
35451                 cfg.grid.container = el;
35452                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35453                 */
35454                 
35455                 if (region == 'center' && this.active ) {
35456                     cfg.background = false;
35457                 }
35458                 
35459                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35460                 
35461                 this.add(region, ret);
35462                 /*
35463                 if (cfg.background) {
35464                     // render grid on panel activation (if panel background)
35465                     ret.on('activate', function(gp) {
35466                         if (!gp.grid.rendered) {
35467                     //        gp.grid.render(el);
35468                         }
35469                     });
35470                 } else {
35471                   //  cfg.grid.render(el);
35472                 }
35473                 */
35474                 break;
35475            
35476            
35477             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35478                 // it was the old xcomponent building that caused this before.
35479                 // espeically if border is the top element in the tree.
35480                 ret = this;
35481                 break; 
35482                 
35483                     
35484                 
35485                 
35486                 
35487             default:
35488                 /*
35489                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35490                     
35491                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35492                     this.add(region, ret);
35493                 } else {
35494                 */
35495                     Roo.log(cfg);
35496                     throw "Can not add '" + cfg.xtype + "' to Border";
35497                     return null;
35498              
35499                                 
35500              
35501         }
35502         this.beginUpdate();
35503         // add children..
35504         var region = '';
35505         var abn = {};
35506         Roo.each(xitems, function(i)  {
35507             region = nb && i.region ? i.region : false;
35508             
35509             var add = ret.addxtype(i);
35510            
35511             if (region) {
35512                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35513                 if (!i.background) {
35514                     abn[region] = nb[region] ;
35515                 }
35516             }
35517             
35518         });
35519         this.endUpdate();
35520
35521         // make the last non-background panel active..
35522         //if (nb) { Roo.log(abn); }
35523         if (nb) {
35524             
35525             for(var r in abn) {
35526                 region = this.getRegion(r);
35527                 if (region) {
35528                     // tried using nb[r], but it does not work..
35529                      
35530                     region.showPanel(abn[r]);
35531                    
35532                 }
35533             }
35534         }
35535         return ret;
35536         
35537     },
35538     
35539     
35540 // private
35541     factory : function(cfg)
35542     {
35543         
35544         var validRegions = Roo.bootstrap.layout.Border.regions;
35545
35546         var target = cfg.region;
35547         cfg.mgr = this;
35548         
35549         var r = Roo.bootstrap.layout;
35550         Roo.log(target);
35551         switch(target){
35552             case "north":
35553                 return new r.North(cfg);
35554             case "south":
35555                 return new r.South(cfg);
35556             case "east":
35557                 return new r.East(cfg);
35558             case "west":
35559                 return new r.West(cfg);
35560             case "center":
35561                 return new r.Center(cfg);
35562         }
35563         throw 'Layout region "'+target+'" not supported.';
35564     }
35565     
35566     
35567 });
35568  /*
35569  * Based on:
35570  * Ext JS Library 1.1.1
35571  * Copyright(c) 2006-2007, Ext JS, LLC.
35572  *
35573  * Originally Released Under LGPL - original licence link has changed is not relivant.
35574  *
35575  * Fork - LGPL
35576  * <script type="text/javascript">
35577  */
35578  
35579 /**
35580  * @class Roo.bootstrap.layout.Basic
35581  * @extends Roo.util.Observable
35582  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35583  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35584  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35585  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35586  * @cfg {string}   region  the region that it inhabits..
35587  * @cfg {bool}   skipConfig skip config?
35588  * 
35589
35590  */
35591 Roo.bootstrap.layout.Basic = function(config){
35592     
35593     this.mgr = config.mgr;
35594     
35595     this.position = config.region;
35596     
35597     var skipConfig = config.skipConfig;
35598     
35599     this.events = {
35600         /**
35601          * @scope Roo.BasicLayoutRegion
35602          */
35603         
35604         /**
35605          * @event beforeremove
35606          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35607          * @param {Roo.LayoutRegion} this
35608          * @param {Roo.ContentPanel} panel The panel
35609          * @param {Object} e The cancel event object
35610          */
35611         "beforeremove" : true,
35612         /**
35613          * @event invalidated
35614          * Fires when the layout for this region is changed.
35615          * @param {Roo.LayoutRegion} this
35616          */
35617         "invalidated" : true,
35618         /**
35619          * @event visibilitychange
35620          * Fires when this region is shown or hidden 
35621          * @param {Roo.LayoutRegion} this
35622          * @param {Boolean} visibility true or false
35623          */
35624         "visibilitychange" : true,
35625         /**
35626          * @event paneladded
35627          * Fires when a panel is added. 
35628          * @param {Roo.LayoutRegion} this
35629          * @param {Roo.ContentPanel} panel The panel
35630          */
35631         "paneladded" : true,
35632         /**
35633          * @event panelremoved
35634          * Fires when a panel is removed. 
35635          * @param {Roo.LayoutRegion} this
35636          * @param {Roo.ContentPanel} panel The panel
35637          */
35638         "panelremoved" : true,
35639         /**
35640          * @event beforecollapse
35641          * Fires when this region before collapse.
35642          * @param {Roo.LayoutRegion} this
35643          */
35644         "beforecollapse" : true,
35645         /**
35646          * @event collapsed
35647          * Fires when this region is collapsed.
35648          * @param {Roo.LayoutRegion} this
35649          */
35650         "collapsed" : true,
35651         /**
35652          * @event expanded
35653          * Fires when this region is expanded.
35654          * @param {Roo.LayoutRegion} this
35655          */
35656         "expanded" : true,
35657         /**
35658          * @event slideshow
35659          * Fires when this region is slid into view.
35660          * @param {Roo.LayoutRegion} this
35661          */
35662         "slideshow" : true,
35663         /**
35664          * @event slidehide
35665          * Fires when this region slides out of view. 
35666          * @param {Roo.LayoutRegion} this
35667          */
35668         "slidehide" : true,
35669         /**
35670          * @event panelactivated
35671          * Fires when a panel is activated. 
35672          * @param {Roo.LayoutRegion} this
35673          * @param {Roo.ContentPanel} panel The activated panel
35674          */
35675         "panelactivated" : true,
35676         /**
35677          * @event resized
35678          * Fires when the user resizes this region. 
35679          * @param {Roo.LayoutRegion} this
35680          * @param {Number} newSize The new size (width for east/west, height for north/south)
35681          */
35682         "resized" : true
35683     };
35684     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35685     this.panels = new Roo.util.MixedCollection();
35686     this.panels.getKey = this.getPanelId.createDelegate(this);
35687     this.box = null;
35688     this.activePanel = null;
35689     // ensure listeners are added...
35690     
35691     if (config.listeners || config.events) {
35692         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35693             listeners : config.listeners || {},
35694             events : config.events || {}
35695         });
35696     }
35697     
35698     if(skipConfig !== true){
35699         this.applyConfig(config);
35700     }
35701 };
35702
35703 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35704 {
35705     getPanelId : function(p){
35706         return p.getId();
35707     },
35708     
35709     applyConfig : function(config){
35710         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35711         this.config = config;
35712         
35713     },
35714     
35715     /**
35716      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35717      * the width, for horizontal (north, south) the height.
35718      * @param {Number} newSize The new width or height
35719      */
35720     resizeTo : function(newSize){
35721         var el = this.el ? this.el :
35722                  (this.activePanel ? this.activePanel.getEl() : null);
35723         if(el){
35724             switch(this.position){
35725                 case "east":
35726                 case "west":
35727                     el.setWidth(newSize);
35728                     this.fireEvent("resized", this, newSize);
35729                 break;
35730                 case "north":
35731                 case "south":
35732                     el.setHeight(newSize);
35733                     this.fireEvent("resized", this, newSize);
35734                 break;                
35735             }
35736         }
35737     },
35738     
35739     getBox : function(){
35740         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35741     },
35742     
35743     getMargins : function(){
35744         return this.margins;
35745     },
35746     
35747     updateBox : function(box){
35748         this.box = box;
35749         var el = this.activePanel.getEl();
35750         el.dom.style.left = box.x + "px";
35751         el.dom.style.top = box.y + "px";
35752         this.activePanel.setSize(box.width, box.height);
35753     },
35754     
35755     /**
35756      * Returns the container element for this region.
35757      * @return {Roo.Element}
35758      */
35759     getEl : function(){
35760         return this.activePanel;
35761     },
35762     
35763     /**
35764      * Returns true if this region is currently visible.
35765      * @return {Boolean}
35766      */
35767     isVisible : function(){
35768         return this.activePanel ? true : false;
35769     },
35770     
35771     setActivePanel : function(panel){
35772         panel = this.getPanel(panel);
35773         if(this.activePanel && this.activePanel != panel){
35774             this.activePanel.setActiveState(false);
35775             this.activePanel.getEl().setLeftTop(-10000,-10000);
35776         }
35777         this.activePanel = panel;
35778         panel.setActiveState(true);
35779         if(this.box){
35780             panel.setSize(this.box.width, this.box.height);
35781         }
35782         this.fireEvent("panelactivated", this, panel);
35783         this.fireEvent("invalidated");
35784     },
35785     
35786     /**
35787      * Show the specified panel.
35788      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35789      * @return {Roo.ContentPanel} The shown panel or null
35790      */
35791     showPanel : function(panel){
35792         panel = this.getPanel(panel);
35793         if(panel){
35794             this.setActivePanel(panel);
35795         }
35796         return panel;
35797     },
35798     
35799     /**
35800      * Get the active panel for this region.
35801      * @return {Roo.ContentPanel} The active panel or null
35802      */
35803     getActivePanel : function(){
35804         return this.activePanel;
35805     },
35806     
35807     /**
35808      * Add the passed ContentPanel(s)
35809      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35810      * @return {Roo.ContentPanel} The panel added (if only one was added)
35811      */
35812     add : function(panel){
35813         if(arguments.length > 1){
35814             for(var i = 0, len = arguments.length; i < len; i++) {
35815                 this.add(arguments[i]);
35816             }
35817             return null;
35818         }
35819         if(this.hasPanel(panel)){
35820             this.showPanel(panel);
35821             return panel;
35822         }
35823         var el = panel.getEl();
35824         if(el.dom.parentNode != this.mgr.el.dom){
35825             this.mgr.el.dom.appendChild(el.dom);
35826         }
35827         if(panel.setRegion){
35828             panel.setRegion(this);
35829         }
35830         this.panels.add(panel);
35831         el.setStyle("position", "absolute");
35832         if(!panel.background){
35833             this.setActivePanel(panel);
35834             if(this.config.initialSize && this.panels.getCount()==1){
35835                 this.resizeTo(this.config.initialSize);
35836             }
35837         }
35838         this.fireEvent("paneladded", this, panel);
35839         return panel;
35840     },
35841     
35842     /**
35843      * Returns true if the panel is in this region.
35844      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35845      * @return {Boolean}
35846      */
35847     hasPanel : function(panel){
35848         if(typeof panel == "object"){ // must be panel obj
35849             panel = panel.getId();
35850         }
35851         return this.getPanel(panel) ? true : false;
35852     },
35853     
35854     /**
35855      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35856      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35857      * @param {Boolean} preservePanel Overrides the config preservePanel option
35858      * @return {Roo.ContentPanel} The panel that was removed
35859      */
35860     remove : function(panel, preservePanel){
35861         panel = this.getPanel(panel);
35862         if(!panel){
35863             return null;
35864         }
35865         var e = {};
35866         this.fireEvent("beforeremove", this, panel, e);
35867         if(e.cancel === true){
35868             return null;
35869         }
35870         var panelId = panel.getId();
35871         this.panels.removeKey(panelId);
35872         return panel;
35873     },
35874     
35875     /**
35876      * Returns the panel specified or null if it's not in this region.
35877      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35878      * @return {Roo.ContentPanel}
35879      */
35880     getPanel : function(id){
35881         if(typeof id == "object"){ // must be panel obj
35882             return id;
35883         }
35884         return this.panels.get(id);
35885     },
35886     
35887     /**
35888      * Returns this regions position (north/south/east/west/center).
35889      * @return {String} 
35890      */
35891     getPosition: function(){
35892         return this.position;    
35893     }
35894 });/*
35895  * Based on:
35896  * Ext JS Library 1.1.1
35897  * Copyright(c) 2006-2007, Ext JS, LLC.
35898  *
35899  * Originally Released Under LGPL - original licence link has changed is not relivant.
35900  *
35901  * Fork - LGPL
35902  * <script type="text/javascript">
35903  */
35904  
35905 /**
35906  * @class Roo.bootstrap.layout.Region
35907  * @extends Roo.bootstrap.layout.Basic
35908  * This class represents a region in a layout manager.
35909  
35910  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35911  * @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})
35912  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35913  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35914  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35915  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35916  * @cfg {String}    title           The title for the region (overrides panel titles)
35917  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35918  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35919  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35920  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35921  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35922  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35923  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35924  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35925  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35926  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35927
35928  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35929  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35930  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35931  * @cfg {Number}    width           For East/West panels
35932  * @cfg {Number}    height          For North/South panels
35933  * @cfg {Boolean}   split           To show the splitter
35934  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35935  * 
35936  * @cfg {string}   cls             Extra CSS classes to add to region
35937  * 
35938  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35939  * @cfg {string}   region  the region that it inhabits..
35940  *
35941
35942  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35943  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35944
35945  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35946  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35947  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35948  */
35949 Roo.bootstrap.layout.Region = function(config)
35950 {
35951     this.applyConfig(config);
35952
35953     var mgr = config.mgr;
35954     var pos = config.region;
35955     config.skipConfig = true;
35956     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35957     
35958     if (mgr.el) {
35959         this.onRender(mgr.el);   
35960     }
35961      
35962     this.visible = true;
35963     this.collapsed = false;
35964     this.unrendered_panels = [];
35965 };
35966
35967 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35968
35969     position: '', // set by wrapper (eg. north/south etc..)
35970     unrendered_panels : null,  // unrendered panels.
35971     
35972     tabPosition : false,
35973     
35974     mgr: false, // points to 'Border'
35975     
35976     
35977     createBody : function(){
35978         /** This region's body element 
35979         * @type Roo.Element */
35980         this.bodyEl = this.el.createChild({
35981                 tag: "div",
35982                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35983         });
35984     },
35985
35986     onRender: function(ctr, pos)
35987     {
35988         var dh = Roo.DomHelper;
35989         /** This region's container element 
35990         * @type Roo.Element */
35991         this.el = dh.append(ctr.dom, {
35992                 tag: "div",
35993                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35994             }, true);
35995         /** This region's title element 
35996         * @type Roo.Element */
35997     
35998         this.titleEl = dh.append(this.el.dom,  {
35999                 tag: "div",
36000                 unselectable: "on",
36001                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36002                 children:[
36003                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36004                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36005                 ]
36006             }, true);
36007         
36008         this.titleEl.enableDisplayMode();
36009         /** This region's title text element 
36010         * @type HTMLElement */
36011         this.titleTextEl = this.titleEl.dom.firstChild;
36012         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36013         /*
36014         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36015         this.closeBtn.enableDisplayMode();
36016         this.closeBtn.on("click", this.closeClicked, this);
36017         this.closeBtn.hide();
36018     */
36019         this.createBody(this.config);
36020         if(this.config.hideWhenEmpty){
36021             this.hide();
36022             this.on("paneladded", this.validateVisibility, this);
36023             this.on("panelremoved", this.validateVisibility, this);
36024         }
36025         if(this.autoScroll){
36026             this.bodyEl.setStyle("overflow", "auto");
36027         }else{
36028             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36029         }
36030         //if(c.titlebar !== false){
36031             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36032                 this.titleEl.hide();
36033             }else{
36034                 this.titleEl.show();
36035                 if(this.config.title){
36036                     this.titleTextEl.innerHTML = this.config.title;
36037                 }
36038             }
36039         //}
36040         if(this.config.collapsed){
36041             this.collapse(true);
36042         }
36043         if(this.config.hidden){
36044             this.hide();
36045         }
36046         
36047         if (this.unrendered_panels && this.unrendered_panels.length) {
36048             for (var i =0;i< this.unrendered_panels.length; i++) {
36049                 this.add(this.unrendered_panels[i]);
36050             }
36051             this.unrendered_panels = null;
36052             
36053         }
36054         
36055     },
36056     
36057     applyConfig : function(c)
36058     {
36059         /*
36060          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36061             var dh = Roo.DomHelper;
36062             if(c.titlebar !== false){
36063                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36064                 this.collapseBtn.on("click", this.collapse, this);
36065                 this.collapseBtn.enableDisplayMode();
36066                 /*
36067                 if(c.showPin === true || this.showPin){
36068                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36069                     this.stickBtn.enableDisplayMode();
36070                     this.stickBtn.on("click", this.expand, this);
36071                     this.stickBtn.hide();
36072                 }
36073                 
36074             }
36075             */
36076             /** This region's collapsed element
36077             * @type Roo.Element */
36078             /*
36079              *
36080             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36081                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36082             ]}, true);
36083             
36084             if(c.floatable !== false){
36085                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36086                this.collapsedEl.on("click", this.collapseClick, this);
36087             }
36088
36089             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36090                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36091                    id: "message", unselectable: "on", style:{"float":"left"}});
36092                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36093              }
36094             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36095             this.expandBtn.on("click", this.expand, this);
36096             
36097         }
36098         
36099         if(this.collapseBtn){
36100             this.collapseBtn.setVisible(c.collapsible == true);
36101         }
36102         
36103         this.cmargins = c.cmargins || this.cmargins ||
36104                          (this.position == "west" || this.position == "east" ?
36105                              {top: 0, left: 2, right:2, bottom: 0} :
36106                              {top: 2, left: 0, right:0, bottom: 2});
36107         */
36108         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36109         
36110         
36111         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36112         
36113         this.autoScroll = c.autoScroll || false;
36114         
36115         
36116        
36117         
36118         this.duration = c.duration || .30;
36119         this.slideDuration = c.slideDuration || .45;
36120         this.config = c;
36121        
36122     },
36123     /**
36124      * Returns true if this region is currently visible.
36125      * @return {Boolean}
36126      */
36127     isVisible : function(){
36128         return this.visible;
36129     },
36130
36131     /**
36132      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36133      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36134      */
36135     //setCollapsedTitle : function(title){
36136     //    title = title || "&#160;";
36137      //   if(this.collapsedTitleTextEl){
36138       //      this.collapsedTitleTextEl.innerHTML = title;
36139        // }
36140     //},
36141
36142     getBox : function(){
36143         var b;
36144       //  if(!this.collapsed){
36145             b = this.el.getBox(false, true);
36146        // }else{
36147           //  b = this.collapsedEl.getBox(false, true);
36148         //}
36149         return b;
36150     },
36151
36152     getMargins : function(){
36153         return this.margins;
36154         //return this.collapsed ? this.cmargins : this.margins;
36155     },
36156 /*
36157     highlight : function(){
36158         this.el.addClass("x-layout-panel-dragover");
36159     },
36160
36161     unhighlight : function(){
36162         this.el.removeClass("x-layout-panel-dragover");
36163     },
36164 */
36165     updateBox : function(box)
36166     {
36167         if (!this.bodyEl) {
36168             return; // not rendered yet..
36169         }
36170         
36171         this.box = box;
36172         if(!this.collapsed){
36173             this.el.dom.style.left = box.x + "px";
36174             this.el.dom.style.top = box.y + "px";
36175             this.updateBody(box.width, box.height);
36176         }else{
36177             this.collapsedEl.dom.style.left = box.x + "px";
36178             this.collapsedEl.dom.style.top = box.y + "px";
36179             this.collapsedEl.setSize(box.width, box.height);
36180         }
36181         if(this.tabs){
36182             this.tabs.autoSizeTabs();
36183         }
36184     },
36185
36186     updateBody : function(w, h)
36187     {
36188         if(w !== null){
36189             this.el.setWidth(w);
36190             w -= this.el.getBorderWidth("rl");
36191             if(this.config.adjustments){
36192                 w += this.config.adjustments[0];
36193             }
36194         }
36195         if(h !== null && h > 0){
36196             this.el.setHeight(h);
36197             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36198             h -= this.el.getBorderWidth("tb");
36199             if(this.config.adjustments){
36200                 h += this.config.adjustments[1];
36201             }
36202             this.bodyEl.setHeight(h);
36203             if(this.tabs){
36204                 h = this.tabs.syncHeight(h);
36205             }
36206         }
36207         if(this.panelSize){
36208             w = w !== null ? w : this.panelSize.width;
36209             h = h !== null ? h : this.panelSize.height;
36210         }
36211         if(this.activePanel){
36212             var el = this.activePanel.getEl();
36213             w = w !== null ? w : el.getWidth();
36214             h = h !== null ? h : el.getHeight();
36215             this.panelSize = {width: w, height: h};
36216             this.activePanel.setSize(w, h);
36217         }
36218         if(Roo.isIE && this.tabs){
36219             this.tabs.el.repaint();
36220         }
36221     },
36222
36223     /**
36224      * Returns the container element for this region.
36225      * @return {Roo.Element}
36226      */
36227     getEl : function(){
36228         return this.el;
36229     },
36230
36231     /**
36232      * Hides this region.
36233      */
36234     hide : function(){
36235         //if(!this.collapsed){
36236             this.el.dom.style.left = "-2000px";
36237             this.el.hide();
36238         //}else{
36239          //   this.collapsedEl.dom.style.left = "-2000px";
36240          //   this.collapsedEl.hide();
36241        // }
36242         this.visible = false;
36243         this.fireEvent("visibilitychange", this, false);
36244     },
36245
36246     /**
36247      * Shows this region if it was previously hidden.
36248      */
36249     show : function(){
36250         //if(!this.collapsed){
36251             this.el.show();
36252         //}else{
36253         //    this.collapsedEl.show();
36254        // }
36255         this.visible = true;
36256         this.fireEvent("visibilitychange", this, true);
36257     },
36258 /*
36259     closeClicked : function(){
36260         if(this.activePanel){
36261             this.remove(this.activePanel);
36262         }
36263     },
36264
36265     collapseClick : function(e){
36266         if(this.isSlid){
36267            e.stopPropagation();
36268            this.slideIn();
36269         }else{
36270            e.stopPropagation();
36271            this.slideOut();
36272         }
36273     },
36274 */
36275     /**
36276      * Collapses this region.
36277      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36278      */
36279     /*
36280     collapse : function(skipAnim, skipCheck = false){
36281         if(this.collapsed) {
36282             return;
36283         }
36284         
36285         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36286             
36287             this.collapsed = true;
36288             if(this.split){
36289                 this.split.el.hide();
36290             }
36291             if(this.config.animate && skipAnim !== true){
36292                 this.fireEvent("invalidated", this);
36293                 this.animateCollapse();
36294             }else{
36295                 this.el.setLocation(-20000,-20000);
36296                 this.el.hide();
36297                 this.collapsedEl.show();
36298                 this.fireEvent("collapsed", this);
36299                 this.fireEvent("invalidated", this);
36300             }
36301         }
36302         
36303     },
36304 */
36305     animateCollapse : function(){
36306         // overridden
36307     },
36308
36309     /**
36310      * Expands this region if it was previously collapsed.
36311      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36312      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36313      */
36314     /*
36315     expand : function(e, skipAnim){
36316         if(e) {
36317             e.stopPropagation();
36318         }
36319         if(!this.collapsed || this.el.hasActiveFx()) {
36320             return;
36321         }
36322         if(this.isSlid){
36323             this.afterSlideIn();
36324             skipAnim = true;
36325         }
36326         this.collapsed = false;
36327         if(this.config.animate && skipAnim !== true){
36328             this.animateExpand();
36329         }else{
36330             this.el.show();
36331             if(this.split){
36332                 this.split.el.show();
36333             }
36334             this.collapsedEl.setLocation(-2000,-2000);
36335             this.collapsedEl.hide();
36336             this.fireEvent("invalidated", this);
36337             this.fireEvent("expanded", this);
36338         }
36339     },
36340 */
36341     animateExpand : function(){
36342         // overridden
36343     },
36344
36345     initTabs : function()
36346     {
36347         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36348         
36349         var ts = new Roo.bootstrap.panel.Tabs({
36350             el: this.bodyEl.dom,
36351             region : this,
36352             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36353             disableTooltips: this.config.disableTabTips,
36354             toolbar : this.config.toolbar
36355         });
36356         
36357         if(this.config.hideTabs){
36358             ts.stripWrap.setDisplayed(false);
36359         }
36360         this.tabs = ts;
36361         ts.resizeTabs = this.config.resizeTabs === true;
36362         ts.minTabWidth = this.config.minTabWidth || 40;
36363         ts.maxTabWidth = this.config.maxTabWidth || 250;
36364         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36365         ts.monitorResize = false;
36366         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36367         ts.bodyEl.addClass('roo-layout-tabs-body');
36368         this.panels.each(this.initPanelAsTab, this);
36369     },
36370
36371     initPanelAsTab : function(panel){
36372         var ti = this.tabs.addTab(
36373             panel.getEl().id,
36374             panel.getTitle(),
36375             null,
36376             this.config.closeOnTab && panel.isClosable(),
36377             panel.tpl
36378         );
36379         if(panel.tabTip !== undefined){
36380             ti.setTooltip(panel.tabTip);
36381         }
36382         ti.on("activate", function(){
36383               this.setActivePanel(panel);
36384         }, this);
36385         
36386         if(this.config.closeOnTab){
36387             ti.on("beforeclose", function(t, e){
36388                 e.cancel = true;
36389                 this.remove(panel);
36390             }, this);
36391         }
36392         
36393         panel.tabItem = ti;
36394         
36395         return ti;
36396     },
36397
36398     updatePanelTitle : function(panel, title)
36399     {
36400         if(this.activePanel == panel){
36401             this.updateTitle(title);
36402         }
36403         if(this.tabs){
36404             var ti = this.tabs.getTab(panel.getEl().id);
36405             ti.setText(title);
36406             if(panel.tabTip !== undefined){
36407                 ti.setTooltip(panel.tabTip);
36408             }
36409         }
36410     },
36411
36412     updateTitle : function(title){
36413         if(this.titleTextEl && !this.config.title){
36414             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36415         }
36416     },
36417
36418     setActivePanel : function(panel)
36419     {
36420         panel = this.getPanel(panel);
36421         if(this.activePanel && this.activePanel != panel){
36422             if(this.activePanel.setActiveState(false) === false){
36423                 return;
36424             }
36425         }
36426         this.activePanel = panel;
36427         panel.setActiveState(true);
36428         if(this.panelSize){
36429             panel.setSize(this.panelSize.width, this.panelSize.height);
36430         }
36431         if(this.closeBtn){
36432             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36433         }
36434         this.updateTitle(panel.getTitle());
36435         if(this.tabs){
36436             this.fireEvent("invalidated", this);
36437         }
36438         this.fireEvent("panelactivated", this, panel);
36439     },
36440
36441     /**
36442      * Shows the specified panel.
36443      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36444      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36445      */
36446     showPanel : function(panel)
36447     {
36448         panel = this.getPanel(panel);
36449         if(panel){
36450             if(this.tabs){
36451                 var tab = this.tabs.getTab(panel.getEl().id);
36452                 if(tab.isHidden()){
36453                     this.tabs.unhideTab(tab.id);
36454                 }
36455                 tab.activate();
36456             }else{
36457                 this.setActivePanel(panel);
36458             }
36459         }
36460         return panel;
36461     },
36462
36463     /**
36464      * Get the active panel for this region.
36465      * @return {Roo.ContentPanel} The active panel or null
36466      */
36467     getActivePanel : function(){
36468         return this.activePanel;
36469     },
36470
36471     validateVisibility : function(){
36472         if(this.panels.getCount() < 1){
36473             this.updateTitle("&#160;");
36474             this.closeBtn.hide();
36475             this.hide();
36476         }else{
36477             if(!this.isVisible()){
36478                 this.show();
36479             }
36480         }
36481     },
36482
36483     /**
36484      * Adds the passed ContentPanel(s) to this region.
36485      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36486      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36487      */
36488     add : function(panel)
36489     {
36490         if(arguments.length > 1){
36491             for(var i = 0, len = arguments.length; i < len; i++) {
36492                 this.add(arguments[i]);
36493             }
36494             return null;
36495         }
36496         
36497         // if we have not been rendered yet, then we can not really do much of this..
36498         if (!this.bodyEl) {
36499             this.unrendered_panels.push(panel);
36500             return panel;
36501         }
36502         
36503         
36504         
36505         
36506         if(this.hasPanel(panel)){
36507             this.showPanel(panel);
36508             return panel;
36509         }
36510         panel.setRegion(this);
36511         this.panels.add(panel);
36512        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36513             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36514             // and hide them... ???
36515             this.bodyEl.dom.appendChild(panel.getEl().dom);
36516             if(panel.background !== true){
36517                 this.setActivePanel(panel);
36518             }
36519             this.fireEvent("paneladded", this, panel);
36520             return panel;
36521         }
36522         */
36523         if(!this.tabs){
36524             this.initTabs();
36525         }else{
36526             this.initPanelAsTab(panel);
36527         }
36528         
36529         
36530         if(panel.background !== true){
36531             this.tabs.activate(panel.getEl().id);
36532         }
36533         this.fireEvent("paneladded", this, panel);
36534         return panel;
36535     },
36536
36537     /**
36538      * Hides the tab for the specified panel.
36539      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36540      */
36541     hidePanel : function(panel){
36542         if(this.tabs && (panel = this.getPanel(panel))){
36543             this.tabs.hideTab(panel.getEl().id);
36544         }
36545     },
36546
36547     /**
36548      * Unhides the tab for a previously hidden panel.
36549      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36550      */
36551     unhidePanel : function(panel){
36552         if(this.tabs && (panel = this.getPanel(panel))){
36553             this.tabs.unhideTab(panel.getEl().id);
36554         }
36555     },
36556
36557     clearPanels : function(){
36558         while(this.panels.getCount() > 0){
36559              this.remove(this.panels.first());
36560         }
36561     },
36562
36563     /**
36564      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36565      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36566      * @param {Boolean} preservePanel Overrides the config preservePanel option
36567      * @return {Roo.ContentPanel} The panel that was removed
36568      */
36569     remove : function(panel, preservePanel)
36570     {
36571         panel = this.getPanel(panel);
36572         if(!panel){
36573             return null;
36574         }
36575         var e = {};
36576         this.fireEvent("beforeremove", this, panel, e);
36577         if(e.cancel === true){
36578             return null;
36579         }
36580         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36581         var panelId = panel.getId();
36582         this.panels.removeKey(panelId);
36583         if(preservePanel){
36584             document.body.appendChild(panel.getEl().dom);
36585         }
36586         if(this.tabs){
36587             this.tabs.removeTab(panel.getEl().id);
36588         }else if (!preservePanel){
36589             this.bodyEl.dom.removeChild(panel.getEl().dom);
36590         }
36591         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36592             var p = this.panels.first();
36593             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36594             tempEl.appendChild(p.getEl().dom);
36595             this.bodyEl.update("");
36596             this.bodyEl.dom.appendChild(p.getEl().dom);
36597             tempEl = null;
36598             this.updateTitle(p.getTitle());
36599             this.tabs = null;
36600             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36601             this.setActivePanel(p);
36602         }
36603         panel.setRegion(null);
36604         if(this.activePanel == panel){
36605             this.activePanel = null;
36606         }
36607         if(this.config.autoDestroy !== false && preservePanel !== true){
36608             try{panel.destroy();}catch(e){}
36609         }
36610         this.fireEvent("panelremoved", this, panel);
36611         return panel;
36612     },
36613
36614     /**
36615      * Returns the TabPanel component used by this region
36616      * @return {Roo.TabPanel}
36617      */
36618     getTabs : function(){
36619         return this.tabs;
36620     },
36621
36622     createTool : function(parentEl, className){
36623         var btn = Roo.DomHelper.append(parentEl, {
36624             tag: "div",
36625             cls: "x-layout-tools-button",
36626             children: [ {
36627                 tag: "div",
36628                 cls: "roo-layout-tools-button-inner " + className,
36629                 html: "&#160;"
36630             }]
36631         }, true);
36632         btn.addClassOnOver("roo-layout-tools-button-over");
36633         return btn;
36634     }
36635 });/*
36636  * Based on:
36637  * Ext JS Library 1.1.1
36638  * Copyright(c) 2006-2007, Ext JS, LLC.
36639  *
36640  * Originally Released Under LGPL - original licence link has changed is not relivant.
36641  *
36642  * Fork - LGPL
36643  * <script type="text/javascript">
36644  */
36645  
36646
36647
36648 /**
36649  * @class Roo.SplitLayoutRegion
36650  * @extends Roo.LayoutRegion
36651  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36652  */
36653 Roo.bootstrap.layout.Split = function(config){
36654     this.cursor = config.cursor;
36655     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36656 };
36657
36658 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36659 {
36660     splitTip : "Drag to resize.",
36661     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36662     useSplitTips : false,
36663
36664     applyConfig : function(config){
36665         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36666     },
36667     
36668     onRender : function(ctr,pos) {
36669         
36670         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36671         if(!this.config.split){
36672             return;
36673         }
36674         if(!this.split){
36675             
36676             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36677                             tag: "div",
36678                             id: this.el.id + "-split",
36679                             cls: "roo-layout-split roo-layout-split-"+this.position,
36680                             html: "&#160;"
36681             });
36682             /** The SplitBar for this region 
36683             * @type Roo.SplitBar */
36684             // does not exist yet...
36685             Roo.log([this.position, this.orientation]);
36686             
36687             this.split = new Roo.bootstrap.SplitBar({
36688                 dragElement : splitEl,
36689                 resizingElement: this.el,
36690                 orientation : this.orientation
36691             });
36692             
36693             this.split.on("moved", this.onSplitMove, this);
36694             this.split.useShim = this.config.useShim === true;
36695             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36696             if(this.useSplitTips){
36697                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36698             }
36699             //if(config.collapsible){
36700             //    this.split.el.on("dblclick", this.collapse,  this);
36701             //}
36702         }
36703         if(typeof this.config.minSize != "undefined"){
36704             this.split.minSize = this.config.minSize;
36705         }
36706         if(typeof this.config.maxSize != "undefined"){
36707             this.split.maxSize = this.config.maxSize;
36708         }
36709         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36710             this.hideSplitter();
36711         }
36712         
36713     },
36714
36715     getHMaxSize : function(){
36716          var cmax = this.config.maxSize || 10000;
36717          var center = this.mgr.getRegion("center");
36718          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36719     },
36720
36721     getVMaxSize : function(){
36722          var cmax = this.config.maxSize || 10000;
36723          var center = this.mgr.getRegion("center");
36724          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36725     },
36726
36727     onSplitMove : function(split, newSize){
36728         this.fireEvent("resized", this, newSize);
36729     },
36730     
36731     /** 
36732      * Returns the {@link Roo.SplitBar} for this region.
36733      * @return {Roo.SplitBar}
36734      */
36735     getSplitBar : function(){
36736         return this.split;
36737     },
36738     
36739     hide : function(){
36740         this.hideSplitter();
36741         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36742     },
36743
36744     hideSplitter : function(){
36745         if(this.split){
36746             this.split.el.setLocation(-2000,-2000);
36747             this.split.el.hide();
36748         }
36749     },
36750
36751     show : function(){
36752         if(this.split){
36753             this.split.el.show();
36754         }
36755         Roo.bootstrap.layout.Split.superclass.show.call(this);
36756     },
36757     
36758     beforeSlide: function(){
36759         if(Roo.isGecko){// firefox overflow auto bug workaround
36760             this.bodyEl.clip();
36761             if(this.tabs) {
36762                 this.tabs.bodyEl.clip();
36763             }
36764             if(this.activePanel){
36765                 this.activePanel.getEl().clip();
36766                 
36767                 if(this.activePanel.beforeSlide){
36768                     this.activePanel.beforeSlide();
36769                 }
36770             }
36771         }
36772     },
36773     
36774     afterSlide : function(){
36775         if(Roo.isGecko){// firefox overflow auto bug workaround
36776             this.bodyEl.unclip();
36777             if(this.tabs) {
36778                 this.tabs.bodyEl.unclip();
36779             }
36780             if(this.activePanel){
36781                 this.activePanel.getEl().unclip();
36782                 if(this.activePanel.afterSlide){
36783                     this.activePanel.afterSlide();
36784                 }
36785             }
36786         }
36787     },
36788
36789     initAutoHide : function(){
36790         if(this.autoHide !== false){
36791             if(!this.autoHideHd){
36792                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36793                 this.autoHideHd = {
36794                     "mouseout": function(e){
36795                         if(!e.within(this.el, true)){
36796                             st.delay(500);
36797                         }
36798                     },
36799                     "mouseover" : function(e){
36800                         st.cancel();
36801                     },
36802                     scope : this
36803                 };
36804             }
36805             this.el.on(this.autoHideHd);
36806         }
36807     },
36808
36809     clearAutoHide : function(){
36810         if(this.autoHide !== false){
36811             this.el.un("mouseout", this.autoHideHd.mouseout);
36812             this.el.un("mouseover", this.autoHideHd.mouseover);
36813         }
36814     },
36815
36816     clearMonitor : function(){
36817         Roo.get(document).un("click", this.slideInIf, this);
36818     },
36819
36820     // these names are backwards but not changed for compat
36821     slideOut : function(){
36822         if(this.isSlid || this.el.hasActiveFx()){
36823             return;
36824         }
36825         this.isSlid = true;
36826         if(this.collapseBtn){
36827             this.collapseBtn.hide();
36828         }
36829         this.closeBtnState = this.closeBtn.getStyle('display');
36830         this.closeBtn.hide();
36831         if(this.stickBtn){
36832             this.stickBtn.show();
36833         }
36834         this.el.show();
36835         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36836         this.beforeSlide();
36837         this.el.setStyle("z-index", 10001);
36838         this.el.slideIn(this.getSlideAnchor(), {
36839             callback: function(){
36840                 this.afterSlide();
36841                 this.initAutoHide();
36842                 Roo.get(document).on("click", this.slideInIf, this);
36843                 this.fireEvent("slideshow", this);
36844             },
36845             scope: this,
36846             block: true
36847         });
36848     },
36849
36850     afterSlideIn : function(){
36851         this.clearAutoHide();
36852         this.isSlid = false;
36853         this.clearMonitor();
36854         this.el.setStyle("z-index", "");
36855         if(this.collapseBtn){
36856             this.collapseBtn.show();
36857         }
36858         this.closeBtn.setStyle('display', this.closeBtnState);
36859         if(this.stickBtn){
36860             this.stickBtn.hide();
36861         }
36862         this.fireEvent("slidehide", this);
36863     },
36864
36865     slideIn : function(cb){
36866         if(!this.isSlid || this.el.hasActiveFx()){
36867             Roo.callback(cb);
36868             return;
36869         }
36870         this.isSlid = false;
36871         this.beforeSlide();
36872         this.el.slideOut(this.getSlideAnchor(), {
36873             callback: function(){
36874                 this.el.setLeftTop(-10000, -10000);
36875                 this.afterSlide();
36876                 this.afterSlideIn();
36877                 Roo.callback(cb);
36878             },
36879             scope: this,
36880             block: true
36881         });
36882     },
36883     
36884     slideInIf : function(e){
36885         if(!e.within(this.el)){
36886             this.slideIn();
36887         }
36888     },
36889
36890     animateCollapse : function(){
36891         this.beforeSlide();
36892         this.el.setStyle("z-index", 20000);
36893         var anchor = this.getSlideAnchor();
36894         this.el.slideOut(anchor, {
36895             callback : function(){
36896                 this.el.setStyle("z-index", "");
36897                 this.collapsedEl.slideIn(anchor, {duration:.3});
36898                 this.afterSlide();
36899                 this.el.setLocation(-10000,-10000);
36900                 this.el.hide();
36901                 this.fireEvent("collapsed", this);
36902             },
36903             scope: this,
36904             block: true
36905         });
36906     },
36907
36908     animateExpand : function(){
36909         this.beforeSlide();
36910         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36911         this.el.setStyle("z-index", 20000);
36912         this.collapsedEl.hide({
36913             duration:.1
36914         });
36915         this.el.slideIn(this.getSlideAnchor(), {
36916             callback : function(){
36917                 this.el.setStyle("z-index", "");
36918                 this.afterSlide();
36919                 if(this.split){
36920                     this.split.el.show();
36921                 }
36922                 this.fireEvent("invalidated", this);
36923                 this.fireEvent("expanded", this);
36924             },
36925             scope: this,
36926             block: true
36927         });
36928     },
36929
36930     anchors : {
36931         "west" : "left",
36932         "east" : "right",
36933         "north" : "top",
36934         "south" : "bottom"
36935     },
36936
36937     sanchors : {
36938         "west" : "l",
36939         "east" : "r",
36940         "north" : "t",
36941         "south" : "b"
36942     },
36943
36944     canchors : {
36945         "west" : "tl-tr",
36946         "east" : "tr-tl",
36947         "north" : "tl-bl",
36948         "south" : "bl-tl"
36949     },
36950
36951     getAnchor : function(){
36952         return this.anchors[this.position];
36953     },
36954
36955     getCollapseAnchor : function(){
36956         return this.canchors[this.position];
36957     },
36958
36959     getSlideAnchor : function(){
36960         return this.sanchors[this.position];
36961     },
36962
36963     getAlignAdj : function(){
36964         var cm = this.cmargins;
36965         switch(this.position){
36966             case "west":
36967                 return [0, 0];
36968             break;
36969             case "east":
36970                 return [0, 0];
36971             break;
36972             case "north":
36973                 return [0, 0];
36974             break;
36975             case "south":
36976                 return [0, 0];
36977             break;
36978         }
36979     },
36980
36981     getExpandAdj : function(){
36982         var c = this.collapsedEl, cm = this.cmargins;
36983         switch(this.position){
36984             case "west":
36985                 return [-(cm.right+c.getWidth()+cm.left), 0];
36986             break;
36987             case "east":
36988                 return [cm.right+c.getWidth()+cm.left, 0];
36989             break;
36990             case "north":
36991                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36992             break;
36993             case "south":
36994                 return [0, cm.top+cm.bottom+c.getHeight()];
36995             break;
36996         }
36997     }
36998 });/*
36999  * Based on:
37000  * Ext JS Library 1.1.1
37001  * Copyright(c) 2006-2007, Ext JS, LLC.
37002  *
37003  * Originally Released Under LGPL - original licence link has changed is not relivant.
37004  *
37005  * Fork - LGPL
37006  * <script type="text/javascript">
37007  */
37008 /*
37009  * These classes are private internal classes
37010  */
37011 Roo.bootstrap.layout.Center = function(config){
37012     config.region = "center";
37013     Roo.bootstrap.layout.Region.call(this, config);
37014     this.visible = true;
37015     this.minWidth = config.minWidth || 20;
37016     this.minHeight = config.minHeight || 20;
37017 };
37018
37019 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37020     hide : function(){
37021         // center panel can't be hidden
37022     },
37023     
37024     show : function(){
37025         // center panel can't be hidden
37026     },
37027     
37028     getMinWidth: function(){
37029         return this.minWidth;
37030     },
37031     
37032     getMinHeight: function(){
37033         return this.minHeight;
37034     }
37035 });
37036
37037
37038
37039
37040  
37041
37042
37043
37044
37045
37046
37047 Roo.bootstrap.layout.North = function(config)
37048 {
37049     config.region = 'north';
37050     config.cursor = 'n-resize';
37051     
37052     Roo.bootstrap.layout.Split.call(this, config);
37053     
37054     
37055     if(this.split){
37056         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37057         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37058         this.split.el.addClass("roo-layout-split-v");
37059     }
37060     var size = config.initialSize || config.height;
37061     if(typeof size != "undefined"){
37062         this.el.setHeight(size);
37063     }
37064 };
37065 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37066 {
37067     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37068     
37069     
37070     
37071     getBox : function(){
37072         if(this.collapsed){
37073             return this.collapsedEl.getBox();
37074         }
37075         var box = this.el.getBox();
37076         if(this.split){
37077             box.height += this.split.el.getHeight();
37078         }
37079         return box;
37080     },
37081     
37082     updateBox : function(box){
37083         if(this.split && !this.collapsed){
37084             box.height -= this.split.el.getHeight();
37085             this.split.el.setLeft(box.x);
37086             this.split.el.setTop(box.y+box.height);
37087             this.split.el.setWidth(box.width);
37088         }
37089         if(this.collapsed){
37090             this.updateBody(box.width, null);
37091         }
37092         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37093     }
37094 });
37095
37096
37097
37098
37099
37100 Roo.bootstrap.layout.South = function(config){
37101     config.region = 'south';
37102     config.cursor = 's-resize';
37103     Roo.bootstrap.layout.Split.call(this, config);
37104     if(this.split){
37105         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37106         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37107         this.split.el.addClass("roo-layout-split-v");
37108     }
37109     var size = config.initialSize || config.height;
37110     if(typeof size != "undefined"){
37111         this.el.setHeight(size);
37112     }
37113 };
37114
37115 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37116     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37117     getBox : function(){
37118         if(this.collapsed){
37119             return this.collapsedEl.getBox();
37120         }
37121         var box = this.el.getBox();
37122         if(this.split){
37123             var sh = this.split.el.getHeight();
37124             box.height += sh;
37125             box.y -= sh;
37126         }
37127         return box;
37128     },
37129     
37130     updateBox : function(box){
37131         if(this.split && !this.collapsed){
37132             var sh = this.split.el.getHeight();
37133             box.height -= sh;
37134             box.y += sh;
37135             this.split.el.setLeft(box.x);
37136             this.split.el.setTop(box.y-sh);
37137             this.split.el.setWidth(box.width);
37138         }
37139         if(this.collapsed){
37140             this.updateBody(box.width, null);
37141         }
37142         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37143     }
37144 });
37145
37146 Roo.bootstrap.layout.East = function(config){
37147     config.region = "east";
37148     config.cursor = "e-resize";
37149     Roo.bootstrap.layout.Split.call(this, config);
37150     if(this.split){
37151         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37152         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37153         this.split.el.addClass("roo-layout-split-h");
37154     }
37155     var size = config.initialSize || config.width;
37156     if(typeof size != "undefined"){
37157         this.el.setWidth(size);
37158     }
37159 };
37160 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37161     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37162     getBox : function(){
37163         if(this.collapsed){
37164             return this.collapsedEl.getBox();
37165         }
37166         var box = this.el.getBox();
37167         if(this.split){
37168             var sw = this.split.el.getWidth();
37169             box.width += sw;
37170             box.x -= sw;
37171         }
37172         return box;
37173     },
37174
37175     updateBox : function(box){
37176         if(this.split && !this.collapsed){
37177             var sw = this.split.el.getWidth();
37178             box.width -= sw;
37179             this.split.el.setLeft(box.x);
37180             this.split.el.setTop(box.y);
37181             this.split.el.setHeight(box.height);
37182             box.x += sw;
37183         }
37184         if(this.collapsed){
37185             this.updateBody(null, box.height);
37186         }
37187         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37188     }
37189 });
37190
37191 Roo.bootstrap.layout.West = function(config){
37192     config.region = "west";
37193     config.cursor = "w-resize";
37194     
37195     Roo.bootstrap.layout.Split.call(this, config);
37196     if(this.split){
37197         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37198         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37199         this.split.el.addClass("roo-layout-split-h");
37200     }
37201     
37202 };
37203 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37204     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37205     
37206     onRender: function(ctr, pos)
37207     {
37208         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37209         var size = this.config.initialSize || this.config.width;
37210         if(typeof size != "undefined"){
37211             this.el.setWidth(size);
37212         }
37213     },
37214     
37215     getBox : function(){
37216         if(this.collapsed){
37217             return this.collapsedEl.getBox();
37218         }
37219         var box = this.el.getBox();
37220         if(this.split){
37221             box.width += this.split.el.getWidth();
37222         }
37223         return box;
37224     },
37225     
37226     updateBox : function(box){
37227         if(this.split && !this.collapsed){
37228             var sw = this.split.el.getWidth();
37229             box.width -= sw;
37230             this.split.el.setLeft(box.x+box.width);
37231             this.split.el.setTop(box.y);
37232             this.split.el.setHeight(box.height);
37233         }
37234         if(this.collapsed){
37235             this.updateBody(null, box.height);
37236         }
37237         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37238     }
37239 });Roo.namespace("Roo.bootstrap.panel");/*
37240  * Based on:
37241  * Ext JS Library 1.1.1
37242  * Copyright(c) 2006-2007, Ext JS, LLC.
37243  *
37244  * Originally Released Under LGPL - original licence link has changed is not relivant.
37245  *
37246  * Fork - LGPL
37247  * <script type="text/javascript">
37248  */
37249 /**
37250  * @class Roo.ContentPanel
37251  * @extends Roo.util.Observable
37252  * A basic ContentPanel element.
37253  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37254  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37255  * @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
37256  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37257  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37258  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37259  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37260  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37261  * @cfg {String} title          The title for this panel
37262  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37263  * @cfg {String} url            Calls {@link #setUrl} with this value
37264  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37265  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37266  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37267  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37268  * @cfg {Boolean} badges render the badges
37269
37270  * @constructor
37271  * Create a new ContentPanel.
37272  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37273  * @param {String/Object} config A string to set only the title or a config object
37274  * @param {String} content (optional) Set the HTML content for this panel
37275  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37276  */
37277 Roo.bootstrap.panel.Content = function( config){
37278     
37279     this.tpl = config.tpl || false;
37280     
37281     var el = config.el;
37282     var content = config.content;
37283
37284     if(config.autoCreate){ // xtype is available if this is called from factory
37285         el = Roo.id();
37286     }
37287     this.el = Roo.get(el);
37288     if(!this.el && config && config.autoCreate){
37289         if(typeof config.autoCreate == "object"){
37290             if(!config.autoCreate.id){
37291                 config.autoCreate.id = config.id||el;
37292             }
37293             this.el = Roo.DomHelper.append(document.body,
37294                         config.autoCreate, true);
37295         }else{
37296             var elcfg =  {   tag: "div",
37297                             cls: "roo-layout-inactive-content",
37298                             id: config.id||el
37299                             };
37300             if (config.html) {
37301                 elcfg.html = config.html;
37302                 
37303             }
37304                         
37305             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37306         }
37307     } 
37308     this.closable = false;
37309     this.loaded = false;
37310     this.active = false;
37311    
37312       
37313     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37314         
37315         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37316         
37317         this.wrapEl = this.el; //this.el.wrap();
37318         var ti = [];
37319         if (config.toolbar.items) {
37320             ti = config.toolbar.items ;
37321             delete config.toolbar.items ;
37322         }
37323         
37324         var nitems = [];
37325         this.toolbar.render(this.wrapEl, 'before');
37326         for(var i =0;i < ti.length;i++) {
37327           //  Roo.log(['add child', items[i]]);
37328             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37329         }
37330         this.toolbar.items = nitems;
37331         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37332         delete config.toolbar;
37333         
37334     }
37335     /*
37336     // xtype created footer. - not sure if will work as we normally have to render first..
37337     if (this.footer && !this.footer.el && this.footer.xtype) {
37338         if (!this.wrapEl) {
37339             this.wrapEl = this.el.wrap();
37340         }
37341     
37342         this.footer.container = this.wrapEl.createChild();
37343          
37344         this.footer = Roo.factory(this.footer, Roo);
37345         
37346     }
37347     */
37348     
37349      if(typeof config == "string"){
37350         this.title = config;
37351     }else{
37352         Roo.apply(this, config);
37353     }
37354     
37355     if(this.resizeEl){
37356         this.resizeEl = Roo.get(this.resizeEl, true);
37357     }else{
37358         this.resizeEl = this.el;
37359     }
37360     // handle view.xtype
37361     
37362  
37363     
37364     
37365     this.addEvents({
37366         /**
37367          * @event activate
37368          * Fires when this panel is activated. 
37369          * @param {Roo.ContentPanel} this
37370          */
37371         "activate" : true,
37372         /**
37373          * @event deactivate
37374          * Fires when this panel is activated. 
37375          * @param {Roo.ContentPanel} this
37376          */
37377         "deactivate" : true,
37378
37379         /**
37380          * @event resize
37381          * Fires when this panel is resized if fitToFrame is true.
37382          * @param {Roo.ContentPanel} this
37383          * @param {Number} width The width after any component adjustments
37384          * @param {Number} height The height after any component adjustments
37385          */
37386         "resize" : true,
37387         
37388          /**
37389          * @event render
37390          * Fires when this tab is created
37391          * @param {Roo.ContentPanel} this
37392          */
37393         "render" : true
37394         
37395         
37396         
37397     });
37398     
37399
37400     
37401     
37402     if(this.autoScroll){
37403         this.resizeEl.setStyle("overflow", "auto");
37404     } else {
37405         // fix randome scrolling
37406         //this.el.on('scroll', function() {
37407         //    Roo.log('fix random scolling');
37408         //    this.scrollTo('top',0); 
37409         //});
37410     }
37411     content = content || this.content;
37412     if(content){
37413         this.setContent(content);
37414     }
37415     if(config && config.url){
37416         this.setUrl(this.url, this.params, this.loadOnce);
37417     }
37418     
37419     
37420     
37421     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37422     
37423     if (this.view && typeof(this.view.xtype) != 'undefined') {
37424         this.view.el = this.el.appendChild(document.createElement("div"));
37425         this.view = Roo.factory(this.view); 
37426         this.view.render  &&  this.view.render(false, '');  
37427     }
37428     
37429     
37430     this.fireEvent('render', this);
37431 };
37432
37433 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37434     
37435     tabTip : '',
37436     
37437     setRegion : function(region){
37438         this.region = region;
37439         this.setActiveClass(region && !this.background);
37440     },
37441     
37442     
37443     setActiveClass: function(state)
37444     {
37445         if(state){
37446            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37447            this.el.setStyle('position','relative');
37448         }else{
37449            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37450            this.el.setStyle('position', 'absolute');
37451         } 
37452     },
37453     
37454     /**
37455      * Returns the toolbar for this Panel if one was configured. 
37456      * @return {Roo.Toolbar} 
37457      */
37458     getToolbar : function(){
37459         return this.toolbar;
37460     },
37461     
37462     setActiveState : function(active)
37463     {
37464         this.active = active;
37465         this.setActiveClass(active);
37466         if(!active){
37467             if(this.fireEvent("deactivate", this) === false){
37468                 return false;
37469             }
37470             return true;
37471         }
37472         this.fireEvent("activate", this);
37473         return true;
37474     },
37475     /**
37476      * Updates this panel's element
37477      * @param {String} content The new content
37478      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37479     */
37480     setContent : function(content, loadScripts){
37481         this.el.update(content, loadScripts);
37482     },
37483
37484     ignoreResize : function(w, h){
37485         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37486             return true;
37487         }else{
37488             this.lastSize = {width: w, height: h};
37489             return false;
37490         }
37491     },
37492     /**
37493      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37494      * @return {Roo.UpdateManager} The UpdateManager
37495      */
37496     getUpdateManager : function(){
37497         return this.el.getUpdateManager();
37498     },
37499      /**
37500      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37501      * @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:
37502 <pre><code>
37503 panel.load({
37504     url: "your-url.php",
37505     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37506     callback: yourFunction,
37507     scope: yourObject, //(optional scope)
37508     discardUrl: false,
37509     nocache: false,
37510     text: "Loading...",
37511     timeout: 30,
37512     scripts: false
37513 });
37514 </code></pre>
37515      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37516      * 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.
37517      * @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}
37518      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37519      * @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.
37520      * @return {Roo.ContentPanel} this
37521      */
37522     load : function(){
37523         var um = this.el.getUpdateManager();
37524         um.update.apply(um, arguments);
37525         return this;
37526     },
37527
37528
37529     /**
37530      * 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.
37531      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37532      * @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)
37533      * @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)
37534      * @return {Roo.UpdateManager} The UpdateManager
37535      */
37536     setUrl : function(url, params, loadOnce){
37537         if(this.refreshDelegate){
37538             this.removeListener("activate", this.refreshDelegate);
37539         }
37540         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37541         this.on("activate", this.refreshDelegate);
37542         return this.el.getUpdateManager();
37543     },
37544     
37545     _handleRefresh : function(url, params, loadOnce){
37546         if(!loadOnce || !this.loaded){
37547             var updater = this.el.getUpdateManager();
37548             updater.update(url, params, this._setLoaded.createDelegate(this));
37549         }
37550     },
37551     
37552     _setLoaded : function(){
37553         this.loaded = true;
37554     }, 
37555     
37556     /**
37557      * Returns this panel's id
37558      * @return {String} 
37559      */
37560     getId : function(){
37561         return this.el.id;
37562     },
37563     
37564     /** 
37565      * Returns this panel's element - used by regiosn to add.
37566      * @return {Roo.Element} 
37567      */
37568     getEl : function(){
37569         return this.wrapEl || this.el;
37570     },
37571     
37572    
37573     
37574     adjustForComponents : function(width, height)
37575     {
37576         //Roo.log('adjustForComponents ');
37577         if(this.resizeEl != this.el){
37578             width -= this.el.getFrameWidth('lr');
37579             height -= this.el.getFrameWidth('tb');
37580         }
37581         if(this.toolbar){
37582             var te = this.toolbar.getEl();
37583             te.setWidth(width);
37584             height -= te.getHeight();
37585         }
37586         if(this.footer){
37587             var te = this.footer.getEl();
37588             te.setWidth(width);
37589             height -= te.getHeight();
37590         }
37591         
37592         
37593         if(this.adjustments){
37594             width += this.adjustments[0];
37595             height += this.adjustments[1];
37596         }
37597         return {"width": width, "height": height};
37598     },
37599     
37600     setSize : function(width, height){
37601         if(this.fitToFrame && !this.ignoreResize(width, height)){
37602             if(this.fitContainer && this.resizeEl != this.el){
37603                 this.el.setSize(width, height);
37604             }
37605             var size = this.adjustForComponents(width, height);
37606             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37607             this.fireEvent('resize', this, size.width, size.height);
37608         }
37609     },
37610     
37611     /**
37612      * Returns this panel's title
37613      * @return {String} 
37614      */
37615     getTitle : function(){
37616         
37617         if (typeof(this.title) != 'object') {
37618             return this.title;
37619         }
37620         
37621         var t = '';
37622         for (var k in this.title) {
37623             if (!this.title.hasOwnProperty(k)) {
37624                 continue;
37625             }
37626             
37627             if (k.indexOf('-') >= 0) {
37628                 var s = k.split('-');
37629                 for (var i = 0; i<s.length; i++) {
37630                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37631                 }
37632             } else {
37633                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37634             }
37635         }
37636         return t;
37637     },
37638     
37639     /**
37640      * Set this panel's title
37641      * @param {String} title
37642      */
37643     setTitle : function(title){
37644         this.title = title;
37645         if(this.region){
37646             this.region.updatePanelTitle(this, title);
37647         }
37648     },
37649     
37650     /**
37651      * Returns true is this panel was configured to be closable
37652      * @return {Boolean} 
37653      */
37654     isClosable : function(){
37655         return this.closable;
37656     },
37657     
37658     beforeSlide : function(){
37659         this.el.clip();
37660         this.resizeEl.clip();
37661     },
37662     
37663     afterSlide : function(){
37664         this.el.unclip();
37665         this.resizeEl.unclip();
37666     },
37667     
37668     /**
37669      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37670      *   Will fail silently if the {@link #setUrl} method has not been called.
37671      *   This does not activate the panel, just updates its content.
37672      */
37673     refresh : function(){
37674         if(this.refreshDelegate){
37675            this.loaded = false;
37676            this.refreshDelegate();
37677         }
37678     },
37679     
37680     /**
37681      * Destroys this panel
37682      */
37683     destroy : function(){
37684         this.el.removeAllListeners();
37685         var tempEl = document.createElement("span");
37686         tempEl.appendChild(this.el.dom);
37687         tempEl.innerHTML = "";
37688         this.el.remove();
37689         this.el = null;
37690     },
37691     
37692     /**
37693      * form - if the content panel contains a form - this is a reference to it.
37694      * @type {Roo.form.Form}
37695      */
37696     form : false,
37697     /**
37698      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37699      *    This contains a reference to it.
37700      * @type {Roo.View}
37701      */
37702     view : false,
37703     
37704       /**
37705      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37706      * <pre><code>
37707
37708 layout.addxtype({
37709        xtype : 'Form',
37710        items: [ .... ]
37711    }
37712 );
37713
37714 </code></pre>
37715      * @param {Object} cfg Xtype definition of item to add.
37716      */
37717     
37718     
37719     getChildContainer: function () {
37720         return this.getEl();
37721     }
37722     
37723     
37724     /*
37725         var  ret = new Roo.factory(cfg);
37726         return ret;
37727         
37728         
37729         // add form..
37730         if (cfg.xtype.match(/^Form$/)) {
37731             
37732             var el;
37733             //if (this.footer) {
37734             //    el = this.footer.container.insertSibling(false, 'before');
37735             //} else {
37736                 el = this.el.createChild();
37737             //}
37738
37739             this.form = new  Roo.form.Form(cfg);
37740             
37741             
37742             if ( this.form.allItems.length) {
37743                 this.form.render(el.dom);
37744             }
37745             return this.form;
37746         }
37747         // should only have one of theses..
37748         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37749             // views.. should not be just added - used named prop 'view''
37750             
37751             cfg.el = this.el.appendChild(document.createElement("div"));
37752             // factory?
37753             
37754             var ret = new Roo.factory(cfg);
37755              
37756              ret.render && ret.render(false, ''); // render blank..
37757             this.view = ret;
37758             return ret;
37759         }
37760         return false;
37761     }
37762     \*/
37763 });
37764  
37765 /**
37766  * @class Roo.bootstrap.panel.Grid
37767  * @extends Roo.bootstrap.panel.Content
37768  * @constructor
37769  * Create a new GridPanel.
37770  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37771  * @param {Object} config A the config object
37772   
37773  */
37774
37775
37776
37777 Roo.bootstrap.panel.Grid = function(config)
37778 {
37779     
37780       
37781     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37782         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37783
37784     config.el = this.wrapper;
37785     //this.el = this.wrapper;
37786     
37787       if (config.container) {
37788         // ctor'ed from a Border/panel.grid
37789         
37790         
37791         this.wrapper.setStyle("overflow", "hidden");
37792         this.wrapper.addClass('roo-grid-container');
37793
37794     }
37795     
37796     
37797     if(config.toolbar){
37798         var tool_el = this.wrapper.createChild();    
37799         this.toolbar = Roo.factory(config.toolbar);
37800         var ti = [];
37801         if (config.toolbar.items) {
37802             ti = config.toolbar.items ;
37803             delete config.toolbar.items ;
37804         }
37805         
37806         var nitems = [];
37807         this.toolbar.render(tool_el);
37808         for(var i =0;i < ti.length;i++) {
37809           //  Roo.log(['add child', items[i]]);
37810             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37811         }
37812         this.toolbar.items = nitems;
37813         
37814         delete config.toolbar;
37815     }
37816     
37817     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37818     config.grid.scrollBody = true;;
37819     config.grid.monitorWindowResize = false; // turn off autosizing
37820     config.grid.autoHeight = false;
37821     config.grid.autoWidth = false;
37822     
37823     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37824     
37825     if (config.background) {
37826         // render grid on panel activation (if panel background)
37827         this.on('activate', function(gp) {
37828             if (!gp.grid.rendered) {
37829                 gp.grid.render(this.wrapper);
37830                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37831             }
37832         });
37833             
37834     } else {
37835         this.grid.render(this.wrapper);
37836         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37837
37838     }
37839     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37840     // ??? needed ??? config.el = this.wrapper;
37841     
37842     
37843     
37844   
37845     // xtype created footer. - not sure if will work as we normally have to render first..
37846     if (this.footer && !this.footer.el && this.footer.xtype) {
37847         
37848         var ctr = this.grid.getView().getFooterPanel(true);
37849         this.footer.dataSource = this.grid.dataSource;
37850         this.footer = Roo.factory(this.footer, Roo);
37851         this.footer.render(ctr);
37852         
37853     }
37854     
37855     
37856     
37857     
37858      
37859 };
37860
37861 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37862     getId : function(){
37863         return this.grid.id;
37864     },
37865     
37866     /**
37867      * Returns the grid for this panel
37868      * @return {Roo.bootstrap.Table} 
37869      */
37870     getGrid : function(){
37871         return this.grid;    
37872     },
37873     
37874     setSize : function(width, height){
37875         if(!this.ignoreResize(width, height)){
37876             var grid = this.grid;
37877             var size = this.adjustForComponents(width, height);
37878             var gridel = grid.getGridEl();
37879             gridel.setSize(size.width, size.height);
37880             /*
37881             var thd = grid.getGridEl().select('thead',true).first();
37882             var tbd = grid.getGridEl().select('tbody', true).first();
37883             if (tbd) {
37884                 tbd.setSize(width, height - thd.getHeight());
37885             }
37886             */
37887             grid.autoSize();
37888         }
37889     },
37890      
37891     
37892     
37893     beforeSlide : function(){
37894         this.grid.getView().scroller.clip();
37895     },
37896     
37897     afterSlide : function(){
37898         this.grid.getView().scroller.unclip();
37899     },
37900     
37901     destroy : function(){
37902         this.grid.destroy();
37903         delete this.grid;
37904         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37905     }
37906 });
37907
37908 /**
37909  * @class Roo.bootstrap.panel.Nest
37910  * @extends Roo.bootstrap.panel.Content
37911  * @constructor
37912  * Create a new Panel, that can contain a layout.Border.
37913  * 
37914  * 
37915  * @param {Roo.BorderLayout} layout The layout for this panel
37916  * @param {String/Object} config A string to set only the title or a config object
37917  */
37918 Roo.bootstrap.panel.Nest = function(config)
37919 {
37920     // construct with only one argument..
37921     /* FIXME - implement nicer consturctors
37922     if (layout.layout) {
37923         config = layout;
37924         layout = config.layout;
37925         delete config.layout;
37926     }
37927     if (layout.xtype && !layout.getEl) {
37928         // then layout needs constructing..
37929         layout = Roo.factory(layout, Roo);
37930     }
37931     */
37932     
37933     config.el =  config.layout.getEl();
37934     
37935     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37936     
37937     config.layout.monitorWindowResize = false; // turn off autosizing
37938     this.layout = config.layout;
37939     this.layout.getEl().addClass("roo-layout-nested-layout");
37940     this.layout.parent = this;
37941     
37942     
37943     
37944     
37945 };
37946
37947 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37948
37949     setSize : function(width, height){
37950         if(!this.ignoreResize(width, height)){
37951             var size = this.adjustForComponents(width, height);
37952             var el = this.layout.getEl();
37953             if (size.height < 1) {
37954                 el.setWidth(size.width);   
37955             } else {
37956                 el.setSize(size.width, size.height);
37957             }
37958             var touch = el.dom.offsetWidth;
37959             this.layout.layout();
37960             // ie requires a double layout on the first pass
37961             if(Roo.isIE && !this.initialized){
37962                 this.initialized = true;
37963                 this.layout.layout();
37964             }
37965         }
37966     },
37967     
37968     // activate all subpanels if not currently active..
37969     
37970     setActiveState : function(active){
37971         this.active = active;
37972         this.setActiveClass(active);
37973         
37974         if(!active){
37975             this.fireEvent("deactivate", this);
37976             return;
37977         }
37978         
37979         this.fireEvent("activate", this);
37980         // not sure if this should happen before or after..
37981         if (!this.layout) {
37982             return; // should not happen..
37983         }
37984         var reg = false;
37985         for (var r in this.layout.regions) {
37986             reg = this.layout.getRegion(r);
37987             if (reg.getActivePanel()) {
37988                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37989                 reg.setActivePanel(reg.getActivePanel());
37990                 continue;
37991             }
37992             if (!reg.panels.length) {
37993                 continue;
37994             }
37995             reg.showPanel(reg.getPanel(0));
37996         }
37997         
37998         
37999         
38000         
38001     },
38002     
38003     /**
38004      * Returns the nested BorderLayout for this panel
38005      * @return {Roo.BorderLayout} 
38006      */
38007     getLayout : function(){
38008         return this.layout;
38009     },
38010     
38011      /**
38012      * Adds a xtype elements to the layout of the nested panel
38013      * <pre><code>
38014
38015 panel.addxtype({
38016        xtype : 'ContentPanel',
38017        region: 'west',
38018        items: [ .... ]
38019    }
38020 );
38021
38022 panel.addxtype({
38023         xtype : 'NestedLayoutPanel',
38024         region: 'west',
38025         layout: {
38026            center: { },
38027            west: { }   
38028         },
38029         items : [ ... list of content panels or nested layout panels.. ]
38030    }
38031 );
38032 </code></pre>
38033      * @param {Object} cfg Xtype definition of item to add.
38034      */
38035     addxtype : function(cfg) {
38036         return this.layout.addxtype(cfg);
38037     
38038     }
38039 });/*
38040  * Based on:
38041  * Ext JS Library 1.1.1
38042  * Copyright(c) 2006-2007, Ext JS, LLC.
38043  *
38044  * Originally Released Under LGPL - original licence link has changed is not relivant.
38045  *
38046  * Fork - LGPL
38047  * <script type="text/javascript">
38048  */
38049 /**
38050  * @class Roo.TabPanel
38051  * @extends Roo.util.Observable
38052  * A lightweight tab container.
38053  * <br><br>
38054  * Usage:
38055  * <pre><code>
38056 // basic tabs 1, built from existing content
38057 var tabs = new Roo.TabPanel("tabs1");
38058 tabs.addTab("script", "View Script");
38059 tabs.addTab("markup", "View Markup");
38060 tabs.activate("script");
38061
38062 // more advanced tabs, built from javascript
38063 var jtabs = new Roo.TabPanel("jtabs");
38064 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38065
38066 // set up the UpdateManager
38067 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38068 var updater = tab2.getUpdateManager();
38069 updater.setDefaultUrl("ajax1.htm");
38070 tab2.on('activate', updater.refresh, updater, true);
38071
38072 // Use setUrl for Ajax loading
38073 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38074 tab3.setUrl("ajax2.htm", null, true);
38075
38076 // Disabled tab
38077 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38078 tab4.disable();
38079
38080 jtabs.activate("jtabs-1");
38081  * </code></pre>
38082  * @constructor
38083  * Create a new TabPanel.
38084  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38085  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38086  */
38087 Roo.bootstrap.panel.Tabs = function(config){
38088     /**
38089     * The container element for this TabPanel.
38090     * @type Roo.Element
38091     */
38092     this.el = Roo.get(config.el);
38093     delete config.el;
38094     if(config){
38095         if(typeof config == "boolean"){
38096             this.tabPosition = config ? "bottom" : "top";
38097         }else{
38098             Roo.apply(this, config);
38099         }
38100     }
38101     
38102     if(this.tabPosition == "bottom"){
38103         // if tabs are at the bottom = create the body first.
38104         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38105         this.el.addClass("roo-tabs-bottom");
38106     }
38107     // next create the tabs holders
38108     
38109     if (this.tabPosition == "west"){
38110         
38111         var reg = this.region; // fake it..
38112         while (reg) {
38113             if (!reg.mgr.parent) {
38114                 break;
38115             }
38116             reg = reg.mgr.parent.region;
38117         }
38118         Roo.log("got nest?");
38119         Roo.log(reg);
38120         if (reg.mgr.getRegion('west')) {
38121             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38122             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38123             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38124             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38125             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38126         
38127             
38128         }
38129         
38130         
38131     } else {
38132      
38133         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38134         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38135         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38136         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38137     }
38138     
38139     
38140     if(Roo.isIE){
38141         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38142     }
38143     
38144     // finally - if tabs are at the top, then create the body last..
38145     if(this.tabPosition != "bottom"){
38146         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38147          * @type Roo.Element
38148          */
38149         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38150         this.el.addClass("roo-tabs-top");
38151     }
38152     this.items = [];
38153
38154     this.bodyEl.setStyle("position", "relative");
38155
38156     this.active = null;
38157     this.activateDelegate = this.activate.createDelegate(this);
38158
38159     this.addEvents({
38160         /**
38161          * @event tabchange
38162          * Fires when the active tab changes
38163          * @param {Roo.TabPanel} this
38164          * @param {Roo.TabPanelItem} activePanel The new active tab
38165          */
38166         "tabchange": true,
38167         /**
38168          * @event beforetabchange
38169          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38170          * @param {Roo.TabPanel} this
38171          * @param {Object} e Set cancel to true on this object to cancel the tab change
38172          * @param {Roo.TabPanelItem} tab The tab being changed to
38173          */
38174         "beforetabchange" : true
38175     });
38176
38177     Roo.EventManager.onWindowResize(this.onResize, this);
38178     this.cpad = this.el.getPadding("lr");
38179     this.hiddenCount = 0;
38180
38181
38182     // toolbar on the tabbar support...
38183     if (this.toolbar) {
38184         alert("no toolbar support yet");
38185         this.toolbar  = false;
38186         /*
38187         var tcfg = this.toolbar;
38188         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38189         this.toolbar = new Roo.Toolbar(tcfg);
38190         if (Roo.isSafari) {
38191             var tbl = tcfg.container.child('table', true);
38192             tbl.setAttribute('width', '100%');
38193         }
38194         */
38195         
38196     }
38197    
38198
38199
38200     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38201 };
38202
38203 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38204     /*
38205      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38206      */
38207     tabPosition : "top",
38208     /*
38209      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38210      */
38211     currentTabWidth : 0,
38212     /*
38213      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38214      */
38215     minTabWidth : 40,
38216     /*
38217      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38218      */
38219     maxTabWidth : 250,
38220     /*
38221      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38222      */
38223     preferredTabWidth : 175,
38224     /*
38225      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38226      */
38227     resizeTabs : false,
38228     /*
38229      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38230      */
38231     monitorResize : true,
38232     /*
38233      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38234      */
38235     toolbar : false,  // set by caller..
38236     
38237     region : false, /// set by caller
38238     
38239     disableTooltips : true, // not used yet...
38240
38241     /**
38242      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38243      * @param {String} id The id of the div to use <b>or create</b>
38244      * @param {String} text The text for the tab
38245      * @param {String} content (optional) Content to put in the TabPanelItem body
38246      * @param {Boolean} closable (optional) True to create a close icon on the tab
38247      * @return {Roo.TabPanelItem} The created TabPanelItem
38248      */
38249     addTab : function(id, text, content, closable, tpl)
38250     {
38251         var item = new Roo.bootstrap.panel.TabItem({
38252             panel: this,
38253             id : id,
38254             text : text,
38255             closable : closable,
38256             tpl : tpl
38257         });
38258         this.addTabItem(item);
38259         if(content){
38260             item.setContent(content);
38261         }
38262         return item;
38263     },
38264
38265     /**
38266      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38267      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38268      * @return {Roo.TabPanelItem}
38269      */
38270     getTab : function(id){
38271         return this.items[id];
38272     },
38273
38274     /**
38275      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38276      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38277      */
38278     hideTab : function(id){
38279         var t = this.items[id];
38280         if(!t.isHidden()){
38281            t.setHidden(true);
38282            this.hiddenCount++;
38283            this.autoSizeTabs();
38284         }
38285     },
38286
38287     /**
38288      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38289      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38290      */
38291     unhideTab : function(id){
38292         var t = this.items[id];
38293         if(t.isHidden()){
38294            t.setHidden(false);
38295            this.hiddenCount--;
38296            this.autoSizeTabs();
38297         }
38298     },
38299
38300     /**
38301      * Adds an existing {@link Roo.TabPanelItem}.
38302      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38303      */
38304     addTabItem : function(item)
38305     {
38306         this.items[item.id] = item;
38307         this.items.push(item);
38308         this.autoSizeTabs();
38309       //  if(this.resizeTabs){
38310     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38311   //         this.autoSizeTabs();
38312 //        }else{
38313 //            item.autoSize();
38314        // }
38315     },
38316
38317     /**
38318      * Removes a {@link Roo.TabPanelItem}.
38319      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38320      */
38321     removeTab : function(id){
38322         var items = this.items;
38323         var tab = items[id];
38324         if(!tab) { return; }
38325         var index = items.indexOf(tab);
38326         if(this.active == tab && items.length > 1){
38327             var newTab = this.getNextAvailable(index);
38328             if(newTab) {
38329                 newTab.activate();
38330             }
38331         }
38332         this.stripEl.dom.removeChild(tab.pnode.dom);
38333         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38334             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38335         }
38336         items.splice(index, 1);
38337         delete this.items[tab.id];
38338         tab.fireEvent("close", tab);
38339         tab.purgeListeners();
38340         this.autoSizeTabs();
38341     },
38342
38343     getNextAvailable : function(start){
38344         var items = this.items;
38345         var index = start;
38346         // look for a next tab that will slide over to
38347         // replace the one being removed
38348         while(index < items.length){
38349             var item = items[++index];
38350             if(item && !item.isHidden()){
38351                 return item;
38352             }
38353         }
38354         // if one isn't found select the previous tab (on the left)
38355         index = start;
38356         while(index >= 0){
38357             var item = items[--index];
38358             if(item && !item.isHidden()){
38359                 return item;
38360             }
38361         }
38362         return null;
38363     },
38364
38365     /**
38366      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38367      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38368      */
38369     disableTab : function(id){
38370         var tab = this.items[id];
38371         if(tab && this.active != tab){
38372             tab.disable();
38373         }
38374     },
38375
38376     /**
38377      * Enables a {@link Roo.TabPanelItem} that is disabled.
38378      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38379      */
38380     enableTab : function(id){
38381         var tab = this.items[id];
38382         tab.enable();
38383     },
38384
38385     /**
38386      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38387      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38388      * @return {Roo.TabPanelItem} The TabPanelItem.
38389      */
38390     activate : function(id)
38391     {
38392         //Roo.log('activite:'  + id);
38393         
38394         var tab = this.items[id];
38395         if(!tab){
38396             return null;
38397         }
38398         if(tab == this.active || tab.disabled){
38399             return tab;
38400         }
38401         var e = {};
38402         this.fireEvent("beforetabchange", this, e, tab);
38403         if(e.cancel !== true && !tab.disabled){
38404             if(this.active){
38405                 this.active.hide();
38406             }
38407             this.active = this.items[id];
38408             this.active.show();
38409             this.fireEvent("tabchange", this, this.active);
38410         }
38411         return tab;
38412     },
38413
38414     /**
38415      * Gets the active {@link Roo.TabPanelItem}.
38416      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38417      */
38418     getActiveTab : function(){
38419         return this.active;
38420     },
38421
38422     /**
38423      * Updates the tab body element to fit the height of the container element
38424      * for overflow scrolling
38425      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38426      */
38427     syncHeight : function(targetHeight){
38428         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38429         var bm = this.bodyEl.getMargins();
38430         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38431         this.bodyEl.setHeight(newHeight);
38432         return newHeight;
38433     },
38434
38435     onResize : function(){
38436         if(this.monitorResize){
38437             this.autoSizeTabs();
38438         }
38439     },
38440
38441     /**
38442      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38443      */
38444     beginUpdate : function(){
38445         this.updating = true;
38446     },
38447
38448     /**
38449      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38450      */
38451     endUpdate : function(){
38452         this.updating = false;
38453         this.autoSizeTabs();
38454     },
38455
38456     /**
38457      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38458      */
38459     autoSizeTabs : function()
38460     {
38461         var count = this.items.length;
38462         var vcount = count - this.hiddenCount;
38463         
38464         if (vcount < 2) {
38465             this.stripEl.hide();
38466         } else {
38467             this.stripEl.show();
38468         }
38469         
38470         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38471             return;
38472         }
38473         
38474         
38475         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38476         var availWidth = Math.floor(w / vcount);
38477         var b = this.stripBody;
38478         if(b.getWidth() > w){
38479             var tabs = this.items;
38480             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38481             if(availWidth < this.minTabWidth){
38482                 /*if(!this.sleft){    // incomplete scrolling code
38483                     this.createScrollButtons();
38484                 }
38485                 this.showScroll();
38486                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38487             }
38488         }else{
38489             if(this.currentTabWidth < this.preferredTabWidth){
38490                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38491             }
38492         }
38493     },
38494
38495     /**
38496      * Returns the number of tabs in this TabPanel.
38497      * @return {Number}
38498      */
38499      getCount : function(){
38500          return this.items.length;
38501      },
38502
38503     /**
38504      * Resizes all the tabs to the passed width
38505      * @param {Number} The new width
38506      */
38507     setTabWidth : function(width){
38508         this.currentTabWidth = width;
38509         for(var i = 0, len = this.items.length; i < len; i++) {
38510                 if(!this.items[i].isHidden()) {
38511                 this.items[i].setWidth(width);
38512             }
38513         }
38514     },
38515
38516     /**
38517      * Destroys this TabPanel
38518      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38519      */
38520     destroy : function(removeEl){
38521         Roo.EventManager.removeResizeListener(this.onResize, this);
38522         for(var i = 0, len = this.items.length; i < len; i++){
38523             this.items[i].purgeListeners();
38524         }
38525         if(removeEl === true){
38526             this.el.update("");
38527             this.el.remove();
38528         }
38529     },
38530     
38531     createStrip : function(container)
38532     {
38533         var strip = document.createElement("nav");
38534         strip.className = Roo.bootstrap.version == 4 ?
38535             "navbar-light bg-light" : 
38536             "navbar navbar-default"; //"x-tabs-wrap";
38537         container.appendChild(strip);
38538         return strip;
38539     },
38540     
38541     createStripList : function(strip)
38542     {
38543         // div wrapper for retard IE
38544         // returns the "tr" element.
38545         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38546         //'<div class="x-tabs-strip-wrap">'+
38547           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38548           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38549         return strip.firstChild; //.firstChild.firstChild.firstChild;
38550     },
38551     createBody : function(container)
38552     {
38553         var body = document.createElement("div");
38554         Roo.id(body, "tab-body");
38555         //Roo.fly(body).addClass("x-tabs-body");
38556         Roo.fly(body).addClass("tab-content");
38557         container.appendChild(body);
38558         return body;
38559     },
38560     createItemBody :function(bodyEl, id){
38561         var body = Roo.getDom(id);
38562         if(!body){
38563             body = document.createElement("div");
38564             body.id = id;
38565         }
38566         //Roo.fly(body).addClass("x-tabs-item-body");
38567         Roo.fly(body).addClass("tab-pane");
38568          bodyEl.insertBefore(body, bodyEl.firstChild);
38569         return body;
38570     },
38571     /** @private */
38572     createStripElements :  function(stripEl, text, closable, tpl)
38573     {
38574         var td = document.createElement("li"); // was td..
38575         td.className = 'nav-item';
38576         
38577         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38578         
38579         
38580         stripEl.appendChild(td);
38581         /*if(closable){
38582             td.className = "x-tabs-closable";
38583             if(!this.closeTpl){
38584                 this.closeTpl = new Roo.Template(
38585                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38586                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38587                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38588                 );
38589             }
38590             var el = this.closeTpl.overwrite(td, {"text": text});
38591             var close = el.getElementsByTagName("div")[0];
38592             var inner = el.getElementsByTagName("em")[0];
38593             return {"el": el, "close": close, "inner": inner};
38594         } else {
38595         */
38596         // not sure what this is..
38597 //            if(!this.tabTpl){
38598                 //this.tabTpl = new Roo.Template(
38599                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38600                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38601                 //);
38602 //                this.tabTpl = new Roo.Template(
38603 //                   '<a href="#">' +
38604 //                   '<span unselectable="on"' +
38605 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38606 //                            ' >{text}</span></a>'
38607 //                );
38608 //                
38609 //            }
38610
38611
38612             var template = tpl || this.tabTpl || false;
38613             
38614             if(!template){
38615                 template =  new Roo.Template(
38616                         Roo.bootstrap.version == 4 ? 
38617                             (
38618                                 '<a class="nav-link" href="#" unselectable="on"' +
38619                                      (this.disableTooltips ? '' : ' title="{text}"') +
38620                                      ' >{text}</a>'
38621                             ) : (
38622                                 '<a class="nav-link" href="#">' +
38623                                 '<span unselectable="on"' +
38624                                          (this.disableTooltips ? '' : ' title="{text}"') +
38625                                     ' >{text}</span></a>'
38626                             )
38627                 );
38628             }
38629             
38630             switch (typeof(template)) {
38631                 case 'object' :
38632                     break;
38633                 case 'string' :
38634                     template = new Roo.Template(template);
38635                     break;
38636                 default :
38637                     break;
38638             }
38639             
38640             var el = template.overwrite(td, {"text": text});
38641             
38642             var inner = el.getElementsByTagName("span")[0];
38643             
38644             return {"el": el, "inner": inner};
38645             
38646     }
38647         
38648     
38649 });
38650
38651 /**
38652  * @class Roo.TabPanelItem
38653  * @extends Roo.util.Observable
38654  * Represents an individual item (tab plus body) in a TabPanel.
38655  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38656  * @param {String} id The id of this TabPanelItem
38657  * @param {String} text The text for the tab of this TabPanelItem
38658  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38659  */
38660 Roo.bootstrap.panel.TabItem = function(config){
38661     /**
38662      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38663      * @type Roo.TabPanel
38664      */
38665     this.tabPanel = config.panel;
38666     /**
38667      * The id for this TabPanelItem
38668      * @type String
38669      */
38670     this.id = config.id;
38671     /** @private */
38672     this.disabled = false;
38673     /** @private */
38674     this.text = config.text;
38675     /** @private */
38676     this.loaded = false;
38677     this.closable = config.closable;
38678
38679     /**
38680      * The body element for this TabPanelItem.
38681      * @type Roo.Element
38682      */
38683     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38684     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38685     this.bodyEl.setStyle("display", "block");
38686     this.bodyEl.setStyle("zoom", "1");
38687     //this.hideAction();
38688
38689     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38690     /** @private */
38691     this.el = Roo.get(els.el);
38692     this.inner = Roo.get(els.inner, true);
38693      this.textEl = Roo.bootstrap.version == 4 ?
38694         this.el : Roo.get(this.el.dom.firstChild, true);
38695
38696     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38697     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38698
38699     
38700 //    this.el.on("mousedown", this.onTabMouseDown, this);
38701     this.el.on("click", this.onTabClick, this);
38702     /** @private */
38703     if(config.closable){
38704         var c = Roo.get(els.close, true);
38705         c.dom.title = this.closeText;
38706         c.addClassOnOver("close-over");
38707         c.on("click", this.closeClick, this);
38708      }
38709
38710     this.addEvents({
38711          /**
38712          * @event activate
38713          * Fires when this tab becomes the active tab.
38714          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38715          * @param {Roo.TabPanelItem} this
38716          */
38717         "activate": true,
38718         /**
38719          * @event beforeclose
38720          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38721          * @param {Roo.TabPanelItem} this
38722          * @param {Object} e Set cancel to true on this object to cancel the close.
38723          */
38724         "beforeclose": true,
38725         /**
38726          * @event close
38727          * Fires when this tab is closed.
38728          * @param {Roo.TabPanelItem} this
38729          */
38730          "close": true,
38731         /**
38732          * @event deactivate
38733          * Fires when this tab is no longer the active tab.
38734          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38735          * @param {Roo.TabPanelItem} this
38736          */
38737          "deactivate" : true
38738     });
38739     this.hidden = false;
38740
38741     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38742 };
38743
38744 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38745            {
38746     purgeListeners : function(){
38747        Roo.util.Observable.prototype.purgeListeners.call(this);
38748        this.el.removeAllListeners();
38749     },
38750     /**
38751      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38752      */
38753     show : function(){
38754         this.status_node.addClass("active");
38755         this.showAction();
38756         if(Roo.isOpera){
38757             this.tabPanel.stripWrap.repaint();
38758         }
38759         this.fireEvent("activate", this.tabPanel, this);
38760     },
38761
38762     /**
38763      * Returns true if this tab is the active tab.
38764      * @return {Boolean}
38765      */
38766     isActive : function(){
38767         return this.tabPanel.getActiveTab() == this;
38768     },
38769
38770     /**
38771      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38772      */
38773     hide : function(){
38774         this.status_node.removeClass("active");
38775         this.hideAction();
38776         this.fireEvent("deactivate", this.tabPanel, this);
38777     },
38778
38779     hideAction : function(){
38780         this.bodyEl.hide();
38781         this.bodyEl.setStyle("position", "absolute");
38782         this.bodyEl.setLeft("-20000px");
38783         this.bodyEl.setTop("-20000px");
38784     },
38785
38786     showAction : function(){
38787         this.bodyEl.setStyle("position", "relative");
38788         this.bodyEl.setTop("");
38789         this.bodyEl.setLeft("");
38790         this.bodyEl.show();
38791     },
38792
38793     /**
38794      * Set the tooltip for the tab.
38795      * @param {String} tooltip The tab's tooltip
38796      */
38797     setTooltip : function(text){
38798         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38799             this.textEl.dom.qtip = text;
38800             this.textEl.dom.removeAttribute('title');
38801         }else{
38802             this.textEl.dom.title = text;
38803         }
38804     },
38805
38806     onTabClick : function(e){
38807         e.preventDefault();
38808         this.tabPanel.activate(this.id);
38809     },
38810
38811     onTabMouseDown : function(e){
38812         e.preventDefault();
38813         this.tabPanel.activate(this.id);
38814     },
38815 /*
38816     getWidth : function(){
38817         return this.inner.getWidth();
38818     },
38819
38820     setWidth : function(width){
38821         var iwidth = width - this.linode.getPadding("lr");
38822         this.inner.setWidth(iwidth);
38823         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38824         this.linode.setWidth(width);
38825     },
38826 */
38827     /**
38828      * Show or hide the tab
38829      * @param {Boolean} hidden True to hide or false to show.
38830      */
38831     setHidden : function(hidden){
38832         this.hidden = hidden;
38833         this.linode.setStyle("display", hidden ? "none" : "");
38834     },
38835
38836     /**
38837      * Returns true if this tab is "hidden"
38838      * @return {Boolean}
38839      */
38840     isHidden : function(){
38841         return this.hidden;
38842     },
38843
38844     /**
38845      * Returns the text for this tab
38846      * @return {String}
38847      */
38848     getText : function(){
38849         return this.text;
38850     },
38851     /*
38852     autoSize : function(){
38853         //this.el.beginMeasure();
38854         this.textEl.setWidth(1);
38855         /*
38856          *  #2804 [new] Tabs in Roojs
38857          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38858          */
38859         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38860         //this.el.endMeasure();
38861     //},
38862
38863     /**
38864      * Sets the text for the tab (Note: this also sets the tooltip text)
38865      * @param {String} text The tab's text and tooltip
38866      */
38867     setText : function(text){
38868         this.text = text;
38869         this.textEl.update(text);
38870         this.setTooltip(text);
38871         //if(!this.tabPanel.resizeTabs){
38872         //    this.autoSize();
38873         //}
38874     },
38875     /**
38876      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38877      */
38878     activate : function(){
38879         this.tabPanel.activate(this.id);
38880     },
38881
38882     /**
38883      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38884      */
38885     disable : function(){
38886         if(this.tabPanel.active != this){
38887             this.disabled = true;
38888             this.status_node.addClass("disabled");
38889         }
38890     },
38891
38892     /**
38893      * Enables this TabPanelItem if it was previously disabled.
38894      */
38895     enable : function(){
38896         this.disabled = false;
38897         this.status_node.removeClass("disabled");
38898     },
38899
38900     /**
38901      * Sets the content for this TabPanelItem.
38902      * @param {String} content The content
38903      * @param {Boolean} loadScripts true to look for and load scripts
38904      */
38905     setContent : function(content, loadScripts){
38906         this.bodyEl.update(content, loadScripts);
38907     },
38908
38909     /**
38910      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38911      * @return {Roo.UpdateManager} The UpdateManager
38912      */
38913     getUpdateManager : function(){
38914         return this.bodyEl.getUpdateManager();
38915     },
38916
38917     /**
38918      * Set a URL to be used to load the content for this TabPanelItem.
38919      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38920      * @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)
38921      * @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)
38922      * @return {Roo.UpdateManager} The UpdateManager
38923      */
38924     setUrl : function(url, params, loadOnce){
38925         if(this.refreshDelegate){
38926             this.un('activate', this.refreshDelegate);
38927         }
38928         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38929         this.on("activate", this.refreshDelegate);
38930         return this.bodyEl.getUpdateManager();
38931     },
38932
38933     /** @private */
38934     _handleRefresh : function(url, params, loadOnce){
38935         if(!loadOnce || !this.loaded){
38936             var updater = this.bodyEl.getUpdateManager();
38937             updater.update(url, params, this._setLoaded.createDelegate(this));
38938         }
38939     },
38940
38941     /**
38942      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38943      *   Will fail silently if the setUrl method has not been called.
38944      *   This does not activate the panel, just updates its content.
38945      */
38946     refresh : function(){
38947         if(this.refreshDelegate){
38948            this.loaded = false;
38949            this.refreshDelegate();
38950         }
38951     },
38952
38953     /** @private */
38954     _setLoaded : function(){
38955         this.loaded = true;
38956     },
38957
38958     /** @private */
38959     closeClick : function(e){
38960         var o = {};
38961         e.stopEvent();
38962         this.fireEvent("beforeclose", this, o);
38963         if(o.cancel !== true){
38964             this.tabPanel.removeTab(this.id);
38965         }
38966     },
38967     /**
38968      * The text displayed in the tooltip for the close icon.
38969      * @type String
38970      */
38971     closeText : "Close this tab"
38972 });
38973 /**
38974 *    This script refer to:
38975 *    Title: International Telephone Input
38976 *    Author: Jack O'Connor
38977 *    Code version:  v12.1.12
38978 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38979 **/
38980
38981 Roo.bootstrap.PhoneInputData = function() {
38982     var d = [
38983       [
38984         "Afghanistan (‫افغانستان‬‎)",
38985         "af",
38986         "93"
38987       ],
38988       [
38989         "Albania (Shqipëri)",
38990         "al",
38991         "355"
38992       ],
38993       [
38994         "Algeria (‫الجزائر‬‎)",
38995         "dz",
38996         "213"
38997       ],
38998       [
38999         "American Samoa",
39000         "as",
39001         "1684"
39002       ],
39003       [
39004         "Andorra",
39005         "ad",
39006         "376"
39007       ],
39008       [
39009         "Angola",
39010         "ao",
39011         "244"
39012       ],
39013       [
39014         "Anguilla",
39015         "ai",
39016         "1264"
39017       ],
39018       [
39019         "Antigua and Barbuda",
39020         "ag",
39021         "1268"
39022       ],
39023       [
39024         "Argentina",
39025         "ar",
39026         "54"
39027       ],
39028       [
39029         "Armenia (Հայաստան)",
39030         "am",
39031         "374"
39032       ],
39033       [
39034         "Aruba",
39035         "aw",
39036         "297"
39037       ],
39038       [
39039         "Australia",
39040         "au",
39041         "61",
39042         0
39043       ],
39044       [
39045         "Austria (Österreich)",
39046         "at",
39047         "43"
39048       ],
39049       [
39050         "Azerbaijan (Azərbaycan)",
39051         "az",
39052         "994"
39053       ],
39054       [
39055         "Bahamas",
39056         "bs",
39057         "1242"
39058       ],
39059       [
39060         "Bahrain (‫البحرين‬‎)",
39061         "bh",
39062         "973"
39063       ],
39064       [
39065         "Bangladesh (বাংলাদেশ)",
39066         "bd",
39067         "880"
39068       ],
39069       [
39070         "Barbados",
39071         "bb",
39072         "1246"
39073       ],
39074       [
39075         "Belarus (Беларусь)",
39076         "by",
39077         "375"
39078       ],
39079       [
39080         "Belgium (België)",
39081         "be",
39082         "32"
39083       ],
39084       [
39085         "Belize",
39086         "bz",
39087         "501"
39088       ],
39089       [
39090         "Benin (Bénin)",
39091         "bj",
39092         "229"
39093       ],
39094       [
39095         "Bermuda",
39096         "bm",
39097         "1441"
39098       ],
39099       [
39100         "Bhutan (འབྲུག)",
39101         "bt",
39102         "975"
39103       ],
39104       [
39105         "Bolivia",
39106         "bo",
39107         "591"
39108       ],
39109       [
39110         "Bosnia and Herzegovina (Босна и Херцеговина)",
39111         "ba",
39112         "387"
39113       ],
39114       [
39115         "Botswana",
39116         "bw",
39117         "267"
39118       ],
39119       [
39120         "Brazil (Brasil)",
39121         "br",
39122         "55"
39123       ],
39124       [
39125         "British Indian Ocean Territory",
39126         "io",
39127         "246"
39128       ],
39129       [
39130         "British Virgin Islands",
39131         "vg",
39132         "1284"
39133       ],
39134       [
39135         "Brunei",
39136         "bn",
39137         "673"
39138       ],
39139       [
39140         "Bulgaria (България)",
39141         "bg",
39142         "359"
39143       ],
39144       [
39145         "Burkina Faso",
39146         "bf",
39147         "226"
39148       ],
39149       [
39150         "Burundi (Uburundi)",
39151         "bi",
39152         "257"
39153       ],
39154       [
39155         "Cambodia (កម្ពុជា)",
39156         "kh",
39157         "855"
39158       ],
39159       [
39160         "Cameroon (Cameroun)",
39161         "cm",
39162         "237"
39163       ],
39164       [
39165         "Canada",
39166         "ca",
39167         "1",
39168         1,
39169         ["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"]
39170       ],
39171       [
39172         "Cape Verde (Kabu Verdi)",
39173         "cv",
39174         "238"
39175       ],
39176       [
39177         "Caribbean Netherlands",
39178         "bq",
39179         "599",
39180         1
39181       ],
39182       [
39183         "Cayman Islands",
39184         "ky",
39185         "1345"
39186       ],
39187       [
39188         "Central African Republic (République centrafricaine)",
39189         "cf",
39190         "236"
39191       ],
39192       [
39193         "Chad (Tchad)",
39194         "td",
39195         "235"
39196       ],
39197       [
39198         "Chile",
39199         "cl",
39200         "56"
39201       ],
39202       [
39203         "China (中国)",
39204         "cn",
39205         "86"
39206       ],
39207       [
39208         "Christmas Island",
39209         "cx",
39210         "61",
39211         2
39212       ],
39213       [
39214         "Cocos (Keeling) Islands",
39215         "cc",
39216         "61",
39217         1
39218       ],
39219       [
39220         "Colombia",
39221         "co",
39222         "57"
39223       ],
39224       [
39225         "Comoros (‫جزر القمر‬‎)",
39226         "km",
39227         "269"
39228       ],
39229       [
39230         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39231         "cd",
39232         "243"
39233       ],
39234       [
39235         "Congo (Republic) (Congo-Brazzaville)",
39236         "cg",
39237         "242"
39238       ],
39239       [
39240         "Cook Islands",
39241         "ck",
39242         "682"
39243       ],
39244       [
39245         "Costa Rica",
39246         "cr",
39247         "506"
39248       ],
39249       [
39250         "Côte d’Ivoire",
39251         "ci",
39252         "225"
39253       ],
39254       [
39255         "Croatia (Hrvatska)",
39256         "hr",
39257         "385"
39258       ],
39259       [
39260         "Cuba",
39261         "cu",
39262         "53"
39263       ],
39264       [
39265         "Curaçao",
39266         "cw",
39267         "599",
39268         0
39269       ],
39270       [
39271         "Cyprus (Κύπρος)",
39272         "cy",
39273         "357"
39274       ],
39275       [
39276         "Czech Republic (Česká republika)",
39277         "cz",
39278         "420"
39279       ],
39280       [
39281         "Denmark (Danmark)",
39282         "dk",
39283         "45"
39284       ],
39285       [
39286         "Djibouti",
39287         "dj",
39288         "253"
39289       ],
39290       [
39291         "Dominica",
39292         "dm",
39293         "1767"
39294       ],
39295       [
39296         "Dominican Republic (República Dominicana)",
39297         "do",
39298         "1",
39299         2,
39300         ["809", "829", "849"]
39301       ],
39302       [
39303         "Ecuador",
39304         "ec",
39305         "593"
39306       ],
39307       [
39308         "Egypt (‫مصر‬‎)",
39309         "eg",
39310         "20"
39311       ],
39312       [
39313         "El Salvador",
39314         "sv",
39315         "503"
39316       ],
39317       [
39318         "Equatorial Guinea (Guinea Ecuatorial)",
39319         "gq",
39320         "240"
39321       ],
39322       [
39323         "Eritrea",
39324         "er",
39325         "291"
39326       ],
39327       [
39328         "Estonia (Eesti)",
39329         "ee",
39330         "372"
39331       ],
39332       [
39333         "Ethiopia",
39334         "et",
39335         "251"
39336       ],
39337       [
39338         "Falkland Islands (Islas Malvinas)",
39339         "fk",
39340         "500"
39341       ],
39342       [
39343         "Faroe Islands (Føroyar)",
39344         "fo",
39345         "298"
39346       ],
39347       [
39348         "Fiji",
39349         "fj",
39350         "679"
39351       ],
39352       [
39353         "Finland (Suomi)",
39354         "fi",
39355         "358",
39356         0
39357       ],
39358       [
39359         "France",
39360         "fr",
39361         "33"
39362       ],
39363       [
39364         "French Guiana (Guyane française)",
39365         "gf",
39366         "594"
39367       ],
39368       [
39369         "French Polynesia (Polynésie française)",
39370         "pf",
39371         "689"
39372       ],
39373       [
39374         "Gabon",
39375         "ga",
39376         "241"
39377       ],
39378       [
39379         "Gambia",
39380         "gm",
39381         "220"
39382       ],
39383       [
39384         "Georgia (საქართველო)",
39385         "ge",
39386         "995"
39387       ],
39388       [
39389         "Germany (Deutschland)",
39390         "de",
39391         "49"
39392       ],
39393       [
39394         "Ghana (Gaana)",
39395         "gh",
39396         "233"
39397       ],
39398       [
39399         "Gibraltar",
39400         "gi",
39401         "350"
39402       ],
39403       [
39404         "Greece (Ελλάδα)",
39405         "gr",
39406         "30"
39407       ],
39408       [
39409         "Greenland (Kalaallit Nunaat)",
39410         "gl",
39411         "299"
39412       ],
39413       [
39414         "Grenada",
39415         "gd",
39416         "1473"
39417       ],
39418       [
39419         "Guadeloupe",
39420         "gp",
39421         "590",
39422         0
39423       ],
39424       [
39425         "Guam",
39426         "gu",
39427         "1671"
39428       ],
39429       [
39430         "Guatemala",
39431         "gt",
39432         "502"
39433       ],
39434       [
39435         "Guernsey",
39436         "gg",
39437         "44",
39438         1
39439       ],
39440       [
39441         "Guinea (Guinée)",
39442         "gn",
39443         "224"
39444       ],
39445       [
39446         "Guinea-Bissau (Guiné Bissau)",
39447         "gw",
39448         "245"
39449       ],
39450       [
39451         "Guyana",
39452         "gy",
39453         "592"
39454       ],
39455       [
39456         "Haiti",
39457         "ht",
39458         "509"
39459       ],
39460       [
39461         "Honduras",
39462         "hn",
39463         "504"
39464       ],
39465       [
39466         "Hong Kong (香港)",
39467         "hk",
39468         "852"
39469       ],
39470       [
39471         "Hungary (Magyarország)",
39472         "hu",
39473         "36"
39474       ],
39475       [
39476         "Iceland (Ísland)",
39477         "is",
39478         "354"
39479       ],
39480       [
39481         "India (भारत)",
39482         "in",
39483         "91"
39484       ],
39485       [
39486         "Indonesia",
39487         "id",
39488         "62"
39489       ],
39490       [
39491         "Iran (‫ایران‬‎)",
39492         "ir",
39493         "98"
39494       ],
39495       [
39496         "Iraq (‫العراق‬‎)",
39497         "iq",
39498         "964"
39499       ],
39500       [
39501         "Ireland",
39502         "ie",
39503         "353"
39504       ],
39505       [
39506         "Isle of Man",
39507         "im",
39508         "44",
39509         2
39510       ],
39511       [
39512         "Israel (‫ישראל‬‎)",
39513         "il",
39514         "972"
39515       ],
39516       [
39517         "Italy (Italia)",
39518         "it",
39519         "39",
39520         0
39521       ],
39522       [
39523         "Jamaica",
39524         "jm",
39525         "1876"
39526       ],
39527       [
39528         "Japan (日本)",
39529         "jp",
39530         "81"
39531       ],
39532       [
39533         "Jersey",
39534         "je",
39535         "44",
39536         3
39537       ],
39538       [
39539         "Jordan (‫الأردن‬‎)",
39540         "jo",
39541         "962"
39542       ],
39543       [
39544         "Kazakhstan (Казахстан)",
39545         "kz",
39546         "7",
39547         1
39548       ],
39549       [
39550         "Kenya",
39551         "ke",
39552         "254"
39553       ],
39554       [
39555         "Kiribati",
39556         "ki",
39557         "686"
39558       ],
39559       [
39560         "Kosovo",
39561         "xk",
39562         "383"
39563       ],
39564       [
39565         "Kuwait (‫الكويت‬‎)",
39566         "kw",
39567         "965"
39568       ],
39569       [
39570         "Kyrgyzstan (Кыргызстан)",
39571         "kg",
39572         "996"
39573       ],
39574       [
39575         "Laos (ລາວ)",
39576         "la",
39577         "856"
39578       ],
39579       [
39580         "Latvia (Latvija)",
39581         "lv",
39582         "371"
39583       ],
39584       [
39585         "Lebanon (‫لبنان‬‎)",
39586         "lb",
39587         "961"
39588       ],
39589       [
39590         "Lesotho",
39591         "ls",
39592         "266"
39593       ],
39594       [
39595         "Liberia",
39596         "lr",
39597         "231"
39598       ],
39599       [
39600         "Libya (‫ليبيا‬‎)",
39601         "ly",
39602         "218"
39603       ],
39604       [
39605         "Liechtenstein",
39606         "li",
39607         "423"
39608       ],
39609       [
39610         "Lithuania (Lietuva)",
39611         "lt",
39612         "370"
39613       ],
39614       [
39615         "Luxembourg",
39616         "lu",
39617         "352"
39618       ],
39619       [
39620         "Macau (澳門)",
39621         "mo",
39622         "853"
39623       ],
39624       [
39625         "Macedonia (FYROM) (Македонија)",
39626         "mk",
39627         "389"
39628       ],
39629       [
39630         "Madagascar (Madagasikara)",
39631         "mg",
39632         "261"
39633       ],
39634       [
39635         "Malawi",
39636         "mw",
39637         "265"
39638       ],
39639       [
39640         "Malaysia",
39641         "my",
39642         "60"
39643       ],
39644       [
39645         "Maldives",
39646         "mv",
39647         "960"
39648       ],
39649       [
39650         "Mali",
39651         "ml",
39652         "223"
39653       ],
39654       [
39655         "Malta",
39656         "mt",
39657         "356"
39658       ],
39659       [
39660         "Marshall Islands",
39661         "mh",
39662         "692"
39663       ],
39664       [
39665         "Martinique",
39666         "mq",
39667         "596"
39668       ],
39669       [
39670         "Mauritania (‫موريتانيا‬‎)",
39671         "mr",
39672         "222"
39673       ],
39674       [
39675         "Mauritius (Moris)",
39676         "mu",
39677         "230"
39678       ],
39679       [
39680         "Mayotte",
39681         "yt",
39682         "262",
39683         1
39684       ],
39685       [
39686         "Mexico (México)",
39687         "mx",
39688         "52"
39689       ],
39690       [
39691         "Micronesia",
39692         "fm",
39693         "691"
39694       ],
39695       [
39696         "Moldova (Republica Moldova)",
39697         "md",
39698         "373"
39699       ],
39700       [
39701         "Monaco",
39702         "mc",
39703         "377"
39704       ],
39705       [
39706         "Mongolia (Монгол)",
39707         "mn",
39708         "976"
39709       ],
39710       [
39711         "Montenegro (Crna Gora)",
39712         "me",
39713         "382"
39714       ],
39715       [
39716         "Montserrat",
39717         "ms",
39718         "1664"
39719       ],
39720       [
39721         "Morocco (‫المغرب‬‎)",
39722         "ma",
39723         "212",
39724         0
39725       ],
39726       [
39727         "Mozambique (Moçambique)",
39728         "mz",
39729         "258"
39730       ],
39731       [
39732         "Myanmar (Burma) (မြန်မာ)",
39733         "mm",
39734         "95"
39735       ],
39736       [
39737         "Namibia (Namibië)",
39738         "na",
39739         "264"
39740       ],
39741       [
39742         "Nauru",
39743         "nr",
39744         "674"
39745       ],
39746       [
39747         "Nepal (नेपाल)",
39748         "np",
39749         "977"
39750       ],
39751       [
39752         "Netherlands (Nederland)",
39753         "nl",
39754         "31"
39755       ],
39756       [
39757         "New Caledonia (Nouvelle-Calédonie)",
39758         "nc",
39759         "687"
39760       ],
39761       [
39762         "New Zealand",
39763         "nz",
39764         "64"
39765       ],
39766       [
39767         "Nicaragua",
39768         "ni",
39769         "505"
39770       ],
39771       [
39772         "Niger (Nijar)",
39773         "ne",
39774         "227"
39775       ],
39776       [
39777         "Nigeria",
39778         "ng",
39779         "234"
39780       ],
39781       [
39782         "Niue",
39783         "nu",
39784         "683"
39785       ],
39786       [
39787         "Norfolk Island",
39788         "nf",
39789         "672"
39790       ],
39791       [
39792         "North Korea (조선 민주주의 인민 공화국)",
39793         "kp",
39794         "850"
39795       ],
39796       [
39797         "Northern Mariana Islands",
39798         "mp",
39799         "1670"
39800       ],
39801       [
39802         "Norway (Norge)",
39803         "no",
39804         "47",
39805         0
39806       ],
39807       [
39808         "Oman (‫عُمان‬‎)",
39809         "om",
39810         "968"
39811       ],
39812       [
39813         "Pakistan (‫پاکستان‬‎)",
39814         "pk",
39815         "92"
39816       ],
39817       [
39818         "Palau",
39819         "pw",
39820         "680"
39821       ],
39822       [
39823         "Palestine (‫فلسطين‬‎)",
39824         "ps",
39825         "970"
39826       ],
39827       [
39828         "Panama (Panamá)",
39829         "pa",
39830         "507"
39831       ],
39832       [
39833         "Papua New Guinea",
39834         "pg",
39835         "675"
39836       ],
39837       [
39838         "Paraguay",
39839         "py",
39840         "595"
39841       ],
39842       [
39843         "Peru (Perú)",
39844         "pe",
39845         "51"
39846       ],
39847       [
39848         "Philippines",
39849         "ph",
39850         "63"
39851       ],
39852       [
39853         "Poland (Polska)",
39854         "pl",
39855         "48"
39856       ],
39857       [
39858         "Portugal",
39859         "pt",
39860         "351"
39861       ],
39862       [
39863         "Puerto Rico",
39864         "pr",
39865         "1",
39866         3,
39867         ["787", "939"]
39868       ],
39869       [
39870         "Qatar (‫قطر‬‎)",
39871         "qa",
39872         "974"
39873       ],
39874       [
39875         "Réunion (La Réunion)",
39876         "re",
39877         "262",
39878         0
39879       ],
39880       [
39881         "Romania (România)",
39882         "ro",
39883         "40"
39884       ],
39885       [
39886         "Russia (Россия)",
39887         "ru",
39888         "7",
39889         0
39890       ],
39891       [
39892         "Rwanda",
39893         "rw",
39894         "250"
39895       ],
39896       [
39897         "Saint Barthélemy",
39898         "bl",
39899         "590",
39900         1
39901       ],
39902       [
39903         "Saint Helena",
39904         "sh",
39905         "290"
39906       ],
39907       [
39908         "Saint Kitts and Nevis",
39909         "kn",
39910         "1869"
39911       ],
39912       [
39913         "Saint Lucia",
39914         "lc",
39915         "1758"
39916       ],
39917       [
39918         "Saint Martin (Saint-Martin (partie française))",
39919         "mf",
39920         "590",
39921         2
39922       ],
39923       [
39924         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39925         "pm",
39926         "508"
39927       ],
39928       [
39929         "Saint Vincent and the Grenadines",
39930         "vc",
39931         "1784"
39932       ],
39933       [
39934         "Samoa",
39935         "ws",
39936         "685"
39937       ],
39938       [
39939         "San Marino",
39940         "sm",
39941         "378"
39942       ],
39943       [
39944         "São Tomé and Príncipe (São Tomé e Príncipe)",
39945         "st",
39946         "239"
39947       ],
39948       [
39949         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39950         "sa",
39951         "966"
39952       ],
39953       [
39954         "Senegal (Sénégal)",
39955         "sn",
39956         "221"
39957       ],
39958       [
39959         "Serbia (Србија)",
39960         "rs",
39961         "381"
39962       ],
39963       [
39964         "Seychelles",
39965         "sc",
39966         "248"
39967       ],
39968       [
39969         "Sierra Leone",
39970         "sl",
39971         "232"
39972       ],
39973       [
39974         "Singapore",
39975         "sg",
39976         "65"
39977       ],
39978       [
39979         "Sint Maarten",
39980         "sx",
39981         "1721"
39982       ],
39983       [
39984         "Slovakia (Slovensko)",
39985         "sk",
39986         "421"
39987       ],
39988       [
39989         "Slovenia (Slovenija)",
39990         "si",
39991         "386"
39992       ],
39993       [
39994         "Solomon Islands",
39995         "sb",
39996         "677"
39997       ],
39998       [
39999         "Somalia (Soomaaliya)",
40000         "so",
40001         "252"
40002       ],
40003       [
40004         "South Africa",
40005         "za",
40006         "27"
40007       ],
40008       [
40009         "South Korea (대한민국)",
40010         "kr",
40011         "82"
40012       ],
40013       [
40014         "South Sudan (‫جنوب السودان‬‎)",
40015         "ss",
40016         "211"
40017       ],
40018       [
40019         "Spain (España)",
40020         "es",
40021         "34"
40022       ],
40023       [
40024         "Sri Lanka (ශ්‍රී ලංකාව)",
40025         "lk",
40026         "94"
40027       ],
40028       [
40029         "Sudan (‫السودان‬‎)",
40030         "sd",
40031         "249"
40032       ],
40033       [
40034         "Suriname",
40035         "sr",
40036         "597"
40037       ],
40038       [
40039         "Svalbard and Jan Mayen",
40040         "sj",
40041         "47",
40042         1
40043       ],
40044       [
40045         "Swaziland",
40046         "sz",
40047         "268"
40048       ],
40049       [
40050         "Sweden (Sverige)",
40051         "se",
40052         "46"
40053       ],
40054       [
40055         "Switzerland (Schweiz)",
40056         "ch",
40057         "41"
40058       ],
40059       [
40060         "Syria (‫سوريا‬‎)",
40061         "sy",
40062         "963"
40063       ],
40064       [
40065         "Taiwan (台灣)",
40066         "tw",
40067         "886"
40068       ],
40069       [
40070         "Tajikistan",
40071         "tj",
40072         "992"
40073       ],
40074       [
40075         "Tanzania",
40076         "tz",
40077         "255"
40078       ],
40079       [
40080         "Thailand (ไทย)",
40081         "th",
40082         "66"
40083       ],
40084       [
40085         "Timor-Leste",
40086         "tl",
40087         "670"
40088       ],
40089       [
40090         "Togo",
40091         "tg",
40092         "228"
40093       ],
40094       [
40095         "Tokelau",
40096         "tk",
40097         "690"
40098       ],
40099       [
40100         "Tonga",
40101         "to",
40102         "676"
40103       ],
40104       [
40105         "Trinidad and Tobago",
40106         "tt",
40107         "1868"
40108       ],
40109       [
40110         "Tunisia (‫تونس‬‎)",
40111         "tn",
40112         "216"
40113       ],
40114       [
40115         "Turkey (Türkiye)",
40116         "tr",
40117         "90"
40118       ],
40119       [
40120         "Turkmenistan",
40121         "tm",
40122         "993"
40123       ],
40124       [
40125         "Turks and Caicos Islands",
40126         "tc",
40127         "1649"
40128       ],
40129       [
40130         "Tuvalu",
40131         "tv",
40132         "688"
40133       ],
40134       [
40135         "U.S. Virgin Islands",
40136         "vi",
40137         "1340"
40138       ],
40139       [
40140         "Uganda",
40141         "ug",
40142         "256"
40143       ],
40144       [
40145         "Ukraine (Україна)",
40146         "ua",
40147         "380"
40148       ],
40149       [
40150         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40151         "ae",
40152         "971"
40153       ],
40154       [
40155         "United Kingdom",
40156         "gb",
40157         "44",
40158         0
40159       ],
40160       [
40161         "United States",
40162         "us",
40163         "1",
40164         0
40165       ],
40166       [
40167         "Uruguay",
40168         "uy",
40169         "598"
40170       ],
40171       [
40172         "Uzbekistan (Oʻzbekiston)",
40173         "uz",
40174         "998"
40175       ],
40176       [
40177         "Vanuatu",
40178         "vu",
40179         "678"
40180       ],
40181       [
40182         "Vatican City (Città del Vaticano)",
40183         "va",
40184         "39",
40185         1
40186       ],
40187       [
40188         "Venezuela",
40189         "ve",
40190         "58"
40191       ],
40192       [
40193         "Vietnam (Việt Nam)",
40194         "vn",
40195         "84"
40196       ],
40197       [
40198         "Wallis and Futuna (Wallis-et-Futuna)",
40199         "wf",
40200         "681"
40201       ],
40202       [
40203         "Western Sahara (‫الصحراء الغربية‬‎)",
40204         "eh",
40205         "212",
40206         1
40207       ],
40208       [
40209         "Yemen (‫اليمن‬‎)",
40210         "ye",
40211         "967"
40212       ],
40213       [
40214         "Zambia",
40215         "zm",
40216         "260"
40217       ],
40218       [
40219         "Zimbabwe",
40220         "zw",
40221         "263"
40222       ],
40223       [
40224         "Åland Islands",
40225         "ax",
40226         "358",
40227         1
40228       ]
40229   ];
40230   
40231   return d;
40232 }/**
40233 *    This script refer to:
40234 *    Title: International Telephone Input
40235 *    Author: Jack O'Connor
40236 *    Code version:  v12.1.12
40237 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40238 **/
40239
40240 /**
40241  * @class Roo.bootstrap.PhoneInput
40242  * @extends Roo.bootstrap.TriggerField
40243  * An input with International dial-code selection
40244  
40245  * @cfg {String} defaultDialCode default '+852'
40246  * @cfg {Array} preferedCountries default []
40247   
40248  * @constructor
40249  * Create a new PhoneInput.
40250  * @param {Object} config Configuration options
40251  */
40252
40253 Roo.bootstrap.PhoneInput = function(config) {
40254     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40255 };
40256
40257 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40258         
40259         listWidth: undefined,
40260         
40261         selectedClass: 'active',
40262         
40263         invalidClass : "has-warning",
40264         
40265         validClass: 'has-success',
40266         
40267         allowed: '0123456789',
40268         
40269         max_length: 15,
40270         
40271         /**
40272          * @cfg {String} defaultDialCode The default dial code when initializing the input
40273          */
40274         defaultDialCode: '+852',
40275         
40276         /**
40277          * @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
40278          */
40279         preferedCountries: false,
40280         
40281         getAutoCreate : function()
40282         {
40283             var data = Roo.bootstrap.PhoneInputData();
40284             var align = this.labelAlign || this.parentLabelAlign();
40285             var id = Roo.id();
40286             
40287             this.allCountries = [];
40288             this.dialCodeMapping = [];
40289             
40290             for (var i = 0; i < data.length; i++) {
40291               var c = data[i];
40292               this.allCountries[i] = {
40293                 name: c[0],
40294                 iso2: c[1],
40295                 dialCode: c[2],
40296                 priority: c[3] || 0,
40297                 areaCodes: c[4] || null
40298               };
40299               this.dialCodeMapping[c[2]] = {
40300                   name: c[0],
40301                   iso2: c[1],
40302                   priority: c[3] || 0,
40303                   areaCodes: c[4] || null
40304               };
40305             }
40306             
40307             var cfg = {
40308                 cls: 'form-group',
40309                 cn: []
40310             };
40311             
40312             var input =  {
40313                 tag: 'input',
40314                 id : id,
40315                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40316                 maxlength: this.max_length,
40317                 cls : 'form-control tel-input',
40318                 autocomplete: 'new-password'
40319             };
40320             
40321             var hiddenInput = {
40322                 tag: 'input',
40323                 type: 'hidden',
40324                 cls: 'hidden-tel-input'
40325             };
40326             
40327             if (this.name) {
40328                 hiddenInput.name = this.name;
40329             }
40330             
40331             if (this.disabled) {
40332                 input.disabled = true;
40333             }
40334             
40335             var flag_container = {
40336                 tag: 'div',
40337                 cls: 'flag-box',
40338                 cn: [
40339                     {
40340                         tag: 'div',
40341                         cls: 'flag'
40342                     },
40343                     {
40344                         tag: 'div',
40345                         cls: 'caret'
40346                     }
40347                 ]
40348             };
40349             
40350             var box = {
40351                 tag: 'div',
40352                 cls: this.hasFeedback ? 'has-feedback' : '',
40353                 cn: [
40354                     hiddenInput,
40355                     input,
40356                     {
40357                         tag: 'input',
40358                         cls: 'dial-code-holder',
40359                         disabled: true
40360                     }
40361                 ]
40362             };
40363             
40364             var container = {
40365                 cls: 'roo-select2-container input-group',
40366                 cn: [
40367                     flag_container,
40368                     box
40369                 ]
40370             };
40371             
40372             if (this.fieldLabel.length) {
40373                 var indicator = {
40374                     tag: 'i',
40375                     tooltip: 'This field is required'
40376                 };
40377                 
40378                 var label = {
40379                     tag: 'label',
40380                     'for':  id,
40381                     cls: 'control-label',
40382                     cn: []
40383                 };
40384                 
40385                 var label_text = {
40386                     tag: 'span',
40387                     html: this.fieldLabel
40388                 };
40389                 
40390                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40391                 label.cn = [
40392                     indicator,
40393                     label_text
40394                 ];
40395                 
40396                 if(this.indicatorpos == 'right') {
40397                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40398                     label.cn = [
40399                         label_text,
40400                         indicator
40401                     ];
40402                 }
40403                 
40404                 if(align == 'left') {
40405                     container = {
40406                         tag: 'div',
40407                         cn: [
40408                             container
40409                         ]
40410                     };
40411                     
40412                     if(this.labelWidth > 12){
40413                         label.style = "width: " + this.labelWidth + 'px';
40414                     }
40415                     if(this.labelWidth < 13 && this.labelmd == 0){
40416                         this.labelmd = this.labelWidth;
40417                     }
40418                     if(this.labellg > 0){
40419                         label.cls += ' col-lg-' + this.labellg;
40420                         input.cls += ' col-lg-' + (12 - this.labellg);
40421                     }
40422                     if(this.labelmd > 0){
40423                         label.cls += ' col-md-' + this.labelmd;
40424                         container.cls += ' col-md-' + (12 - this.labelmd);
40425                     }
40426                     if(this.labelsm > 0){
40427                         label.cls += ' col-sm-' + this.labelsm;
40428                         container.cls += ' col-sm-' + (12 - this.labelsm);
40429                     }
40430                     if(this.labelxs > 0){
40431                         label.cls += ' col-xs-' + this.labelxs;
40432                         container.cls += ' col-xs-' + (12 - this.labelxs);
40433                     }
40434                 }
40435             }
40436             
40437             cfg.cn = [
40438                 label,
40439                 container
40440             ];
40441             
40442             var settings = this;
40443             
40444             ['xs','sm','md','lg'].map(function(size){
40445                 if (settings[size]) {
40446                     cfg.cls += ' col-' + size + '-' + settings[size];
40447                 }
40448             });
40449             
40450             this.store = new Roo.data.Store({
40451                 proxy : new Roo.data.MemoryProxy({}),
40452                 reader : new Roo.data.JsonReader({
40453                     fields : [
40454                         {
40455                             'name' : 'name',
40456                             'type' : 'string'
40457                         },
40458                         {
40459                             'name' : 'iso2',
40460                             'type' : 'string'
40461                         },
40462                         {
40463                             'name' : 'dialCode',
40464                             'type' : 'string'
40465                         },
40466                         {
40467                             'name' : 'priority',
40468                             'type' : 'string'
40469                         },
40470                         {
40471                             'name' : 'areaCodes',
40472                             'type' : 'string'
40473                         }
40474                     ]
40475                 })
40476             });
40477             
40478             if(!this.preferedCountries) {
40479                 this.preferedCountries = [
40480                     'hk',
40481                     'gb',
40482                     'us'
40483                 ];
40484             }
40485             
40486             var p = this.preferedCountries.reverse();
40487             
40488             if(p) {
40489                 for (var i = 0; i < p.length; i++) {
40490                     for (var j = 0; j < this.allCountries.length; j++) {
40491                         if(this.allCountries[j].iso2 == p[i]) {
40492                             var t = this.allCountries[j];
40493                             this.allCountries.splice(j,1);
40494                             this.allCountries.unshift(t);
40495                         }
40496                     } 
40497                 }
40498             }
40499             
40500             this.store.proxy.data = {
40501                 success: true,
40502                 data: this.allCountries
40503             };
40504             
40505             return cfg;
40506         },
40507         
40508         initEvents : function()
40509         {
40510             this.createList();
40511             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40512             
40513             this.indicator = this.indicatorEl();
40514             this.flag = this.flagEl();
40515             this.dialCodeHolder = this.dialCodeHolderEl();
40516             
40517             this.trigger = this.el.select('div.flag-box',true).first();
40518             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40519             
40520             var _this = this;
40521             
40522             (function(){
40523                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40524                 _this.list.setWidth(lw);
40525             }).defer(100);
40526             
40527             this.list.on('mouseover', this.onViewOver, this);
40528             this.list.on('mousemove', this.onViewMove, this);
40529             this.inputEl().on("keyup", this.onKeyUp, this);
40530             this.inputEl().on("keypress", this.onKeyPress, this);
40531             
40532             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40533
40534             this.view = new Roo.View(this.list, this.tpl, {
40535                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40536             });
40537             
40538             this.view.on('click', this.onViewClick, this);
40539             this.setValue(this.defaultDialCode);
40540         },
40541         
40542         onTriggerClick : function(e)
40543         {
40544             Roo.log('trigger click');
40545             if(this.disabled){
40546                 return;
40547             }
40548             
40549             if(this.isExpanded()){
40550                 this.collapse();
40551                 this.hasFocus = false;
40552             }else {
40553                 this.store.load({});
40554                 this.hasFocus = true;
40555                 this.expand();
40556             }
40557         },
40558         
40559         isExpanded : function()
40560         {
40561             return this.list.isVisible();
40562         },
40563         
40564         collapse : function()
40565         {
40566             if(!this.isExpanded()){
40567                 return;
40568             }
40569             this.list.hide();
40570             Roo.get(document).un('mousedown', this.collapseIf, this);
40571             Roo.get(document).un('mousewheel', this.collapseIf, this);
40572             this.fireEvent('collapse', this);
40573             this.validate();
40574         },
40575         
40576         expand : function()
40577         {
40578             Roo.log('expand');
40579
40580             if(this.isExpanded() || !this.hasFocus){
40581                 return;
40582             }
40583             
40584             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40585             this.list.setWidth(lw);
40586             
40587             this.list.show();
40588             this.restrictHeight();
40589             
40590             Roo.get(document).on('mousedown', this.collapseIf, this);
40591             Roo.get(document).on('mousewheel', this.collapseIf, this);
40592             
40593             this.fireEvent('expand', this);
40594         },
40595         
40596         restrictHeight : function()
40597         {
40598             this.list.alignTo(this.inputEl(), this.listAlign);
40599             this.list.alignTo(this.inputEl(), this.listAlign);
40600         },
40601         
40602         onViewOver : function(e, t)
40603         {
40604             if(this.inKeyMode){
40605                 return;
40606             }
40607             var item = this.view.findItemFromChild(t);
40608             
40609             if(item){
40610                 var index = this.view.indexOf(item);
40611                 this.select(index, false);
40612             }
40613         },
40614
40615         // private
40616         onViewClick : function(view, doFocus, el, e)
40617         {
40618             var index = this.view.getSelectedIndexes()[0];
40619             
40620             var r = this.store.getAt(index);
40621             
40622             if(r){
40623                 this.onSelect(r, index);
40624             }
40625             if(doFocus !== false && !this.blockFocus){
40626                 this.inputEl().focus();
40627             }
40628         },
40629         
40630         onViewMove : function(e, t)
40631         {
40632             this.inKeyMode = false;
40633         },
40634         
40635         select : function(index, scrollIntoView)
40636         {
40637             this.selectedIndex = index;
40638             this.view.select(index);
40639             if(scrollIntoView !== false){
40640                 var el = this.view.getNode(index);
40641                 if(el){
40642                     this.list.scrollChildIntoView(el, false);
40643                 }
40644             }
40645         },
40646         
40647         createList : function()
40648         {
40649             this.list = Roo.get(document.body).createChild({
40650                 tag: 'ul',
40651                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40652                 style: 'display:none'
40653             });
40654             
40655             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40656         },
40657         
40658         collapseIf : function(e)
40659         {
40660             var in_combo  = e.within(this.el);
40661             var in_list =  e.within(this.list);
40662             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40663             
40664             if (in_combo || in_list || is_list) {
40665                 return;
40666             }
40667             this.collapse();
40668         },
40669         
40670         onSelect : function(record, index)
40671         {
40672             if(this.fireEvent('beforeselect', this, record, index) !== false){
40673                 
40674                 this.setFlagClass(record.data.iso2);
40675                 this.setDialCode(record.data.dialCode);
40676                 this.hasFocus = false;
40677                 this.collapse();
40678                 this.fireEvent('select', this, record, index);
40679             }
40680         },
40681         
40682         flagEl : function()
40683         {
40684             var flag = this.el.select('div.flag',true).first();
40685             if(!flag){
40686                 return false;
40687             }
40688             return flag;
40689         },
40690         
40691         dialCodeHolderEl : function()
40692         {
40693             var d = this.el.select('input.dial-code-holder',true).first();
40694             if(!d){
40695                 return false;
40696             }
40697             return d;
40698         },
40699         
40700         setDialCode : function(v)
40701         {
40702             this.dialCodeHolder.dom.value = '+'+v;
40703         },
40704         
40705         setFlagClass : function(n)
40706         {
40707             this.flag.dom.className = 'flag '+n;
40708         },
40709         
40710         getValue : function()
40711         {
40712             var v = this.inputEl().getValue();
40713             if(this.dialCodeHolder) {
40714                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40715             }
40716             return v;
40717         },
40718         
40719         setValue : function(v)
40720         {
40721             var d = this.getDialCode(v);
40722             
40723             //invalid dial code
40724             if(v.length == 0 || !d || d.length == 0) {
40725                 if(this.rendered){
40726                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40727                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40728                 }
40729                 return;
40730             }
40731             
40732             //valid dial code
40733             this.setFlagClass(this.dialCodeMapping[d].iso2);
40734             this.setDialCode(d);
40735             this.inputEl().dom.value = v.replace('+'+d,'');
40736             this.hiddenEl().dom.value = this.getValue();
40737             
40738             this.validate();
40739         },
40740         
40741         getDialCode : function(v)
40742         {
40743             v = v ||  '';
40744             
40745             if (v.length == 0) {
40746                 return this.dialCodeHolder.dom.value;
40747             }
40748             
40749             var dialCode = "";
40750             if (v.charAt(0) != "+") {
40751                 return false;
40752             }
40753             var numericChars = "";
40754             for (var i = 1; i < v.length; i++) {
40755               var c = v.charAt(i);
40756               if (!isNaN(c)) {
40757                 numericChars += c;
40758                 if (this.dialCodeMapping[numericChars]) {
40759                   dialCode = v.substr(1, i);
40760                 }
40761                 if (numericChars.length == 4) {
40762                   break;
40763                 }
40764               }
40765             }
40766             return dialCode;
40767         },
40768         
40769         reset : function()
40770         {
40771             this.setValue(this.defaultDialCode);
40772             this.validate();
40773         },
40774         
40775         hiddenEl : function()
40776         {
40777             return this.el.select('input.hidden-tel-input',true).first();
40778         },
40779         
40780         // after setting val
40781         onKeyUp : function(e){
40782             this.setValue(this.getValue());
40783         },
40784         
40785         onKeyPress : function(e){
40786             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40787                 e.stopEvent();
40788             }
40789         }
40790         
40791 });
40792 /**
40793  * @class Roo.bootstrap.MoneyField
40794  * @extends Roo.bootstrap.ComboBox
40795  * Bootstrap MoneyField class
40796  * 
40797  * @constructor
40798  * Create a new MoneyField.
40799  * @param {Object} config Configuration options
40800  */
40801
40802 Roo.bootstrap.MoneyField = function(config) {
40803     
40804     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40805     
40806 };
40807
40808 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40809     
40810     /**
40811      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40812      */
40813     allowDecimals : true,
40814     /**
40815      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40816      */
40817     decimalSeparator : ".",
40818     /**
40819      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40820      */
40821     decimalPrecision : 0,
40822     /**
40823      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40824      */
40825     allowNegative : true,
40826     /**
40827      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40828      */
40829     allowZero: true,
40830     /**
40831      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40832      */
40833     minValue : Number.NEGATIVE_INFINITY,
40834     /**
40835      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40836      */
40837     maxValue : Number.MAX_VALUE,
40838     /**
40839      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40840      */
40841     minText : "The minimum value for this field is {0}",
40842     /**
40843      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40844      */
40845     maxText : "The maximum value for this field is {0}",
40846     /**
40847      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40848      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40849      */
40850     nanText : "{0} is not a valid number",
40851     /**
40852      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40853      */
40854     castInt : true,
40855     /**
40856      * @cfg {String} defaults currency of the MoneyField
40857      * value should be in lkey
40858      */
40859     defaultCurrency : false,
40860     /**
40861      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40862      */
40863     thousandsDelimiter : false,
40864     /**
40865      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40866      */
40867     max_length: false,
40868     
40869     inputlg : 9,
40870     inputmd : 9,
40871     inputsm : 9,
40872     inputxs : 6,
40873     
40874     store : false,
40875     
40876     getAutoCreate : function()
40877     {
40878         var align = this.labelAlign || this.parentLabelAlign();
40879         
40880         var id = Roo.id();
40881
40882         var cfg = {
40883             cls: 'form-group',
40884             cn: []
40885         };
40886
40887         var input =  {
40888             tag: 'input',
40889             id : id,
40890             cls : 'form-control roo-money-amount-input',
40891             autocomplete: 'new-password'
40892         };
40893         
40894         var hiddenInput = {
40895             tag: 'input',
40896             type: 'hidden',
40897             id: Roo.id(),
40898             cls: 'hidden-number-input'
40899         };
40900         
40901         if(this.max_length) {
40902             input.maxlength = this.max_length; 
40903         }
40904         
40905         if (this.name) {
40906             hiddenInput.name = this.name;
40907         }
40908
40909         if (this.disabled) {
40910             input.disabled = true;
40911         }
40912
40913         var clg = 12 - this.inputlg;
40914         var cmd = 12 - this.inputmd;
40915         var csm = 12 - this.inputsm;
40916         var cxs = 12 - this.inputxs;
40917         
40918         var container = {
40919             tag : 'div',
40920             cls : 'row roo-money-field',
40921             cn : [
40922                 {
40923                     tag : 'div',
40924                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40925                     cn : [
40926                         {
40927                             tag : 'div',
40928                             cls: 'roo-select2-container input-group',
40929                             cn: [
40930                                 {
40931                                     tag : 'input',
40932                                     cls : 'form-control roo-money-currency-input',
40933                                     autocomplete: 'new-password',
40934                                     readOnly : 1,
40935                                     name : this.currencyName
40936                                 },
40937                                 {
40938                                     tag :'span',
40939                                     cls : 'input-group-addon',
40940                                     cn : [
40941                                         {
40942                                             tag: 'span',
40943                                             cls: 'caret'
40944                                         }
40945                                     ]
40946                                 }
40947                             ]
40948                         }
40949                     ]
40950                 },
40951                 {
40952                     tag : 'div',
40953                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40954                     cn : [
40955                         {
40956                             tag: 'div',
40957                             cls: this.hasFeedback ? 'has-feedback' : '',
40958                             cn: [
40959                                 input
40960                             ]
40961                         }
40962                     ]
40963                 }
40964             ]
40965             
40966         };
40967         
40968         if (this.fieldLabel.length) {
40969             var indicator = {
40970                 tag: 'i',
40971                 tooltip: 'This field is required'
40972             };
40973
40974             var label = {
40975                 tag: 'label',
40976                 'for':  id,
40977                 cls: 'control-label',
40978                 cn: []
40979             };
40980
40981             var label_text = {
40982                 tag: 'span',
40983                 html: this.fieldLabel
40984             };
40985
40986             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40987             label.cn = [
40988                 indicator,
40989                 label_text
40990             ];
40991
40992             if(this.indicatorpos == 'right') {
40993                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40994                 label.cn = [
40995                     label_text,
40996                     indicator
40997                 ];
40998             }
40999
41000             if(align == 'left') {
41001                 container = {
41002                     tag: 'div',
41003                     cn: [
41004                         container
41005                     ]
41006                 };
41007
41008                 if(this.labelWidth > 12){
41009                     label.style = "width: " + this.labelWidth + 'px';
41010                 }
41011                 if(this.labelWidth < 13 && this.labelmd == 0){
41012                     this.labelmd = this.labelWidth;
41013                 }
41014                 if(this.labellg > 0){
41015                     label.cls += ' col-lg-' + this.labellg;
41016                     input.cls += ' col-lg-' + (12 - this.labellg);
41017                 }
41018                 if(this.labelmd > 0){
41019                     label.cls += ' col-md-' + this.labelmd;
41020                     container.cls += ' col-md-' + (12 - this.labelmd);
41021                 }
41022                 if(this.labelsm > 0){
41023                     label.cls += ' col-sm-' + this.labelsm;
41024                     container.cls += ' col-sm-' + (12 - this.labelsm);
41025                 }
41026                 if(this.labelxs > 0){
41027                     label.cls += ' col-xs-' + this.labelxs;
41028                     container.cls += ' col-xs-' + (12 - this.labelxs);
41029                 }
41030             }
41031         }
41032
41033         cfg.cn = [
41034             label,
41035             container,
41036             hiddenInput
41037         ];
41038         
41039         var settings = this;
41040
41041         ['xs','sm','md','lg'].map(function(size){
41042             if (settings[size]) {
41043                 cfg.cls += ' col-' + size + '-' + settings[size];
41044             }
41045         });
41046         
41047         return cfg;
41048     },
41049     
41050     initEvents : function()
41051     {
41052         this.indicator = this.indicatorEl();
41053         
41054         this.initCurrencyEvent();
41055         
41056         this.initNumberEvent();
41057     },
41058     
41059     initCurrencyEvent : function()
41060     {
41061         if (!this.store) {
41062             throw "can not find store for combo";
41063         }
41064         
41065         this.store = Roo.factory(this.store, Roo.data);
41066         this.store.parent = this;
41067         
41068         this.createList();
41069         
41070         this.triggerEl = this.el.select('.input-group-addon', true).first();
41071         
41072         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41073         
41074         var _this = this;
41075         
41076         (function(){
41077             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41078             _this.list.setWidth(lw);
41079         }).defer(100);
41080         
41081         this.list.on('mouseover', this.onViewOver, this);
41082         this.list.on('mousemove', this.onViewMove, this);
41083         this.list.on('scroll', this.onViewScroll, this);
41084         
41085         if(!this.tpl){
41086             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41087         }
41088         
41089         this.view = new Roo.View(this.list, this.tpl, {
41090             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41091         });
41092         
41093         this.view.on('click', this.onViewClick, this);
41094         
41095         this.store.on('beforeload', this.onBeforeLoad, this);
41096         this.store.on('load', this.onLoad, this);
41097         this.store.on('loadexception', this.onLoadException, this);
41098         
41099         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41100             "up" : function(e){
41101                 this.inKeyMode = true;
41102                 this.selectPrev();
41103             },
41104
41105             "down" : function(e){
41106                 if(!this.isExpanded()){
41107                     this.onTriggerClick();
41108                 }else{
41109                     this.inKeyMode = true;
41110                     this.selectNext();
41111                 }
41112             },
41113
41114             "enter" : function(e){
41115                 this.collapse();
41116                 
41117                 if(this.fireEvent("specialkey", this, e)){
41118                     this.onViewClick(false);
41119                 }
41120                 
41121                 return true;
41122             },
41123
41124             "esc" : function(e){
41125                 this.collapse();
41126             },
41127
41128             "tab" : function(e){
41129                 this.collapse();
41130                 
41131                 if(this.fireEvent("specialkey", this, e)){
41132                     this.onViewClick(false);
41133                 }
41134                 
41135                 return true;
41136             },
41137
41138             scope : this,
41139
41140             doRelay : function(foo, bar, hname){
41141                 if(hname == 'down' || this.scope.isExpanded()){
41142                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41143                 }
41144                 return true;
41145             },
41146
41147             forceKeyDown: true
41148         });
41149         
41150         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41151         
41152     },
41153     
41154     initNumberEvent : function(e)
41155     {
41156         this.inputEl().on("keydown" , this.fireKey,  this);
41157         this.inputEl().on("focus", this.onFocus,  this);
41158         this.inputEl().on("blur", this.onBlur,  this);
41159         
41160         this.inputEl().relayEvent('keyup', this);
41161         
41162         if(this.indicator){
41163             this.indicator.addClass('invisible');
41164         }
41165  
41166         this.originalValue = this.getValue();
41167         
41168         if(this.validationEvent == 'keyup'){
41169             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41170             this.inputEl().on('keyup', this.filterValidation, this);
41171         }
41172         else if(this.validationEvent !== false){
41173             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41174         }
41175         
41176         if(this.selectOnFocus){
41177             this.on("focus", this.preFocus, this);
41178             
41179         }
41180         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41181             this.inputEl().on("keypress", this.filterKeys, this);
41182         } else {
41183             this.inputEl().relayEvent('keypress', this);
41184         }
41185         
41186         var allowed = "0123456789";
41187         
41188         if(this.allowDecimals){
41189             allowed += this.decimalSeparator;
41190         }
41191         
41192         if(this.allowNegative){
41193             allowed += "-";
41194         }
41195         
41196         if(this.thousandsDelimiter) {
41197             allowed += ",";
41198         }
41199         
41200         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41201         
41202         var keyPress = function(e){
41203             
41204             var k = e.getKey();
41205             
41206             var c = e.getCharCode();
41207             
41208             if(
41209                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41210                     allowed.indexOf(String.fromCharCode(c)) === -1
41211             ){
41212                 e.stopEvent();
41213                 return;
41214             }
41215             
41216             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41217                 return;
41218             }
41219             
41220             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41221                 e.stopEvent();
41222             }
41223         };
41224         
41225         this.inputEl().on("keypress", keyPress, this);
41226         
41227     },
41228     
41229     onTriggerClick : function(e)
41230     {   
41231         if(this.disabled){
41232             return;
41233         }
41234         
41235         this.page = 0;
41236         this.loadNext = false;
41237         
41238         if(this.isExpanded()){
41239             this.collapse();
41240             return;
41241         }
41242         
41243         this.hasFocus = true;
41244         
41245         if(this.triggerAction == 'all') {
41246             this.doQuery(this.allQuery, true);
41247             return;
41248         }
41249         
41250         this.doQuery(this.getRawValue());
41251     },
41252     
41253     getCurrency : function()
41254     {   
41255         var v = this.currencyEl().getValue();
41256         
41257         return v;
41258     },
41259     
41260     restrictHeight : function()
41261     {
41262         this.list.alignTo(this.currencyEl(), this.listAlign);
41263         this.list.alignTo(this.currencyEl(), this.listAlign);
41264     },
41265     
41266     onViewClick : function(view, doFocus, el, e)
41267     {
41268         var index = this.view.getSelectedIndexes()[0];
41269         
41270         var r = this.store.getAt(index);
41271         
41272         if(r){
41273             this.onSelect(r, index);
41274         }
41275     },
41276     
41277     onSelect : function(record, index){
41278         
41279         if(this.fireEvent('beforeselect', this, record, index) !== false){
41280         
41281             this.setFromCurrencyData(index > -1 ? record.data : false);
41282             
41283             this.collapse();
41284             
41285             this.fireEvent('select', this, record, index);
41286         }
41287     },
41288     
41289     setFromCurrencyData : function(o)
41290     {
41291         var currency = '';
41292         
41293         this.lastCurrency = o;
41294         
41295         if (this.currencyField) {
41296             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41297         } else {
41298             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41299         }
41300         
41301         this.lastSelectionText = currency;
41302         
41303         //setting default currency
41304         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41305             this.setCurrency(this.defaultCurrency);
41306             return;
41307         }
41308         
41309         this.setCurrency(currency);
41310     },
41311     
41312     setFromData : function(o)
41313     {
41314         var c = {};
41315         
41316         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41317         
41318         this.setFromCurrencyData(c);
41319         
41320         var value = '';
41321         
41322         if (this.name) {
41323             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41324         } else {
41325             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41326         }
41327         
41328         this.setValue(value);
41329         
41330     },
41331     
41332     setCurrency : function(v)
41333     {   
41334         this.currencyValue = v;
41335         
41336         if(this.rendered){
41337             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41338             this.validate();
41339         }
41340     },
41341     
41342     setValue : function(v)
41343     {
41344         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41345         
41346         this.value = v;
41347         
41348         if(this.rendered){
41349             
41350             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41351             
41352             this.inputEl().dom.value = (v == '') ? '' :
41353                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41354             
41355             if(!this.allowZero && v === '0') {
41356                 this.hiddenEl().dom.value = '';
41357                 this.inputEl().dom.value = '';
41358             }
41359             
41360             this.validate();
41361         }
41362     },
41363     
41364     getRawValue : function()
41365     {
41366         var v = this.inputEl().getValue();
41367         
41368         return v;
41369     },
41370     
41371     getValue : function()
41372     {
41373         return this.fixPrecision(this.parseValue(this.getRawValue()));
41374     },
41375     
41376     parseValue : function(value)
41377     {
41378         if(this.thousandsDelimiter) {
41379             value += "";
41380             r = new RegExp(",", "g");
41381             value = value.replace(r, "");
41382         }
41383         
41384         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41385         return isNaN(value) ? '' : value;
41386         
41387     },
41388     
41389     fixPrecision : function(value)
41390     {
41391         if(this.thousandsDelimiter) {
41392             value += "";
41393             r = new RegExp(",", "g");
41394             value = value.replace(r, "");
41395         }
41396         
41397         var nan = isNaN(value);
41398         
41399         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41400             return nan ? '' : value;
41401         }
41402         return parseFloat(value).toFixed(this.decimalPrecision);
41403     },
41404     
41405     decimalPrecisionFcn : function(v)
41406     {
41407         return Math.floor(v);
41408     },
41409     
41410     validateValue : function(value)
41411     {
41412         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41413             return false;
41414         }
41415         
41416         var num = this.parseValue(value);
41417         
41418         if(isNaN(num)){
41419             this.markInvalid(String.format(this.nanText, value));
41420             return false;
41421         }
41422         
41423         if(num < this.minValue){
41424             this.markInvalid(String.format(this.minText, this.minValue));
41425             return false;
41426         }
41427         
41428         if(num > this.maxValue){
41429             this.markInvalid(String.format(this.maxText, this.maxValue));
41430             return false;
41431         }
41432         
41433         return true;
41434     },
41435     
41436     validate : function()
41437     {
41438         if(this.disabled || this.allowBlank){
41439             this.markValid();
41440             return true;
41441         }
41442         
41443         var currency = this.getCurrency();
41444         
41445         if(this.validateValue(this.getRawValue()) && currency.length){
41446             this.markValid();
41447             return true;
41448         }
41449         
41450         this.markInvalid();
41451         return false;
41452     },
41453     
41454     getName: function()
41455     {
41456         return this.name;
41457     },
41458     
41459     beforeBlur : function()
41460     {
41461         if(!this.castInt){
41462             return;
41463         }
41464         
41465         var v = this.parseValue(this.getRawValue());
41466         
41467         if(v || v == 0){
41468             this.setValue(v);
41469         }
41470     },
41471     
41472     onBlur : function()
41473     {
41474         this.beforeBlur();
41475         
41476         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41477             //this.el.removeClass(this.focusClass);
41478         }
41479         
41480         this.hasFocus = false;
41481         
41482         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41483             this.validate();
41484         }
41485         
41486         var v = this.getValue();
41487         
41488         if(String(v) !== String(this.startValue)){
41489             this.fireEvent('change', this, v, this.startValue);
41490         }
41491         
41492         this.fireEvent("blur", this);
41493     },
41494     
41495     inputEl : function()
41496     {
41497         return this.el.select('.roo-money-amount-input', true).first();
41498     },
41499     
41500     currencyEl : function()
41501     {
41502         return this.el.select('.roo-money-currency-input', true).first();
41503     },
41504     
41505     hiddenEl : function()
41506     {
41507         return this.el.select('input.hidden-number-input',true).first();
41508     }
41509     
41510 });/**
41511 *    This script refer to:
41512 *    Title: Signature Pad
41513 *    Author: szimek
41514 *    Availability: https://github.com/szimek/signature_pad
41515 **/
41516
41517 /**
41518  * @class Roo.bootstrap.BezierSignature
41519  * @extends Roo.bootstrap.Component
41520  * Bootstrap BezierSignature class
41521  * 
41522  * @constructor
41523  * Create a new BezierSignature
41524  * @param {Object} config The config object
41525  */
41526
41527 Roo.bootstrap.BezierSignature = function(config){
41528     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41529     this.addEvents({
41530         "resize" : true
41531     });
41532 };
41533
41534 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,  {
41535     
41536     curve_data: [],
41537     
41538     is_empty: true,
41539     
41540     mouse_btn_down: true,
41541     
41542     /**
41543      * @cfg(int) canvas height
41544      */
41545     canvas_height: '200px',
41546     
41547     /**
41548      * @cfg(float or function) Radius of a single dot.
41549      */ 
41550     dot_size: false,
41551     
41552     /**
41553      * @cfg(float) Minimum width of a line. Defaults to 0.5.
41554      */
41555     min_width: 0.5,
41556     
41557     /**
41558      * @cfg(float) Maximum width of a line. Defaults to 2.5.
41559      */
41560     max_width: 2.5,
41561     
41562     /**
41563      * @cfg(integer) Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41564      */
41565     throttle: 16,
41566     
41567     /**
41568      * @cfg(integer) Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41569      */
41570     min_distance: 5,
41571     
41572     /**
41573      * @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.
41574      */
41575     bg_color: 'rgba(0, 0, 0, 0)',
41576     
41577     /**
41578      * @cfg(string) Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41579      */
41580     dot_color: 'black',
41581     
41582     /**
41583      * @cfg(float) Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41584      */
41585     velocity_filter_weight: 0.7,
41586     
41587     /**
41588      * @cfg(function) Callback when stroke begin.
41589      */
41590     onBegin: false,
41591     
41592     /**
41593      * @cfg(function) Callback when stroke end.
41594      */
41595     onEnd: false,
41596     
41597     getAutoCreate : function()
41598     {
41599         var cls = 'roo-signature column';
41600         
41601         if(this.cls){
41602             cls += ' ' + this.cls;
41603         }
41604         
41605         var col_sizes = [
41606             'lg',
41607             'md',
41608             'sm',
41609             'xs'
41610         ];
41611         
41612         for(var i = 0; i < col_sizes.length; i++) {
41613             if(this[col_sizes[i]]) {
41614                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41615             }
41616         }
41617         
41618         var cfg = {
41619             tag: 'div',
41620             cls: cls,
41621             cn: [
41622                 {
41623                     tag: 'div',
41624                     cls: 'roo-signature-body',
41625                     cn: [
41626                         {
41627                             tag: 'canvas',
41628                             cls: 'roo-signature-body-canvas',
41629                             height: this.canvas_height,
41630                             width: this.canvas_width
41631                         }
41632                     ]
41633                 },
41634                 {
41635                     tag: 'input',
41636                     type: 'file',
41637                     style: 'display: none'
41638                 }
41639             ]
41640         };
41641         
41642         return cfg;
41643     },
41644     
41645     initEvents: function() 
41646     {
41647         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41648         
41649         var canvas = this.canvasEl();
41650         
41651         // mouse && touch event swapping...
41652         canvas.dom.style.touchAction = 'none';
41653         canvas.dom.style.msTouchAction = 'none';
41654         
41655         this.mouse_btn_down = false;
41656         canvas.on('mousedown', this._handleMouseDown, this);
41657         canvas.on('mousemove', this._handleMouseMove, this);
41658         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41659         
41660         if (window.PointerEvent) {
41661             canvas.on('pointerdown', this._handleMouseDown, this);
41662             canvas.on('pointermove', this._handleMouseMove, this);
41663             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41664         }
41665         
41666         if ('ontouchstart' in window) {
41667             canvas.on('touchstart', this._handleTouchStart, this);
41668             canvas.on('touchmove', this._handleTouchMove, this);
41669             canvas.on('touchend', this._handleTouchEnd, this);
41670         }
41671         
41672         Roo.EventManager.onWindowResize(this.resize, this, true);
41673         
41674         // file input event
41675         this.fileEl().on('change', this.uploadImage, this);
41676         
41677         this.clear();
41678         
41679         this.resize();
41680     },
41681     
41682     resize: function(){
41683         
41684         var canvas = this.canvasEl().dom;
41685         var ctx = this.canvasElCtx();
41686         var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41687         
41688         // setting canvas width will clean img data
41689         canvas.width = 0;
41690         
41691         var style = window.getComputedStyle ? 
41692             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41693             
41694         var padding_left = parseInt(style.paddingLeft) || 0;
41695         var padding_right = parseInt(style.paddingRight) || 0;
41696         
41697         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41698         
41699         ctx.putImageData(img_data, 0, 0);
41700     },
41701     
41702     _handleMouseDown: function(e)
41703     {
41704         if (e.browserEvent.which === 1) {
41705             this.mouse_btn_down = true;
41706             this.strokeBegin(e);
41707         }
41708     },
41709     
41710     _handleMouseMove: function (e)
41711     {
41712         if (this.mouse_btn_down) {
41713             this.strokeMoveUpdate(e);
41714         }
41715     },
41716     
41717     _handleMouseUp: function (e)
41718     {
41719         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41720             this.mouse_btn_down = false;
41721             this.strokeEnd(e);
41722         }
41723     },
41724     
41725     _handleTouchStart: function (e) {
41726         
41727         e.preventDefault();
41728         if (e.browserEvent.targetTouches.length === 1) {
41729             // var touch = e.browserEvent.changedTouches[0];
41730             // this.strokeBegin(touch);
41731             
41732              this.strokeBegin(e); // assume e catching the correct xy...
41733         }
41734     },
41735     
41736     _handleTouchMove: function (e) {
41737         e.preventDefault();
41738         // var touch = event.targetTouches[0];
41739         // _this._strokeMoveUpdate(touch);
41740         this.strokeMoveUpdate(e);
41741     },
41742     
41743     _handleTouchEnd: function (e) {
41744         var wasCanvasTouched = e.target === this.canvasEl().dom;
41745         if (wasCanvasTouched) {
41746             e.preventDefault();
41747             // var touch = event.changedTouches[0];
41748             // _this._strokeEnd(touch);
41749             this.strokeEnd(e);
41750         }
41751     },
41752     
41753     reset: function () {
41754         this._lastPoints = [];
41755         this._lastVelocity = 0;
41756         this._lastWidth = (this.min_width + this.max_width) / 2;
41757         this.canvasElCtx().fillStyle = this.dot_color;
41758     },
41759     
41760     strokeMoveUpdate: function(e)
41761     {
41762         this.strokeUpdate(e);
41763         
41764         if (this.throttle) {
41765             this.throttle(this.strokeUpdate, this.throttle);
41766         }
41767         else {
41768             this.strokeUpdate(e);
41769         }
41770     },
41771     
41772     strokeBegin: function(e)
41773     {
41774         var newPointGroup = {
41775             color: this.dot_color,
41776             points: []
41777         };
41778         
41779         if (typeof this.onBegin === 'function') {
41780             this.onBegin(e);
41781         }
41782         
41783         this.curve_data.push(newPointGroup);
41784         this.reset();
41785         this.strokeUpdate(e);
41786     },
41787     
41788     strokeUpdate: function(e)
41789     {
41790         var rect = this.canvasEl().dom.getBoundingClientRect();
41791         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41792         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41793         var lastPoints = lastPointGroup.points;
41794         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41795         var isLastPointTooClose = lastPoint
41796             ? point.distanceTo(lastPoint) <= this.min_distance
41797             : false;
41798         var color = lastPointGroup.color;
41799         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41800             var curve = this.addPoint(point);
41801             if (!lastPoint) {
41802                 this.drawDot({color: color, point: point});
41803             }
41804             else if (curve) {
41805                 this.drawCurve({color: color, curve: curve});
41806             }
41807             lastPoints.push({
41808                 time: point.time,
41809                 x: point.x,
41810                 y: point.y
41811             });
41812         }
41813     },
41814     
41815     strokeEnd: function(e)
41816     {
41817         this.strokeUpdate(e);
41818         if (typeof this.onEnd === 'function') {
41819             this.onEnd(e);
41820         }
41821     },
41822     
41823     addPoint:  function (point) {
41824         var _lastPoints = this._lastPoints;
41825         _lastPoints.push(point);
41826         if (_lastPoints.length > 2) {
41827             if (_lastPoints.length === 3) {
41828                 _lastPoints.unshift(_lastPoints[0]);
41829             }
41830             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41831             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41832             _lastPoints.shift();
41833             return curve;
41834         }
41835         return null;
41836     },
41837     
41838     calculateCurveWidths: function (startPoint, endPoint) {
41839         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41840             (1 - this.velocity_filter_weight) * this._lastVelocity;
41841
41842         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41843         var widths = {
41844             end: newWidth,
41845             start: this._lastWidth
41846         };
41847         
41848         this._lastVelocity = velocity;
41849         this._lastWidth = newWidth;
41850         return widths;
41851     },
41852     
41853     drawDot: function (_a) {
41854         var color = _a.color, point = _a.point;
41855         var ctx = this.canvasElCtx();
41856         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41857         ctx.beginPath();
41858         this.drawCurveSegment(point.x, point.y, width);
41859         ctx.closePath();
41860         ctx.fillStyle = color;
41861         ctx.fill();
41862     },
41863     
41864     drawCurve: function (_a) {
41865         var color = _a.color, curve = _a.curve;
41866         var ctx = this.canvasElCtx();
41867         var widthDelta = curve.endWidth - curve.startWidth;
41868         var drawSteps = Math.floor(curve.length()) * 2;
41869         ctx.beginPath();
41870         ctx.fillStyle = color;
41871         for (var i = 0; i < drawSteps; i += 1) {
41872         var t = i / drawSteps;
41873         var tt = t * t;
41874         var ttt = tt * t;
41875         var u = 1 - t;
41876         var uu = u * u;
41877         var uuu = uu * u;
41878         var x = uuu * curve.startPoint.x;
41879         x += 3 * uu * t * curve.control1.x;
41880         x += 3 * u * tt * curve.control2.x;
41881         x += ttt * curve.endPoint.x;
41882         var y = uuu * curve.startPoint.y;
41883         y += 3 * uu * t * curve.control1.y;
41884         y += 3 * u * tt * curve.control2.y;
41885         y += ttt * curve.endPoint.y;
41886         var width = curve.startWidth + ttt * widthDelta;
41887         this.drawCurveSegment(x, y, width);
41888         }
41889         ctx.closePath();
41890         ctx.fill();
41891     },
41892     
41893     drawCurveSegment: function (x, y, width) {
41894         var ctx = this.canvasElCtx();
41895         ctx.moveTo(x, y);
41896         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
41897         this.is_empty = false;
41898     },
41899     
41900     clear: function()
41901     {
41902         var ctx = this.canvasElCtx();
41903         var canvas = this.canvasEl().dom;
41904         ctx.fillStyle = this.bg_color;
41905         ctx.clearRect(0, 0, canvas.width, canvas.height);
41906         ctx.fillRect(0, 0, canvas.width, canvas.height);
41907         this.curve_data = [];
41908         this.reset();
41909         this.is_empty = true;
41910     },
41911     
41912     fileEl: function()
41913     {
41914         return  this.el.select('input',true).first();
41915     },
41916     
41917     canvasEl: function()
41918     {
41919         return this.el.select('canvas',true).first();
41920     },
41921     
41922     canvasElCtx: function()
41923     {
41924         return this.el.select('canvas',true).first().dom.getContext('2d');
41925     },
41926     
41927     getImage: function(type)
41928     {
41929         if(this.is_empty) {
41930             return false;
41931         }
41932         
41933         // encryption ?
41934         return this.canvasEl().dom.toDataURL('image/'+type, 1);
41935     },
41936     
41937     drawFromImage: function(img_src)
41938     {
41939         var img = new Image();
41940         
41941         img.onload = function(){
41942             this.canvasElCtx().drawImage(img, 0, 0);
41943         }.bind(this);
41944         
41945         img.src = img_src;
41946         
41947         this.is_empty = false;
41948     },
41949     
41950     selectImage: function()
41951     {
41952         this.fileEl().dom.click();
41953     },
41954     
41955     uploadImage: function(e)
41956     {
41957         var reader = new FileReader();
41958         
41959         reader.onload = function(e){
41960             var img = new Image();
41961             img.onload = function(){
41962                 this.reset();
41963                 this.canvasElCtx().drawImage(img, 0, 0);
41964             }.bind(this);
41965             img.src = e.target.result;
41966         }.bind(this);
41967         
41968         reader.readAsDataURL(e.target.files[0]);
41969     },
41970     
41971     // Bezier Point Constructor
41972     Point: (function () {
41973         function Point(x, y, time) {
41974             this.x = x;
41975             this.y = y;
41976             this.time = time || Date.now();
41977         }
41978         Point.prototype.distanceTo = function (start) {
41979             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
41980         };
41981         Point.prototype.equals = function (other) {
41982             return this.x === other.x && this.y === other.y && this.time === other.time;
41983         };
41984         Point.prototype.velocityFrom = function (start) {
41985             return this.time !== start.time
41986             ? this.distanceTo(start) / (this.time - start.time)
41987             : 0;
41988         };
41989         return Point;
41990     }()),
41991     
41992     
41993     // Bezier Constructor
41994     Bezier: (function () {
41995         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
41996             this.startPoint = startPoint;
41997             this.control2 = control2;
41998             this.control1 = control1;
41999             this.endPoint = endPoint;
42000             this.startWidth = startWidth;
42001             this.endWidth = endWidth;
42002         }
42003         Bezier.fromPoints = function (points, widths, scope) {
42004             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42005             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42006             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42007         };
42008         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42009             var dx1 = s1.x - s2.x;
42010             var dy1 = s1.y - s2.y;
42011             var dx2 = s2.x - s3.x;
42012             var dy2 = s2.y - s3.y;
42013             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42014             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42015             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42016             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42017             var dxm = m1.x - m2.x;
42018             var dym = m1.y - m2.y;
42019             var k = l2 / (l1 + l2);
42020             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42021             var tx = s2.x - cm.x;
42022             var ty = s2.y - cm.y;
42023             return {
42024                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42025                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42026             };
42027         };
42028         Bezier.prototype.length = function () {
42029             var steps = 10;
42030             var length = 0;
42031             var px;
42032             var py;
42033             for (var i = 0; i <= steps; i += 1) {
42034                 var t = i / steps;
42035                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42036                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42037                 if (i > 0) {
42038                     var xdiff = cx - px;
42039                     var ydiff = cy - py;
42040                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42041                 }
42042                 px = cx;
42043                 py = cy;
42044             }
42045             return length;
42046         };
42047         Bezier.prototype.point = function (t, start, c1, c2, end) {
42048             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42049             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42050             + (3.0 * c2 * (1.0 - t) * t * t)
42051             + (end * t * t * t);
42052         };
42053         return Bezier;
42054     }()),
42055     
42056     throttle: function(fn, wait) {
42057       if (wait === void 0) { wait = 250; }
42058       var previous = 0;
42059       var timeout = null;
42060       var result;
42061       var storedContext;
42062       var storedArgs;
42063       var later = function () {
42064           previous = Date.now();
42065           timeout = null;
42066           result = fn.apply(storedContext, storedArgs);
42067           if (!timeout) {
42068               storedContext = null;
42069               storedArgs = [];
42070           }
42071       };
42072       return function wrapper() {
42073           var args = [];
42074           for (var _i = 0; _i < arguments.length; _i++) {
42075               args[_i] = arguments[_i];
42076           }
42077           var now = Date.now();
42078           var remaining = wait - (now - previous);
42079           storedContext = this;
42080           storedArgs = args;
42081           if (remaining <= 0 || remaining > wait) {
42082               if (timeout) {
42083                   clearTimeout(timeout);
42084                   timeout = null;
42085               }
42086               previous = now;
42087               result = fn.apply(storedContext, storedArgs);
42088               if (!timeout) {
42089                   storedContext = null;
42090                   storedArgs = [];
42091               }
42092           }
42093           else if (!timeout) {
42094               timeout = window.setTimeout(later, remaining);
42095           }
42096           return result;
42097       };
42098   }
42099   
42100 });
42101
42102  
42103
42104