FIX: compiler
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass(['hidden','d-none']);
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass(['hidden','d-none']);
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fa
593  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594  * @cfg {String} badge text for badge
595  * @cfg {String} theme (default|glow)  
596  * @cfg {Boolean} inverse dark themed version
597  * @cfg {Boolean} toggle is it a slidy toggle button
598  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599  * @cfg {String} ontext text for on slidy toggle state
600  * @cfg {String} offtext text for off slidy toggle state
601  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
602  * @cfg {Boolean} removeClass remove the standard class..
603  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
604  * 
605  * @constructor
606  * Create a new button
607  * @param {Object} config The config object
608  */
609
610
611 Roo.bootstrap.Button = function(config){
612     Roo.bootstrap.Button.superclass.constructor.call(this, config);
613     this.weightClass = ["btn-default btn-outline-secondary", 
614                        "btn-primary", 
615                        "btn-success", 
616                        "btn-info", 
617                        "btn-warning",
618                        "btn-danger",
619                        "btn-link"
620                       ],  
621     this.addEvents({
622         // raw events
623         /**
624          * @event click
625          * When a butotn is pressed
626          * @param {Roo.bootstrap.Button} btn
627          * @param {Roo.EventObject} e
628          */
629         "click" : true,
630          /**
631          * @event toggle
632          * After the button has been toggles
633          * @param {Roo.bootstrap.Button} btn
634          * @param {Roo.EventObject} e
635          * @param {boolean} pressed (also available as button.pressed)
636          */
637         "toggle" : true
638     });
639 };
640
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
642     html: false,
643     active: false,
644     weight: '',
645     badge_weight: '',
646     outline : false,
647     size: '',
648     tag: 'button',
649     href: '',
650     disabled: false,
651     isClose: false,
652     glyphicon: '',
653     fa: '',
654     badge: '',
655     theme: 'default',
656     inverse: false,
657     
658     toggle: false,
659     ontext: 'ON',
660     offtext: 'OFF',
661     defaulton: true,
662     preventDefault: true,
663     removeClass: false,
664     name: false,
665     target: false,
666      
667     pressed : null,
668      
669     
670     getAutoCreate : function(){
671         
672         var cfg = {
673             tag : 'button',
674             cls : 'roo-button',
675             html: ''
676         };
677         
678         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
680             this.tag = 'button';
681         } else {
682             cfg.tag = this.tag;
683         }
684         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
685         
686         if (this.toggle == true) {
687             cfg={
688                 tag: 'div',
689                 cls: 'slider-frame roo-button',
690                 cn: [
691                     {
692                         tag: 'span',
693                         'data-on-text':'ON',
694                         'data-off-text':'OFF',
695                         cls: 'slider-button',
696                         html: this.offtext
697                     }
698                 ]
699             };
700             
701             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702                 cfg.cls += ' '+this.weight;
703             }
704             
705             return cfg;
706         }
707         
708         if (this.isClose) {
709             cfg.cls += ' close';
710             
711             cfg["aria-hidden"] = true;
712             
713             cfg.html = "&times;";
714             
715             return cfg;
716         }
717         
718          
719         if (this.theme==='default') {
720             cfg.cls = 'btn roo-button';
721             
722             //if (this.parentType != 'Navbar') {
723             this.weight = this.weight.length ?  this.weight : 'default';
724             //}
725             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
726                 
727                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729                 cfg.cls += ' btn-' + outline + weight;
730                 if (this.weight == 'default') {
731                     // BC
732                     cfg.cls += ' btn-' + this.weight;
733                 }
734             }
735         } else if (this.theme==='glow') {
736             
737             cfg.tag = 'a';
738             cfg.cls = 'btn-glow roo-button';
739             
740             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
741                 
742                 cfg.cls += ' ' + this.weight;
743             }
744         }
745    
746         
747         if (this.inverse) {
748             this.cls += ' inverse';
749         }
750         
751         
752         if (this.active || this.pressed === true) {
753             cfg.cls += ' active';
754         }
755         
756         if (this.disabled) {
757             cfg.disabled = 'disabled';
758         }
759         
760         if (this.items) {
761             Roo.log('changing to ul' );
762             cfg.tag = 'ul';
763             this.glyphicon = 'caret';
764             if (Roo.bootstrap.version == 4) {
765                 this.fa = 'caret-down';
766             }
767             
768         }
769         
770         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
771          
772         //gsRoo.log(this.parentType);
773         if (this.parentType === 'Navbar' && !this.parent().bar) {
774             Roo.log('changing to li?');
775             
776             cfg.tag = 'li';
777             
778             cfg.cls = '';
779             cfg.cn =  [{
780                 tag : 'a',
781                 cls : 'roo-button',
782                 html : this.html,
783                 href : this.href || '#'
784             }];
785             if (this.menu) {
786                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
787                 cfg.cls += ' dropdown';
788             }   
789             
790             delete cfg.html;
791             
792         }
793         
794        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
795         
796         if (this.glyphicon) {
797             cfg.html = ' ' + cfg.html;
798             
799             cfg.cn = [
800                 {
801                     tag: 'span',
802                     cls: 'glyphicon glyphicon-' + this.glyphicon
803                 }
804             ];
805         }
806         if (this.fa) {
807             cfg.html = ' ' + cfg.html;
808             
809             cfg.cn = [
810                 {
811                     tag: 'i',
812                     cls: 'fa fas fa-' + this.fa
813                 }
814             ];
815         }
816         
817         if (this.badge) {
818             cfg.html += ' ';
819             
820             cfg.tag = 'a';
821             
822 //            cfg.cls='btn roo-button';
823             
824             cfg.href=this.href;
825             
826             var value = cfg.html;
827             
828             if(this.glyphicon){
829                 value = {
830                     tag: 'span',
831                     cls: 'glyphicon glyphicon-' + this.glyphicon,
832                     html: this.html
833                 };
834             }
835             if(this.fa){
836                 value = {
837                     tag: 'i',
838                     cls: 'fa fas fa-' + this.fa,
839                     html: this.html
840                 };
841             }
842             
843             var bw = this.badge_weight.length ? this.badge_weight :
844                 (this.weight.length ? this.weight : 'secondary');
845             bw = bw == 'default' ? 'secondary' : bw;
846             
847             cfg.cn = [
848                 value,
849                 {
850                     tag: 'span',
851                     cls: 'badge badge-' + bw,
852                     html: this.badge
853                 }
854             ];
855             
856             cfg.html='';
857         }
858         
859         if (this.menu) {
860             cfg.cls += ' dropdown';
861             cfg.html = typeof(cfg.html) != 'undefined' ?
862                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
863         }
864         
865         if (cfg.tag !== 'a' && this.href !== '') {
866             throw "Tag must be a to set href.";
867         } else if (this.href.length > 0) {
868             cfg.href = this.href;
869         }
870         
871         if(this.removeClass){
872             cfg.cls = '';
873         }
874         
875         if(this.target){
876             cfg.target = this.target;
877         }
878         
879         return cfg;
880     },
881     initEvents: function() {
882        // Roo.log('init events?');
883 //        Roo.log(this.el.dom);
884         // add the menu...
885         
886         if (typeof (this.menu) != 'undefined') {
887             this.menu.parentType = this.xtype;
888             this.menu.triggerEl = this.el;
889             this.addxtype(Roo.apply({}, this.menu));
890         }
891
892
893        if (this.el.hasClass('roo-button')) {
894             this.el.on('click', this.onClick, this);
895        } else {
896             this.el.select('.roo-button').on('click', this.onClick, this);
897        }
898        
899        if(this.removeClass){
900            this.el.on('click', this.onClick, this);
901        }
902        
903        this.el.enableDisplayMode();
904         
905     },
906     onClick : function(e)
907     {
908         if (this.disabled) {
909             return;
910         }
911         
912         Roo.log('button on click ');
913         if(this.preventDefault){
914             e.preventDefault();
915         }
916         
917         if (this.pressed === true || this.pressed === false) {
918             this.toggleActive(e);
919         }
920         
921         
922         this.fireEvent('click', this, e);
923     },
924     
925     /**
926      * Enables this button
927      */
928     enable : function()
929     {
930         this.disabled = false;
931         this.el.removeClass('disabled');
932     },
933     
934     /**
935      * Disable this button
936      */
937     disable : function()
938     {
939         this.disabled = true;
940         this.el.addClass('disabled');
941     },
942      /**
943      * sets the active state on/off, 
944      * @param {Boolean} state (optional) Force a particular state
945      */
946     setActive : function(v) {
947         
948         this.el[v ? 'addClass' : 'removeClass']('active');
949         this.pressed = v;
950     },
951      /**
952      * toggles the current active state 
953      */
954     toggleActive : function(e)
955     {
956         this.setActive(!this.pressed);
957         this.fireEvent('toggle', this, e, !this.pressed);
958     },
959      /**
960      * get the current active state
961      * @return {boolean} true if it's active
962      */
963     isActive : function()
964     {
965         return this.el.hasClass('active');
966     },
967     /**
968      * set the text of the first selected button
969      */
970     setText : function(str)
971     {
972         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
973     },
974     /**
975      * get the text of the first selected button
976      */
977     getText : function()
978     {
979         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
980     },
981     
982     setWeight : function(str)
983     {
984         this.el.removeClass(this.weightClass);
985         this.weight = str;
986         var outline = this.outline ? 'outline-' : '';
987         if (str == 'default') {
988             this.el.addClass('btn-default btn-outline-secondary');        
989             return;
990         }
991         this.el.addClass('btn-' + outline + str);        
992     }
993     
994     
995 });
996
997  /*
998  * - LGPL
999  *
1000  * column
1001  * 
1002  */
1003
1004 /**
1005  * @class Roo.bootstrap.Column
1006  * @extends Roo.bootstrap.Component
1007  * Bootstrap Column class
1008  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1016  *
1017  * 
1018  * @cfg {Boolean} hidden (true|false) hide the element
1019  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020  * @cfg {String} fa (ban|check|...) font awesome icon
1021  * @cfg {Number} fasize (1|2|....) font awsome size
1022
1023  * @cfg {String} icon (info-sign|check|...) glyphicon name
1024
1025  * @cfg {String} html content of column.
1026  * 
1027  * @constructor
1028  * Create a new Column
1029  * @param {Object} config The config object
1030  */
1031
1032 Roo.bootstrap.Column = function(config){
1033     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1034 };
1035
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1037     
1038     xs: false,
1039     sm: false,
1040     md: false,
1041     lg: false,
1042     xsoff: false,
1043     smoff: false,
1044     mdoff: false,
1045     lgoff: false,
1046     html: '',
1047     offset: 0,
1048     alert: false,
1049     fa: false,
1050     icon : false,
1051     hidden : false,
1052     fasize : 1,
1053     
1054     getAutoCreate : function(){
1055         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1056         
1057         cfg = {
1058             tag: 'div',
1059             cls: 'column'
1060         };
1061         
1062         var settings=this;
1063         ['xs','sm','md','lg'].map(function(size){
1064             //Roo.log( size + ':' + settings[size]);
1065             
1066             if (settings[size+'off'] !== false) {
1067                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1068             }
1069             
1070             if (settings[size] === false) {
1071                 return;
1072             }
1073             
1074             if (!settings[size]) { // 0 = hidden
1075                 cfg.cls += ' hidden-' + size;
1076                 return;
1077             }
1078             cfg.cls += ' col-' + size + '-' + settings[size];
1079             
1080         });
1081         
1082         if (this.hidden) {
1083             cfg.cls += ' hidden';
1084         }
1085         
1086         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1087             cfg.cls +=' alert alert-' + this.alert;
1088         }
1089         
1090         
1091         if (this.html.length) {
1092             cfg.html = this.html;
1093         }
1094         if (this.fa) {
1095             var fasize = '';
1096             if (this.fasize > 1) {
1097                 fasize = ' fa-' + this.fasize + 'x';
1098             }
1099             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1100             
1101             
1102         }
1103         if (this.icon) {
1104             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1105         }
1106         
1107         return cfg;
1108     }
1109    
1110 });
1111
1112  
1113
1114  /*
1115  * - LGPL
1116  *
1117  * page container.
1118  * 
1119  */
1120
1121
1122 /**
1123  * @class Roo.bootstrap.Container
1124  * @extends Roo.bootstrap.Component
1125  * Bootstrap Container class
1126  * @cfg {Boolean} jumbotron is it a jumbotron element
1127  * @cfg {String} html content of element
1128  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1129  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1130  * @cfg {String} header content of header (for panel)
1131  * @cfg {String} footer content of footer (for panel)
1132  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1133  * @cfg {String} tag (header|aside|section) type of HTML tag.
1134  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1135  * @cfg {String} fa font awesome icon
1136  * @cfg {String} icon (info-sign|check|...) glyphicon name
1137  * @cfg {Boolean} hidden (true|false) hide the element
1138  * @cfg {Boolean} expandable (true|false) default false
1139  * @cfg {Boolean} expanded (true|false) default true
1140  * @cfg {String} rheader contet on the right of header
1141  * @cfg {Boolean} clickable (true|false) default false
1142
1143  *     
1144  * @constructor
1145  * Create a new Container
1146  * @param {Object} config The config object
1147  */
1148
1149 Roo.bootstrap.Container = function(config){
1150     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1151     
1152     this.addEvents({
1153         // raw events
1154          /**
1155          * @event expand
1156          * After the panel has been expand
1157          * 
1158          * @param {Roo.bootstrap.Container} this
1159          */
1160         "expand" : true,
1161         /**
1162          * @event collapse
1163          * After the panel has been collapsed
1164          * 
1165          * @param {Roo.bootstrap.Container} this
1166          */
1167         "collapse" : true,
1168         /**
1169          * @event click
1170          * When a element is chick
1171          * @param {Roo.bootstrap.Container} this
1172          * @param {Roo.EventObject} e
1173          */
1174         "click" : true
1175     });
1176 };
1177
1178 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1179     
1180     jumbotron : false,
1181     well: '',
1182     panel : '',
1183     header: '',
1184     footer : '',
1185     sticky: '',
1186     tag : false,
1187     alert : false,
1188     fa: false,
1189     icon : false,
1190     expandable : false,
1191     rheader : '',
1192     expanded : true,
1193     clickable: false,
1194   
1195      
1196     getChildContainer : function() {
1197         
1198         if(!this.el){
1199             return false;
1200         }
1201         
1202         if (this.panel.length) {
1203             return this.el.select('.panel-body',true).first();
1204         }
1205         
1206         return this.el;
1207     },
1208     
1209     
1210     getAutoCreate : function(){
1211         
1212         var cfg = {
1213             tag : this.tag || 'div',
1214             html : '',
1215             cls : ''
1216         };
1217         if (this.jumbotron) {
1218             cfg.cls = 'jumbotron';
1219         }
1220         
1221         
1222         
1223         // - this is applied by the parent..
1224         //if (this.cls) {
1225         //    cfg.cls = this.cls + '';
1226         //}
1227         
1228         if (this.sticky.length) {
1229             
1230             var bd = Roo.get(document.body);
1231             if (!bd.hasClass('bootstrap-sticky')) {
1232                 bd.addClass('bootstrap-sticky');
1233                 Roo.select('html',true).setStyle('height', '100%');
1234             }
1235              
1236             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1237         }
1238         
1239         
1240         if (this.well.length) {
1241             switch (this.well) {
1242                 case 'lg':
1243                 case 'sm':
1244                     cfg.cls +=' well well-' +this.well;
1245                     break;
1246                 default:
1247                     cfg.cls +=' well';
1248                     break;
1249             }
1250         }
1251         
1252         if (this.hidden) {
1253             cfg.cls += ' hidden';
1254         }
1255         
1256         
1257         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1258             cfg.cls +=' alert alert-' + this.alert;
1259         }
1260         
1261         var body = cfg;
1262         
1263         if (this.panel.length) {
1264             cfg.cls += ' panel panel-' + this.panel;
1265             cfg.cn = [];
1266             if (this.header.length) {
1267                 
1268                 var h = [];
1269                 
1270                 if(this.expandable){
1271                     
1272                     cfg.cls = cfg.cls + ' expandable';
1273                     
1274                     h.push({
1275                         tag: 'i',
1276                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1277                     });
1278                     
1279                 }
1280                 
1281                 h.push(
1282                     {
1283                         tag: 'span',
1284                         cls : 'panel-title',
1285                         html : (this.expandable ? '&nbsp;' : '') + this.header
1286                     },
1287                     {
1288                         tag: 'span',
1289                         cls: 'panel-header-right',
1290                         html: this.rheader
1291                     }
1292                 );
1293                 
1294                 cfg.cn.push({
1295                     cls : 'panel-heading',
1296                     style : this.expandable ? 'cursor: pointer' : '',
1297                     cn : h
1298                 });
1299                 
1300             }
1301             
1302             body = false;
1303             cfg.cn.push({
1304                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1305                 html : this.html
1306             });
1307             
1308             
1309             if (this.footer.length) {
1310                 cfg.cn.push({
1311                     cls : 'panel-footer',
1312                     html : this.footer
1313                     
1314                 });
1315             }
1316             
1317         }
1318         
1319         if (body) {
1320             body.html = this.html || cfg.html;
1321             // prefix with the icons..
1322             if (this.fa) {
1323                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1324             }
1325             if (this.icon) {
1326                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1327             }
1328             
1329             
1330         }
1331         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1332             cfg.cls =  'container';
1333         }
1334         
1335         return cfg;
1336     },
1337     
1338     initEvents: function() 
1339     {
1340         if(this.expandable){
1341             var headerEl = this.headerEl();
1342         
1343             if(headerEl){
1344                 headerEl.on('click', this.onToggleClick, this);
1345             }
1346         }
1347         
1348         if(this.clickable){
1349             this.el.on('click', this.onClick, this);
1350         }
1351         
1352     },
1353     
1354     onToggleClick : function()
1355     {
1356         var headerEl = this.headerEl();
1357         
1358         if(!headerEl){
1359             return;
1360         }
1361         
1362         if(this.expanded){
1363             this.collapse();
1364             return;
1365         }
1366         
1367         this.expand();
1368     },
1369     
1370     expand : function()
1371     {
1372         if(this.fireEvent('expand', this)) {
1373             
1374             this.expanded = true;
1375             
1376             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1377             
1378             this.el.select('.panel-body',true).first().removeClass('hide');
1379             
1380             var toggleEl = this.toggleEl();
1381
1382             if(!toggleEl){
1383                 return;
1384             }
1385
1386             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1387         }
1388         
1389     },
1390     
1391     collapse : function()
1392     {
1393         if(this.fireEvent('collapse', this)) {
1394             
1395             this.expanded = false;
1396             
1397             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1398             this.el.select('.panel-body',true).first().addClass('hide');
1399         
1400             var toggleEl = this.toggleEl();
1401
1402             if(!toggleEl){
1403                 return;
1404             }
1405
1406             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1407         }
1408     },
1409     
1410     toggleEl : function()
1411     {
1412         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1413             return;
1414         }
1415         
1416         return this.el.select('.panel-heading .fa',true).first();
1417     },
1418     
1419     headerEl : function()
1420     {
1421         if(!this.el || !this.panel.length || !this.header.length){
1422             return;
1423         }
1424         
1425         return this.el.select('.panel-heading',true).first()
1426     },
1427     
1428     bodyEl : function()
1429     {
1430         if(!this.el || !this.panel.length){
1431             return;
1432         }
1433         
1434         return this.el.select('.panel-body',true).first()
1435     },
1436     
1437     titleEl : function()
1438     {
1439         if(!this.el || !this.panel.length || !this.header.length){
1440             return;
1441         }
1442         
1443         return this.el.select('.panel-title',true).first();
1444     },
1445     
1446     setTitle : function(v)
1447     {
1448         var titleEl = this.titleEl();
1449         
1450         if(!titleEl){
1451             return;
1452         }
1453         
1454         titleEl.dom.innerHTML = v;
1455     },
1456     
1457     getTitle : function()
1458     {
1459         
1460         var titleEl = this.titleEl();
1461         
1462         if(!titleEl){
1463             return '';
1464         }
1465         
1466         return titleEl.dom.innerHTML;
1467     },
1468     
1469     setRightTitle : function(v)
1470     {
1471         var t = this.el.select('.panel-header-right',true).first();
1472         
1473         if(!t){
1474             return;
1475         }
1476         
1477         t.dom.innerHTML = v;
1478     },
1479     
1480     onClick : function(e)
1481     {
1482         e.preventDefault();
1483         
1484         this.fireEvent('click', this, e);
1485     }
1486 });
1487
1488  /*
1489  * - LGPL
1490  *
1491  * image
1492  * 
1493  */
1494
1495
1496 /**
1497  * @class Roo.bootstrap.Img
1498  * @extends Roo.bootstrap.Component
1499  * Bootstrap Img class
1500  * @cfg {Boolean} imgResponsive false | true
1501  * @cfg {String} border rounded | circle | thumbnail
1502  * @cfg {String} src image source
1503  * @cfg {String} alt image alternative text
1504  * @cfg {String} href a tag href
1505  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1506  * @cfg {String} xsUrl xs image source
1507  * @cfg {String} smUrl sm image source
1508  * @cfg {String} mdUrl md image source
1509  * @cfg {String} lgUrl lg image source
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Img = function(config){
1517     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1531     
1532     imgResponsive: true,
1533     border: '',
1534     src: 'about:blank',
1535     href: false,
1536     target: false,
1537     xsUrl: '',
1538     smUrl: '',
1539     mdUrl: '',
1540     lgUrl: '',
1541
1542     getAutoCreate : function()
1543     {   
1544         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1545             return this.createSingleImg();
1546         }
1547         
1548         var cfg = {
1549             tag: 'div',
1550             cls: 'roo-image-responsive-group',
1551             cn: []
1552         };
1553         var _this = this;
1554         
1555         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1556             
1557             if(!_this[size + 'Url']){
1558                 return;
1559             }
1560             
1561             var img = {
1562                 tag: 'img',
1563                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1564                 html: _this.html || cfg.html,
1565                 src: _this[size + 'Url']
1566             };
1567             
1568             img.cls += ' roo-image-responsive-' + size;
1569             
1570             var s = ['xs', 'sm', 'md', 'lg'];
1571             
1572             s.splice(s.indexOf(size), 1);
1573             
1574             Roo.each(s, function(ss){
1575                 img.cls += ' hidden-' + ss;
1576             });
1577             
1578             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1579                 cfg.cls += ' img-' + _this.border;
1580             }
1581             
1582             if(_this.alt){
1583                 cfg.alt = _this.alt;
1584             }
1585             
1586             if(_this.href){
1587                 var a = {
1588                     tag: 'a',
1589                     href: _this.href,
1590                     cn: [
1591                         img
1592                     ]
1593                 };
1594
1595                 if(this.target){
1596                     a.target = _this.target;
1597                 }
1598             }
1599             
1600             cfg.cn.push((_this.href) ? a : img);
1601             
1602         });
1603         
1604         return cfg;
1605     },
1606     
1607     createSingleImg : function()
1608     {
1609         var cfg = {
1610             tag: 'img',
1611             cls: (this.imgResponsive) ? 'img-responsive' : '',
1612             html : null,
1613             src : 'about:blank'  // just incase src get's set to undefined?!?
1614         };
1615         
1616         cfg.html = this.html || cfg.html;
1617         
1618         cfg.src = this.src || cfg.src;
1619         
1620         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1621             cfg.cls += ' img-' + this.border;
1622         }
1623         
1624         if(this.alt){
1625             cfg.alt = this.alt;
1626         }
1627         
1628         if(this.href){
1629             var a = {
1630                 tag: 'a',
1631                 href: this.href,
1632                 cn: [
1633                     cfg
1634                 ]
1635             };
1636             
1637             if(this.target){
1638                 a.target = this.target;
1639             }
1640             
1641         }
1642         
1643         return (this.href) ? a : cfg;
1644     },
1645     
1646     initEvents: function() 
1647     {
1648         if(!this.href){
1649             this.el.on('click', this.onClick, this);
1650         }
1651         
1652     },
1653     
1654     onClick : function(e)
1655     {
1656         Roo.log('img onclick');
1657         this.fireEvent('click', this, e);
1658     },
1659     /**
1660      * Sets the url of the image - used to update it
1661      * @param {String} url the url of the image
1662      */
1663     
1664     setSrc : function(url)
1665     {
1666         this.src =  url;
1667         
1668         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1669             this.el.dom.src =  url;
1670             return;
1671         }
1672         
1673         this.el.select('img', true).first().dom.src =  url;
1674     }
1675     
1676     
1677    
1678 });
1679
1680  /*
1681  * - LGPL
1682  *
1683  * image
1684  * 
1685  */
1686
1687
1688 /**
1689  * @class Roo.bootstrap.Link
1690  * @extends Roo.bootstrap.Component
1691  * Bootstrap Link Class
1692  * @cfg {String} alt image alternative text
1693  * @cfg {String} href a tag href
1694  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1695  * @cfg {String} html the content of the link.
1696  * @cfg {String} anchor name for the anchor link
1697  * @cfg {String} fa - favicon
1698
1699  * @cfg {Boolean} preventDefault (true | false) default false
1700
1701  * 
1702  * @constructor
1703  * Create a new Input
1704  * @param {Object} config The config object
1705  */
1706
1707 Roo.bootstrap.Link = function(config){
1708     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1709     
1710     this.addEvents({
1711         // img events
1712         /**
1713          * @event click
1714          * The img click event for the img.
1715          * @param {Roo.EventObject} e
1716          */
1717         "click" : true
1718     });
1719 };
1720
1721 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1722     
1723     href: false,
1724     target: false,
1725     preventDefault: false,
1726     anchor : false,
1727     alt : false,
1728     fa: false,
1729
1730
1731     getAutoCreate : function()
1732     {
1733         var html = this.html || '';
1734         
1735         if (this.fa !== false) {
1736             html = '<i class="fa fa-' + this.fa + '"></i>';
1737         }
1738         var cfg = {
1739             tag: 'a'
1740         };
1741         // anchor's do not require html/href...
1742         if (this.anchor === false) {
1743             cfg.html = html;
1744             cfg.href = this.href || '#';
1745         } else {
1746             cfg.name = this.anchor;
1747             if (this.html !== false || this.fa !== false) {
1748                 cfg.html = html;
1749             }
1750             if (this.href !== false) {
1751                 cfg.href = this.href;
1752             }
1753         }
1754         
1755         if(this.alt !== false){
1756             cfg.alt = this.alt;
1757         }
1758         
1759         
1760         if(this.target !== false) {
1761             cfg.target = this.target;
1762         }
1763         
1764         return cfg;
1765     },
1766     
1767     initEvents: function() {
1768         
1769         if(!this.href || this.preventDefault){
1770             this.el.on('click', this.onClick, this);
1771         }
1772     },
1773     
1774     onClick : function(e)
1775     {
1776         if(this.preventDefault){
1777             e.preventDefault();
1778         }
1779         //Roo.log('img onclick');
1780         this.fireEvent('click', this, e);
1781     }
1782    
1783 });
1784
1785  /*
1786  * - LGPL
1787  *
1788  * header
1789  * 
1790  */
1791
1792 /**
1793  * @class Roo.bootstrap.Header
1794  * @extends Roo.bootstrap.Component
1795  * Bootstrap Header class
1796  * @cfg {String} html content of header
1797  * @cfg {Number} level (1|2|3|4|5|6) default 1
1798  * 
1799  * @constructor
1800  * Create a new Header
1801  * @param {Object} config The config object
1802  */
1803
1804
1805 Roo.bootstrap.Header  = function(config){
1806     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1807 };
1808
1809 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1810     
1811     //href : false,
1812     html : false,
1813     level : 1,
1814     
1815     
1816     
1817     getAutoCreate : function(){
1818         
1819         
1820         
1821         var cfg = {
1822             tag: 'h' + (1 *this.level),
1823             html: this.html || ''
1824         } ;
1825         
1826         return cfg;
1827     }
1828    
1829 });
1830
1831  
1832
1833  /*
1834  * Based on:
1835  * Ext JS Library 1.1.1
1836  * Copyright(c) 2006-2007, Ext JS, LLC.
1837  *
1838  * Originally Released Under LGPL - original licence link has changed is not relivant.
1839  *
1840  * Fork - LGPL
1841  * <script type="text/javascript">
1842  */
1843  
1844 /**
1845  * @class Roo.bootstrap.MenuMgr
1846  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1847  * @singleton
1848  */
1849 Roo.bootstrap.MenuMgr = function(){
1850    var menus, active, groups = {}, attached = false, lastShow = new Date();
1851
1852    // private - called when first menu is created
1853    function init(){
1854        menus = {};
1855        active = new Roo.util.MixedCollection();
1856        Roo.get(document).addKeyListener(27, function(){
1857            if(active.length > 0){
1858                hideAll();
1859            }
1860        });
1861    }
1862
1863    // private
1864    function hideAll(){
1865        if(active && active.length > 0){
1866            var c = active.clone();
1867            c.each(function(m){
1868                m.hide();
1869            });
1870        }
1871    }
1872
1873    // private
1874    function onHide(m){
1875        active.remove(m);
1876        if(active.length < 1){
1877            Roo.get(document).un("mouseup", onMouseDown);
1878             
1879            attached = false;
1880        }
1881    }
1882
1883    // private
1884    function onShow(m){
1885        var last = active.last();
1886        lastShow = new Date();
1887        active.add(m);
1888        if(!attached){
1889           Roo.get(document).on("mouseup", onMouseDown);
1890            
1891            attached = true;
1892        }
1893        if(m.parentMenu){
1894           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1895           m.parentMenu.activeChild = m;
1896        }else if(last && last.isVisible()){
1897           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1898        }
1899    }
1900
1901    // private
1902    function onBeforeHide(m){
1903        if(m.activeChild){
1904            m.activeChild.hide();
1905        }
1906        if(m.autoHideTimer){
1907            clearTimeout(m.autoHideTimer);
1908            delete m.autoHideTimer;
1909        }
1910    }
1911
1912    // private
1913    function onBeforeShow(m){
1914        var pm = m.parentMenu;
1915        if(!pm && !m.allowOtherMenus){
1916            hideAll();
1917        }else if(pm && pm.activeChild && active != m){
1918            pm.activeChild.hide();
1919        }
1920    }
1921
1922    // private this should really trigger on mouseup..
1923    function onMouseDown(e){
1924         Roo.log("on Mouse Up");
1925         
1926         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1927             Roo.log("MenuManager hideAll");
1928             hideAll();
1929             e.stopEvent();
1930         }
1931         
1932         
1933    }
1934
1935    // private
1936    function onBeforeCheck(mi, state){
1937        if(state){
1938            var g = groups[mi.group];
1939            for(var i = 0, l = g.length; i < l; i++){
1940                if(g[i] != mi){
1941                    g[i].setChecked(false);
1942                }
1943            }
1944        }
1945    }
1946
1947    return {
1948
1949        /**
1950         * Hides all menus that are currently visible
1951         */
1952        hideAll : function(){
1953             hideAll();  
1954        },
1955
1956        // private
1957        register : function(menu){
1958            if(!menus){
1959                init();
1960            }
1961            menus[menu.id] = menu;
1962            menu.on("beforehide", onBeforeHide);
1963            menu.on("hide", onHide);
1964            menu.on("beforeshow", onBeforeShow);
1965            menu.on("show", onShow);
1966            var g = menu.group;
1967            if(g && menu.events["checkchange"]){
1968                if(!groups[g]){
1969                    groups[g] = [];
1970                }
1971                groups[g].push(menu);
1972                menu.on("checkchange", onCheck);
1973            }
1974        },
1975
1976         /**
1977          * Returns a {@link Roo.menu.Menu} object
1978          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1979          * be used to generate and return a new Menu instance.
1980          */
1981        get : function(menu){
1982            if(typeof menu == "string"){ // menu id
1983                return menus[menu];
1984            }else if(menu.events){  // menu instance
1985                return menu;
1986            }
1987            /*else if(typeof menu.length == 'number'){ // array of menu items?
1988                return new Roo.bootstrap.Menu({items:menu});
1989            }else{ // otherwise, must be a config
1990                return new Roo.bootstrap.Menu(menu);
1991            }
1992            */
1993            return false;
1994        },
1995
1996        // private
1997        unregister : function(menu){
1998            delete menus[menu.id];
1999            menu.un("beforehide", onBeforeHide);
2000            menu.un("hide", onHide);
2001            menu.un("beforeshow", onBeforeShow);
2002            menu.un("show", onShow);
2003            var g = menu.group;
2004            if(g && menu.events["checkchange"]){
2005                groups[g].remove(menu);
2006                menu.un("checkchange", onCheck);
2007            }
2008        },
2009
2010        // private
2011        registerCheckable : function(menuItem){
2012            var g = menuItem.group;
2013            if(g){
2014                if(!groups[g]){
2015                    groups[g] = [];
2016                }
2017                groups[g].push(menuItem);
2018                menuItem.on("beforecheckchange", onBeforeCheck);
2019            }
2020        },
2021
2022        // private
2023        unregisterCheckable : function(menuItem){
2024            var g = menuItem.group;
2025            if(g){
2026                groups[g].remove(menuItem);
2027                menuItem.un("beforecheckchange", onBeforeCheck);
2028            }
2029        }
2030    };
2031 }();/*
2032  * - LGPL
2033  *
2034  * menu
2035  * 
2036  */
2037
2038 /**
2039  * @class Roo.bootstrap.Menu
2040  * @extends Roo.bootstrap.Component
2041  * Bootstrap Menu class - container for MenuItems
2042  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2043  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2044  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2045  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2046  * 
2047  * @constructor
2048  * Create a new Menu
2049  * @param {Object} config The config object
2050  */
2051
2052
2053 Roo.bootstrap.Menu = function(config){
2054     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2055     if (this.registerMenu && this.type != 'treeview')  {
2056         Roo.bootstrap.MenuMgr.register(this);
2057     }
2058     
2059     
2060     this.addEvents({
2061         /**
2062          * @event beforeshow
2063          * Fires before this menu is displayed
2064          * @param {Roo.menu.Menu} this
2065          */
2066         beforeshow : true,
2067         /**
2068          * @event beforehide
2069          * Fires before this menu is hidden
2070          * @param {Roo.menu.Menu} this
2071          */
2072         beforehide : true,
2073         /**
2074          * @event show
2075          * Fires after this menu is displayed
2076          * @param {Roo.menu.Menu} this
2077          */
2078         show : true,
2079         /**
2080          * @event hide
2081          * Fires after this menu is hidden
2082          * @param {Roo.menu.Menu} this
2083          */
2084         hide : true,
2085         /**
2086          * @event click
2087          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2088          * @param {Roo.menu.Menu} this
2089          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2090          * @param {Roo.EventObject} e
2091          */
2092         click : true,
2093         /**
2094          * @event mouseover
2095          * Fires when the mouse is hovering over this menu
2096          * @param {Roo.menu.Menu} this
2097          * @param {Roo.EventObject} e
2098          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2099          */
2100         mouseover : true,
2101         /**
2102          * @event mouseout
2103          * Fires when the mouse exits this menu
2104          * @param {Roo.menu.Menu} this
2105          * @param {Roo.EventObject} e
2106          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2107          */
2108         mouseout : true,
2109         /**
2110          * @event itemclick
2111          * Fires when a menu item contained in this menu is clicked
2112          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2113          * @param {Roo.EventObject} e
2114          */
2115         itemclick: true
2116     });
2117     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2118 };
2119
2120 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2121     
2122    /// html : false,
2123     //align : '',
2124     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2125     type: false,
2126     /**
2127      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2128      */
2129     registerMenu : true,
2130     
2131     menuItems :false, // stores the menu items..
2132     
2133     hidden:true,
2134         
2135     parentMenu : false,
2136     
2137     stopEvent : true,
2138     
2139     isLink : false,
2140     
2141     getChildContainer : function() {
2142         return this.el;  
2143     },
2144     
2145     getAutoCreate : function(){
2146          
2147         //if (['right'].indexOf(this.align)!==-1) {
2148         //    cfg.cn[1].cls += ' pull-right'
2149         //}
2150         
2151         
2152         var cfg = {
2153             tag : 'ul',
2154             cls : 'dropdown-menu' ,
2155             style : 'z-index:1000'
2156             
2157         };
2158         
2159         if (this.type === 'submenu') {
2160             cfg.cls = 'submenu active';
2161         }
2162         if (this.type === 'treeview') {
2163             cfg.cls = 'treeview-menu';
2164         }
2165         
2166         return cfg;
2167     },
2168     initEvents : function() {
2169         
2170        // Roo.log("ADD event");
2171        // Roo.log(this.triggerEl.dom);
2172         
2173         this.triggerEl.on('click', this.onTriggerClick, this);
2174         
2175         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2176         
2177         
2178         if (this.triggerEl.hasClass('nav-item')) {
2179             // dropdown toggle on the 'a' in BS4?
2180             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2181         } else {
2182             this.triggerEl.addClass('dropdown-toggle');
2183         }
2184         if (Roo.isTouch) {
2185             this.el.on('touchstart'  , this.onTouch, this);
2186         }
2187         this.el.on('click' , this.onClick, this);
2188
2189         this.el.on("mouseover", this.onMouseOver, this);
2190         this.el.on("mouseout", this.onMouseOut, this);
2191         
2192     },
2193     
2194     findTargetItem : function(e)
2195     {
2196         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2197         if(!t){
2198             return false;
2199         }
2200         //Roo.log(t);         Roo.log(t.id);
2201         if(t && t.id){
2202             //Roo.log(this.menuitems);
2203             return this.menuitems.get(t.id);
2204             
2205             //return this.items.get(t.menuItemId);
2206         }
2207         
2208         return false;
2209     },
2210     
2211     onTouch : function(e) 
2212     {
2213         Roo.log("menu.onTouch");
2214         //e.stopEvent(); this make the user popdown broken
2215         this.onClick(e);
2216     },
2217     
2218     onClick : function(e)
2219     {
2220         Roo.log("menu.onClick");
2221         
2222         var t = this.findTargetItem(e);
2223         if(!t || t.isContainer){
2224             return;
2225         }
2226         Roo.log(e);
2227         /*
2228         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2229             if(t == this.activeItem && t.shouldDeactivate(e)){
2230                 this.activeItem.deactivate();
2231                 delete this.activeItem;
2232                 return;
2233             }
2234             if(t.canActivate){
2235                 this.setActiveItem(t, true);
2236             }
2237             return;
2238             
2239             
2240         }
2241         */
2242        
2243         Roo.log('pass click event');
2244         
2245         t.onClick(e);
2246         
2247         this.fireEvent("click", this, t, e);
2248         
2249         var _this = this;
2250         
2251         if(!t.href.length || t.href == '#'){
2252             (function() { _this.hide(); }).defer(100);
2253         }
2254         
2255     },
2256     
2257     onMouseOver : function(e){
2258         var t  = this.findTargetItem(e);
2259         //Roo.log(t);
2260         //if(t){
2261         //    if(t.canActivate && !t.disabled){
2262         //        this.setActiveItem(t, true);
2263         //    }
2264         //}
2265         
2266         this.fireEvent("mouseover", this, e, t);
2267     },
2268     isVisible : function(){
2269         return !this.hidden;
2270     },
2271      onMouseOut : function(e){
2272         var t  = this.findTargetItem(e);
2273         
2274         //if(t ){
2275         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2276         //        this.activeItem.deactivate();
2277         //        delete this.activeItem;
2278         //    }
2279         //}
2280         this.fireEvent("mouseout", this, e, t);
2281     },
2282     
2283     
2284     /**
2285      * Displays this menu relative to another element
2286      * @param {String/HTMLElement/Roo.Element} element The element to align to
2287      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2288      * the element (defaults to this.defaultAlign)
2289      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2290      */
2291     show : function(el, pos, parentMenu){
2292         this.parentMenu = parentMenu;
2293         if(!this.el){
2294             this.render();
2295         }
2296         this.fireEvent("beforeshow", this);
2297         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2298     },
2299      /**
2300      * Displays this menu at a specific xy position
2301      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2302      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2303      */
2304     showAt : function(xy, parentMenu, /* private: */_e){
2305         this.parentMenu = parentMenu;
2306         if(!this.el){
2307             this.render();
2308         }
2309         if(_e !== false){
2310             this.fireEvent("beforeshow", this);
2311             //xy = this.el.adjustForConstraints(xy);
2312         }
2313         
2314         //this.el.show();
2315         this.hideMenuItems();
2316         this.hidden = false;
2317         this.triggerEl.addClass('open');
2318         this.el.addClass('show');
2319         
2320         // reassign x when hitting right
2321         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2322             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2323         }
2324         
2325         // reassign y when hitting bottom
2326         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2327             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2328         }
2329         
2330         // but the list may align on trigger left or trigger top... should it be a properity?
2331         
2332         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2333             this.el.setXY(xy);
2334         }
2335         
2336         this.focus();
2337         this.fireEvent("show", this);
2338     },
2339     
2340     focus : function(){
2341         return;
2342         if(!this.hidden){
2343             this.doFocus.defer(50, this);
2344         }
2345     },
2346
2347     doFocus : function(){
2348         if(!this.hidden){
2349             this.focusEl.focus();
2350         }
2351     },
2352
2353     /**
2354      * Hides this menu and optionally all parent menus
2355      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2356      */
2357     hide : function(deep)
2358     {
2359         
2360         this.hideMenuItems();
2361         if(this.el && this.isVisible()){
2362             this.fireEvent("beforehide", this);
2363             if(this.activeItem){
2364                 this.activeItem.deactivate();
2365                 this.activeItem = null;
2366             }
2367             this.triggerEl.removeClass('open');;
2368             this.el.removeClass('show');
2369             this.hidden = true;
2370             this.fireEvent("hide", this);
2371         }
2372         if(deep === true && this.parentMenu){
2373             this.parentMenu.hide(true);
2374         }
2375     },
2376     
2377     onTriggerClick : function(e)
2378     {
2379         Roo.log('trigger click');
2380         
2381         var target = e.getTarget();
2382         
2383         Roo.log(target.nodeName.toLowerCase());
2384         
2385         if(target.nodeName.toLowerCase() === 'i'){
2386             e.preventDefault();
2387         }
2388         
2389     },
2390     
2391     onTriggerPress  : function(e)
2392     {
2393         Roo.log('trigger press');
2394         //Roo.log(e.getTarget());
2395        // Roo.log(this.triggerEl.dom);
2396        
2397         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2398         var pel = Roo.get(e.getTarget());
2399         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2400             Roo.log('is treeview or dropdown?');
2401             return;
2402         }
2403         
2404         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2405             return;
2406         }
2407         
2408         if (this.isVisible()) {
2409             Roo.log('hide');
2410             this.hide();
2411         } else {
2412             Roo.log('show');
2413             this.show(this.triggerEl, '?', false);
2414         }
2415         
2416         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2417             e.stopEvent();
2418         }
2419         
2420     },
2421        
2422     
2423     hideMenuItems : function()
2424     {
2425         Roo.log("hide Menu Items");
2426         if (!this.el) { 
2427             return;
2428         }
2429         //$(backdrop).remove()
2430         this.el.select('.open',true).each(function(aa) {
2431             
2432             aa.removeClass('open');
2433           //var parent = getParent($(this))
2434           //var relatedTarget = { relatedTarget: this }
2435           
2436            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2437           //if (e.isDefaultPrevented()) return
2438            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2439         });
2440     },
2441     addxtypeChild : function (tree, cntr) {
2442         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2443           
2444         this.menuitems.add(comp);
2445         return comp;
2446
2447     },
2448     getEl : function()
2449     {
2450         Roo.log(this.el);
2451         return this.el;
2452     },
2453     
2454     clear : function()
2455     {
2456         this.getEl().dom.innerHTML = '';
2457         this.menuitems.clear();
2458     }
2459 });
2460
2461  
2462  /*
2463  * - LGPL
2464  *
2465  * menu item
2466  * 
2467  */
2468
2469
2470 /**
2471  * @class Roo.bootstrap.MenuItem
2472  * @extends Roo.bootstrap.Component
2473  * Bootstrap MenuItem class
2474  * @cfg {String} html the menu label
2475  * @cfg {String} href the link
2476  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2477  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2478  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2479  * @cfg {String} fa favicon to show on left of menu item.
2480  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2481  * 
2482  * 
2483  * @constructor
2484  * Create a new MenuItem
2485  * @param {Object} config The config object
2486  */
2487
2488
2489 Roo.bootstrap.MenuItem = function(config){
2490     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2491     this.addEvents({
2492         // raw events
2493         /**
2494          * @event click
2495          * The raw click event for the entire grid.
2496          * @param {Roo.bootstrap.MenuItem} this
2497          * @param {Roo.EventObject} e
2498          */
2499         "click" : true
2500     });
2501 };
2502
2503 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2504     
2505     href : false,
2506     html : false,
2507     preventDefault: false,
2508     isContainer : false,
2509     active : false,
2510     fa: false,
2511     
2512     getAutoCreate : function(){
2513         
2514         if(this.isContainer){
2515             return {
2516                 tag: 'li',
2517                 cls: 'dropdown-menu-item '
2518             };
2519         }
2520         var ctag = {
2521             tag: 'span',
2522             html: 'Link'
2523         };
2524         
2525         var anc = {
2526             tag : 'a',
2527             cls : 'dropdown-item',
2528             href : '#',
2529             cn : [  ]
2530         };
2531         
2532         if (this.fa !== false) {
2533             anc.cn.push({
2534                 tag : 'i',
2535                 cls : 'fa fa-' + this.fa
2536             });
2537         }
2538         
2539         anc.cn.push(ctag);
2540         
2541         
2542         var cfg= {
2543             tag: 'li',
2544             cls: 'dropdown-menu-item',
2545             cn: [ anc ]
2546         };
2547         if (this.parent().type == 'treeview') {
2548             cfg.cls = 'treeview-menu';
2549         }
2550         if (this.active) {
2551             cfg.cls += ' active';
2552         }
2553         
2554         
2555         
2556         anc.href = this.href || cfg.cn[0].href ;
2557         ctag.html = this.html || cfg.cn[0].html ;
2558         return cfg;
2559     },
2560     
2561     initEvents: function()
2562     {
2563         if (this.parent().type == 'treeview') {
2564             this.el.select('a').on('click', this.onClick, this);
2565         }
2566         
2567         if (this.menu) {
2568             this.menu.parentType = this.xtype;
2569             this.menu.triggerEl = this.el;
2570             this.menu = this.addxtype(Roo.apply({}, this.menu));
2571         }
2572         
2573     },
2574     onClick : function(e)
2575     {
2576         Roo.log('item on click ');
2577         
2578         if(this.preventDefault){
2579             e.preventDefault();
2580         }
2581         //this.parent().hideMenuItems();
2582         
2583         this.fireEvent('click', this, e);
2584     },
2585     getEl : function()
2586     {
2587         return this.el;
2588     } 
2589 });
2590
2591  
2592
2593  /*
2594  * - LGPL
2595  *
2596  * menu separator
2597  * 
2598  */
2599
2600
2601 /**
2602  * @class Roo.bootstrap.MenuSeparator
2603  * @extends Roo.bootstrap.Component
2604  * Bootstrap MenuSeparator class
2605  * 
2606  * @constructor
2607  * Create a new MenuItem
2608  * @param {Object} config The config object
2609  */
2610
2611
2612 Roo.bootstrap.MenuSeparator = function(config){
2613     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2614 };
2615
2616 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2617     
2618     getAutoCreate : function(){
2619         var cfg = {
2620             cls: 'divider',
2621             tag : 'li'
2622         };
2623         
2624         return cfg;
2625     }
2626    
2627 });
2628
2629  
2630
2631  
2632 /*
2633 * Licence: LGPL
2634 */
2635
2636 /**
2637  * @class Roo.bootstrap.Modal
2638  * @extends Roo.bootstrap.Component
2639  * Bootstrap Modal class
2640  * @cfg {String} title Title of dialog
2641  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2642  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2643  * @cfg {Boolean} specificTitle default false
2644  * @cfg {Array} buttons Array of buttons or standard button set..
2645  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2646  * @cfg {Boolean} animate default true
2647  * @cfg {Boolean} allow_close default true
2648  * @cfg {Boolean} fitwindow default false
2649  * @cfg {String} size (sm|lg) default empty
2650  * @cfg {Number} max_width set the max width of modal
2651  *
2652  *
2653  * @constructor
2654  * Create a new Modal Dialog
2655  * @param {Object} config The config object
2656  */
2657
2658 Roo.bootstrap.Modal = function(config){
2659     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2660     this.addEvents({
2661         // raw events
2662         /**
2663          * @event btnclick
2664          * The raw btnclick event for the button
2665          * @param {Roo.EventObject} e
2666          */
2667         "btnclick" : true,
2668         /**
2669          * @event resize
2670          * Fire when dialog resize
2671          * @param {Roo.bootstrap.Modal} this
2672          * @param {Roo.EventObject} e
2673          */
2674         "resize" : true,
2675          /**
2676          * @event close
2677          * Fire when the top 'x' close button is pressed.
2678          * @param {Roo.bootstrap.Modal} this
2679          * @param {Roo.EventObject} e
2680          */
2681         "close" : true
2682     });
2683     this.buttons = this.buttons || [];
2684
2685     if (this.tmpl) {
2686         this.tmpl = Roo.factory(this.tmpl);
2687     }
2688
2689 };
2690
2691 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2692
2693     title : 'test dialog',
2694
2695     buttons : false,
2696
2697     // set on load...
2698
2699     html: false,
2700
2701     tmp: false,
2702
2703     specificTitle: false,
2704
2705     buttonPosition: 'right',
2706
2707     allow_close : true,
2708
2709     animate : true,
2710
2711     fitwindow: false,
2712     
2713      // private
2714     dialogEl: false,
2715     bodyEl:  false,
2716     footerEl:  false,
2717     titleEl:  false,
2718     closeEl:  false,
2719
2720     size: '',
2721     
2722     max_width: 0,
2723     
2724     max_height: 0,
2725     
2726     fit_content: false,
2727
2728     onRender : function(ct, position)
2729     {
2730         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2731
2732         if(!this.el){
2733             var cfg = Roo.apply({},  this.getAutoCreate());
2734             cfg.id = Roo.id();
2735             //if(!cfg.name){
2736             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2737             //}
2738             //if (!cfg.name.length) {
2739             //    delete cfg.name;
2740            // }
2741             if (this.cls) {
2742                 cfg.cls += ' ' + this.cls;
2743             }
2744             if (this.style) {
2745                 cfg.style = this.style;
2746             }
2747             this.el = Roo.get(document.body).createChild(cfg, position);
2748         }
2749         //var type = this.el.dom.type;
2750
2751
2752         if(this.tabIndex !== undefined){
2753             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2754         }
2755
2756         this.dialogEl = this.el.select('.modal-dialog',true).first();
2757         this.bodyEl = this.el.select('.modal-body',true).first();
2758         this.closeEl = this.el.select('.modal-header .close', true).first();
2759         this.headerEl = this.el.select('.modal-header',true).first();
2760         this.titleEl = this.el.select('.modal-title',true).first();
2761         this.footerEl = this.el.select('.modal-footer',true).first();
2762
2763         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2764         
2765         //this.el.addClass("x-dlg-modal");
2766
2767         if (this.buttons.length) {
2768             Roo.each(this.buttons, function(bb) {
2769                 var b = Roo.apply({}, bb);
2770                 b.xns = b.xns || Roo.bootstrap;
2771                 b.xtype = b.xtype || 'Button';
2772                 if (typeof(b.listeners) == 'undefined') {
2773                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2774                 }
2775
2776                 var btn = Roo.factory(b);
2777
2778                 btn.render(this.getButtonContainer());
2779
2780             },this);
2781         }
2782         // render the children.
2783         var nitems = [];
2784
2785         if(typeof(this.items) != 'undefined'){
2786             var items = this.items;
2787             delete this.items;
2788
2789             for(var i =0;i < items.length;i++) {
2790                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2791             }
2792         }
2793
2794         this.items = nitems;
2795
2796         // where are these used - they used to be body/close/footer
2797
2798
2799         this.initEvents();
2800         //this.el.addClass([this.fieldClass, this.cls]);
2801
2802     },
2803
2804     getAutoCreate : function()
2805     {
2806         var bdy = {
2807                 cls : 'modal-body',
2808                 html : this.html || ''
2809         };
2810
2811         var title = {
2812             tag: 'h4',
2813             cls : 'modal-title',
2814             html : this.title
2815         };
2816
2817         if(this.specificTitle){
2818             title = this.title;
2819
2820         };
2821
2822         var header = [];
2823         if (this.allow_close && Roo.bootstrap.version == 3) {
2824             header.push({
2825                 tag: 'button',
2826                 cls : 'close',
2827                 html : '&times'
2828             });
2829         }
2830
2831         header.push(title);
2832
2833         if (this.allow_close && Roo.bootstrap.version == 4) {
2834             header.push({
2835                 tag: 'button',
2836                 cls : 'close',
2837                 html : '&times'
2838             });
2839         }
2840         
2841         var size = '';
2842
2843         if(this.size.length){
2844             size = 'modal-' + this.size;
2845         }
2846         
2847         var footer = Roo.bootstrap.version == 3 ?
2848             {
2849                 cls : 'modal-footer',
2850                 cn : [
2851                     {
2852                         tag: 'div',
2853                         cls: 'btn-' + this.buttonPosition
2854                     }
2855                 ]
2856
2857             } :
2858             {  // BS4 uses mr-auto on left buttons....
2859                 cls : 'modal-footer'
2860             };
2861
2862             
2863
2864         
2865         
2866         var modal = {
2867             cls: "modal",
2868              cn : [
2869                 {
2870                     cls: "modal-dialog " + size,
2871                     cn : [
2872                         {
2873                             cls : "modal-content",
2874                             cn : [
2875                                 {
2876                                     cls : 'modal-header',
2877                                     cn : header
2878                                 },
2879                                 bdy,
2880                                 footer
2881                             ]
2882
2883                         }
2884                     ]
2885
2886                 }
2887             ]
2888         };
2889
2890         if(this.animate){
2891             modal.cls += ' fade';
2892         }
2893
2894         return modal;
2895
2896     },
2897     getChildContainer : function() {
2898
2899          return this.bodyEl;
2900
2901     },
2902     getButtonContainer : function() {
2903         
2904          return Roo.bootstrap.version == 4 ?
2905             this.el.select('.modal-footer',true).first()
2906             : this.el.select('.modal-footer div',true).first();
2907
2908     },
2909     initEvents : function()
2910     {
2911         if (this.allow_close) {
2912             this.closeEl.on('click', this.onClosePress, this);
2913         }
2914         Roo.EventManager.onWindowResize(this.resize, this, true);
2915
2916
2917     },
2918     
2919     onClosePress : function()
2920     {
2921         
2922     },
2923
2924     resize : function()
2925     {
2926         this.maskEl.setSize(
2927             Roo.lib.Dom.getViewWidth(true),
2928             Roo.lib.Dom.getViewHeight(true)
2929         );
2930         
2931         if (this.fitwindow) {
2932             
2933             var view_height = Roo.lib.Dom.getViewportHeight(true);
2934             
2935             this.setSize(
2936                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2937                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2938             );
2939             return;
2940         }
2941         
2942         if(this.max_width !== 0) {
2943             
2944             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2945             
2946             if(this.height) {
2947                 this.setSize(w, this.height);
2948                 return;
2949             }
2950             
2951             if(this.max_height) {
2952                 this.setSize(w,Math.min(
2953                     this.max_height,
2954                     Roo.lib.Dom.getViewportHeight(true) - 60
2955                 ));
2956                 
2957                 return;
2958             }
2959             
2960             if(!this.fit_content) {
2961                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2962                 return;
2963             }
2964             
2965             this.setSize(w, Math.min(
2966                 60 +
2967                 this.headerEl.getHeight() + 
2968                 this.footerEl.getHeight() + 
2969                 this.getChildHeight(this.bodyEl.dom.childNodes),
2970                 Roo.lib.Dom.getViewportHeight(true) - 60)
2971             );
2972         }
2973         
2974     },
2975
2976     setSize : function(w,h)
2977     {
2978         if (!w && !h) {
2979             return;
2980         }
2981         
2982         this.resizeTo(w,h);
2983     },
2984
2985     show : function() {
2986
2987         if (!this.rendered) {
2988             this.render();
2989         }
2990
2991         //this.el.setStyle('display', 'block');
2992         this.el.removeClass('hideing');
2993         this.el.dom.style.display='block';
2994         
2995         Roo.get(document.body).addClass('modal-open');
2996  
2997         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2998             var _this = this;
2999             (function(){
3000                 this.el.addClass('show');
3001                 this.el.addClass('in');
3002             }).defer(50, this);
3003         }else{
3004             this.el.addClass('show');
3005             this.el.addClass('in');
3006         }
3007
3008         // not sure how we can show data in here..
3009         //if (this.tmpl) {
3010         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3011         //}
3012
3013         Roo.get(document.body).addClass("x-body-masked");
3014         
3015         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3016         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3017         this.maskEl.dom.style.display = 'block';
3018         this.maskEl.addClass('show');
3019         
3020         
3021         this.resize();
3022         
3023         this.fireEvent('show', this);
3024
3025         // set zindex here - otherwise it appears to be ignored...
3026         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3027
3028         (function () {
3029             this.items.forEach( function(e) {
3030                 e.layout ? e.layout() : false;
3031
3032             });
3033         }).defer(100,this);
3034
3035     },
3036     hide : function()
3037     {
3038         if(this.fireEvent("beforehide", this) !== false){
3039             
3040             this.maskEl.removeClass('show');
3041             
3042             this.maskEl.dom.style.display = '';
3043             Roo.get(document.body).removeClass("x-body-masked");
3044             this.el.removeClass('in');
3045             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3046
3047             if(this.animate){ // why
3048                 this.el.addClass('hideing');
3049                 this.el.removeClass('show');
3050                 (function(){
3051                     if (!this.el.hasClass('hideing')) {
3052                         return; // it's been shown again...
3053                     }
3054                     
3055                     this.el.dom.style.display='';
3056
3057                     Roo.get(document.body).removeClass('modal-open');
3058                     this.el.removeClass('hideing');
3059                 }).defer(150,this);
3060                 
3061             }else{
3062                 this.el.removeClass('show');
3063                 this.el.dom.style.display='';
3064                 Roo.get(document.body).removeClass('modal-open');
3065
3066             }
3067             this.fireEvent('hide', this);
3068         }
3069     },
3070     isVisible : function()
3071     {
3072         
3073         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3074         
3075     },
3076
3077     addButton : function(str, cb)
3078     {
3079
3080
3081         var b = Roo.apply({}, { html : str } );
3082         b.xns = b.xns || Roo.bootstrap;
3083         b.xtype = b.xtype || 'Button';
3084         if (typeof(b.listeners) == 'undefined') {
3085             b.listeners = { click : cb.createDelegate(this)  };
3086         }
3087
3088         var btn = Roo.factory(b);
3089
3090         btn.render(this.getButtonContainer());
3091
3092         return btn;
3093
3094     },
3095
3096     setDefaultButton : function(btn)
3097     {
3098         //this.el.select('.modal-footer').()
3099     },
3100
3101     resizeTo: function(w,h)
3102     {
3103         this.dialogEl.setWidth(w);
3104         
3105         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3106
3107         this.bodyEl.setHeight(h - diff);
3108         
3109         this.fireEvent('resize', this);
3110     },
3111     
3112     setContentSize  : function(w, h)
3113     {
3114
3115     },
3116     onButtonClick: function(btn,e)
3117     {
3118         //Roo.log([a,b,c]);
3119         this.fireEvent('btnclick', btn.name, e);
3120     },
3121      /**
3122      * Set the title of the Dialog
3123      * @param {String} str new Title
3124      */
3125     setTitle: function(str) {
3126         this.titleEl.dom.innerHTML = str;
3127     },
3128     /**
3129      * Set the body of the Dialog
3130      * @param {String} str new Title
3131      */
3132     setBody: function(str) {
3133         this.bodyEl.dom.innerHTML = str;
3134     },
3135     /**
3136      * Set the body of the Dialog using the template
3137      * @param {Obj} data - apply this data to the template and replace the body contents.
3138      */
3139     applyBody: function(obj)
3140     {
3141         if (!this.tmpl) {
3142             Roo.log("Error - using apply Body without a template");
3143             //code
3144         }
3145         this.tmpl.overwrite(this.bodyEl, obj);
3146     },
3147     
3148     getChildHeight : function(child_nodes)
3149     {
3150         if(
3151             !child_nodes ||
3152             child_nodes.length == 0
3153         ) {
3154             return;
3155         }
3156         
3157         var child_height = 0;
3158         
3159         for(var i = 0; i < child_nodes.length; i++) {
3160             
3161             /*
3162             * for modal with tabs...
3163             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3164                 
3165                 var layout_childs = child_nodes[i].childNodes;
3166                 
3167                 for(var j = 0; j < layout_childs.length; j++) {
3168                     
3169                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3170                         
3171                         var layout_body_childs = layout_childs[j].childNodes;
3172                         
3173                         for(var k = 0; k < layout_body_childs.length; k++) {
3174                             
3175                             if(layout_body_childs[k].classList.contains('navbar')) {
3176                                 child_height += layout_body_childs[k].offsetHeight;
3177                                 continue;
3178                             }
3179                             
3180                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3181                                 
3182                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3183                                 
3184                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3185                                     
3186                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3187                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3188                                         continue;
3189                                     }
3190                                     
3191                                 }
3192                                 
3193                             }
3194                             
3195                         }
3196                     }
3197                 }
3198                 continue;
3199             }
3200             */
3201             
3202             child_height += child_nodes[i].offsetHeight;
3203             // Roo.log(child_nodes[i].offsetHeight);
3204         }
3205         
3206         return child_height;
3207     }
3208
3209 });
3210
3211
3212 Roo.apply(Roo.bootstrap.Modal,  {
3213     /**
3214          * Button config that displays a single OK button
3215          * @type Object
3216          */
3217         OK :  [{
3218             name : 'ok',
3219             weight : 'primary',
3220             html : 'OK'
3221         }],
3222         /**
3223          * Button config that displays Yes and No buttons
3224          * @type Object
3225          */
3226         YESNO : [
3227             {
3228                 name  : 'no',
3229                 html : 'No'
3230             },
3231             {
3232                 name  :'yes',
3233                 weight : 'primary',
3234                 html : 'Yes'
3235             }
3236         ],
3237
3238         /**
3239          * Button config that displays OK and Cancel buttons
3240          * @type Object
3241          */
3242         OKCANCEL : [
3243             {
3244                name : 'cancel',
3245                 html : 'Cancel'
3246             },
3247             {
3248                 name : 'ok',
3249                 weight : 'primary',
3250                 html : 'OK'
3251             }
3252         ],
3253         /**
3254          * Button config that displays Yes, No and Cancel buttons
3255          * @type Object
3256          */
3257         YESNOCANCEL : [
3258             {
3259                 name : 'yes',
3260                 weight : 'primary',
3261                 html : 'Yes'
3262             },
3263             {
3264                 name : 'no',
3265                 html : 'No'
3266             },
3267             {
3268                 name : 'cancel',
3269                 html : 'Cancel'
3270             }
3271         ],
3272         
3273         zIndex : 10001
3274 });
3275 /*
3276  * - LGPL
3277  *
3278  * messagebox - can be used as a replace
3279  * 
3280  */
3281 /**
3282  * @class Roo.MessageBox
3283  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3284  * Example usage:
3285  *<pre><code>
3286 // Basic alert:
3287 Roo.Msg.alert('Status', 'Changes saved successfully.');
3288
3289 // Prompt for user data:
3290 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3291     if (btn == 'ok'){
3292         // process text value...
3293     }
3294 });
3295
3296 // Show a dialog using config options:
3297 Roo.Msg.show({
3298    title:'Save Changes?',
3299    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3300    buttons: Roo.Msg.YESNOCANCEL,
3301    fn: processResult,
3302    animEl: 'elId'
3303 });
3304 </code></pre>
3305  * @singleton
3306  */
3307 Roo.bootstrap.MessageBox = function(){
3308     var dlg, opt, mask, waitTimer;
3309     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3310     var buttons, activeTextEl, bwidth;
3311
3312     
3313     // private
3314     var handleButton = function(button){
3315         dlg.hide();
3316         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3317     };
3318
3319     // private
3320     var handleHide = function(){
3321         if(opt && opt.cls){
3322             dlg.el.removeClass(opt.cls);
3323         }
3324         //if(waitTimer){
3325         //    Roo.TaskMgr.stop(waitTimer);
3326         //    waitTimer = null;
3327         //}
3328     };
3329
3330     // private
3331     var updateButtons = function(b){
3332         var width = 0;
3333         if(!b){
3334             buttons["ok"].hide();
3335             buttons["cancel"].hide();
3336             buttons["yes"].hide();
3337             buttons["no"].hide();
3338             dlg.footerEl.hide();
3339             
3340             return width;
3341         }
3342         dlg.footerEl.show();
3343         for(var k in buttons){
3344             if(typeof buttons[k] != "function"){
3345                 if(b[k]){
3346                     buttons[k].show();
3347                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3348                     width += buttons[k].el.getWidth()+15;
3349                 }else{
3350                     buttons[k].hide();
3351                 }
3352             }
3353         }
3354         return width;
3355     };
3356
3357     // private
3358     var handleEsc = function(d, k, e){
3359         if(opt && opt.closable !== false){
3360             dlg.hide();
3361         }
3362         if(e){
3363             e.stopEvent();
3364         }
3365     };
3366
3367     return {
3368         /**
3369          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3370          * @return {Roo.BasicDialog} The BasicDialog element
3371          */
3372         getDialog : function(){
3373            if(!dlg){
3374                 dlg = new Roo.bootstrap.Modal( {
3375                     //draggable: true,
3376                     //resizable:false,
3377                     //constraintoviewport:false,
3378                     //fixedcenter:true,
3379                     //collapsible : false,
3380                     //shim:true,
3381                     //modal: true,
3382                 //    width: 'auto',
3383                   //  height:100,
3384                     //buttonAlign:"center",
3385                     closeClick : function(){
3386                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3387                             handleButton("no");
3388                         }else{
3389                             handleButton("cancel");
3390                         }
3391                     }
3392                 });
3393                 dlg.render();
3394                 dlg.on("hide", handleHide);
3395                 mask = dlg.mask;
3396                 //dlg.addKeyListener(27, handleEsc);
3397                 buttons = {};
3398                 this.buttons = buttons;
3399                 var bt = this.buttonText;
3400                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3401                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3402                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3403                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3404                 //Roo.log(buttons);
3405                 bodyEl = dlg.bodyEl.createChild({
3406
3407                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3408                         '<textarea class="roo-mb-textarea"></textarea>' +
3409                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3410                 });
3411                 msgEl = bodyEl.dom.firstChild;
3412                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3413                 textboxEl.enableDisplayMode();
3414                 textboxEl.addKeyListener([10,13], function(){
3415                     if(dlg.isVisible() && opt && opt.buttons){
3416                         if(opt.buttons.ok){
3417                             handleButton("ok");
3418                         }else if(opt.buttons.yes){
3419                             handleButton("yes");
3420                         }
3421                     }
3422                 });
3423                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3424                 textareaEl.enableDisplayMode();
3425                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3426                 progressEl.enableDisplayMode();
3427                 
3428                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3429                 var pf = progressEl.dom.firstChild;
3430                 if (pf) {
3431                     pp = Roo.get(pf.firstChild);
3432                     pp.setHeight(pf.offsetHeight);
3433                 }
3434                 
3435             }
3436             return dlg;
3437         },
3438
3439         /**
3440          * Updates the message box body text
3441          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3442          * the XHTML-compliant non-breaking space character '&amp;#160;')
3443          * @return {Roo.MessageBox} This message box
3444          */
3445         updateText : function(text)
3446         {
3447             if(!dlg.isVisible() && !opt.width){
3448                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3449                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3450             }
3451             msgEl.innerHTML = text || '&#160;';
3452       
3453             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3454             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3455             var w = Math.max(
3456                     Math.min(opt.width || cw , this.maxWidth), 
3457                     Math.max(opt.minWidth || this.minWidth, bwidth)
3458             );
3459             if(opt.prompt){
3460                 activeTextEl.setWidth(w);
3461             }
3462             if(dlg.isVisible()){
3463                 dlg.fixedcenter = false;
3464             }
3465             // to big, make it scroll. = But as usual stupid IE does not support
3466             // !important..
3467             
3468             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3469                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3470                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3471             } else {
3472                 bodyEl.dom.style.height = '';
3473                 bodyEl.dom.style.overflowY = '';
3474             }
3475             if (cw > w) {
3476                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3477             } else {
3478                 bodyEl.dom.style.overflowX = '';
3479             }
3480             
3481             dlg.setContentSize(w, bodyEl.getHeight());
3482             if(dlg.isVisible()){
3483                 dlg.fixedcenter = true;
3484             }
3485             return this;
3486         },
3487
3488         /**
3489          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3490          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3491          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3492          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3493          * @return {Roo.MessageBox} This message box
3494          */
3495         updateProgress : function(value, text){
3496             if(text){
3497                 this.updateText(text);
3498             }
3499             
3500             if (pp) { // weird bug on my firefox - for some reason this is not defined
3501                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3502                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3503             }
3504             return this;
3505         },        
3506
3507         /**
3508          * Returns true if the message box is currently displayed
3509          * @return {Boolean} True if the message box is visible, else false
3510          */
3511         isVisible : function(){
3512             return dlg && dlg.isVisible();  
3513         },
3514
3515         /**
3516          * Hides the message box if it is displayed
3517          */
3518         hide : function(){
3519             if(this.isVisible()){
3520                 dlg.hide();
3521             }  
3522         },
3523
3524         /**
3525          * Displays a new message box, or reinitializes an existing message box, based on the config options
3526          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3527          * The following config object properties are supported:
3528          * <pre>
3529 Property    Type             Description
3530 ----------  ---------------  ------------------------------------------------------------------------------------
3531 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3532                                    closes (defaults to undefined)
3533 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3534                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3535 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3536                                    progress and wait dialogs will ignore this property and always hide the
3537                                    close button as they can only be closed programmatically.
3538 cls               String           A custom CSS class to apply to the message box element
3539 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3540                                    displayed (defaults to 75)
3541 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3542                                    function will be btn (the name of the button that was clicked, if applicable,
3543                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3544                                    Progress and wait dialogs will ignore this option since they do not respond to
3545                                    user actions and can only be closed programmatically, so any required function
3546                                    should be called by the same code after it closes the dialog.
3547 icon              String           A CSS class that provides a background image to be used as an icon for
3548                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3549 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3550 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3551 modal             Boolean          False to allow user interaction with the page while the message box is
3552                                    displayed (defaults to true)
3553 msg               String           A string that will replace the existing message box body text (defaults
3554                                    to the XHTML-compliant non-breaking space character '&#160;')
3555 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3556 progress          Boolean          True to display a progress bar (defaults to false)
3557 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3558 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3559 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3560 title             String           The title text
3561 value             String           The string value to set into the active textbox element if displayed
3562 wait              Boolean          True to display a progress bar (defaults to false)
3563 width             Number           The width of the dialog in pixels
3564 </pre>
3565          *
3566          * Example usage:
3567          * <pre><code>
3568 Roo.Msg.show({
3569    title: 'Address',
3570    msg: 'Please enter your address:',
3571    width: 300,
3572    buttons: Roo.MessageBox.OKCANCEL,
3573    multiline: true,
3574    fn: saveAddress,
3575    animEl: 'addAddressBtn'
3576 });
3577 </code></pre>
3578          * @param {Object} config Configuration options
3579          * @return {Roo.MessageBox} This message box
3580          */
3581         show : function(options)
3582         {
3583             
3584             // this causes nightmares if you show one dialog after another
3585             // especially on callbacks..
3586              
3587             if(this.isVisible()){
3588                 
3589                 this.hide();
3590                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3591                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3592                 Roo.log("New Dialog Message:" +  options.msg )
3593                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3594                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3595                 
3596             }
3597             var d = this.getDialog();
3598             opt = options;
3599             d.setTitle(opt.title || "&#160;");
3600             d.closeEl.setDisplayed(opt.closable !== false);
3601             activeTextEl = textboxEl;
3602             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3603             if(opt.prompt){
3604                 if(opt.multiline){
3605                     textboxEl.hide();
3606                     textareaEl.show();
3607                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3608                         opt.multiline : this.defaultTextHeight);
3609                     activeTextEl = textareaEl;
3610                 }else{
3611                     textboxEl.show();
3612                     textareaEl.hide();
3613                 }
3614             }else{
3615                 textboxEl.hide();
3616                 textareaEl.hide();
3617             }
3618             progressEl.setDisplayed(opt.progress === true);
3619             if (opt.progress) {
3620                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3621             }
3622             this.updateProgress(0);
3623             activeTextEl.dom.value = opt.value || "";
3624             if(opt.prompt){
3625                 dlg.setDefaultButton(activeTextEl);
3626             }else{
3627                 var bs = opt.buttons;
3628                 var db = null;
3629                 if(bs && bs.ok){
3630                     db = buttons["ok"];
3631                 }else if(bs && bs.yes){
3632                     db = buttons["yes"];
3633                 }
3634                 dlg.setDefaultButton(db);
3635             }
3636             bwidth = updateButtons(opt.buttons);
3637             this.updateText(opt.msg);
3638             if(opt.cls){
3639                 d.el.addClass(opt.cls);
3640             }
3641             d.proxyDrag = opt.proxyDrag === true;
3642             d.modal = opt.modal !== false;
3643             d.mask = opt.modal !== false ? mask : false;
3644             if(!d.isVisible()){
3645                 // force it to the end of the z-index stack so it gets a cursor in FF
3646                 document.body.appendChild(dlg.el.dom);
3647                 d.animateTarget = null;
3648                 d.show(options.animEl);
3649             }
3650             return this;
3651         },
3652
3653         /**
3654          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3655          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3656          * and closing the message box when the process is complete.
3657          * @param {String} title The title bar text
3658          * @param {String} msg The message box body text
3659          * @return {Roo.MessageBox} This message box
3660          */
3661         progress : function(title, msg){
3662             this.show({
3663                 title : title,
3664                 msg : msg,
3665                 buttons: false,
3666                 progress:true,
3667                 closable:false,
3668                 minWidth: this.minProgressWidth,
3669                 modal : true
3670             });
3671             return this;
3672         },
3673
3674         /**
3675          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3676          * If a callback function is passed it will be called after the user clicks the button, and the
3677          * id of the button that was clicked will be passed as the only parameter to the callback
3678          * (could also be the top-right close button).
3679          * @param {String} title The title bar text
3680          * @param {String} msg The message box body text
3681          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3682          * @param {Object} scope (optional) The scope of the callback function
3683          * @return {Roo.MessageBox} This message box
3684          */
3685         alert : function(title, msg, fn, scope)
3686         {
3687             this.show({
3688                 title : title,
3689                 msg : msg,
3690                 buttons: this.OK,
3691                 fn: fn,
3692                 closable : false,
3693                 scope : scope,
3694                 modal : true
3695             });
3696             return this;
3697         },
3698
3699         /**
3700          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3701          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3702          * You are responsible for closing the message box when the process is complete.
3703          * @param {String} msg The message box body text
3704          * @param {String} title (optional) The title bar text
3705          * @return {Roo.MessageBox} This message box
3706          */
3707         wait : function(msg, title){
3708             this.show({
3709                 title : title,
3710                 msg : msg,
3711                 buttons: false,
3712                 closable:false,
3713                 progress:true,
3714                 modal:true,
3715                 width:300,
3716                 wait:true
3717             });
3718             waitTimer = Roo.TaskMgr.start({
3719                 run: function(i){
3720                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3721                 },
3722                 interval: 1000
3723             });
3724             return this;
3725         },
3726
3727         /**
3728          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3729          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3730          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3731          * @param {String} title The title bar text
3732          * @param {String} msg The message box body text
3733          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3734          * @param {Object} scope (optional) The scope of the callback function
3735          * @return {Roo.MessageBox} This message box
3736          */
3737         confirm : function(title, msg, fn, scope){
3738             this.show({
3739                 title : title,
3740                 msg : msg,
3741                 buttons: this.YESNO,
3742                 fn: fn,
3743                 scope : scope,
3744                 modal : true
3745             });
3746             return this;
3747         },
3748
3749         /**
3750          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3751          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3752          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3753          * (could also be the top-right close button) and the text that was entered will be passed as the two
3754          * parameters to the callback.
3755          * @param {String} title The title bar text
3756          * @param {String} msg The message box body text
3757          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3758          * @param {Object} scope (optional) The scope of the callback function
3759          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3760          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3761          * @return {Roo.MessageBox} This message box
3762          */
3763         prompt : function(title, msg, fn, scope, multiline){
3764             this.show({
3765                 title : title,
3766                 msg : msg,
3767                 buttons: this.OKCANCEL,
3768                 fn: fn,
3769                 minWidth:250,
3770                 scope : scope,
3771                 prompt:true,
3772                 multiline: multiline,
3773                 modal : true
3774             });
3775             return this;
3776         },
3777
3778         /**
3779          * Button config that displays a single OK button
3780          * @type Object
3781          */
3782         OK : {ok:true},
3783         /**
3784          * Button config that displays Yes and No buttons
3785          * @type Object
3786          */
3787         YESNO : {yes:true, no:true},
3788         /**
3789          * Button config that displays OK and Cancel buttons
3790          * @type Object
3791          */
3792         OKCANCEL : {ok:true, cancel:true},
3793         /**
3794          * Button config that displays Yes, No and Cancel buttons
3795          * @type Object
3796          */
3797         YESNOCANCEL : {yes:true, no:true, cancel:true},
3798
3799         /**
3800          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3801          * @type Number
3802          */
3803         defaultTextHeight : 75,
3804         /**
3805          * The maximum width in pixels of the message box (defaults to 600)
3806          * @type Number
3807          */
3808         maxWidth : 600,
3809         /**
3810          * The minimum width in pixels of the message box (defaults to 100)
3811          * @type Number
3812          */
3813         minWidth : 100,
3814         /**
3815          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3816          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3817          * @type Number
3818          */
3819         minProgressWidth : 250,
3820         /**
3821          * An object containing the default button text strings that can be overriden for localized language support.
3822          * Supported properties are: ok, cancel, yes and no.
3823          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3824          * @type Object
3825          */
3826         buttonText : {
3827             ok : "OK",
3828             cancel : "Cancel",
3829             yes : "Yes",
3830             no : "No"
3831         }
3832     };
3833 }();
3834
3835 /**
3836  * Shorthand for {@link Roo.MessageBox}
3837  */
3838 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3839 Roo.Msg = Roo.Msg || Roo.MessageBox;
3840 /*
3841  * - LGPL
3842  *
3843  * navbar
3844  * 
3845  */
3846
3847 /**
3848  * @class Roo.bootstrap.Navbar
3849  * @extends Roo.bootstrap.Component
3850  * Bootstrap Navbar class
3851
3852  * @constructor
3853  * Create a new Navbar
3854  * @param {Object} config The config object
3855  */
3856
3857
3858 Roo.bootstrap.Navbar = function(config){
3859     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3860     this.addEvents({
3861         // raw events
3862         /**
3863          * @event beforetoggle
3864          * Fire before toggle the menu
3865          * @param {Roo.EventObject} e
3866          */
3867         "beforetoggle" : true
3868     });
3869 };
3870
3871 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3872     
3873     
3874    
3875     // private
3876     navItems : false,
3877     loadMask : false,
3878     
3879     
3880     getAutoCreate : function(){
3881         
3882         
3883         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3884         
3885     },
3886     
3887     initEvents :function ()
3888     {
3889         //Roo.log(this.el.select('.navbar-toggle',true));
3890         this.el.select('.navbar-toggle',true).on('click', function() {
3891             if(this.fireEvent('beforetoggle', this) !== false){
3892                 var ce = this.el.select('.navbar-collapse',true).first();
3893                 ce.toggleClass('in'); // old...
3894                 if (ce.hasClass('collapse')) {
3895                     // show it...
3896                     ce.removeClass('collapse');
3897                     ce.addClass('show');
3898                     var h = ce.getHeight();
3899                     Roo.log(h);
3900                     ce.removeClass('show');
3901                     // at this point we should be able to see it..
3902                     ce.addClass('collapsing');
3903                     
3904                     ce.setHeight(0); // resize it ...
3905                     ce.on('transitionend', function() {
3906                         Roo.log('done transition');
3907                         ce.removeClass('collapsing');
3908                         ce.addClass('show');
3909                         ce.removeClass('collapse');
3910
3911                         ce.dom.style.height = '';
3912                     }, this, { single: true} );
3913                     ce.setHeight(h);
3914                     
3915                 } else {
3916                     ce.setHeight(ce.getHeight());
3917                     ce.removeClass('show');
3918                     ce.addClass('collapsing');
3919                     
3920                     ce.on('transitionend', function() {
3921                         ce.dom.style.height = '';
3922                         ce.removeClass('collapsing');
3923                         ce.addClass('collapse');
3924                     }, this, { single: true} );
3925                     ce.setHeight(0);
3926                 }
3927             }
3928             
3929         }, this);
3930         
3931         var mark = {
3932             tag: "div",
3933             cls:"x-dlg-mask"
3934         };
3935         
3936         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3937         
3938         var size = this.el.getSize();
3939         this.maskEl.setSize(size.width, size.height);
3940         this.maskEl.enableDisplayMode("block");
3941         this.maskEl.hide();
3942         
3943         if(this.loadMask){
3944             this.maskEl.show();
3945         }
3946     },
3947     
3948     
3949     getChildContainer : function()
3950     {
3951         if (this.el.select('.collapse').getCount()) {
3952             return this.el.select('.collapse',true).first();
3953         }
3954         
3955         return this.el;
3956     },
3957     
3958     mask : function()
3959     {
3960         this.maskEl.show();
3961     },
3962     
3963     unmask : function()
3964     {
3965         this.maskEl.hide();
3966     } 
3967     
3968     
3969     
3970     
3971 });
3972
3973
3974
3975  
3976
3977  /*
3978  * - LGPL
3979  *
3980  * navbar
3981  * 
3982  */
3983
3984 /**
3985  * @class Roo.bootstrap.NavSimplebar
3986  * @extends Roo.bootstrap.Navbar
3987  * Bootstrap Sidebar class
3988  *
3989  * @cfg {Boolean} inverse is inverted color
3990  * 
3991  * @cfg {String} type (nav | pills | tabs)
3992  * @cfg {Boolean} arrangement stacked | justified
3993  * @cfg {String} align (left | right) alignment
3994  * 
3995  * @cfg {Boolean} main (true|false) main nav bar? default false
3996  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3997  * 
3998  * @cfg {String} tag (header|footer|nav|div) default is nav 
3999
4000  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4001  * 
4002  * 
4003  * @constructor
4004  * Create a new Sidebar
4005  * @param {Object} config The config object
4006  */
4007
4008
4009 Roo.bootstrap.NavSimplebar = function(config){
4010     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4011 };
4012
4013 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4014     
4015     inverse: false,
4016     
4017     type: false,
4018     arrangement: '',
4019     align : false,
4020     
4021     weight : 'light',
4022     
4023     main : false,
4024     
4025     
4026     tag : false,
4027     
4028     
4029     getAutoCreate : function(){
4030         
4031         
4032         var cfg = {
4033             tag : this.tag || 'div',
4034             cls : 'navbar navbar-expand-lg roo-navbar-simple'
4035         };
4036         if (['light','white'].indexOf(this.weight) > -1) {
4037             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4038         }
4039         cfg.cls += ' bg-' + this.weight;
4040         
4041         if (this.inverse) {
4042             cfg.cls += ' navbar-inverse';
4043             
4044         }
4045         
4046         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4047         
4048         //if (Roo.bootstrap.version == 4) {
4049         //    return cfg;
4050         //}
4051         
4052         cfg.cn = [
4053             {
4054                 cls: 'nav',
4055                 tag : 'ul'
4056             }
4057         ];
4058         
4059          
4060         this.type = this.type || 'nav';
4061         if (['tabs','pills'].indexOf(this.type) != -1) {
4062             cfg.cn[0].cls += ' nav-' + this.type
4063         
4064         
4065         } else {
4066             if (this.type!=='nav') {
4067                 Roo.log('nav type must be nav/tabs/pills')
4068             }
4069             cfg.cn[0].cls += ' navbar-nav'
4070         }
4071         
4072         
4073         
4074         
4075         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4076             cfg.cn[0].cls += ' nav-' + this.arrangement;
4077         }
4078         
4079         
4080         if (this.align === 'right') {
4081             cfg.cn[0].cls += ' navbar-right';
4082         }
4083         
4084         
4085         
4086         
4087         return cfg;
4088     
4089         
4090     }
4091     
4092     
4093     
4094 });
4095
4096
4097
4098  
4099
4100  
4101        /*
4102  * - LGPL
4103  *
4104  * navbar
4105  * navbar-fixed-top
4106  * navbar-expand-md  fixed-top 
4107  */
4108
4109 /**
4110  * @class Roo.bootstrap.NavHeaderbar
4111  * @extends Roo.bootstrap.NavSimplebar
4112  * Bootstrap Sidebar class
4113  *
4114  * @cfg {String} brand what is brand
4115  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4116  * @cfg {String} brand_href href of the brand
4117  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4118  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4119  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4120  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4121  * 
4122  * @constructor
4123  * Create a new Sidebar
4124  * @param {Object} config The config object
4125  */
4126
4127
4128 Roo.bootstrap.NavHeaderbar = function(config){
4129     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4130       
4131 };
4132
4133 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4134     
4135     position: '',
4136     brand: '',
4137     brand_href: false,
4138     srButton : true,
4139     autohide : false,
4140     desktopCenter : false,
4141    
4142     
4143     getAutoCreate : function(){
4144         
4145         var   cfg = {
4146             tag: this.nav || 'nav',
4147             cls: 'navbar navbar-expand-md',
4148             role: 'navigation',
4149             cn: []
4150         };
4151         
4152         var cn = cfg.cn;
4153         if (this.desktopCenter) {
4154             cn.push({cls : 'container', cn : []});
4155             cn = cn[0].cn;
4156         }
4157         
4158         if(this.srButton){
4159             var btn = {
4160                 tag: 'button',
4161                 type: 'button',
4162                 cls: 'navbar-toggle navbar-toggler',
4163                 'data-toggle': 'collapse',
4164                 cn: [
4165                     {
4166                         tag: 'span',
4167                         cls: 'sr-only',
4168                         html: 'Toggle navigation'
4169                     },
4170                     {
4171                         tag: 'span',
4172                         cls: 'icon-bar navbar-toggler-icon'
4173                     },
4174                     {
4175                         tag: 'span',
4176                         cls: 'icon-bar'
4177                     },
4178                     {
4179                         tag: 'span',
4180                         cls: 'icon-bar'
4181                     }
4182                 ]
4183             };
4184             
4185             cn.push( Roo.bootstrap.version == 4 ? btn : {
4186                 tag: 'div',
4187                 cls: 'navbar-header',
4188                 cn: [
4189                     btn
4190                 ]
4191             });
4192         }
4193         
4194         cn.push({
4195             tag: 'div',
4196             cls: 'collapse navbar-collapse',
4197             cn : []
4198         });
4199         
4200         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4201         
4202         if (['light','white'].indexOf(this.weight) > -1) {
4203             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4204         }
4205         cfg.cls += ' bg-' + this.weight;
4206         
4207         
4208         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4209             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4210             
4211             // tag can override this..
4212             
4213             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4214         }
4215         
4216         if (this.brand !== '') {
4217             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4218             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4219                 tag: 'a',
4220                 href: this.brand_href ? this.brand_href : '#',
4221                 cls: 'navbar-brand',
4222                 cn: [
4223                 this.brand
4224                 ]
4225             });
4226         }
4227         
4228         if(this.main){
4229             cfg.cls += ' main-nav';
4230         }
4231         
4232         
4233         return cfg;
4234
4235         
4236     },
4237     getHeaderChildContainer : function()
4238     {
4239         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4240             return this.el.select('.navbar-header',true).first();
4241         }
4242         
4243         return this.getChildContainer();
4244     },
4245     
4246     
4247     initEvents : function()
4248     {
4249         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4250         
4251         if (this.autohide) {
4252             
4253             var prevScroll = 0;
4254             var ft = this.el;
4255             
4256             Roo.get(document).on('scroll',function(e) {
4257                 var ns = Roo.get(document).getScroll().top;
4258                 var os = prevScroll;
4259                 prevScroll = ns;
4260                 
4261                 if(ns > os){
4262                     ft.removeClass('slideDown');
4263                     ft.addClass('slideUp');
4264                     return;
4265                 }
4266                 ft.removeClass('slideUp');
4267                 ft.addClass('slideDown');
4268                  
4269               
4270           },this);
4271         }
4272     }    
4273     
4274 });
4275
4276
4277
4278  
4279
4280  /*
4281  * - LGPL
4282  *
4283  * navbar
4284  * 
4285  */
4286
4287 /**
4288  * @class Roo.bootstrap.NavSidebar
4289  * @extends Roo.bootstrap.Navbar
4290  * Bootstrap Sidebar class
4291  * 
4292  * @constructor
4293  * Create a new Sidebar
4294  * @param {Object} config The config object
4295  */
4296
4297
4298 Roo.bootstrap.NavSidebar = function(config){
4299     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4300 };
4301
4302 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4303     
4304     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4305     
4306     getAutoCreate : function(){
4307         
4308         
4309         return  {
4310             tag: 'div',
4311             cls: 'sidebar sidebar-nav'
4312         };
4313     
4314         
4315     }
4316     
4317     
4318     
4319 });
4320
4321
4322
4323  
4324
4325  /*
4326  * - LGPL
4327  *
4328  * nav group
4329  * 
4330  */
4331
4332 /**
4333  * @class Roo.bootstrap.NavGroup
4334  * @extends Roo.bootstrap.Component
4335  * Bootstrap NavGroup class
4336  * @cfg {String} align (left|right)
4337  * @cfg {Boolean} inverse
4338  * @cfg {String} type (nav|pills|tab) default nav
4339  * @cfg {String} navId - reference Id for navbar.
4340
4341  * 
4342  * @constructor
4343  * Create a new nav group
4344  * @param {Object} config The config object
4345  */
4346
4347 Roo.bootstrap.NavGroup = function(config){
4348     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4349     this.navItems = [];
4350    
4351     Roo.bootstrap.NavGroup.register(this);
4352      this.addEvents({
4353         /**
4354              * @event changed
4355              * Fires when the active item changes
4356              * @param {Roo.bootstrap.NavGroup} this
4357              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4358              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4359          */
4360         'changed': true
4361      });
4362     
4363 };
4364
4365 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4366     
4367     align: '',
4368     inverse: false,
4369     form: false,
4370     type: 'nav',
4371     navId : '',
4372     // private
4373     
4374     navItems : false, 
4375     
4376     getAutoCreate : function()
4377     {
4378         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4379         
4380         cfg = {
4381             tag : 'ul',
4382             cls: 'nav' 
4383         };
4384         if (Roo.bootstrap.version == 4) {
4385             if (['tabs','pills'].indexOf(this.type) != -1) {
4386                 cfg.cls += ' nav-' + this.type; 
4387             } else {
4388                 cfg.cls += ' navbar-nav';
4389             }
4390         } else {
4391             if (['tabs','pills'].indexOf(this.type) != -1) {
4392                 cfg.cls += ' nav-' + this.type
4393             } else {
4394                 if (this.type !== 'nav') {
4395                     Roo.log('nav type must be nav/tabs/pills')
4396                 }
4397                 cfg.cls += ' navbar-nav'
4398             }
4399         }
4400         
4401         if (this.parent() && this.parent().sidebar) {
4402             cfg = {
4403                 tag: 'ul',
4404                 cls: 'dashboard-menu sidebar-menu'
4405             };
4406             
4407             return cfg;
4408         }
4409         
4410         if (this.form === true) {
4411             cfg = {
4412                 tag: 'form',
4413                 cls: 'navbar-form form-inline'
4414             };
4415             
4416             if (this.align === 'right') {
4417                 cfg.cls += ' navbar-right ml-md-auto';
4418             } else {
4419                 cfg.cls += ' navbar-left';
4420             }
4421         }
4422         
4423         if (this.align === 'right') {
4424             cfg.cls += ' navbar-right ml-md-auto';
4425         } else {
4426             cfg.cls += ' mr-auto';
4427         }
4428         
4429         if (this.inverse) {
4430             cfg.cls += ' navbar-inverse';
4431             
4432         }
4433         
4434         
4435         return cfg;
4436     },
4437     /**
4438     * sets the active Navigation item
4439     * @param {Roo.bootstrap.NavItem} the new current navitem
4440     */
4441     setActiveItem : function(item)
4442     {
4443         var prev = false;
4444         Roo.each(this.navItems, function(v){
4445             if (v == item) {
4446                 return ;
4447             }
4448             if (v.isActive()) {
4449                 v.setActive(false, true);
4450                 prev = v;
4451                 
4452             }
4453             
4454         });
4455
4456         item.setActive(true, true);
4457         this.fireEvent('changed', this, item, prev);
4458         
4459         
4460     },
4461     /**
4462     * gets the active Navigation item
4463     * @return {Roo.bootstrap.NavItem} the current navitem
4464     */
4465     getActive : function()
4466     {
4467         
4468         var prev = false;
4469         Roo.each(this.navItems, function(v){
4470             
4471             if (v.isActive()) {
4472                 prev = v;
4473                 
4474             }
4475             
4476         });
4477         return prev;
4478     },
4479     
4480     indexOfNav : function()
4481     {
4482         
4483         var prev = false;
4484         Roo.each(this.navItems, function(v,i){
4485             
4486             if (v.isActive()) {
4487                 prev = i;
4488                 
4489             }
4490             
4491         });
4492         return prev;
4493     },
4494     /**
4495     * adds a Navigation item
4496     * @param {Roo.bootstrap.NavItem} the navitem to add
4497     */
4498     addItem : function(cfg)
4499     {
4500         if (this.form && Roo.bootstrap.version == 4) {
4501             cfg.tag = 'div';
4502         }
4503         var cn = new Roo.bootstrap.NavItem(cfg);
4504         this.register(cn);
4505         cn.parentId = this.id;
4506         cn.onRender(this.el, null);
4507         return cn;
4508     },
4509     /**
4510     * register a Navigation item
4511     * @param {Roo.bootstrap.NavItem} the navitem to add
4512     */
4513     register : function(item)
4514     {
4515         this.navItems.push( item);
4516         item.navId = this.navId;
4517     
4518     },
4519     
4520     /**
4521     * clear all the Navigation item
4522     */
4523    
4524     clearAll : function()
4525     {
4526         this.navItems = [];
4527         this.el.dom.innerHTML = '';
4528     },
4529     
4530     getNavItem: function(tabId)
4531     {
4532         var ret = false;
4533         Roo.each(this.navItems, function(e) {
4534             if (e.tabId == tabId) {
4535                ret =  e;
4536                return false;
4537             }
4538             return true;
4539             
4540         });
4541         return ret;
4542     },
4543     
4544     setActiveNext : function()
4545     {
4546         var i = this.indexOfNav(this.getActive());
4547         if (i > this.navItems.length) {
4548             return;
4549         }
4550         this.setActiveItem(this.navItems[i+1]);
4551     },
4552     setActivePrev : function()
4553     {
4554         var i = this.indexOfNav(this.getActive());
4555         if (i  < 1) {
4556             return;
4557         }
4558         this.setActiveItem(this.navItems[i-1]);
4559     },
4560     clearWasActive : function(except) {
4561         Roo.each(this.navItems, function(e) {
4562             if (e.tabId != except.tabId && e.was_active) {
4563                e.was_active = false;
4564                return false;
4565             }
4566             return true;
4567             
4568         });
4569     },
4570     getWasActive : function ()
4571     {
4572         var r = false;
4573         Roo.each(this.navItems, function(e) {
4574             if (e.was_active) {
4575                r = e;
4576                return false;
4577             }
4578             return true;
4579             
4580         });
4581         return r;
4582     }
4583     
4584     
4585 });
4586
4587  
4588 Roo.apply(Roo.bootstrap.NavGroup, {
4589     
4590     groups: {},
4591      /**
4592     * register a Navigation Group
4593     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4594     */
4595     register : function(navgrp)
4596     {
4597         this.groups[navgrp.navId] = navgrp;
4598         
4599     },
4600     /**
4601     * fetch a Navigation Group based on the navigation ID
4602     * @param {string} the navgroup to add
4603     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4604     */
4605     get: function(navId) {
4606         if (typeof(this.groups[navId]) == 'undefined') {
4607             return false;
4608             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4609         }
4610         return this.groups[navId] ;
4611     }
4612     
4613     
4614     
4615 });
4616
4617  /*
4618  * - LGPL
4619  *
4620  * row
4621  * 
4622  */
4623
4624 /**
4625  * @class Roo.bootstrap.NavItem
4626  * @extends Roo.bootstrap.Component
4627  * Bootstrap Navbar.NavItem class
4628  * @cfg {String} href  link to
4629  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4630
4631  * @cfg {String} html content of button
4632  * @cfg {String} badge text inside badge
4633  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4634  * @cfg {String} glyphicon DEPRICATED - use fa
4635  * @cfg {String} icon DEPRICATED - use fa
4636  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4637  * @cfg {Boolean} active Is item active
4638  * @cfg {Boolean} disabled Is item disabled
4639  
4640  * @cfg {Boolean} preventDefault (true | false) default false
4641  * @cfg {String} tabId the tab that this item activates.
4642  * @cfg {String} tagtype (a|span) render as a href or span?
4643  * @cfg {Boolean} animateRef (true|false) link to element default false  
4644   
4645  * @constructor
4646  * Create a new Navbar Item
4647  * @param {Object} config The config object
4648  */
4649 Roo.bootstrap.NavItem = function(config){
4650     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4651     this.addEvents({
4652         // raw events
4653         /**
4654          * @event click
4655          * The raw click event for the entire grid.
4656          * @param {Roo.EventObject} e
4657          */
4658         "click" : true,
4659          /**
4660             * @event changed
4661             * Fires when the active item active state changes
4662             * @param {Roo.bootstrap.NavItem} this
4663             * @param {boolean} state the new state
4664              
4665          */
4666         'changed': true,
4667         /**
4668             * @event scrollto
4669             * Fires when scroll to element
4670             * @param {Roo.bootstrap.NavItem} this
4671             * @param {Object} options
4672             * @param {Roo.EventObject} e
4673              
4674          */
4675         'scrollto': true
4676     });
4677    
4678 };
4679
4680 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4681     
4682     href: false,
4683     html: '',
4684     badge: '',
4685     icon: false,
4686     fa : false,
4687     glyphicon: false,
4688     active: false,
4689     preventDefault : false,
4690     tabId : false,
4691     tagtype : 'a',
4692     tag: 'li',
4693     disabled : false,
4694     animateRef : false,
4695     was_active : false,
4696     button_weight : '',
4697     button_outline : false,
4698     
4699     navLink: false,
4700     
4701     getAutoCreate : function(){
4702          
4703         var cfg = {
4704             tag: this.tag,
4705             cls: 'nav-item'
4706         };
4707         
4708         if (this.active) {
4709             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4710         }
4711         if (this.disabled) {
4712             cfg.cls += ' disabled';
4713         }
4714         
4715         // BS4 only?
4716         if (this.button_weight.length) {
4717             cfg.tag = this.href ? 'a' : 'button';
4718             cfg.html = this.html || '';
4719             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4720             if (this.href) {
4721                 cfg.href = this.href;
4722             }
4723             if (this.fa) {
4724                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4725             }
4726             
4727             // menu .. should add dropdown-menu class - so no need for carat..
4728             
4729             if (this.badge !== '') {
4730                  
4731                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4732             }
4733             return cfg;
4734         }
4735         
4736         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4737             cfg.cn = [
4738                 {
4739                     tag: this.tagtype,
4740                     href : this.href || "#",
4741                     html: this.html || ''
4742                 }
4743             ];
4744             if (this.tagtype == 'a') {
4745                 cfg.cn[0].cls = 'nav-link';
4746             }
4747             if (this.icon) {
4748                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4749             }
4750             if (this.fa) {
4751                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4752             }
4753             if(this.glyphicon) {
4754                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4755             }
4756             
4757             if (this.menu) {
4758                 
4759                 cfg.cn[0].html += " <span class='caret'></span>";
4760              
4761             }
4762             
4763             if (this.badge !== '') {
4764                  
4765                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4766             }
4767         }
4768         
4769         
4770         
4771         return cfg;
4772     },
4773     onRender : function(ct, position)
4774     {
4775        // Roo.log("Call onRender: " + this.xtype);
4776         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4777             this.tag = 'div';
4778         }
4779         
4780         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4781         this.navLink = this.el.select('.nav-link',true).first();
4782         return ret;
4783     },
4784       
4785     
4786     initEvents: function() 
4787     {
4788         if (typeof (this.menu) != 'undefined') {
4789             this.menu.parentType = this.xtype;
4790             this.menu.triggerEl = this.el;
4791             this.menu = this.addxtype(Roo.apply({}, this.menu));
4792         }
4793         
4794         this.el.select('a',true).on('click', this.onClick, this);
4795         
4796         if(this.tagtype == 'span'){
4797             this.el.select('span',true).on('click', this.onClick, this);
4798         }
4799        
4800         // at this point parent should be available..
4801         this.parent().register(this);
4802     },
4803     
4804     onClick : function(e)
4805     {
4806         if (e.getTarget('.dropdown-menu-item')) {
4807             // did you click on a menu itemm.... - then don't trigger onclick..
4808             return;
4809         }
4810         
4811         if(
4812                 this.preventDefault || 
4813                 this.href == '#' 
4814         ){
4815             Roo.log("NavItem - prevent Default?");
4816             e.preventDefault();
4817         }
4818         
4819         if (this.disabled) {
4820             return;
4821         }
4822         
4823         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4824         if (tg && tg.transition) {
4825             Roo.log("waiting for the transitionend");
4826             return;
4827         }
4828         
4829         
4830         
4831         //Roo.log("fire event clicked");
4832         if(this.fireEvent('click', this, e) === false){
4833             return;
4834         };
4835         
4836         if(this.tagtype == 'span'){
4837             return;
4838         }
4839         
4840         //Roo.log(this.href);
4841         var ael = this.el.select('a',true).first();
4842         //Roo.log(ael);
4843         
4844         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4845             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4846             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4847                 return; // ignore... - it's a 'hash' to another page.
4848             }
4849             Roo.log("NavItem - prevent Default?");
4850             e.preventDefault();
4851             this.scrollToElement(e);
4852         }
4853         
4854         
4855         var p =  this.parent();
4856    
4857         if (['tabs','pills'].indexOf(p.type)!==-1) {
4858             if (typeof(p.setActiveItem) !== 'undefined') {
4859                 p.setActiveItem(this);
4860             }
4861         }
4862         
4863         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4864         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4865             // remove the collapsed menu expand...
4866             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4867         }
4868     },
4869     
4870     isActive: function () {
4871         return this.active
4872     },
4873     setActive : function(state, fire, is_was_active)
4874     {
4875         if (this.active && !state && this.navId) {
4876             this.was_active = true;
4877             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4878             if (nv) {
4879                 nv.clearWasActive(this);
4880             }
4881             
4882         }
4883         this.active = state;
4884         
4885         if (!state ) {
4886             this.el.removeClass('active');
4887             this.navLink ? this.navLink.removeClass('active') : false;
4888         } else if (!this.el.hasClass('active')) {
4889             
4890             this.el.addClass('active');
4891             if (Roo.bootstrap.version == 4 && this.navLink ) {
4892                 this.navLink.addClass('active');
4893             }
4894             
4895         }
4896         if (fire) {
4897             this.fireEvent('changed', this, state);
4898         }
4899         
4900         // show a panel if it's registered and related..
4901         
4902         if (!this.navId || !this.tabId || !state || is_was_active) {
4903             return;
4904         }
4905         
4906         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4907         if (!tg) {
4908             return;
4909         }
4910         var pan = tg.getPanelByName(this.tabId);
4911         if (!pan) {
4912             return;
4913         }
4914         // if we can not flip to new panel - go back to old nav highlight..
4915         if (false == tg.showPanel(pan)) {
4916             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4917             if (nv) {
4918                 var onav = nv.getWasActive();
4919                 if (onav) {
4920                     onav.setActive(true, false, true);
4921                 }
4922             }
4923             
4924         }
4925         
4926         
4927         
4928     },
4929      // this should not be here...
4930     setDisabled : function(state)
4931     {
4932         this.disabled = state;
4933         if (!state ) {
4934             this.el.removeClass('disabled');
4935         } else if (!this.el.hasClass('disabled')) {
4936             this.el.addClass('disabled');
4937         }
4938         
4939     },
4940     
4941     /**
4942      * Fetch the element to display the tooltip on.
4943      * @return {Roo.Element} defaults to this.el
4944      */
4945     tooltipEl : function()
4946     {
4947         return this.el.select('' + this.tagtype + '', true).first();
4948     },
4949     
4950     scrollToElement : function(e)
4951     {
4952         var c = document.body;
4953         
4954         /*
4955          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4956          */
4957         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4958             c = document.documentElement;
4959         }
4960         
4961         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4962         
4963         if(!target){
4964             return;
4965         }
4966
4967         var o = target.calcOffsetsTo(c);
4968         
4969         var options = {
4970             target : target,
4971             value : o[1]
4972         };
4973         
4974         this.fireEvent('scrollto', this, options, e);
4975         
4976         Roo.get(c).scrollTo('top', options.value, true);
4977         
4978         return;
4979     }
4980 });
4981  
4982
4983  /*
4984  * - LGPL
4985  *
4986  * sidebar item
4987  *
4988  *  li
4989  *    <span> icon </span>
4990  *    <span> text </span>
4991  *    <span>badge </span>
4992  */
4993
4994 /**
4995  * @class Roo.bootstrap.NavSidebarItem
4996  * @extends Roo.bootstrap.NavItem
4997  * Bootstrap Navbar.NavSidebarItem class
4998  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4999  * {Boolean} open is the menu open
5000  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5001  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5002  * {String} buttonSize (sm|md|lg)the extra classes for the button
5003  * {Boolean} showArrow show arrow next to the text (default true)
5004  * @constructor
5005  * Create a new Navbar Button
5006  * @param {Object} config The config object
5007  */
5008 Roo.bootstrap.NavSidebarItem = function(config){
5009     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5010     this.addEvents({
5011         // raw events
5012         /**
5013          * @event click
5014          * The raw click event for the entire grid.
5015          * @param {Roo.EventObject} e
5016          */
5017         "click" : true,
5018          /**
5019             * @event changed
5020             * Fires when the active item active state changes
5021             * @param {Roo.bootstrap.NavSidebarItem} this
5022             * @param {boolean} state the new state
5023              
5024          */
5025         'changed': true
5026     });
5027    
5028 };
5029
5030 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5031     
5032     badgeWeight : 'default',
5033     
5034     open: false,
5035     
5036     buttonView : false,
5037     
5038     buttonWeight : 'default',
5039     
5040     buttonSize : 'md',
5041     
5042     showArrow : true,
5043     
5044     getAutoCreate : function(){
5045         
5046         
5047         var a = {
5048                 tag: 'a',
5049                 href : this.href || '#',
5050                 cls: '',
5051                 html : '',
5052                 cn : []
5053         };
5054         
5055         if(this.buttonView){
5056             a = {
5057                 tag: 'button',
5058                 href : this.href || '#',
5059                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5060                 html : this.html,
5061                 cn : []
5062             };
5063         }
5064         
5065         var cfg = {
5066             tag: 'li',
5067             cls: '',
5068             cn: [ a ]
5069         };
5070         
5071         if (this.active) {
5072             cfg.cls += ' active';
5073         }
5074         
5075         if (this.disabled) {
5076             cfg.cls += ' disabled';
5077         }
5078         if (this.open) {
5079             cfg.cls += ' open x-open';
5080         }
5081         // left icon..
5082         if (this.glyphicon || this.icon) {
5083             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5084             a.cn.push({ tag : 'i', cls : c }) ;
5085         }
5086         
5087         if(!this.buttonView){
5088             var span = {
5089                 tag: 'span',
5090                 html : this.html || ''
5091             };
5092
5093             a.cn.push(span);
5094             
5095         }
5096         
5097         if (this.badge !== '') {
5098             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5099         }
5100         
5101         if (this.menu) {
5102             
5103             if(this.showArrow){
5104                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5105             }
5106             
5107             a.cls += ' dropdown-toggle treeview' ;
5108         }
5109         
5110         return cfg;
5111     },
5112     
5113     initEvents : function()
5114     { 
5115         if (typeof (this.menu) != 'undefined') {
5116             this.menu.parentType = this.xtype;
5117             this.menu.triggerEl = this.el;
5118             this.menu = this.addxtype(Roo.apply({}, this.menu));
5119         }
5120         
5121         this.el.on('click', this.onClick, this);
5122         
5123         if(this.badge !== ''){
5124             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5125         }
5126         
5127     },
5128     
5129     onClick : function(e)
5130     {
5131         if(this.disabled){
5132             e.preventDefault();
5133             return;
5134         }
5135         
5136         if(this.preventDefault){
5137             e.preventDefault();
5138         }
5139         
5140         this.fireEvent('click', this);
5141     },
5142     
5143     disable : function()
5144     {
5145         this.setDisabled(true);
5146     },
5147     
5148     enable : function()
5149     {
5150         this.setDisabled(false);
5151     },
5152     
5153     setDisabled : function(state)
5154     {
5155         if(this.disabled == state){
5156             return;
5157         }
5158         
5159         this.disabled = state;
5160         
5161         if (state) {
5162             this.el.addClass('disabled');
5163             return;
5164         }
5165         
5166         this.el.removeClass('disabled');
5167         
5168         return;
5169     },
5170     
5171     setActive : function(state)
5172     {
5173         if(this.active == state){
5174             return;
5175         }
5176         
5177         this.active = state;
5178         
5179         if (state) {
5180             this.el.addClass('active');
5181             return;
5182         }
5183         
5184         this.el.removeClass('active');
5185         
5186         return;
5187     },
5188     
5189     isActive: function () 
5190     {
5191         return this.active;
5192     },
5193     
5194     setBadge : function(str)
5195     {
5196         if(!this.badgeEl){
5197             return;
5198         }
5199         
5200         this.badgeEl.dom.innerHTML = str;
5201     }
5202     
5203    
5204      
5205  
5206 });
5207  
5208
5209  /*
5210  * - LGPL
5211  *
5212  * row
5213  * 
5214  */
5215
5216 /**
5217  * @class Roo.bootstrap.Row
5218  * @extends Roo.bootstrap.Component
5219  * Bootstrap Row class (contains columns...)
5220  * 
5221  * @constructor
5222  * Create a new Row
5223  * @param {Object} config The config object
5224  */
5225
5226 Roo.bootstrap.Row = function(config){
5227     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5228 };
5229
5230 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5231     
5232     getAutoCreate : function(){
5233        return {
5234             cls: 'row clearfix'
5235        };
5236     }
5237     
5238     
5239 });
5240
5241  
5242
5243  /*
5244  * - LGPL
5245  *
5246  * element
5247  * 
5248  */
5249
5250 /**
5251  * @class Roo.bootstrap.Element
5252  * @extends Roo.bootstrap.Component
5253  * Bootstrap Element class
5254  * @cfg {String} html contents of the element
5255  * @cfg {String} tag tag of the element
5256  * @cfg {String} cls class of the element
5257  * @cfg {Boolean} preventDefault (true|false) default false
5258  * @cfg {Boolean} clickable (true|false) default false
5259  * 
5260  * @constructor
5261  * Create a new Element
5262  * @param {Object} config The config object
5263  */
5264
5265 Roo.bootstrap.Element = function(config){
5266     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5267     
5268     this.addEvents({
5269         // raw events
5270         /**
5271          * @event click
5272          * When a element is chick
5273          * @param {Roo.bootstrap.Element} this
5274          * @param {Roo.EventObject} e
5275          */
5276         "click" : true
5277     });
5278 };
5279
5280 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5281     
5282     tag: 'div',
5283     cls: '',
5284     html: '',
5285     preventDefault: false, 
5286     clickable: false,
5287     
5288     getAutoCreate : function(){
5289         
5290         var cfg = {
5291             tag: this.tag,
5292             // cls: this.cls, double assign in parent class Component.js :: onRender
5293             html: this.html
5294         };
5295         
5296         return cfg;
5297     },
5298     
5299     initEvents: function() 
5300     {
5301         Roo.bootstrap.Element.superclass.initEvents.call(this);
5302         
5303         if(this.clickable){
5304             this.el.on('click', this.onClick, this);
5305         }
5306         
5307     },
5308     
5309     onClick : function(e)
5310     {
5311         if(this.preventDefault){
5312             e.preventDefault();
5313         }
5314         
5315         this.fireEvent('click', this, e);
5316     },
5317     
5318     getValue : function()
5319     {
5320         return this.el.dom.innerHTML;
5321     },
5322     
5323     setValue : function(value)
5324     {
5325         this.el.dom.innerHTML = value;
5326     }
5327    
5328 });
5329
5330  
5331
5332  /*
5333  * - LGPL
5334  *
5335  * pagination
5336  * 
5337  */
5338
5339 /**
5340  * @class Roo.bootstrap.Pagination
5341  * @extends Roo.bootstrap.Component
5342  * Bootstrap Pagination class
5343  * @cfg {String} size xs | sm | md | lg
5344  * @cfg {Boolean} inverse false | true
5345  * 
5346  * @constructor
5347  * Create a new Pagination
5348  * @param {Object} config The config object
5349  */
5350
5351 Roo.bootstrap.Pagination = function(config){
5352     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5353 };
5354
5355 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5356     
5357     cls: false,
5358     size: false,
5359     inverse: false,
5360     
5361     getAutoCreate : function(){
5362         var cfg = {
5363             tag: 'ul',
5364                 cls: 'pagination'
5365         };
5366         if (this.inverse) {
5367             cfg.cls += ' inverse';
5368         }
5369         if (this.html) {
5370             cfg.html=this.html;
5371         }
5372         if (this.cls) {
5373             cfg.cls += " " + this.cls;
5374         }
5375         return cfg;
5376     }
5377    
5378 });
5379
5380  
5381
5382  /*
5383  * - LGPL
5384  *
5385  * Pagination item
5386  * 
5387  */
5388
5389
5390 /**
5391  * @class Roo.bootstrap.PaginationItem
5392  * @extends Roo.bootstrap.Component
5393  * Bootstrap PaginationItem class
5394  * @cfg {String} html text
5395  * @cfg {String} href the link
5396  * @cfg {Boolean} preventDefault (true | false) default true
5397  * @cfg {Boolean} active (true | false) default false
5398  * @cfg {Boolean} disabled default false
5399  * 
5400  * 
5401  * @constructor
5402  * Create a new PaginationItem
5403  * @param {Object} config The config object
5404  */
5405
5406
5407 Roo.bootstrap.PaginationItem = function(config){
5408     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5409     this.addEvents({
5410         // raw events
5411         /**
5412          * @event click
5413          * The raw click event for the entire grid.
5414          * @param {Roo.EventObject} e
5415          */
5416         "click" : true
5417     });
5418 };
5419
5420 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5421     
5422     href : false,
5423     html : false,
5424     preventDefault: true,
5425     active : false,
5426     cls : false,
5427     disabled: false,
5428     
5429     getAutoCreate : function(){
5430         var cfg= {
5431             tag: 'li',
5432             cn: [
5433                 {
5434                     tag : 'a',
5435                     href : this.href ? this.href : '#',
5436                     html : this.html ? this.html : ''
5437                 }
5438             ]
5439         };
5440         
5441         if(this.cls){
5442             cfg.cls = this.cls;
5443         }
5444         
5445         if(this.disabled){
5446             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5447         }
5448         
5449         if(this.active){
5450             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5451         }
5452         
5453         return cfg;
5454     },
5455     
5456     initEvents: function() {
5457         
5458         this.el.on('click', this.onClick, this);
5459         
5460     },
5461     onClick : function(e)
5462     {
5463         Roo.log('PaginationItem on click ');
5464         if(this.preventDefault){
5465             e.preventDefault();
5466         }
5467         
5468         if(this.disabled){
5469             return;
5470         }
5471         
5472         this.fireEvent('click', this, e);
5473     }
5474    
5475 });
5476
5477  
5478
5479  /*
5480  * - LGPL
5481  *
5482  * slider
5483  * 
5484  */
5485
5486
5487 /**
5488  * @class Roo.bootstrap.Slider
5489  * @extends Roo.bootstrap.Component
5490  * Bootstrap Slider class
5491  *    
5492  * @constructor
5493  * Create a new Slider
5494  * @param {Object} config The config object
5495  */
5496
5497 Roo.bootstrap.Slider = function(config){
5498     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5499 };
5500
5501 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5502     
5503     getAutoCreate : function(){
5504         
5505         var cfg = {
5506             tag: 'div',
5507             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5508             cn: [
5509                 {
5510                     tag: 'a',
5511                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5512                 }
5513             ]
5514         };
5515         
5516         return cfg;
5517     }
5518    
5519 });
5520
5521  /*
5522  * Based on:
5523  * Ext JS Library 1.1.1
5524  * Copyright(c) 2006-2007, Ext JS, LLC.
5525  *
5526  * Originally Released Under LGPL - original licence link has changed is not relivant.
5527  *
5528  * Fork - LGPL
5529  * <script type="text/javascript">
5530  */
5531  
5532
5533 /**
5534  * @class Roo.grid.ColumnModel
5535  * @extends Roo.util.Observable
5536  * This is the default implementation of a ColumnModel used by the Grid. It defines
5537  * the columns in the grid.
5538  * <br>Usage:<br>
5539  <pre><code>
5540  var colModel = new Roo.grid.ColumnModel([
5541         {header: "Ticker", width: 60, sortable: true, locked: true},
5542         {header: "Company Name", width: 150, sortable: true},
5543         {header: "Market Cap.", width: 100, sortable: true},
5544         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5545         {header: "Employees", width: 100, sortable: true, resizable: false}
5546  ]);
5547  </code></pre>
5548  * <p>
5549  
5550  * The config options listed for this class are options which may appear in each
5551  * individual column definition.
5552  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5553  * @constructor
5554  * @param {Object} config An Array of column config objects. See this class's
5555  * config objects for details.
5556 */
5557 Roo.grid.ColumnModel = function(config){
5558         /**
5559      * The config passed into the constructor
5560      */
5561     this.config = config;
5562     this.lookup = {};
5563
5564     // if no id, create one
5565     // if the column does not have a dataIndex mapping,
5566     // map it to the order it is in the config
5567     for(var i = 0, len = config.length; i < len; i++){
5568         var c = config[i];
5569         if(typeof c.dataIndex == "undefined"){
5570             c.dataIndex = i;
5571         }
5572         if(typeof c.renderer == "string"){
5573             c.renderer = Roo.util.Format[c.renderer];
5574         }
5575         if(typeof c.id == "undefined"){
5576             c.id = Roo.id();
5577         }
5578         if(c.editor && c.editor.xtype){
5579             c.editor  = Roo.factory(c.editor, Roo.grid);
5580         }
5581         if(c.editor && c.editor.isFormField){
5582             c.editor = new Roo.grid.GridEditor(c.editor);
5583         }
5584         this.lookup[c.id] = c;
5585     }
5586
5587     /**
5588      * The width of columns which have no width specified (defaults to 100)
5589      * @type Number
5590      */
5591     this.defaultWidth = 100;
5592
5593     /**
5594      * Default sortable of columns which have no sortable specified (defaults to false)
5595      * @type Boolean
5596      */
5597     this.defaultSortable = false;
5598
5599     this.addEvents({
5600         /**
5601              * @event widthchange
5602              * Fires when the width of a column changes.
5603              * @param {ColumnModel} this
5604              * @param {Number} columnIndex The column index
5605              * @param {Number} newWidth The new width
5606              */
5607             "widthchange": true,
5608         /**
5609              * @event headerchange
5610              * Fires when the text of a header changes.
5611              * @param {ColumnModel} this
5612              * @param {Number} columnIndex The column index
5613              * @param {Number} newText The new header text
5614              */
5615             "headerchange": true,
5616         /**
5617              * @event hiddenchange
5618              * Fires when a column is hidden or "unhidden".
5619              * @param {ColumnModel} this
5620              * @param {Number} columnIndex The column index
5621              * @param {Boolean} hidden true if hidden, false otherwise
5622              */
5623             "hiddenchange": true,
5624             /**
5625          * @event columnmoved
5626          * Fires when a column is moved.
5627          * @param {ColumnModel} this
5628          * @param {Number} oldIndex
5629          * @param {Number} newIndex
5630          */
5631         "columnmoved" : true,
5632         /**
5633          * @event columlockchange
5634          * Fires when a column's locked state is changed
5635          * @param {ColumnModel} this
5636          * @param {Number} colIndex
5637          * @param {Boolean} locked true if locked
5638          */
5639         "columnlockchange" : true
5640     });
5641     Roo.grid.ColumnModel.superclass.constructor.call(this);
5642 };
5643 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5644     /**
5645      * @cfg {String} header The header text to display in the Grid view.
5646      */
5647     /**
5648      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5649      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5650      * specified, the column's index is used as an index into the Record's data Array.
5651      */
5652     /**
5653      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5654      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5655      */
5656     /**
5657      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5658      * Defaults to the value of the {@link #defaultSortable} property.
5659      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5660      */
5661     /**
5662      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5663      */
5664     /**
5665      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5666      */
5667     /**
5668      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5669      */
5670     /**
5671      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5672      */
5673     /**
5674      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5675      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5676      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5677      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5678      */
5679        /**
5680      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5681      */
5682     /**
5683      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5684      */
5685     /**
5686      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5687      */
5688     /**
5689      * @cfg {String} cursor (Optional)
5690      */
5691     /**
5692      * @cfg {String} tooltip (Optional)
5693      */
5694     /**
5695      * @cfg {Number} xs (Optional)
5696      */
5697     /**
5698      * @cfg {Number} sm (Optional)
5699      */
5700     /**
5701      * @cfg {Number} md (Optional)
5702      */
5703     /**
5704      * @cfg {Number} lg (Optional)
5705      */
5706     /**
5707      * Returns the id of the column at the specified index.
5708      * @param {Number} index The column index
5709      * @return {String} the id
5710      */
5711     getColumnId : function(index){
5712         return this.config[index].id;
5713     },
5714
5715     /**
5716      * Returns the column for a specified id.
5717      * @param {String} id The column id
5718      * @return {Object} the column
5719      */
5720     getColumnById : function(id){
5721         return this.lookup[id];
5722     },
5723
5724     
5725     /**
5726      * Returns the column for a specified dataIndex.
5727      * @param {String} dataIndex The column dataIndex
5728      * @return {Object|Boolean} the column or false if not found
5729      */
5730     getColumnByDataIndex: function(dataIndex){
5731         var index = this.findColumnIndex(dataIndex);
5732         return index > -1 ? this.config[index] : false;
5733     },
5734     
5735     /**
5736      * Returns the index for a specified column id.
5737      * @param {String} id The column id
5738      * @return {Number} the index, or -1 if not found
5739      */
5740     getIndexById : function(id){
5741         for(var i = 0, len = this.config.length; i < len; i++){
5742             if(this.config[i].id == id){
5743                 return i;
5744             }
5745         }
5746         return -1;
5747     },
5748     
5749     /**
5750      * Returns the index for a specified column dataIndex.
5751      * @param {String} dataIndex The column dataIndex
5752      * @return {Number} the index, or -1 if not found
5753      */
5754     
5755     findColumnIndex : function(dataIndex){
5756         for(var i = 0, len = this.config.length; i < len; i++){
5757             if(this.config[i].dataIndex == dataIndex){
5758                 return i;
5759             }
5760         }
5761         return -1;
5762     },
5763     
5764     
5765     moveColumn : function(oldIndex, newIndex){
5766         var c = this.config[oldIndex];
5767         this.config.splice(oldIndex, 1);
5768         this.config.splice(newIndex, 0, c);
5769         this.dataMap = null;
5770         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5771     },
5772
5773     isLocked : function(colIndex){
5774         return this.config[colIndex].locked === true;
5775     },
5776
5777     setLocked : function(colIndex, value, suppressEvent){
5778         if(this.isLocked(colIndex) == value){
5779             return;
5780         }
5781         this.config[colIndex].locked = value;
5782         if(!suppressEvent){
5783             this.fireEvent("columnlockchange", this, colIndex, value);
5784         }
5785     },
5786
5787     getTotalLockedWidth : function(){
5788         var totalWidth = 0;
5789         for(var i = 0; i < this.config.length; i++){
5790             if(this.isLocked(i) && !this.isHidden(i)){
5791                 this.totalWidth += this.getColumnWidth(i);
5792             }
5793         }
5794         return totalWidth;
5795     },
5796
5797     getLockedCount : function(){
5798         for(var i = 0, len = this.config.length; i < len; i++){
5799             if(!this.isLocked(i)){
5800                 return i;
5801             }
5802         }
5803         
5804         return this.config.length;
5805     },
5806
5807     /**
5808      * Returns the number of columns.
5809      * @return {Number}
5810      */
5811     getColumnCount : function(visibleOnly){
5812         if(visibleOnly === true){
5813             var c = 0;
5814             for(var i = 0, len = this.config.length; i < len; i++){
5815                 if(!this.isHidden(i)){
5816                     c++;
5817                 }
5818             }
5819             return c;
5820         }
5821         return this.config.length;
5822     },
5823
5824     /**
5825      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5826      * @param {Function} fn
5827      * @param {Object} scope (optional)
5828      * @return {Array} result
5829      */
5830     getColumnsBy : function(fn, scope){
5831         var r = [];
5832         for(var i = 0, len = this.config.length; i < len; i++){
5833             var c = this.config[i];
5834             if(fn.call(scope||this, c, i) === true){
5835                 r[r.length] = c;
5836             }
5837         }
5838         return r;
5839     },
5840
5841     /**
5842      * Returns true if the specified column is sortable.
5843      * @param {Number} col The column index
5844      * @return {Boolean}
5845      */
5846     isSortable : function(col){
5847         if(typeof this.config[col].sortable == "undefined"){
5848             return this.defaultSortable;
5849         }
5850         return this.config[col].sortable;
5851     },
5852
5853     /**
5854      * Returns the rendering (formatting) function defined for the column.
5855      * @param {Number} col The column index.
5856      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5857      */
5858     getRenderer : function(col){
5859         if(!this.config[col].renderer){
5860             return Roo.grid.ColumnModel.defaultRenderer;
5861         }
5862         return this.config[col].renderer;
5863     },
5864
5865     /**
5866      * Sets the rendering (formatting) function for a column.
5867      * @param {Number} col The column index
5868      * @param {Function} fn The function to use to process the cell's raw data
5869      * to return HTML markup for the grid view. The render function is called with
5870      * the following parameters:<ul>
5871      * <li>Data value.</li>
5872      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5873      * <li>css A CSS style string to apply to the table cell.</li>
5874      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5875      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5876      * <li>Row index</li>
5877      * <li>Column index</li>
5878      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5879      */
5880     setRenderer : function(col, fn){
5881         this.config[col].renderer = fn;
5882     },
5883
5884     /**
5885      * Returns the width for the specified column.
5886      * @param {Number} col The column index
5887      * @return {Number}
5888      */
5889     getColumnWidth : function(col){
5890         return this.config[col].width * 1 || this.defaultWidth;
5891     },
5892
5893     /**
5894      * Sets the width for a column.
5895      * @param {Number} col The column index
5896      * @param {Number} width The new width
5897      */
5898     setColumnWidth : function(col, width, suppressEvent){
5899         this.config[col].width = width;
5900         this.totalWidth = null;
5901         if(!suppressEvent){
5902              this.fireEvent("widthchange", this, col, width);
5903         }
5904     },
5905
5906     /**
5907      * Returns the total width of all columns.
5908      * @param {Boolean} includeHidden True to include hidden column widths
5909      * @return {Number}
5910      */
5911     getTotalWidth : function(includeHidden){
5912         if(!this.totalWidth){
5913             this.totalWidth = 0;
5914             for(var i = 0, len = this.config.length; i < len; i++){
5915                 if(includeHidden || !this.isHidden(i)){
5916                     this.totalWidth += this.getColumnWidth(i);
5917                 }
5918             }
5919         }
5920         return this.totalWidth;
5921     },
5922
5923     /**
5924      * Returns the header for the specified column.
5925      * @param {Number} col The column index
5926      * @return {String}
5927      */
5928     getColumnHeader : function(col){
5929         return this.config[col].header;
5930     },
5931
5932     /**
5933      * Sets the header for a column.
5934      * @param {Number} col The column index
5935      * @param {String} header The new header
5936      */
5937     setColumnHeader : function(col, header){
5938         this.config[col].header = header;
5939         this.fireEvent("headerchange", this, col, header);
5940     },
5941
5942     /**
5943      * Returns the tooltip for the specified column.
5944      * @param {Number} col The column index
5945      * @return {String}
5946      */
5947     getColumnTooltip : function(col){
5948             return this.config[col].tooltip;
5949     },
5950     /**
5951      * Sets the tooltip for a column.
5952      * @param {Number} col The column index
5953      * @param {String} tooltip The new tooltip
5954      */
5955     setColumnTooltip : function(col, tooltip){
5956             this.config[col].tooltip = tooltip;
5957     },
5958
5959     /**
5960      * Returns the dataIndex for the specified column.
5961      * @param {Number} col The column index
5962      * @return {Number}
5963      */
5964     getDataIndex : function(col){
5965         return this.config[col].dataIndex;
5966     },
5967
5968     /**
5969      * Sets the dataIndex for a column.
5970      * @param {Number} col The column index
5971      * @param {Number} dataIndex The new dataIndex
5972      */
5973     setDataIndex : function(col, dataIndex){
5974         this.config[col].dataIndex = dataIndex;
5975     },
5976
5977     
5978     
5979     /**
5980      * Returns true if the cell is editable.
5981      * @param {Number} colIndex The column index
5982      * @param {Number} rowIndex The row index - this is nto actually used..?
5983      * @return {Boolean}
5984      */
5985     isCellEditable : function(colIndex, rowIndex){
5986         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5987     },
5988
5989     /**
5990      * Returns the editor defined for the cell/column.
5991      * return false or null to disable editing.
5992      * @param {Number} colIndex The column index
5993      * @param {Number} rowIndex The row index
5994      * @return {Object}
5995      */
5996     getCellEditor : function(colIndex, rowIndex){
5997         return this.config[colIndex].editor;
5998     },
5999
6000     /**
6001      * Sets if a column is editable.
6002      * @param {Number} col The column index
6003      * @param {Boolean} editable True if the column is editable
6004      */
6005     setEditable : function(col, editable){
6006         this.config[col].editable = editable;
6007     },
6008
6009
6010     /**
6011      * Returns true if the column is hidden.
6012      * @param {Number} colIndex The column index
6013      * @return {Boolean}
6014      */
6015     isHidden : function(colIndex){
6016         return this.config[colIndex].hidden;
6017     },
6018
6019
6020     /**
6021      * Returns true if the column width cannot be changed
6022      */
6023     isFixed : function(colIndex){
6024         return this.config[colIndex].fixed;
6025     },
6026
6027     /**
6028      * Returns true if the column can be resized
6029      * @return {Boolean}
6030      */
6031     isResizable : function(colIndex){
6032         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6033     },
6034     /**
6035      * Sets if a column is hidden.
6036      * @param {Number} colIndex The column index
6037      * @param {Boolean} hidden True if the column is hidden
6038      */
6039     setHidden : function(colIndex, hidden){
6040         this.config[colIndex].hidden = hidden;
6041         this.totalWidth = null;
6042         this.fireEvent("hiddenchange", this, colIndex, hidden);
6043     },
6044
6045     /**
6046      * Sets the editor for a column.
6047      * @param {Number} col The column index
6048      * @param {Object} editor The editor object
6049      */
6050     setEditor : function(col, editor){
6051         this.config[col].editor = editor;
6052     }
6053 });
6054
6055 Roo.grid.ColumnModel.defaultRenderer = function(value)
6056 {
6057     if(typeof value == "object") {
6058         return value;
6059     }
6060         if(typeof value == "string" && value.length < 1){
6061             return "&#160;";
6062         }
6063     
6064         return String.format("{0}", value);
6065 };
6066
6067 // Alias for backwards compatibility
6068 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6069 /*
6070  * Based on:
6071  * Ext JS Library 1.1.1
6072  * Copyright(c) 2006-2007, Ext JS, LLC.
6073  *
6074  * Originally Released Under LGPL - original licence link has changed is not relivant.
6075  *
6076  * Fork - LGPL
6077  * <script type="text/javascript">
6078  */
6079  
6080 /**
6081  * @class Roo.LoadMask
6082  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6083  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6084  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6085  * element's UpdateManager load indicator and will be destroyed after the initial load.
6086  * @constructor
6087  * Create a new LoadMask
6088  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6089  * @param {Object} config The config object
6090  */
6091 Roo.LoadMask = function(el, config){
6092     this.el = Roo.get(el);
6093     Roo.apply(this, config);
6094     if(this.store){
6095         this.store.on('beforeload', this.onBeforeLoad, this);
6096         this.store.on('load', this.onLoad, this);
6097         this.store.on('loadexception', this.onLoadException, this);
6098         this.removeMask = false;
6099     }else{
6100         var um = this.el.getUpdateManager();
6101         um.showLoadIndicator = false; // disable the default indicator
6102         um.on('beforeupdate', this.onBeforeLoad, this);
6103         um.on('update', this.onLoad, this);
6104         um.on('failure', this.onLoad, this);
6105         this.removeMask = true;
6106     }
6107 };
6108
6109 Roo.LoadMask.prototype = {
6110     /**
6111      * @cfg {Boolean} removeMask
6112      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6113      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6114      */
6115     /**
6116      * @cfg {String} msg
6117      * The text to display in a centered loading message box (defaults to 'Loading...')
6118      */
6119     msg : 'Loading...',
6120     /**
6121      * @cfg {String} msgCls
6122      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6123      */
6124     msgCls : 'x-mask-loading',
6125
6126     /**
6127      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6128      * @type Boolean
6129      */
6130     disabled: false,
6131
6132     /**
6133      * Disables the mask to prevent it from being displayed
6134      */
6135     disable : function(){
6136        this.disabled = true;
6137     },
6138
6139     /**
6140      * Enables the mask so that it can be displayed
6141      */
6142     enable : function(){
6143         this.disabled = false;
6144     },
6145     
6146     onLoadException : function()
6147     {
6148         Roo.log(arguments);
6149         
6150         if (typeof(arguments[3]) != 'undefined') {
6151             Roo.MessageBox.alert("Error loading",arguments[3]);
6152         } 
6153         /*
6154         try {
6155             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6156                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6157             }   
6158         } catch(e) {
6159             
6160         }
6161         */
6162     
6163         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6164     },
6165     // private
6166     onLoad : function()
6167     {
6168         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6169     },
6170
6171     // private
6172     onBeforeLoad : function(){
6173         if(!this.disabled){
6174             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6175         }
6176     },
6177
6178     // private
6179     destroy : function(){
6180         if(this.store){
6181             this.store.un('beforeload', this.onBeforeLoad, this);
6182             this.store.un('load', this.onLoad, this);
6183             this.store.un('loadexception', this.onLoadException, this);
6184         }else{
6185             var um = this.el.getUpdateManager();
6186             um.un('beforeupdate', this.onBeforeLoad, this);
6187             um.un('update', this.onLoad, this);
6188             um.un('failure', this.onLoad, this);
6189         }
6190     }
6191 };/*
6192  * - LGPL
6193  *
6194  * table
6195  * 
6196  */
6197
6198 /**
6199  * @class Roo.bootstrap.Table
6200  * @extends Roo.bootstrap.Component
6201  * Bootstrap Table class
6202  * @cfg {String} cls table class
6203  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6204  * @cfg {String} bgcolor Specifies the background color for a table
6205  * @cfg {Number} border Specifies whether the table cells should have borders or not
6206  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6207  * @cfg {Number} cellspacing Specifies the space between cells
6208  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6209  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6210  * @cfg {String} sortable Specifies that the table should be sortable
6211  * @cfg {String} summary Specifies a summary of the content of a table
6212  * @cfg {Number} width Specifies the width of a table
6213  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6214  * 
6215  * @cfg {boolean} striped Should the rows be alternative striped
6216  * @cfg {boolean} bordered Add borders to the table
6217  * @cfg {boolean} hover Add hover highlighting
6218  * @cfg {boolean} condensed Format condensed
6219  * @cfg {boolean} responsive Format condensed
6220  * @cfg {Boolean} loadMask (true|false) default false
6221  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6222  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6223  * @cfg {Boolean} rowSelection (true|false) default false
6224  * @cfg {Boolean} cellSelection (true|false) default false
6225  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6226  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6227  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6228  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6229  
6230  * 
6231  * @constructor
6232  * Create a new Table
6233  * @param {Object} config The config object
6234  */
6235
6236 Roo.bootstrap.Table = function(config){
6237     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6238     
6239   
6240     
6241     // BC...
6242     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6243     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6244     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6245     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6246     
6247     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6248     if (this.sm) {
6249         this.sm.grid = this;
6250         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6251         this.sm = this.selModel;
6252         this.sm.xmodule = this.xmodule || false;
6253     }
6254     
6255     if (this.cm && typeof(this.cm.config) == 'undefined') {
6256         this.colModel = new Roo.grid.ColumnModel(this.cm);
6257         this.cm = this.colModel;
6258         this.cm.xmodule = this.xmodule || false;
6259     }
6260     if (this.store) {
6261         this.store= Roo.factory(this.store, Roo.data);
6262         this.ds = this.store;
6263         this.ds.xmodule = this.xmodule || false;
6264          
6265     }
6266     if (this.footer && this.store) {
6267         this.footer.dataSource = this.ds;
6268         this.footer = Roo.factory(this.footer);
6269     }
6270     
6271     /** @private */
6272     this.addEvents({
6273         /**
6274          * @event cellclick
6275          * Fires when a cell is clicked
6276          * @param {Roo.bootstrap.Table} this
6277          * @param {Roo.Element} el
6278          * @param {Number} rowIndex
6279          * @param {Number} columnIndex
6280          * @param {Roo.EventObject} e
6281          */
6282         "cellclick" : true,
6283         /**
6284          * @event celldblclick
6285          * Fires when a cell is double clicked
6286          * @param {Roo.bootstrap.Table} this
6287          * @param {Roo.Element} el
6288          * @param {Number} rowIndex
6289          * @param {Number} columnIndex
6290          * @param {Roo.EventObject} e
6291          */
6292         "celldblclick" : true,
6293         /**
6294          * @event rowclick
6295          * Fires when a row is clicked
6296          * @param {Roo.bootstrap.Table} this
6297          * @param {Roo.Element} el
6298          * @param {Number} rowIndex
6299          * @param {Roo.EventObject} e
6300          */
6301         "rowclick" : true,
6302         /**
6303          * @event rowdblclick
6304          * Fires when a row is double clicked
6305          * @param {Roo.bootstrap.Table} this
6306          * @param {Roo.Element} el
6307          * @param {Number} rowIndex
6308          * @param {Roo.EventObject} e
6309          */
6310         "rowdblclick" : true,
6311         /**
6312          * @event mouseover
6313          * Fires when a mouseover occur
6314          * @param {Roo.bootstrap.Table} this
6315          * @param {Roo.Element} el
6316          * @param {Number} rowIndex
6317          * @param {Number} columnIndex
6318          * @param {Roo.EventObject} e
6319          */
6320         "mouseover" : true,
6321         /**
6322          * @event mouseout
6323          * Fires when a mouseout occur
6324          * @param {Roo.bootstrap.Table} this
6325          * @param {Roo.Element} el
6326          * @param {Number} rowIndex
6327          * @param {Number} columnIndex
6328          * @param {Roo.EventObject} e
6329          */
6330         "mouseout" : true,
6331         /**
6332          * @event rowclass
6333          * Fires when a row is rendered, so you can change add a style to it.
6334          * @param {Roo.bootstrap.Table} this
6335          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6336          */
6337         'rowclass' : true,
6338           /**
6339          * @event rowsrendered
6340          * Fires when all the  rows have been rendered
6341          * @param {Roo.bootstrap.Table} this
6342          */
6343         'rowsrendered' : true,
6344         /**
6345          * @event contextmenu
6346          * The raw contextmenu event for the entire grid.
6347          * @param {Roo.EventObject} e
6348          */
6349         "contextmenu" : true,
6350         /**
6351          * @event rowcontextmenu
6352          * Fires when a row is right clicked
6353          * @param {Roo.bootstrap.Table} this
6354          * @param {Number} rowIndex
6355          * @param {Roo.EventObject} e
6356          */
6357         "rowcontextmenu" : true,
6358         /**
6359          * @event cellcontextmenu
6360          * Fires when a cell is right clicked
6361          * @param {Roo.bootstrap.Table} this
6362          * @param {Number} rowIndex
6363          * @param {Number} cellIndex
6364          * @param {Roo.EventObject} e
6365          */
6366          "cellcontextmenu" : true,
6367          /**
6368          * @event headercontextmenu
6369          * Fires when a header is right clicked
6370          * @param {Roo.bootstrap.Table} this
6371          * @param {Number} columnIndex
6372          * @param {Roo.EventObject} e
6373          */
6374         "headercontextmenu" : true
6375     });
6376 };
6377
6378 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6379     
6380     cls: false,
6381     align: false,
6382     bgcolor: false,
6383     border: false,
6384     cellpadding: false,
6385     cellspacing: false,
6386     frame: false,
6387     rules: false,
6388     sortable: false,
6389     summary: false,
6390     width: false,
6391     striped : false,
6392     scrollBody : false,
6393     bordered: false,
6394     hover:  false,
6395     condensed : false,
6396     responsive : false,
6397     sm : false,
6398     cm : false,
6399     store : false,
6400     loadMask : false,
6401     footerShow : true,
6402     headerShow : true,
6403   
6404     rowSelection : false,
6405     cellSelection : false,
6406     layout : false,
6407     
6408     // Roo.Element - the tbody
6409     mainBody: false,
6410     // Roo.Element - thead element
6411     mainHead: false,
6412     
6413     container: false, // used by gridpanel...
6414     
6415     lazyLoad : false,
6416     
6417     CSS : Roo.util.CSS,
6418     
6419     auto_hide_footer : false,
6420     
6421     getAutoCreate : function()
6422     {
6423         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6424         
6425         cfg = {
6426             tag: 'table',
6427             cls : 'table',
6428             cn : []
6429         };
6430         if (this.scrollBody) {
6431             cfg.cls += ' table-body-fixed';
6432         }    
6433         if (this.striped) {
6434             cfg.cls += ' table-striped';
6435         }
6436         
6437         if (this.hover) {
6438             cfg.cls += ' table-hover';
6439         }
6440         if (this.bordered) {
6441             cfg.cls += ' table-bordered';
6442         }
6443         if (this.condensed) {
6444             cfg.cls += ' table-condensed';
6445         }
6446         if (this.responsive) {
6447             cfg.cls += ' table-responsive';
6448         }
6449         
6450         if (this.cls) {
6451             cfg.cls+=  ' ' +this.cls;
6452         }
6453         
6454         // this lot should be simplifed...
6455         var _t = this;
6456         var cp = [
6457             'align',
6458             'bgcolor',
6459             'border',
6460             'cellpadding',
6461             'cellspacing',
6462             'frame',
6463             'rules',
6464             'sortable',
6465             'summary',
6466             'width'
6467         ].forEach(function(k) {
6468             if (_t[k]) {
6469                 cfg[k] = _t[k];
6470             }
6471         });
6472         
6473         
6474         if (this.layout) {
6475             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6476         }
6477         
6478         if(this.store || this.cm){
6479             if(this.headerShow){
6480                 cfg.cn.push(this.renderHeader());
6481             }
6482             
6483             cfg.cn.push(this.renderBody());
6484             
6485             if(this.footerShow){
6486                 cfg.cn.push(this.renderFooter());
6487             }
6488             // where does this come from?
6489             //cfg.cls+=  ' TableGrid';
6490         }
6491         
6492         return { cn : [ cfg ] };
6493     },
6494     
6495     initEvents : function()
6496     {   
6497         if(!this.store || !this.cm){
6498             return;
6499         }
6500         if (this.selModel) {
6501             this.selModel.initEvents();
6502         }
6503         
6504         
6505         //Roo.log('initEvents with ds!!!!');
6506         
6507         this.mainBody = this.el.select('tbody', true).first();
6508         this.mainHead = this.el.select('thead', true).first();
6509         this.mainFoot = this.el.select('tfoot', true).first();
6510         
6511         
6512         
6513         var _this = this;
6514         
6515         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6516             e.on('click', _this.sort, _this);
6517         });
6518         
6519         this.mainBody.on("click", this.onClick, this);
6520         this.mainBody.on("dblclick", this.onDblClick, this);
6521         
6522         // why is this done????? = it breaks dialogs??
6523         //this.parent().el.setStyle('position', 'relative');
6524         
6525         
6526         if (this.footer) {
6527             this.footer.parentId = this.id;
6528             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6529             
6530             if(this.lazyLoad){
6531                 this.el.select('tfoot tr td').first().addClass('hide');
6532             }
6533         } 
6534         
6535         if(this.loadMask) {
6536             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6537         }
6538         
6539         this.store.on('load', this.onLoad, this);
6540         this.store.on('beforeload', this.onBeforeLoad, this);
6541         this.store.on('update', this.onUpdate, this);
6542         this.store.on('add', this.onAdd, this);
6543         this.store.on("clear", this.clear, this);
6544         
6545         this.el.on("contextmenu", this.onContextMenu, this);
6546         
6547         this.mainBody.on('scroll', this.onBodyScroll, this);
6548         
6549         this.cm.on("headerchange", this.onHeaderChange, this);
6550         
6551         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6552         
6553     },
6554     
6555     onContextMenu : function(e, t)
6556     {
6557         this.processEvent("contextmenu", e);
6558     },
6559     
6560     processEvent : function(name, e)
6561     {
6562         if (name != 'touchstart' ) {
6563             this.fireEvent(name, e);    
6564         }
6565         
6566         var t = e.getTarget();
6567         
6568         var cell = Roo.get(t);
6569         
6570         if(!cell){
6571             return;
6572         }
6573         
6574         if(cell.findParent('tfoot', false, true)){
6575             return;
6576         }
6577         
6578         if(cell.findParent('thead', false, true)){
6579             
6580             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6581                 cell = Roo.get(t).findParent('th', false, true);
6582                 if (!cell) {
6583                     Roo.log("failed to find th in thead?");
6584                     Roo.log(e.getTarget());
6585                     return;
6586                 }
6587             }
6588             
6589             var cellIndex = cell.dom.cellIndex;
6590             
6591             var ename = name == 'touchstart' ? 'click' : name;
6592             this.fireEvent("header" + ename, this, cellIndex, e);
6593             
6594             return;
6595         }
6596         
6597         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6598             cell = Roo.get(t).findParent('td', false, true);
6599             if (!cell) {
6600                 Roo.log("failed to find th in tbody?");
6601                 Roo.log(e.getTarget());
6602                 return;
6603             }
6604         }
6605         
6606         var row = cell.findParent('tr', false, true);
6607         var cellIndex = cell.dom.cellIndex;
6608         var rowIndex = row.dom.rowIndex - 1;
6609         
6610         if(row !== false){
6611             
6612             this.fireEvent("row" + name, this, rowIndex, e);
6613             
6614             if(cell !== false){
6615             
6616                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6617             }
6618         }
6619         
6620     },
6621     
6622     onMouseover : function(e, el)
6623     {
6624         var cell = Roo.get(el);
6625         
6626         if(!cell){
6627             return;
6628         }
6629         
6630         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6631             cell = cell.findParent('td', false, true);
6632         }
6633         
6634         var row = cell.findParent('tr', false, true);
6635         var cellIndex = cell.dom.cellIndex;
6636         var rowIndex = row.dom.rowIndex - 1; // start from 0
6637         
6638         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6639         
6640     },
6641     
6642     onMouseout : function(e, el)
6643     {
6644         var cell = Roo.get(el);
6645         
6646         if(!cell){
6647             return;
6648         }
6649         
6650         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6651             cell = cell.findParent('td', false, true);
6652         }
6653         
6654         var row = cell.findParent('tr', false, true);
6655         var cellIndex = cell.dom.cellIndex;
6656         var rowIndex = row.dom.rowIndex - 1; // start from 0
6657         
6658         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6659         
6660     },
6661     
6662     onClick : function(e, el)
6663     {
6664         var cell = Roo.get(el);
6665         
6666         if(!cell || (!this.cellSelection && !this.rowSelection)){
6667             return;
6668         }
6669         
6670         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6671             cell = cell.findParent('td', false, true);
6672         }
6673         
6674         if(!cell || typeof(cell) == 'undefined'){
6675             return;
6676         }
6677         
6678         var row = cell.findParent('tr', false, true);
6679         
6680         if(!row || typeof(row) == 'undefined'){
6681             return;
6682         }
6683         
6684         var cellIndex = cell.dom.cellIndex;
6685         var rowIndex = this.getRowIndex(row);
6686         
6687         // why??? - should these not be based on SelectionModel?
6688         if(this.cellSelection){
6689             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6690         }
6691         
6692         if(this.rowSelection){
6693             this.fireEvent('rowclick', this, row, rowIndex, e);
6694         }
6695         
6696         
6697     },
6698         
6699     onDblClick : function(e,el)
6700     {
6701         var cell = Roo.get(el);
6702         
6703         if(!cell || (!this.cellSelection && !this.rowSelection)){
6704             return;
6705         }
6706         
6707         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6708             cell = cell.findParent('td', false, true);
6709         }
6710         
6711         if(!cell || typeof(cell) == 'undefined'){
6712             return;
6713         }
6714         
6715         var row = cell.findParent('tr', false, true);
6716         
6717         if(!row || typeof(row) == 'undefined'){
6718             return;
6719         }
6720         
6721         var cellIndex = cell.dom.cellIndex;
6722         var rowIndex = this.getRowIndex(row);
6723         
6724         if(this.cellSelection){
6725             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6726         }
6727         
6728         if(this.rowSelection){
6729             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6730         }
6731     },
6732     
6733     sort : function(e,el)
6734     {
6735         var col = Roo.get(el);
6736         
6737         if(!col.hasClass('sortable')){
6738             return;
6739         }
6740         
6741         var sort = col.attr('sort');
6742         var dir = 'ASC';
6743         
6744         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6745             dir = 'DESC';
6746         }
6747         
6748         this.store.sortInfo = {field : sort, direction : dir};
6749         
6750         if (this.footer) {
6751             Roo.log("calling footer first");
6752             this.footer.onClick('first');
6753         } else {
6754         
6755             this.store.load({ params : { start : 0 } });
6756         }
6757     },
6758     
6759     renderHeader : function()
6760     {
6761         var header = {
6762             tag: 'thead',
6763             cn : []
6764         };
6765         
6766         var cm = this.cm;
6767         this.totalWidth = 0;
6768         
6769         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6770             
6771             var config = cm.config[i];
6772             
6773             var c = {
6774                 tag: 'th',
6775                 cls : 'x-hcol-' + i,
6776                 style : '',
6777                 html: cm.getColumnHeader(i)
6778             };
6779             
6780             var hh = '';
6781             
6782             if(typeof(config.sortable) != 'undefined' && config.sortable){
6783                 c.cls = 'sortable';
6784                 c.html = '<i class="glyphicon"></i>' + c.html;
6785             }
6786             
6787             if(typeof(config.lgHeader) != 'undefined'){
6788                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6789             }
6790             
6791             if(typeof(config.mdHeader) != 'undefined'){
6792                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6793             }
6794             
6795             if(typeof(config.smHeader) != 'undefined'){
6796                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6797             }
6798             
6799             if(typeof(config.xsHeader) != 'undefined'){
6800                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6801             }
6802             
6803             if(hh.length){
6804                 c.html = hh;
6805             }
6806             
6807             if(typeof(config.tooltip) != 'undefined'){
6808                 c.tooltip = config.tooltip;
6809             }
6810             
6811             if(typeof(config.colspan) != 'undefined'){
6812                 c.colspan = config.colspan;
6813             }
6814             
6815             if(typeof(config.hidden) != 'undefined' && config.hidden){
6816                 c.style += ' display:none;';
6817             }
6818             
6819             if(typeof(config.dataIndex) != 'undefined'){
6820                 c.sort = config.dataIndex;
6821             }
6822             
6823            
6824             
6825             if(typeof(config.align) != 'undefined' && config.align.length){
6826                 c.style += ' text-align:' + config.align + ';';
6827             }
6828             
6829             if(typeof(config.width) != 'undefined'){
6830                 c.style += ' width:' + config.width + 'px;';
6831                 this.totalWidth += config.width;
6832             } else {
6833                 this.totalWidth += 100; // assume minimum of 100 per column?
6834             }
6835             
6836             if(typeof(config.cls) != 'undefined'){
6837                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6838             }
6839             
6840             ['xs','sm','md','lg'].map(function(size){
6841                 
6842                 if(typeof(config[size]) == 'undefined'){
6843                     return;
6844                 }
6845                 
6846                 if (!config[size]) { // 0 = hidden
6847                     c.cls += ' hidden-' + size;
6848                     return;
6849                 }
6850                 
6851                 c.cls += ' col-' + size + '-' + config[size];
6852
6853             });
6854             
6855             header.cn.push(c)
6856         }
6857         
6858         return header;
6859     },
6860     
6861     renderBody : function()
6862     {
6863         var body = {
6864             tag: 'tbody',
6865             cn : [
6866                 {
6867                     tag: 'tr',
6868                     cn : [
6869                         {
6870                             tag : 'td',
6871                             colspan :  this.cm.getColumnCount()
6872                         }
6873                     ]
6874                 }
6875             ]
6876         };
6877         
6878         return body;
6879     },
6880     
6881     renderFooter : function()
6882     {
6883         var footer = {
6884             tag: 'tfoot',
6885             cn : [
6886                 {
6887                     tag: 'tr',
6888                     cn : [
6889                         {
6890                             tag : 'td',
6891                             colspan :  this.cm.getColumnCount()
6892                         }
6893                     ]
6894                 }
6895             ]
6896         };
6897         
6898         return footer;
6899     },
6900     
6901     
6902     
6903     onLoad : function()
6904     {
6905 //        Roo.log('ds onload');
6906         this.clear();
6907         
6908         var _this = this;
6909         var cm = this.cm;
6910         var ds = this.store;
6911         
6912         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6913             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6914             if (_this.store.sortInfo) {
6915                     
6916                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6917                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6918                 }
6919                 
6920                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6921                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6922                 }
6923             }
6924         });
6925         
6926         var tbody =  this.mainBody;
6927               
6928         if(ds.getCount() > 0){
6929             ds.data.each(function(d,rowIndex){
6930                 var row =  this.renderRow(cm, ds, rowIndex);
6931                 
6932                 tbody.createChild(row);
6933                 
6934                 var _this = this;
6935                 
6936                 if(row.cellObjects.length){
6937                     Roo.each(row.cellObjects, function(r){
6938                         _this.renderCellObject(r);
6939                     })
6940                 }
6941                 
6942             }, this);
6943         }
6944         
6945         var tfoot = this.el.select('tfoot', true).first();
6946         
6947         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6948             
6949             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6950             
6951             var total = this.ds.getTotalCount();
6952             
6953             if(this.footer.pageSize < total){
6954                 this.mainFoot.show();
6955             }
6956         }
6957         
6958         Roo.each(this.el.select('tbody td', true).elements, function(e){
6959             e.on('mouseover', _this.onMouseover, _this);
6960         });
6961         
6962         Roo.each(this.el.select('tbody td', true).elements, function(e){
6963             e.on('mouseout', _this.onMouseout, _this);
6964         });
6965         this.fireEvent('rowsrendered', this);
6966         
6967         this.autoSize();
6968     },
6969     
6970     
6971     onUpdate : function(ds,record)
6972     {
6973         this.refreshRow(record);
6974         this.autoSize();
6975     },
6976     
6977     onRemove : function(ds, record, index, isUpdate){
6978         if(isUpdate !== true){
6979             this.fireEvent("beforerowremoved", this, index, record);
6980         }
6981         var bt = this.mainBody.dom;
6982         
6983         var rows = this.el.select('tbody > tr', true).elements;
6984         
6985         if(typeof(rows[index]) != 'undefined'){
6986             bt.removeChild(rows[index].dom);
6987         }
6988         
6989 //        if(bt.rows[index]){
6990 //            bt.removeChild(bt.rows[index]);
6991 //        }
6992         
6993         if(isUpdate !== true){
6994             //this.stripeRows(index);
6995             //this.syncRowHeights(index, index);
6996             //this.layout();
6997             this.fireEvent("rowremoved", this, index, record);
6998         }
6999     },
7000     
7001     onAdd : function(ds, records, rowIndex)
7002     {
7003         //Roo.log('on Add called');
7004         // - note this does not handle multiple adding very well..
7005         var bt = this.mainBody.dom;
7006         for (var i =0 ; i < records.length;i++) {
7007             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7008             //Roo.log(records[i]);
7009             //Roo.log(this.store.getAt(rowIndex+i));
7010             this.insertRow(this.store, rowIndex + i, false);
7011             return;
7012         }
7013         
7014     },
7015     
7016     
7017     refreshRow : function(record){
7018         var ds = this.store, index;
7019         if(typeof record == 'number'){
7020             index = record;
7021             record = ds.getAt(index);
7022         }else{
7023             index = ds.indexOf(record);
7024         }
7025         this.insertRow(ds, index, true);
7026         this.autoSize();
7027         this.onRemove(ds, record, index+1, true);
7028         this.autoSize();
7029         //this.syncRowHeights(index, index);
7030         //this.layout();
7031         this.fireEvent("rowupdated", this, index, record);
7032     },
7033     
7034     insertRow : function(dm, rowIndex, isUpdate){
7035         
7036         if(!isUpdate){
7037             this.fireEvent("beforerowsinserted", this, rowIndex);
7038         }
7039             //var s = this.getScrollState();
7040         var row = this.renderRow(this.cm, this.store, rowIndex);
7041         // insert before rowIndex..
7042         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7043         
7044         var _this = this;
7045                 
7046         if(row.cellObjects.length){
7047             Roo.each(row.cellObjects, function(r){
7048                 _this.renderCellObject(r);
7049             })
7050         }
7051             
7052         if(!isUpdate){
7053             this.fireEvent("rowsinserted", this, rowIndex);
7054             //this.syncRowHeights(firstRow, lastRow);
7055             //this.stripeRows(firstRow);
7056             //this.layout();
7057         }
7058         
7059     },
7060     
7061     
7062     getRowDom : function(rowIndex)
7063     {
7064         var rows = this.el.select('tbody > tr', true).elements;
7065         
7066         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7067         
7068     },
7069     // returns the object tree for a tr..
7070   
7071     
7072     renderRow : function(cm, ds, rowIndex) 
7073     {
7074         var d = ds.getAt(rowIndex);
7075         
7076         var row = {
7077             tag : 'tr',
7078             cls : 'x-row-' + rowIndex,
7079             cn : []
7080         };
7081             
7082         var cellObjects = [];
7083         
7084         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7085             var config = cm.config[i];
7086             
7087             var renderer = cm.getRenderer(i);
7088             var value = '';
7089             var id = false;
7090             
7091             if(typeof(renderer) !== 'undefined'){
7092                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7093             }
7094             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7095             // and are rendered into the cells after the row is rendered - using the id for the element.
7096             
7097             if(typeof(value) === 'object'){
7098                 id = Roo.id();
7099                 cellObjects.push({
7100                     container : id,
7101                     cfg : value 
7102                 })
7103             }
7104             
7105             var rowcfg = {
7106                 record: d,
7107                 rowIndex : rowIndex,
7108                 colIndex : i,
7109                 rowClass : ''
7110             };
7111
7112             this.fireEvent('rowclass', this, rowcfg);
7113             
7114             var td = {
7115                 tag: 'td',
7116                 cls : rowcfg.rowClass + ' x-col-' + i,
7117                 style: '',
7118                 html: (typeof(value) === 'object') ? '' : value
7119             };
7120             
7121             if (id) {
7122                 td.id = id;
7123             }
7124             
7125             if(typeof(config.colspan) != 'undefined'){
7126                 td.colspan = config.colspan;
7127             }
7128             
7129             if(typeof(config.hidden) != 'undefined' && config.hidden){
7130                 td.style += ' display:none;';
7131             }
7132             
7133             if(typeof(config.align) != 'undefined' && config.align.length){
7134                 td.style += ' text-align:' + config.align + ';';
7135             }
7136             if(typeof(config.valign) != 'undefined' && config.valign.length){
7137                 td.style += ' vertical-align:' + config.valign + ';';
7138             }
7139             
7140             if(typeof(config.width) != 'undefined'){
7141                 td.style += ' width:' +  config.width + 'px;';
7142             }
7143             
7144             if(typeof(config.cursor) != 'undefined'){
7145                 td.style += ' cursor:' +  config.cursor + ';';
7146             }
7147             
7148             if(typeof(config.cls) != 'undefined'){
7149                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7150             }
7151             
7152             ['xs','sm','md','lg'].map(function(size){
7153                 
7154                 if(typeof(config[size]) == 'undefined'){
7155                     return;
7156                 }
7157                 
7158                 if (!config[size]) { // 0 = hidden
7159                     td.cls += ' hidden-' + size;
7160                     return;
7161                 }
7162                 
7163                 td.cls += ' col-' + size + '-' + config[size];
7164
7165             });
7166             
7167             row.cn.push(td);
7168            
7169         }
7170         
7171         row.cellObjects = cellObjects;
7172         
7173         return row;
7174           
7175     },
7176     
7177     
7178     
7179     onBeforeLoad : function()
7180     {
7181         
7182     },
7183      /**
7184      * Remove all rows
7185      */
7186     clear : function()
7187     {
7188         this.el.select('tbody', true).first().dom.innerHTML = '';
7189     },
7190     /**
7191      * Show or hide a row.
7192      * @param {Number} rowIndex to show or hide
7193      * @param {Boolean} state hide
7194      */
7195     setRowVisibility : function(rowIndex, state)
7196     {
7197         var bt = this.mainBody.dom;
7198         
7199         var rows = this.el.select('tbody > tr', true).elements;
7200         
7201         if(typeof(rows[rowIndex]) == 'undefined'){
7202             return;
7203         }
7204         rows[rowIndex].dom.style.display = state ? '' : 'none';
7205     },
7206     
7207     
7208     getSelectionModel : function(){
7209         if(!this.selModel){
7210             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7211         }
7212         return this.selModel;
7213     },
7214     /*
7215      * Render the Roo.bootstrap object from renderder
7216      */
7217     renderCellObject : function(r)
7218     {
7219         var _this = this;
7220         
7221         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7222         
7223         var t = r.cfg.render(r.container);
7224         
7225         if(r.cfg.cn){
7226             Roo.each(r.cfg.cn, function(c){
7227                 var child = {
7228                     container: t.getChildContainer(),
7229                     cfg: c
7230                 };
7231                 _this.renderCellObject(child);
7232             })
7233         }
7234     },
7235     
7236     getRowIndex : function(row)
7237     {
7238         var rowIndex = -1;
7239         
7240         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7241             if(el != row){
7242                 return;
7243             }
7244             
7245             rowIndex = index;
7246         });
7247         
7248         return rowIndex;
7249     },
7250      /**
7251      * Returns the grid's underlying element = used by panel.Grid
7252      * @return {Element} The element
7253      */
7254     getGridEl : function(){
7255         return this.el;
7256     },
7257      /**
7258      * Forces a resize - used by panel.Grid
7259      * @return {Element} The element
7260      */
7261     autoSize : function()
7262     {
7263         //var ctr = Roo.get(this.container.dom.parentElement);
7264         var ctr = Roo.get(this.el.dom);
7265         
7266         var thd = this.getGridEl().select('thead',true).first();
7267         var tbd = this.getGridEl().select('tbody', true).first();
7268         var tfd = this.getGridEl().select('tfoot', true).first();
7269         
7270         var cw = ctr.getWidth();
7271         
7272         if (tbd) {
7273             
7274             tbd.setSize(ctr.getWidth(),
7275                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7276             );
7277             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7278             cw -= barsize;
7279         }
7280         cw = Math.max(cw, this.totalWidth);
7281         this.getGridEl().select('tr',true).setWidth(cw);
7282         // resize 'expandable coloumn?
7283         
7284         return; // we doe not have a view in this design..
7285         
7286     },
7287     onBodyScroll: function()
7288     {
7289         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7290         if(this.mainHead){
7291             this.mainHead.setStyle({
7292                 'position' : 'relative',
7293                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7294             });
7295         }
7296         
7297         if(this.lazyLoad){
7298             
7299             var scrollHeight = this.mainBody.dom.scrollHeight;
7300             
7301             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7302             
7303             var height = this.mainBody.getHeight();
7304             
7305             if(scrollHeight - height == scrollTop) {
7306                 
7307                 var total = this.ds.getTotalCount();
7308                 
7309                 if(this.footer.cursor + this.footer.pageSize < total){
7310                     
7311                     this.footer.ds.load({
7312                         params : {
7313                             start : this.footer.cursor + this.footer.pageSize,
7314                             limit : this.footer.pageSize
7315                         },
7316                         add : true
7317                     });
7318                 }
7319             }
7320             
7321         }
7322     },
7323     
7324     onHeaderChange : function()
7325     {
7326         var header = this.renderHeader();
7327         var table = this.el.select('table', true).first();
7328         
7329         this.mainHead.remove();
7330         this.mainHead = table.createChild(header, this.mainBody, false);
7331     },
7332     
7333     onHiddenChange : function(colModel, colIndex, hidden)
7334     {
7335         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7336         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7337         
7338         this.CSS.updateRule(thSelector, "display", "");
7339         this.CSS.updateRule(tdSelector, "display", "");
7340         
7341         if(hidden){
7342             this.CSS.updateRule(thSelector, "display", "none");
7343             this.CSS.updateRule(tdSelector, "display", "none");
7344         }
7345         
7346         this.onHeaderChange();
7347         this.onLoad();
7348     },
7349     
7350     setColumnWidth: function(col_index, width)
7351     {
7352         // width = "md-2 xs-2..."
7353         if(!this.colModel.config[col_index]) {
7354             return;
7355         }
7356         
7357         var w = width.split(" ");
7358         
7359         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7360         
7361         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7362         
7363         
7364         for(var j = 0; j < w.length; j++) {
7365             
7366             if(!w[j]) {
7367                 continue;
7368             }
7369             
7370             var size_cls = w[j].split("-");
7371             
7372             if(!Number.isInteger(size_cls[1] * 1)) {
7373                 continue;
7374             }
7375             
7376             if(!this.colModel.config[col_index][size_cls[0]]) {
7377                 continue;
7378             }
7379             
7380             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7381                 continue;
7382             }
7383             
7384             h_row[0].classList.replace(
7385                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7386                 "col-"+size_cls[0]+"-"+size_cls[1]
7387             );
7388             
7389             for(var i = 0; i < rows.length; i++) {
7390                 
7391                 var size_cls = w[j].split("-");
7392                 
7393                 if(!Number.isInteger(size_cls[1] * 1)) {
7394                     continue;
7395                 }
7396                 
7397                 if(!this.colModel.config[col_index][size_cls[0]]) {
7398                     continue;
7399                 }
7400                 
7401                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7402                     continue;
7403                 }
7404                 
7405                 rows[i].classList.replace(
7406                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7407                     "col-"+size_cls[0]+"-"+size_cls[1]
7408                 );
7409             }
7410             
7411             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7412         }
7413     }
7414 });
7415
7416  
7417
7418  /*
7419  * - LGPL
7420  *
7421  * table cell
7422  * 
7423  */
7424
7425 /**
7426  * @class Roo.bootstrap.TableCell
7427  * @extends Roo.bootstrap.Component
7428  * Bootstrap TableCell class
7429  * @cfg {String} html cell contain text
7430  * @cfg {String} cls cell class
7431  * @cfg {String} tag cell tag (td|th) default td
7432  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7433  * @cfg {String} align Aligns the content in a cell
7434  * @cfg {String} axis Categorizes cells
7435  * @cfg {String} bgcolor Specifies the background color of a cell
7436  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7437  * @cfg {Number} colspan Specifies the number of columns a cell should span
7438  * @cfg {String} headers Specifies one or more header cells a cell is related to
7439  * @cfg {Number} height Sets the height of a cell
7440  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7441  * @cfg {Number} rowspan Sets the number of rows a cell should span
7442  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7443  * @cfg {String} valign Vertical aligns the content in a cell
7444  * @cfg {Number} width Specifies the width of a cell
7445  * 
7446  * @constructor
7447  * Create a new TableCell
7448  * @param {Object} config The config object
7449  */
7450
7451 Roo.bootstrap.TableCell = function(config){
7452     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7453 };
7454
7455 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7456     
7457     html: false,
7458     cls: false,
7459     tag: false,
7460     abbr: false,
7461     align: false,
7462     axis: false,
7463     bgcolor: false,
7464     charoff: false,
7465     colspan: false,
7466     headers: false,
7467     height: false,
7468     nowrap: false,
7469     rowspan: false,
7470     scope: false,
7471     valign: false,
7472     width: false,
7473     
7474     
7475     getAutoCreate : function(){
7476         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7477         
7478         cfg = {
7479             tag: 'td'
7480         };
7481         
7482         if(this.tag){
7483             cfg.tag = this.tag;
7484         }
7485         
7486         if (this.html) {
7487             cfg.html=this.html
7488         }
7489         if (this.cls) {
7490             cfg.cls=this.cls
7491         }
7492         if (this.abbr) {
7493             cfg.abbr=this.abbr
7494         }
7495         if (this.align) {
7496             cfg.align=this.align
7497         }
7498         if (this.axis) {
7499             cfg.axis=this.axis
7500         }
7501         if (this.bgcolor) {
7502             cfg.bgcolor=this.bgcolor
7503         }
7504         if (this.charoff) {
7505             cfg.charoff=this.charoff
7506         }
7507         if (this.colspan) {
7508             cfg.colspan=this.colspan
7509         }
7510         if (this.headers) {
7511             cfg.headers=this.headers
7512         }
7513         if (this.height) {
7514             cfg.height=this.height
7515         }
7516         if (this.nowrap) {
7517             cfg.nowrap=this.nowrap
7518         }
7519         if (this.rowspan) {
7520             cfg.rowspan=this.rowspan
7521         }
7522         if (this.scope) {
7523             cfg.scope=this.scope
7524         }
7525         if (this.valign) {
7526             cfg.valign=this.valign
7527         }
7528         if (this.width) {
7529             cfg.width=this.width
7530         }
7531         
7532         
7533         return cfg;
7534     }
7535    
7536 });
7537
7538  
7539
7540  /*
7541  * - LGPL
7542  *
7543  * table row
7544  * 
7545  */
7546
7547 /**
7548  * @class Roo.bootstrap.TableRow
7549  * @extends Roo.bootstrap.Component
7550  * Bootstrap TableRow class
7551  * @cfg {String} cls row class
7552  * @cfg {String} align Aligns the content in a table row
7553  * @cfg {String} bgcolor Specifies a background color for a table row
7554  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7555  * @cfg {String} valign Vertical aligns the content in a table row
7556  * 
7557  * @constructor
7558  * Create a new TableRow
7559  * @param {Object} config The config object
7560  */
7561
7562 Roo.bootstrap.TableRow = function(config){
7563     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7564 };
7565
7566 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7567     
7568     cls: false,
7569     align: false,
7570     bgcolor: false,
7571     charoff: false,
7572     valign: false,
7573     
7574     getAutoCreate : function(){
7575         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7576         
7577         cfg = {
7578             tag: 'tr'
7579         };
7580             
7581         if(this.cls){
7582             cfg.cls = this.cls;
7583         }
7584         if(this.align){
7585             cfg.align = this.align;
7586         }
7587         if(this.bgcolor){
7588             cfg.bgcolor = this.bgcolor;
7589         }
7590         if(this.charoff){
7591             cfg.charoff = this.charoff;
7592         }
7593         if(this.valign){
7594             cfg.valign = this.valign;
7595         }
7596         
7597         return cfg;
7598     }
7599    
7600 });
7601
7602  
7603
7604  /*
7605  * - LGPL
7606  *
7607  * table body
7608  * 
7609  */
7610
7611 /**
7612  * @class Roo.bootstrap.TableBody
7613  * @extends Roo.bootstrap.Component
7614  * Bootstrap TableBody class
7615  * @cfg {String} cls element class
7616  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7617  * @cfg {String} align Aligns the content inside the element
7618  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7619  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7620  * 
7621  * @constructor
7622  * Create a new TableBody
7623  * @param {Object} config The config object
7624  */
7625
7626 Roo.bootstrap.TableBody = function(config){
7627     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7628 };
7629
7630 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7631     
7632     cls: false,
7633     tag: false,
7634     align: false,
7635     charoff: false,
7636     valign: false,
7637     
7638     getAutoCreate : function(){
7639         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7640         
7641         cfg = {
7642             tag: 'tbody'
7643         };
7644             
7645         if (this.cls) {
7646             cfg.cls=this.cls
7647         }
7648         if(this.tag){
7649             cfg.tag = this.tag;
7650         }
7651         
7652         if(this.align){
7653             cfg.align = this.align;
7654         }
7655         if(this.charoff){
7656             cfg.charoff = this.charoff;
7657         }
7658         if(this.valign){
7659             cfg.valign = this.valign;
7660         }
7661         
7662         return cfg;
7663     }
7664     
7665     
7666 //    initEvents : function()
7667 //    {
7668 //        
7669 //        if(!this.store){
7670 //            return;
7671 //        }
7672 //        
7673 //        this.store = Roo.factory(this.store, Roo.data);
7674 //        this.store.on('load', this.onLoad, this);
7675 //        
7676 //        this.store.load();
7677 //        
7678 //    },
7679 //    
7680 //    onLoad: function () 
7681 //    {   
7682 //        this.fireEvent('load', this);
7683 //    }
7684 //    
7685 //   
7686 });
7687
7688  
7689
7690  /*
7691  * Based on:
7692  * Ext JS Library 1.1.1
7693  * Copyright(c) 2006-2007, Ext JS, LLC.
7694  *
7695  * Originally Released Under LGPL - original licence link has changed is not relivant.
7696  *
7697  * Fork - LGPL
7698  * <script type="text/javascript">
7699  */
7700
7701 // as we use this in bootstrap.
7702 Roo.namespace('Roo.form');
7703  /**
7704  * @class Roo.form.Action
7705  * Internal Class used to handle form actions
7706  * @constructor
7707  * @param {Roo.form.BasicForm} el The form element or its id
7708  * @param {Object} config Configuration options
7709  */
7710
7711  
7712  
7713 // define the action interface
7714 Roo.form.Action = function(form, options){
7715     this.form = form;
7716     this.options = options || {};
7717 };
7718 /**
7719  * Client Validation Failed
7720  * @const 
7721  */
7722 Roo.form.Action.CLIENT_INVALID = 'client';
7723 /**
7724  * Server Validation Failed
7725  * @const 
7726  */
7727 Roo.form.Action.SERVER_INVALID = 'server';
7728  /**
7729  * Connect to Server Failed
7730  * @const 
7731  */
7732 Roo.form.Action.CONNECT_FAILURE = 'connect';
7733 /**
7734  * Reading Data from Server Failed
7735  * @const 
7736  */
7737 Roo.form.Action.LOAD_FAILURE = 'load';
7738
7739 Roo.form.Action.prototype = {
7740     type : 'default',
7741     failureType : undefined,
7742     response : undefined,
7743     result : undefined,
7744
7745     // interface method
7746     run : function(options){
7747
7748     },
7749
7750     // interface method
7751     success : function(response){
7752
7753     },
7754
7755     // interface method
7756     handleResponse : function(response){
7757
7758     },
7759
7760     // default connection failure
7761     failure : function(response){
7762         
7763         this.response = response;
7764         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7765         this.form.afterAction(this, false);
7766     },
7767
7768     processResponse : function(response){
7769         this.response = response;
7770         if(!response.responseText){
7771             return true;
7772         }
7773         this.result = this.handleResponse(response);
7774         return this.result;
7775     },
7776
7777     // utility functions used internally
7778     getUrl : function(appendParams){
7779         var url = this.options.url || this.form.url || this.form.el.dom.action;
7780         if(appendParams){
7781             var p = this.getParams();
7782             if(p){
7783                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7784             }
7785         }
7786         return url;
7787     },
7788
7789     getMethod : function(){
7790         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7791     },
7792
7793     getParams : function(){
7794         var bp = this.form.baseParams;
7795         var p = this.options.params;
7796         if(p){
7797             if(typeof p == "object"){
7798                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7799             }else if(typeof p == 'string' && bp){
7800                 p += '&' + Roo.urlEncode(bp);
7801             }
7802         }else if(bp){
7803             p = Roo.urlEncode(bp);
7804         }
7805         return p;
7806     },
7807
7808     createCallback : function(){
7809         return {
7810             success: this.success,
7811             failure: this.failure,
7812             scope: this,
7813             timeout: (this.form.timeout*1000),
7814             upload: this.form.fileUpload ? this.success : undefined
7815         };
7816     }
7817 };
7818
7819 Roo.form.Action.Submit = function(form, options){
7820     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7821 };
7822
7823 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7824     type : 'submit',
7825
7826     haveProgress : false,
7827     uploadComplete : false,
7828     
7829     // uploadProgress indicator.
7830     uploadProgress : function()
7831     {
7832         if (!this.form.progressUrl) {
7833             return;
7834         }
7835         
7836         if (!this.haveProgress) {
7837             Roo.MessageBox.progress("Uploading", "Uploading");
7838         }
7839         if (this.uploadComplete) {
7840            Roo.MessageBox.hide();
7841            return;
7842         }
7843         
7844         this.haveProgress = true;
7845    
7846         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7847         
7848         var c = new Roo.data.Connection();
7849         c.request({
7850             url : this.form.progressUrl,
7851             params: {
7852                 id : uid
7853             },
7854             method: 'GET',
7855             success : function(req){
7856                //console.log(data);
7857                 var rdata = false;
7858                 var edata;
7859                 try  {
7860                    rdata = Roo.decode(req.responseText)
7861                 } catch (e) {
7862                     Roo.log("Invalid data from server..");
7863                     Roo.log(edata);
7864                     return;
7865                 }
7866                 if (!rdata || !rdata.success) {
7867                     Roo.log(rdata);
7868                     Roo.MessageBox.alert(Roo.encode(rdata));
7869                     return;
7870                 }
7871                 var data = rdata.data;
7872                 
7873                 if (this.uploadComplete) {
7874                    Roo.MessageBox.hide();
7875                    return;
7876                 }
7877                    
7878                 if (data){
7879                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7880                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7881                     );
7882                 }
7883                 this.uploadProgress.defer(2000,this);
7884             },
7885        
7886             failure: function(data) {
7887                 Roo.log('progress url failed ');
7888                 Roo.log(data);
7889             },
7890             scope : this
7891         });
7892            
7893     },
7894     
7895     
7896     run : function()
7897     {
7898         // run get Values on the form, so it syncs any secondary forms.
7899         this.form.getValues();
7900         
7901         var o = this.options;
7902         var method = this.getMethod();
7903         var isPost = method == 'POST';
7904         if(o.clientValidation === false || this.form.isValid()){
7905             
7906             if (this.form.progressUrl) {
7907                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7908                     (new Date() * 1) + '' + Math.random());
7909                     
7910             } 
7911             
7912             
7913             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7914                 form:this.form.el.dom,
7915                 url:this.getUrl(!isPost),
7916                 method: method,
7917                 params:isPost ? this.getParams() : null,
7918                 isUpload: this.form.fileUpload
7919             }));
7920             
7921             this.uploadProgress();
7922
7923         }else if (o.clientValidation !== false){ // client validation failed
7924             this.failureType = Roo.form.Action.CLIENT_INVALID;
7925             this.form.afterAction(this, false);
7926         }
7927     },
7928
7929     success : function(response)
7930     {
7931         this.uploadComplete= true;
7932         if (this.haveProgress) {
7933             Roo.MessageBox.hide();
7934         }
7935         
7936         
7937         var result = this.processResponse(response);
7938         if(result === true || result.success){
7939             this.form.afterAction(this, true);
7940             return;
7941         }
7942         if(result.errors){
7943             this.form.markInvalid(result.errors);
7944             this.failureType = Roo.form.Action.SERVER_INVALID;
7945         }
7946         this.form.afterAction(this, false);
7947     },
7948     failure : function(response)
7949     {
7950         this.uploadComplete= true;
7951         if (this.haveProgress) {
7952             Roo.MessageBox.hide();
7953         }
7954         
7955         this.response = response;
7956         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7957         this.form.afterAction(this, false);
7958     },
7959     
7960     handleResponse : function(response){
7961         if(this.form.errorReader){
7962             var rs = this.form.errorReader.read(response);
7963             var errors = [];
7964             if(rs.records){
7965                 for(var i = 0, len = rs.records.length; i < len; i++) {
7966                     var r = rs.records[i];
7967                     errors[i] = r.data;
7968                 }
7969             }
7970             if(errors.length < 1){
7971                 errors = null;
7972             }
7973             return {
7974                 success : rs.success,
7975                 errors : errors
7976             };
7977         }
7978         var ret = false;
7979         try {
7980             ret = Roo.decode(response.responseText);
7981         } catch (e) {
7982             ret = {
7983                 success: false,
7984                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7985                 errors : []
7986             };
7987         }
7988         return ret;
7989         
7990     }
7991 });
7992
7993
7994 Roo.form.Action.Load = function(form, options){
7995     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7996     this.reader = this.form.reader;
7997 };
7998
7999 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8000     type : 'load',
8001
8002     run : function(){
8003         
8004         Roo.Ajax.request(Roo.apply(
8005                 this.createCallback(), {
8006                     method:this.getMethod(),
8007                     url:this.getUrl(false),
8008                     params:this.getParams()
8009         }));
8010     },
8011
8012     success : function(response){
8013         
8014         var result = this.processResponse(response);
8015         if(result === true || !result.success || !result.data){
8016             this.failureType = Roo.form.Action.LOAD_FAILURE;
8017             this.form.afterAction(this, false);
8018             return;
8019         }
8020         this.form.clearInvalid();
8021         this.form.setValues(result.data);
8022         this.form.afterAction(this, true);
8023     },
8024
8025     handleResponse : function(response){
8026         if(this.form.reader){
8027             var rs = this.form.reader.read(response);
8028             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8029             return {
8030                 success : rs.success,
8031                 data : data
8032             };
8033         }
8034         return Roo.decode(response.responseText);
8035     }
8036 });
8037
8038 Roo.form.Action.ACTION_TYPES = {
8039     'load' : Roo.form.Action.Load,
8040     'submit' : Roo.form.Action.Submit
8041 };/*
8042  * - LGPL
8043  *
8044  * form
8045  *
8046  */
8047
8048 /**
8049  * @class Roo.bootstrap.Form
8050  * @extends Roo.bootstrap.Component
8051  * Bootstrap Form class
8052  * @cfg {String} method  GET | POST (default POST)
8053  * @cfg {String} labelAlign top | left (default top)
8054  * @cfg {String} align left  | right - for navbars
8055  * @cfg {Boolean} loadMask load mask when submit (default true)
8056
8057  *
8058  * @constructor
8059  * Create a new Form
8060  * @param {Object} config The config object
8061  */
8062
8063
8064 Roo.bootstrap.Form = function(config){
8065     
8066     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8067     
8068     Roo.bootstrap.Form.popover.apply();
8069     
8070     this.addEvents({
8071         /**
8072          * @event clientvalidation
8073          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8074          * @param {Form} this
8075          * @param {Boolean} valid true if the form has passed client-side validation
8076          */
8077         clientvalidation: true,
8078         /**
8079          * @event beforeaction
8080          * Fires before any action is performed. Return false to cancel the action.
8081          * @param {Form} this
8082          * @param {Action} action The action to be performed
8083          */
8084         beforeaction: true,
8085         /**
8086          * @event actionfailed
8087          * Fires when an action fails.
8088          * @param {Form} this
8089          * @param {Action} action The action that failed
8090          */
8091         actionfailed : true,
8092         /**
8093          * @event actioncomplete
8094          * Fires when an action is completed.
8095          * @param {Form} this
8096          * @param {Action} action The action that completed
8097          */
8098         actioncomplete : true
8099     });
8100 };
8101
8102 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8103
8104      /**
8105      * @cfg {String} method
8106      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8107      */
8108     method : 'POST',
8109     /**
8110      * @cfg {String} url
8111      * The URL to use for form actions if one isn't supplied in the action options.
8112      */
8113     /**
8114      * @cfg {Boolean} fileUpload
8115      * Set to true if this form is a file upload.
8116      */
8117
8118     /**
8119      * @cfg {Object} baseParams
8120      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8121      */
8122
8123     /**
8124      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8125      */
8126     timeout: 30,
8127     /**
8128      * @cfg {Sting} align (left|right) for navbar forms
8129      */
8130     align : 'left',
8131
8132     // private
8133     activeAction : null,
8134
8135     /**
8136      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8137      * element by passing it or its id or mask the form itself by passing in true.
8138      * @type Mixed
8139      */
8140     waitMsgTarget : false,
8141
8142     loadMask : true,
8143     
8144     /**
8145      * @cfg {Boolean} errorMask (true|false) default false
8146      */
8147     errorMask : false,
8148     
8149     /**
8150      * @cfg {Number} maskOffset Default 100
8151      */
8152     maskOffset : 100,
8153     
8154     /**
8155      * @cfg {Boolean} maskBody
8156      */
8157     maskBody : false,
8158
8159     getAutoCreate : function(){
8160
8161         var cfg = {
8162             tag: 'form',
8163             method : this.method || 'POST',
8164             id : this.id || Roo.id(),
8165             cls : ''
8166         };
8167         if (this.parent().xtype.match(/^Nav/)) {
8168             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8169
8170         }
8171
8172         if (this.labelAlign == 'left' ) {
8173             cfg.cls += ' form-horizontal';
8174         }
8175
8176
8177         return cfg;
8178     },
8179     initEvents : function()
8180     {
8181         this.el.on('submit', this.onSubmit, this);
8182         // this was added as random key presses on the form where triggering form submit.
8183         this.el.on('keypress', function(e) {
8184             if (e.getCharCode() != 13) {
8185                 return true;
8186             }
8187             // we might need to allow it for textareas.. and some other items.
8188             // check e.getTarget().
8189
8190             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8191                 return true;
8192             }
8193
8194             Roo.log("keypress blocked");
8195
8196             e.preventDefault();
8197             return false;
8198         });
8199         
8200     },
8201     // private
8202     onSubmit : function(e){
8203         e.stopEvent();
8204     },
8205
8206      /**
8207      * Returns true if client-side validation on the form is successful.
8208      * @return Boolean
8209      */
8210     isValid : function(){
8211         var items = this.getItems();
8212         var valid = true;
8213         var target = false;
8214         
8215         items.each(function(f){
8216             
8217             if(f.validate()){
8218                 return;
8219             }
8220             
8221             Roo.log('invalid field: ' + f.name);
8222             
8223             valid = false;
8224
8225             if(!target && f.el.isVisible(true)){
8226                 target = f;
8227             }
8228            
8229         });
8230         
8231         if(this.errorMask && !valid){
8232             Roo.bootstrap.Form.popover.mask(this, target);
8233         }
8234         
8235         return valid;
8236     },
8237     
8238     /**
8239      * Returns true if any fields in this form have changed since their original load.
8240      * @return Boolean
8241      */
8242     isDirty : function(){
8243         var dirty = false;
8244         var items = this.getItems();
8245         items.each(function(f){
8246            if(f.isDirty()){
8247                dirty = true;
8248                return false;
8249            }
8250            return true;
8251         });
8252         return dirty;
8253     },
8254      /**
8255      * Performs a predefined action (submit or load) or custom actions you define on this form.
8256      * @param {String} actionName The name of the action type
8257      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8258      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8259      * accept other config options):
8260      * <pre>
8261 Property          Type             Description
8262 ----------------  ---------------  ----------------------------------------------------------------------------------
8263 url               String           The url for the action (defaults to the form's url)
8264 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8265 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8266 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8267                                    validate the form on the client (defaults to false)
8268      * </pre>
8269      * @return {BasicForm} this
8270      */
8271     doAction : function(action, options){
8272         if(typeof action == 'string'){
8273             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8274         }
8275         if(this.fireEvent('beforeaction', this, action) !== false){
8276             this.beforeAction(action);
8277             action.run.defer(100, action);
8278         }
8279         return this;
8280     },
8281
8282     // private
8283     beforeAction : function(action){
8284         var o = action.options;
8285         
8286         if(this.loadMask){
8287             
8288             if(this.maskBody){
8289                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8290             } else {
8291                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8292             }
8293         }
8294         // not really supported yet.. ??
8295
8296         //if(this.waitMsgTarget === true){
8297         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8298         //}else if(this.waitMsgTarget){
8299         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8300         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8301         //}else {
8302         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8303        // }
8304
8305     },
8306
8307     // private
8308     afterAction : function(action, success){
8309         this.activeAction = null;
8310         var o = action.options;
8311
8312         if(this.loadMask){
8313             
8314             if(this.maskBody){
8315                 Roo.get(document.body).unmask();
8316             } else {
8317                 this.el.unmask();
8318             }
8319         }
8320         
8321         //if(this.waitMsgTarget === true){
8322 //            this.el.unmask();
8323         //}else if(this.waitMsgTarget){
8324         //    this.waitMsgTarget.unmask();
8325         //}else{
8326         //    Roo.MessageBox.updateProgress(1);
8327         //    Roo.MessageBox.hide();
8328        // }
8329         //
8330         if(success){
8331             if(o.reset){
8332                 this.reset();
8333             }
8334             Roo.callback(o.success, o.scope, [this, action]);
8335             this.fireEvent('actioncomplete', this, action);
8336
8337         }else{
8338
8339             // failure condition..
8340             // we have a scenario where updates need confirming.
8341             // eg. if a locking scenario exists..
8342             // we look for { errors : { needs_confirm : true }} in the response.
8343             if (
8344                 (typeof(action.result) != 'undefined')  &&
8345                 (typeof(action.result.errors) != 'undefined')  &&
8346                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8347            ){
8348                 var _t = this;
8349                 Roo.log("not supported yet");
8350                  /*
8351
8352                 Roo.MessageBox.confirm(
8353                     "Change requires confirmation",
8354                     action.result.errorMsg,
8355                     function(r) {
8356                         if (r != 'yes') {
8357                             return;
8358                         }
8359                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8360                     }
8361
8362                 );
8363                 */
8364
8365
8366                 return;
8367             }
8368
8369             Roo.callback(o.failure, o.scope, [this, action]);
8370             // show an error message if no failed handler is set..
8371             if (!this.hasListener('actionfailed')) {
8372                 Roo.log("need to add dialog support");
8373                 /*
8374                 Roo.MessageBox.alert("Error",
8375                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8376                         action.result.errorMsg :
8377                         "Saving Failed, please check your entries or try again"
8378                 );
8379                 */
8380             }
8381
8382             this.fireEvent('actionfailed', this, action);
8383         }
8384
8385     },
8386     /**
8387      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8388      * @param {String} id The value to search for
8389      * @return Field
8390      */
8391     findField : function(id){
8392         var items = this.getItems();
8393         var field = items.get(id);
8394         if(!field){
8395              items.each(function(f){
8396                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8397                     field = f;
8398                     return false;
8399                 }
8400                 return true;
8401             });
8402         }
8403         return field || null;
8404     },
8405      /**
8406      * Mark fields in this form invalid in bulk.
8407      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8408      * @return {BasicForm} this
8409      */
8410     markInvalid : function(errors){
8411         if(errors instanceof Array){
8412             for(var i = 0, len = errors.length; i < len; i++){
8413                 var fieldError = errors[i];
8414                 var f = this.findField(fieldError.id);
8415                 if(f){
8416                     f.markInvalid(fieldError.msg);
8417                 }
8418             }
8419         }else{
8420             var field, id;
8421             for(id in errors){
8422                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8423                     field.markInvalid(errors[id]);
8424                 }
8425             }
8426         }
8427         //Roo.each(this.childForms || [], function (f) {
8428         //    f.markInvalid(errors);
8429         //});
8430
8431         return this;
8432     },
8433
8434     /**
8435      * Set values for fields in this form in bulk.
8436      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8437      * @return {BasicForm} this
8438      */
8439     setValues : function(values){
8440         if(values instanceof Array){ // array of objects
8441             for(var i = 0, len = values.length; i < len; i++){
8442                 var v = values[i];
8443                 var f = this.findField(v.id);
8444                 if(f){
8445                     f.setValue(v.value);
8446                     if(this.trackResetOnLoad){
8447                         f.originalValue = f.getValue();
8448                     }
8449                 }
8450             }
8451         }else{ // object hash
8452             var field, id;
8453             for(id in values){
8454                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8455
8456                     if (field.setFromData &&
8457                         field.valueField &&
8458                         field.displayField &&
8459                         // combos' with local stores can
8460                         // be queried via setValue()
8461                         // to set their value..
8462                         (field.store && !field.store.isLocal)
8463                         ) {
8464                         // it's a combo
8465                         var sd = { };
8466                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8467                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8468                         field.setFromData(sd);
8469
8470                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8471                         
8472                         field.setFromData(values);
8473                         
8474                     } else {
8475                         field.setValue(values[id]);
8476                     }
8477
8478
8479                     if(this.trackResetOnLoad){
8480                         field.originalValue = field.getValue();
8481                     }
8482                 }
8483             }
8484         }
8485
8486         //Roo.each(this.childForms || [], function (f) {
8487         //    f.setValues(values);
8488         //});
8489
8490         return this;
8491     },
8492
8493     /**
8494      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8495      * they are returned as an array.
8496      * @param {Boolean} asString
8497      * @return {Object}
8498      */
8499     getValues : function(asString){
8500         //if (this.childForms) {
8501             // copy values from the child forms
8502         //    Roo.each(this.childForms, function (f) {
8503         //        this.setValues(f.getValues());
8504         //    }, this);
8505         //}
8506
8507
8508
8509         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8510         if(asString === true){
8511             return fs;
8512         }
8513         return Roo.urlDecode(fs);
8514     },
8515
8516     /**
8517      * Returns the fields in this form as an object with key/value pairs.
8518      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8519      * @return {Object}
8520      */
8521     getFieldValues : function(with_hidden)
8522     {
8523         var items = this.getItems();
8524         var ret = {};
8525         items.each(function(f){
8526             
8527             if (!f.getName()) {
8528                 return;
8529             }
8530             
8531             var v = f.getValue();
8532             
8533             if (f.inputType =='radio') {
8534                 if (typeof(ret[f.getName()]) == 'undefined') {
8535                     ret[f.getName()] = ''; // empty..
8536                 }
8537
8538                 if (!f.el.dom.checked) {
8539                     return;
8540
8541                 }
8542                 v = f.el.dom.value;
8543
8544             }
8545             
8546             if(f.xtype == 'MoneyField'){
8547                 ret[f.currencyName] = f.getCurrency();
8548             }
8549
8550             // not sure if this supported any more..
8551             if ((typeof(v) == 'object') && f.getRawValue) {
8552                 v = f.getRawValue() ; // dates..
8553             }
8554             // combo boxes where name != hiddenName...
8555             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8556                 ret[f.name] = f.getRawValue();
8557             }
8558             ret[f.getName()] = v;
8559         });
8560
8561         return ret;
8562     },
8563
8564     /**
8565      * Clears all invalid messages in this form.
8566      * @return {BasicForm} this
8567      */
8568     clearInvalid : function(){
8569         var items = this.getItems();
8570
8571         items.each(function(f){
8572            f.clearInvalid();
8573         });
8574
8575         return this;
8576     },
8577
8578     /**
8579      * Resets this form.
8580      * @return {BasicForm} this
8581      */
8582     reset : function(){
8583         var items = this.getItems();
8584         items.each(function(f){
8585             f.reset();
8586         });
8587
8588         Roo.each(this.childForms || [], function (f) {
8589             f.reset();
8590         });
8591
8592
8593         return this;
8594     },
8595     
8596     getItems : function()
8597     {
8598         var r=new Roo.util.MixedCollection(false, function(o){
8599             return o.id || (o.id = Roo.id());
8600         });
8601         var iter = function(el) {
8602             if (el.inputEl) {
8603                 r.add(el);
8604             }
8605             if (!el.items) {
8606                 return;
8607             }
8608             Roo.each(el.items,function(e) {
8609                 iter(e);
8610             });
8611         };
8612
8613         iter(this);
8614         return r;
8615     },
8616     
8617     hideFields : function(items)
8618     {
8619         Roo.each(items, function(i){
8620             
8621             var f = this.findField(i);
8622             
8623             if(!f){
8624                 return;
8625             }
8626             
8627             f.hide();
8628             
8629         }, this);
8630     },
8631     
8632     showFields : function(items)
8633     {
8634         Roo.each(items, function(i){
8635             
8636             var f = this.findField(i);
8637             
8638             if(!f){
8639                 return;
8640             }
8641             
8642             f.show();
8643             
8644         }, this);
8645     }
8646
8647 });
8648
8649 Roo.apply(Roo.bootstrap.Form, {
8650     
8651     popover : {
8652         
8653         padding : 5,
8654         
8655         isApplied : false,
8656         
8657         isMasked : false,
8658         
8659         form : false,
8660         
8661         target : false,
8662         
8663         toolTip : false,
8664         
8665         intervalID : false,
8666         
8667         maskEl : false,
8668         
8669         apply : function()
8670         {
8671             if(this.isApplied){
8672                 return;
8673             }
8674             
8675             this.maskEl = {
8676                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8677                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8678                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8679                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8680             };
8681             
8682             this.maskEl.top.enableDisplayMode("block");
8683             this.maskEl.left.enableDisplayMode("block");
8684             this.maskEl.bottom.enableDisplayMode("block");
8685             this.maskEl.right.enableDisplayMode("block");
8686             
8687             this.toolTip = new Roo.bootstrap.Tooltip({
8688                 cls : 'roo-form-error-popover',
8689                 alignment : {
8690                     'left' : ['r-l', [-2,0], 'right'],
8691                     'right' : ['l-r', [2,0], 'left'],
8692                     'bottom' : ['tl-bl', [0,2], 'top'],
8693                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8694                 }
8695             });
8696             
8697             this.toolTip.render(Roo.get(document.body));
8698
8699             this.toolTip.el.enableDisplayMode("block");
8700             
8701             Roo.get(document.body).on('click', function(){
8702                 this.unmask();
8703             }, this);
8704             
8705             Roo.get(document.body).on('touchstart', function(){
8706                 this.unmask();
8707             }, this);
8708             
8709             this.isApplied = true
8710         },
8711         
8712         mask : function(form, target)
8713         {
8714             this.form = form;
8715             
8716             this.target = target;
8717             
8718             if(!this.form.errorMask || !target.el){
8719                 return;
8720             }
8721             
8722             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8723             
8724             Roo.log(scrollable);
8725             
8726             var ot = this.target.el.calcOffsetsTo(scrollable);
8727             
8728             var scrollTo = ot[1] - this.form.maskOffset;
8729             
8730             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8731             
8732             scrollable.scrollTo('top', scrollTo);
8733             
8734             var box = this.target.el.getBox();
8735             Roo.log(box);
8736             var zIndex = Roo.bootstrap.Modal.zIndex++;
8737
8738             
8739             this.maskEl.top.setStyle('position', 'absolute');
8740             this.maskEl.top.setStyle('z-index', zIndex);
8741             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8742             this.maskEl.top.setLeft(0);
8743             this.maskEl.top.setTop(0);
8744             this.maskEl.top.show();
8745             
8746             this.maskEl.left.setStyle('position', 'absolute');
8747             this.maskEl.left.setStyle('z-index', zIndex);
8748             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8749             this.maskEl.left.setLeft(0);
8750             this.maskEl.left.setTop(box.y - this.padding);
8751             this.maskEl.left.show();
8752
8753             this.maskEl.bottom.setStyle('position', 'absolute');
8754             this.maskEl.bottom.setStyle('z-index', zIndex);
8755             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8756             this.maskEl.bottom.setLeft(0);
8757             this.maskEl.bottom.setTop(box.bottom + this.padding);
8758             this.maskEl.bottom.show();
8759
8760             this.maskEl.right.setStyle('position', 'absolute');
8761             this.maskEl.right.setStyle('z-index', zIndex);
8762             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8763             this.maskEl.right.setLeft(box.right + this.padding);
8764             this.maskEl.right.setTop(box.y - this.padding);
8765             this.maskEl.right.show();
8766
8767             this.toolTip.bindEl = this.target.el;
8768
8769             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8770
8771             var tip = this.target.blankText;
8772
8773             if(this.target.getValue() !== '' ) {
8774                 
8775                 if (this.target.invalidText.length) {
8776                     tip = this.target.invalidText;
8777                 } else if (this.target.regexText.length){
8778                     tip = this.target.regexText;
8779                 }
8780             }
8781
8782             this.toolTip.show(tip);
8783
8784             this.intervalID = window.setInterval(function() {
8785                 Roo.bootstrap.Form.popover.unmask();
8786             }, 10000);
8787
8788             window.onwheel = function(){ return false;};
8789             
8790             (function(){ this.isMasked = true; }).defer(500, this);
8791             
8792         },
8793         
8794         unmask : function()
8795         {
8796             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8797                 return;
8798             }
8799             
8800             this.maskEl.top.setStyle('position', 'absolute');
8801             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8802             this.maskEl.top.hide();
8803
8804             this.maskEl.left.setStyle('position', 'absolute');
8805             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8806             this.maskEl.left.hide();
8807
8808             this.maskEl.bottom.setStyle('position', 'absolute');
8809             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8810             this.maskEl.bottom.hide();
8811
8812             this.maskEl.right.setStyle('position', 'absolute');
8813             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8814             this.maskEl.right.hide();
8815             
8816             this.toolTip.hide();
8817             
8818             this.toolTip.el.hide();
8819             
8820             window.onwheel = function(){ return true;};
8821             
8822             if(this.intervalID){
8823                 window.clearInterval(this.intervalID);
8824                 this.intervalID = false;
8825             }
8826             
8827             this.isMasked = false;
8828             
8829         }
8830         
8831     }
8832     
8833 });
8834
8835 /*
8836  * Based on:
8837  * Ext JS Library 1.1.1
8838  * Copyright(c) 2006-2007, Ext JS, LLC.
8839  *
8840  * Originally Released Under LGPL - original licence link has changed is not relivant.
8841  *
8842  * Fork - LGPL
8843  * <script type="text/javascript">
8844  */
8845 /**
8846  * @class Roo.form.VTypes
8847  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8848  * @singleton
8849  */
8850 Roo.form.VTypes = function(){
8851     // closure these in so they are only created once.
8852     var alpha = /^[a-zA-Z_]+$/;
8853     var alphanum = /^[a-zA-Z0-9_]+$/;
8854     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8855     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8856
8857     // All these messages and functions are configurable
8858     return {
8859         /**
8860          * The function used to validate email addresses
8861          * @param {String} value The email address
8862          */
8863         'email' : function(v){
8864             return email.test(v);
8865         },
8866         /**
8867          * The error text to display when the email validation function returns false
8868          * @type String
8869          */
8870         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8871         /**
8872          * The keystroke filter mask to be applied on email input
8873          * @type RegExp
8874          */
8875         'emailMask' : /[a-z0-9_\.\-@]/i,
8876
8877         /**
8878          * The function used to validate URLs
8879          * @param {String} value The URL
8880          */
8881         'url' : function(v){
8882             return url.test(v);
8883         },
8884         /**
8885          * The error text to display when the url validation function returns false
8886          * @type String
8887          */
8888         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8889         
8890         /**
8891          * The function used to validate alpha values
8892          * @param {String} value The value
8893          */
8894         'alpha' : function(v){
8895             return alpha.test(v);
8896         },
8897         /**
8898          * The error text to display when the alpha validation function returns false
8899          * @type String
8900          */
8901         'alphaText' : 'This field should only contain letters and _',
8902         /**
8903          * The keystroke filter mask to be applied on alpha input
8904          * @type RegExp
8905          */
8906         'alphaMask' : /[a-z_]/i,
8907
8908         /**
8909          * The function used to validate alphanumeric values
8910          * @param {String} value The value
8911          */
8912         'alphanum' : function(v){
8913             return alphanum.test(v);
8914         },
8915         /**
8916          * The error text to display when the alphanumeric validation function returns false
8917          * @type String
8918          */
8919         'alphanumText' : 'This field should only contain letters, numbers and _',
8920         /**
8921          * The keystroke filter mask to be applied on alphanumeric input
8922          * @type RegExp
8923          */
8924         'alphanumMask' : /[a-z0-9_]/i
8925     };
8926 }();/*
8927  * - LGPL
8928  *
8929  * Input
8930  * 
8931  */
8932
8933 /**
8934  * @class Roo.bootstrap.Input
8935  * @extends Roo.bootstrap.Component
8936  * Bootstrap Input class
8937  * @cfg {Boolean} disabled is it disabled
8938  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8939  * @cfg {String} name name of the input
8940  * @cfg {string} fieldLabel - the label associated
8941  * @cfg {string} placeholder - placeholder to put in text.
8942  * @cfg {string}  before - input group add on before
8943  * @cfg {string} after - input group add on after
8944  * @cfg {string} size - (lg|sm) or leave empty..
8945  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8946  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8947  * @cfg {Number} md colspan out of 12 for computer-sized screens
8948  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8949  * @cfg {string} value default value of the input
8950  * @cfg {Number} labelWidth set the width of label 
8951  * @cfg {Number} labellg set the width of label (1-12)
8952  * @cfg {Number} labelmd set the width of label (1-12)
8953  * @cfg {Number} labelsm set the width of label (1-12)
8954  * @cfg {Number} labelxs set the width of label (1-12)
8955  * @cfg {String} labelAlign (top|left)
8956  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8957  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8958  * @cfg {String} indicatorpos (left|right) default left
8959  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8960  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8961
8962  * @cfg {String} align (left|center|right) Default left
8963  * @cfg {Boolean} forceFeedback (true|false) Default false
8964  * 
8965  * @constructor
8966  * Create a new Input
8967  * @param {Object} config The config object
8968  */
8969
8970 Roo.bootstrap.Input = function(config){
8971     
8972     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8973     
8974     this.addEvents({
8975         /**
8976          * @event focus
8977          * Fires when this field receives input focus.
8978          * @param {Roo.form.Field} this
8979          */
8980         focus : true,
8981         /**
8982          * @event blur
8983          * Fires when this field loses input focus.
8984          * @param {Roo.form.Field} this
8985          */
8986         blur : true,
8987         /**
8988          * @event specialkey
8989          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8990          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8991          * @param {Roo.form.Field} this
8992          * @param {Roo.EventObject} e The event object
8993          */
8994         specialkey : true,
8995         /**
8996          * @event change
8997          * Fires just before the field blurs if the field value has changed.
8998          * @param {Roo.form.Field} this
8999          * @param {Mixed} newValue The new value
9000          * @param {Mixed} oldValue The original value
9001          */
9002         change : true,
9003         /**
9004          * @event invalid
9005          * Fires after the field has been marked as invalid.
9006          * @param {Roo.form.Field} this
9007          * @param {String} msg The validation message
9008          */
9009         invalid : true,
9010         /**
9011          * @event valid
9012          * Fires after the field has been validated with no errors.
9013          * @param {Roo.form.Field} this
9014          */
9015         valid : true,
9016          /**
9017          * @event keyup
9018          * Fires after the key up
9019          * @param {Roo.form.Field} this
9020          * @param {Roo.EventObject}  e The event Object
9021          */
9022         keyup : true
9023     });
9024 };
9025
9026 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9027      /**
9028      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9029       automatic validation (defaults to "keyup").
9030      */
9031     validationEvent : "keyup",
9032      /**
9033      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9034      */
9035     validateOnBlur : true,
9036     /**
9037      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9038      */
9039     validationDelay : 250,
9040      /**
9041      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9042      */
9043     focusClass : "x-form-focus",  // not needed???
9044     
9045        
9046     /**
9047      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9048      */
9049     invalidClass : "has-warning",
9050     
9051     /**
9052      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9053      */
9054     validClass : "has-success",
9055     
9056     /**
9057      * @cfg {Boolean} hasFeedback (true|false) default true
9058      */
9059     hasFeedback : true,
9060     
9061     /**
9062      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9063      */
9064     invalidFeedbackClass : "glyphicon-warning-sign",
9065     
9066     /**
9067      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9068      */
9069     validFeedbackClass : "glyphicon-ok",
9070     
9071     /**
9072      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9073      */
9074     selectOnFocus : false,
9075     
9076      /**
9077      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9078      */
9079     maskRe : null,
9080        /**
9081      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9082      */
9083     vtype : null,
9084     
9085       /**
9086      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9087      */
9088     disableKeyFilter : false,
9089     
9090        /**
9091      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9092      */
9093     disabled : false,
9094      /**
9095      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9096      */
9097     allowBlank : true,
9098     /**
9099      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9100      */
9101     blankText : "Please complete this mandatory field",
9102     
9103      /**
9104      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9105      */
9106     minLength : 0,
9107     /**
9108      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9109      */
9110     maxLength : Number.MAX_VALUE,
9111     /**
9112      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9113      */
9114     minLengthText : "The minimum length for this field is {0}",
9115     /**
9116      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9117      */
9118     maxLengthText : "The maximum length for this field is {0}",
9119   
9120     
9121     /**
9122      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9123      * If available, this function will be called only after the basic validators all return true, and will be passed the
9124      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9125      */
9126     validator : null,
9127     /**
9128      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9129      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9130      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9131      */
9132     regex : null,
9133     /**
9134      * @cfg {String} regexText -- Depricated - use Invalid Text
9135      */
9136     regexText : "",
9137     
9138     /**
9139      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9140      */
9141     invalidText : "",
9142     
9143     
9144     
9145     autocomplete: false,
9146     
9147     
9148     fieldLabel : '',
9149     inputType : 'text',
9150     
9151     name : false,
9152     placeholder: false,
9153     before : false,
9154     after : false,
9155     size : false,
9156     hasFocus : false,
9157     preventMark: false,
9158     isFormField : true,
9159     value : '',
9160     labelWidth : 2,
9161     labelAlign : false,
9162     readOnly : false,
9163     align : false,
9164     formatedValue : false,
9165     forceFeedback : false,
9166     
9167     indicatorpos : 'left',
9168     
9169     labellg : 0,
9170     labelmd : 0,
9171     labelsm : 0,
9172     labelxs : 0,
9173     
9174     capture : '',
9175     accept : '',
9176     
9177     parentLabelAlign : function()
9178     {
9179         var parent = this;
9180         while (parent.parent()) {
9181             parent = parent.parent();
9182             if (typeof(parent.labelAlign) !='undefined') {
9183                 return parent.labelAlign;
9184             }
9185         }
9186         return 'left';
9187         
9188     },
9189     
9190     getAutoCreate : function()
9191     {
9192         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9193         
9194         var id = Roo.id();
9195         
9196         var cfg = {};
9197         
9198         if(this.inputType != 'hidden'){
9199             cfg.cls = 'form-group' //input-group
9200         }
9201         
9202         var input =  {
9203             tag: 'input',
9204             id : id,
9205             type : this.inputType,
9206             value : this.value,
9207             cls : 'form-control',
9208             placeholder : this.placeholder || '',
9209             autocomplete : this.autocomplete || 'new-password'
9210         };
9211         
9212         if(this.capture.length){
9213             input.capture = this.capture;
9214         }
9215         
9216         if(this.accept.length){
9217             input.accept = this.accept + "/*";
9218         }
9219         
9220         if(this.align){
9221             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9222         }
9223         
9224         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9225             input.maxLength = this.maxLength;
9226         }
9227         
9228         if (this.disabled) {
9229             input.disabled=true;
9230         }
9231         
9232         if (this.readOnly) {
9233             input.readonly=true;
9234         }
9235         
9236         if (this.name) {
9237             input.name = this.name;
9238         }
9239         
9240         if (this.size) {
9241             input.cls += ' input-' + this.size;
9242         }
9243         
9244         var settings=this;
9245         ['xs','sm','md','lg'].map(function(size){
9246             if (settings[size]) {
9247                 cfg.cls += ' col-' + size + '-' + settings[size];
9248             }
9249         });
9250         
9251         var inputblock = input;
9252         
9253         var feedback = {
9254             tag: 'span',
9255             cls: 'glyphicon form-control-feedback'
9256         };
9257             
9258         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9259             
9260             inputblock = {
9261                 cls : 'has-feedback',
9262                 cn :  [
9263                     input,
9264                     feedback
9265                 ] 
9266             };  
9267         }
9268         
9269         if (this.before || this.after) {
9270             
9271             inputblock = {
9272                 cls : 'input-group',
9273                 cn :  [] 
9274             };
9275             
9276             if (this.before && typeof(this.before) == 'string') {
9277                 
9278                 inputblock.cn.push({
9279                     tag :'span',
9280                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9281                     html : this.before
9282                 });
9283             }
9284             if (this.before && typeof(this.before) == 'object') {
9285                 this.before = Roo.factory(this.before);
9286                 
9287                 inputblock.cn.push({
9288                     tag :'span',
9289                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9290                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9291                 });
9292             }
9293             
9294             inputblock.cn.push(input);
9295             
9296             if (this.after && typeof(this.after) == 'string') {
9297                 inputblock.cn.push({
9298                     tag :'span',
9299                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9300                     html : this.after
9301                 });
9302             }
9303             if (this.after && typeof(this.after) == 'object') {
9304                 this.after = Roo.factory(this.after);
9305                 
9306                 inputblock.cn.push({
9307                     tag :'span',
9308                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9309                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9310                 });
9311             }
9312             
9313             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9314                 inputblock.cls += ' has-feedback';
9315                 inputblock.cn.push(feedback);
9316             }
9317         };
9318         var indicator = {
9319             tag : 'i',
9320             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9321             tooltip : 'This field is required'
9322         };
9323         if (Roo.bootstrap.version == 4) {
9324             indicator = {
9325                 tag : 'i',
9326                 style : 'display-none'
9327             };
9328         }
9329         if (align ==='left' && this.fieldLabel.length) {
9330             
9331             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9332             
9333             cfg.cn = [
9334                 indicator,
9335                 {
9336                     tag: 'label',
9337                     'for' :  id,
9338                     cls : 'control-label col-form-label',
9339                     html : this.fieldLabel
9340
9341                 },
9342                 {
9343                     cls : "", 
9344                     cn: [
9345                         inputblock
9346                     ]
9347                 }
9348             ];
9349             
9350             var labelCfg = cfg.cn[1];
9351             var contentCfg = cfg.cn[2];
9352             
9353             if(this.indicatorpos == 'right'){
9354                 cfg.cn = [
9355                     {
9356                         tag: 'label',
9357                         'for' :  id,
9358                         cls : 'control-label col-form-label',
9359                         cn : [
9360                             {
9361                                 tag : 'span',
9362                                 html : this.fieldLabel
9363                             },
9364                             indicator
9365                         ]
9366                     },
9367                     {
9368                         cls : "",
9369                         cn: [
9370                             inputblock
9371                         ]
9372                     }
9373
9374                 ];
9375                 
9376                 labelCfg = cfg.cn[0];
9377                 contentCfg = cfg.cn[1];
9378             
9379             }
9380             
9381             if(this.labelWidth > 12){
9382                 labelCfg.style = "width: " + this.labelWidth + 'px';
9383             }
9384             
9385             if(this.labelWidth < 13 && this.labelmd == 0){
9386                 this.labelmd = this.labelWidth;
9387             }
9388             
9389             if(this.labellg > 0){
9390                 labelCfg.cls += ' col-lg-' + this.labellg;
9391                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9392             }
9393             
9394             if(this.labelmd > 0){
9395                 labelCfg.cls += ' col-md-' + this.labelmd;
9396                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9397             }
9398             
9399             if(this.labelsm > 0){
9400                 labelCfg.cls += ' col-sm-' + this.labelsm;
9401                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9402             }
9403             
9404             if(this.labelxs > 0){
9405                 labelCfg.cls += ' col-xs-' + this.labelxs;
9406                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9407             }
9408             
9409             
9410         } else if ( this.fieldLabel.length) {
9411                 
9412             cfg.cn = [
9413                 {
9414                     tag : 'i',
9415                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9416                     tooltip : 'This field is required'
9417                 },
9418                 {
9419                     tag: 'label',
9420                    //cls : 'input-group-addon',
9421                     html : this.fieldLabel
9422
9423                 },
9424
9425                inputblock
9426
9427            ];
9428            
9429            if(this.indicatorpos == 'right'){
9430                 
9431                 cfg.cn = [
9432                     {
9433                         tag: 'label',
9434                        //cls : 'input-group-addon',
9435                         html : this.fieldLabel
9436
9437                     },
9438                     {
9439                         tag : 'i',
9440                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9441                         tooltip : 'This field is required'
9442                     },
9443
9444                    inputblock
9445
9446                ];
9447
9448             }
9449
9450         } else {
9451             
9452             cfg.cn = [
9453
9454                     inputblock
9455
9456             ];
9457                 
9458                 
9459         };
9460         
9461         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9462            cfg.cls += ' navbar-form';
9463         }
9464         
9465         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9466             // on BS4 we do this only if not form 
9467             cfg.cls += ' navbar-form';
9468             cfg.tag = 'li';
9469         }
9470         
9471         return cfg;
9472         
9473     },
9474     /**
9475      * return the real input element.
9476      */
9477     inputEl: function ()
9478     {
9479         return this.el.select('input.form-control',true).first();
9480     },
9481     
9482     tooltipEl : function()
9483     {
9484         return this.inputEl();
9485     },
9486     
9487     indicatorEl : function()
9488     {
9489         if (Roo.bootstrap.version == 4) {
9490             return false; // not enabled in v4 yet.
9491         }
9492         
9493         var indicator = this.el.select('i.roo-required-indicator',true).first();
9494         
9495         if(!indicator){
9496             return false;
9497         }
9498         
9499         return indicator;
9500         
9501     },
9502     
9503     setDisabled : function(v)
9504     {
9505         var i  = this.inputEl().dom;
9506         if (!v) {
9507             i.removeAttribute('disabled');
9508             return;
9509             
9510         }
9511         i.setAttribute('disabled','true');
9512     },
9513     initEvents : function()
9514     {
9515           
9516         this.inputEl().on("keydown" , this.fireKey,  this);
9517         this.inputEl().on("focus", this.onFocus,  this);
9518         this.inputEl().on("blur", this.onBlur,  this);
9519         
9520         this.inputEl().relayEvent('keyup', this);
9521         
9522         this.indicator = this.indicatorEl();
9523         
9524         if(this.indicator){
9525             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9526         }
9527  
9528         // reference to original value for reset
9529         this.originalValue = this.getValue();
9530         //Roo.form.TextField.superclass.initEvents.call(this);
9531         if(this.validationEvent == 'keyup'){
9532             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9533             this.inputEl().on('keyup', this.filterValidation, this);
9534         }
9535         else if(this.validationEvent !== false){
9536             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9537         }
9538         
9539         if(this.selectOnFocus){
9540             this.on("focus", this.preFocus, this);
9541             
9542         }
9543         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9544             this.inputEl().on("keypress", this.filterKeys, this);
9545         } else {
9546             this.inputEl().relayEvent('keypress', this);
9547         }
9548        /* if(this.grow){
9549             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9550             this.el.on("click", this.autoSize,  this);
9551         }
9552         */
9553         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9554             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9555         }
9556         
9557         if (typeof(this.before) == 'object') {
9558             this.before.render(this.el.select('.roo-input-before',true).first());
9559         }
9560         if (typeof(this.after) == 'object') {
9561             this.after.render(this.el.select('.roo-input-after',true).first());
9562         }
9563         
9564         this.inputEl().on('change', this.onChange, this);
9565         
9566     },
9567     filterValidation : function(e){
9568         if(!e.isNavKeyPress()){
9569             this.validationTask.delay(this.validationDelay);
9570         }
9571     },
9572      /**
9573      * Validates the field value
9574      * @return {Boolean} True if the value is valid, else false
9575      */
9576     validate : function(){
9577         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9578         if(this.disabled || this.validateValue(this.getRawValue())){
9579             this.markValid();
9580             return true;
9581         }
9582         
9583         this.markInvalid();
9584         return false;
9585     },
9586     
9587     
9588     /**
9589      * Validates a value according to the field's validation rules and marks the field as invalid
9590      * if the validation fails
9591      * @param {Mixed} value The value to validate
9592      * @return {Boolean} True if the value is valid, else false
9593      */
9594     validateValue : function(value)
9595     {
9596         if(this.getVisibilityEl().hasClass('hidden')){
9597             return true;
9598         }
9599         
9600         if(value.length < 1)  { // if it's blank
9601             if(this.allowBlank){
9602                 return true;
9603             }
9604             return false;
9605         }
9606         
9607         if(value.length < this.minLength){
9608             return false;
9609         }
9610         if(value.length > this.maxLength){
9611             return false;
9612         }
9613         if(this.vtype){
9614             var vt = Roo.form.VTypes;
9615             if(!vt[this.vtype](value, this)){
9616                 return false;
9617             }
9618         }
9619         if(typeof this.validator == "function"){
9620             var msg = this.validator(value);
9621             if(msg !== true){
9622                 return false;
9623             }
9624             if (typeof(msg) == 'string') {
9625                 this.invalidText = msg;
9626             }
9627         }
9628         
9629         if(this.regex && !this.regex.test(value)){
9630             return false;
9631         }
9632         
9633         return true;
9634     },
9635     
9636      // private
9637     fireKey : function(e){
9638         //Roo.log('field ' + e.getKey());
9639         if(e.isNavKeyPress()){
9640             this.fireEvent("specialkey", this, e);
9641         }
9642     },
9643     focus : function (selectText){
9644         if(this.rendered){
9645             this.inputEl().focus();
9646             if(selectText === true){
9647                 this.inputEl().dom.select();
9648             }
9649         }
9650         return this;
9651     } ,
9652     
9653     onFocus : function(){
9654         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9655            // this.el.addClass(this.focusClass);
9656         }
9657         if(!this.hasFocus){
9658             this.hasFocus = true;
9659             this.startValue = this.getValue();
9660             this.fireEvent("focus", this);
9661         }
9662     },
9663     
9664     beforeBlur : Roo.emptyFn,
9665
9666     
9667     // private
9668     onBlur : function(){
9669         this.beforeBlur();
9670         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9671             //this.el.removeClass(this.focusClass);
9672         }
9673         this.hasFocus = false;
9674         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9675             this.validate();
9676         }
9677         var v = this.getValue();
9678         if(String(v) !== String(this.startValue)){
9679             this.fireEvent('change', this, v, this.startValue);
9680         }
9681         this.fireEvent("blur", this);
9682     },
9683     
9684     onChange : function(e)
9685     {
9686         var v = this.getValue();
9687         if(String(v) !== String(this.startValue)){
9688             this.fireEvent('change', this, v, this.startValue);
9689         }
9690         
9691     },
9692     
9693     /**
9694      * Resets the current field value to the originally loaded value and clears any validation messages
9695      */
9696     reset : function(){
9697         this.setValue(this.originalValue);
9698         this.validate();
9699     },
9700      /**
9701      * Returns the name of the field
9702      * @return {Mixed} name The name field
9703      */
9704     getName: function(){
9705         return this.name;
9706     },
9707      /**
9708      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9709      * @return {Mixed} value The field value
9710      */
9711     getValue : function(){
9712         
9713         var v = this.inputEl().getValue();
9714         
9715         return v;
9716     },
9717     /**
9718      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9719      * @return {Mixed} value The field value
9720      */
9721     getRawValue : function(){
9722         var v = this.inputEl().getValue();
9723         
9724         return v;
9725     },
9726     
9727     /**
9728      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9729      * @param {Mixed} value The value to set
9730      */
9731     setRawValue : function(v){
9732         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9733     },
9734     
9735     selectText : function(start, end){
9736         var v = this.getRawValue();
9737         if(v.length > 0){
9738             start = start === undefined ? 0 : start;
9739             end = end === undefined ? v.length : end;
9740             var d = this.inputEl().dom;
9741             if(d.setSelectionRange){
9742                 d.setSelectionRange(start, end);
9743             }else if(d.createTextRange){
9744                 var range = d.createTextRange();
9745                 range.moveStart("character", start);
9746                 range.moveEnd("character", v.length-end);
9747                 range.select();
9748             }
9749         }
9750     },
9751     
9752     /**
9753      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9754      * @param {Mixed} value The value to set
9755      */
9756     setValue : function(v){
9757         this.value = v;
9758         if(this.rendered){
9759             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9760             this.validate();
9761         }
9762     },
9763     
9764     /*
9765     processValue : function(value){
9766         if(this.stripCharsRe){
9767             var newValue = value.replace(this.stripCharsRe, '');
9768             if(newValue !== value){
9769                 this.setRawValue(newValue);
9770                 return newValue;
9771             }
9772         }
9773         return value;
9774     },
9775   */
9776     preFocus : function(){
9777         
9778         if(this.selectOnFocus){
9779             this.inputEl().dom.select();
9780         }
9781     },
9782     filterKeys : function(e){
9783         var k = e.getKey();
9784         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9785             return;
9786         }
9787         var c = e.getCharCode(), cc = String.fromCharCode(c);
9788         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9789             return;
9790         }
9791         if(!this.maskRe.test(cc)){
9792             e.stopEvent();
9793         }
9794     },
9795      /**
9796      * Clear any invalid styles/messages for this field
9797      */
9798     clearInvalid : function(){
9799         
9800         if(!this.el || this.preventMark){ // not rendered
9801             return;
9802         }
9803         
9804         
9805         this.el.removeClass([this.invalidClass, 'is-invalid']);
9806         
9807         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9808             
9809             var feedback = this.el.select('.form-control-feedback', true).first();
9810             
9811             if(feedback){
9812                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9813             }
9814             
9815         }
9816         
9817         if(this.indicator){
9818             this.indicator.removeClass('visible');
9819             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9820         }
9821         
9822         this.fireEvent('valid', this);
9823     },
9824     
9825      /**
9826      * Mark this field as valid
9827      */
9828     markValid : function()
9829     {
9830         if(!this.el  || this.preventMark){ // not rendered...
9831             return;
9832         }
9833         
9834         this.el.removeClass([this.invalidClass, this.validClass]);
9835         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9836
9837         var feedback = this.el.select('.form-control-feedback', true).first();
9838             
9839         if(feedback){
9840             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9841         }
9842         
9843         if(this.indicator){
9844             this.indicator.removeClass('visible');
9845             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9846         }
9847         
9848         if(this.disabled){
9849             return;
9850         }
9851         
9852         if(this.allowBlank && !this.getRawValue().length){
9853             return;
9854         }
9855         if (Roo.bootstrap.version == 3) {
9856             this.el.addClass(this.validClass);
9857         } else {
9858             this.inputEl().addClass('is-valid');
9859         }
9860
9861         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9862             
9863             var feedback = this.el.select('.form-control-feedback', true).first();
9864             
9865             if(feedback){
9866                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9867                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9868             }
9869             
9870         }
9871         
9872         this.fireEvent('valid', this);
9873     },
9874     
9875      /**
9876      * Mark this field as invalid
9877      * @param {String} msg The validation message
9878      */
9879     markInvalid : function(msg)
9880     {
9881         if(!this.el  || this.preventMark){ // not rendered
9882             return;
9883         }
9884         
9885         this.el.removeClass([this.invalidClass, this.validClass]);
9886         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9887         
9888         var feedback = this.el.select('.form-control-feedback', true).first();
9889             
9890         if(feedback){
9891             this.el.select('.form-control-feedback', true).first().removeClass(
9892                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9893         }
9894
9895         if(this.disabled){
9896             return;
9897         }
9898         
9899         if(this.allowBlank && !this.getRawValue().length){
9900             return;
9901         }
9902         
9903         if(this.indicator){
9904             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9905             this.indicator.addClass('visible');
9906         }
9907         if (Roo.bootstrap.version == 3) {
9908             this.el.addClass(this.invalidClass);
9909         } else {
9910             this.inputEl().addClass('is-invalid');
9911         }
9912         
9913         
9914         
9915         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9916             
9917             var feedback = this.el.select('.form-control-feedback', true).first();
9918             
9919             if(feedback){
9920                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9921                 
9922                 if(this.getValue().length || this.forceFeedback){
9923                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9924                 }
9925                 
9926             }
9927             
9928         }
9929         
9930         this.fireEvent('invalid', this, msg);
9931     },
9932     // private
9933     SafariOnKeyDown : function(event)
9934     {
9935         // this is a workaround for a password hang bug on chrome/ webkit.
9936         if (this.inputEl().dom.type != 'password') {
9937             return;
9938         }
9939         
9940         var isSelectAll = false;
9941         
9942         if(this.inputEl().dom.selectionEnd > 0){
9943             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9944         }
9945         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9946             event.preventDefault();
9947             this.setValue('');
9948             return;
9949         }
9950         
9951         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9952             
9953             event.preventDefault();
9954             // this is very hacky as keydown always get's upper case.
9955             //
9956             var cc = String.fromCharCode(event.getCharCode());
9957             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9958             
9959         }
9960     },
9961     adjustWidth : function(tag, w){
9962         tag = tag.toLowerCase();
9963         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9964             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9965                 if(tag == 'input'){
9966                     return w + 2;
9967                 }
9968                 if(tag == 'textarea'){
9969                     return w-2;
9970                 }
9971             }else if(Roo.isOpera){
9972                 if(tag == 'input'){
9973                     return w + 2;
9974                 }
9975                 if(tag == 'textarea'){
9976                     return w-2;
9977                 }
9978             }
9979         }
9980         return w;
9981     },
9982     
9983     setFieldLabel : function(v)
9984     {
9985         if(!this.rendered){
9986             return;
9987         }
9988         
9989         if(this.indicatorEl()){
9990             var ar = this.el.select('label > span',true);
9991             
9992             if (ar.elements.length) {
9993                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9994                 this.fieldLabel = v;
9995                 return;
9996             }
9997             
9998             var br = this.el.select('label',true);
9999             
10000             if(br.elements.length) {
10001                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10002                 this.fieldLabel = v;
10003                 return;
10004             }
10005             
10006             Roo.log('Cannot Found any of label > span || label in input');
10007             return;
10008         }
10009         
10010         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10011         this.fieldLabel = v;
10012         
10013         
10014     }
10015 });
10016
10017  
10018 /*
10019  * - LGPL
10020  *
10021  * Input
10022  * 
10023  */
10024
10025 /**
10026  * @class Roo.bootstrap.TextArea
10027  * @extends Roo.bootstrap.Input
10028  * Bootstrap TextArea class
10029  * @cfg {Number} cols Specifies the visible width of a text area
10030  * @cfg {Number} rows Specifies the visible number of lines in a text area
10031  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10032  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10033  * @cfg {string} html text
10034  * 
10035  * @constructor
10036  * Create a new TextArea
10037  * @param {Object} config The config object
10038  */
10039
10040 Roo.bootstrap.TextArea = function(config){
10041     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10042    
10043 };
10044
10045 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10046      
10047     cols : false,
10048     rows : 5,
10049     readOnly : false,
10050     warp : 'soft',
10051     resize : false,
10052     value: false,
10053     html: false,
10054     
10055     getAutoCreate : function(){
10056         
10057         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10058         
10059         var id = Roo.id();
10060         
10061         var cfg = {};
10062         
10063         if(this.inputType != 'hidden'){
10064             cfg.cls = 'form-group' //input-group
10065         }
10066         
10067         var input =  {
10068             tag: 'textarea',
10069             id : id,
10070             warp : this.warp,
10071             rows : this.rows,
10072             value : this.value || '',
10073             html: this.html || '',
10074             cls : 'form-control',
10075             placeholder : this.placeholder || '' 
10076             
10077         };
10078         
10079         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10080             input.maxLength = this.maxLength;
10081         }
10082         
10083         if(this.resize){
10084             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10085         }
10086         
10087         if(this.cols){
10088             input.cols = this.cols;
10089         }
10090         
10091         if (this.readOnly) {
10092             input.readonly = true;
10093         }
10094         
10095         if (this.name) {
10096             input.name = this.name;
10097         }
10098         
10099         if (this.size) {
10100             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10101         }
10102         
10103         var settings=this;
10104         ['xs','sm','md','lg'].map(function(size){
10105             if (settings[size]) {
10106                 cfg.cls += ' col-' + size + '-' + settings[size];
10107             }
10108         });
10109         
10110         var inputblock = input;
10111         
10112         if(this.hasFeedback && !this.allowBlank){
10113             
10114             var feedback = {
10115                 tag: 'span',
10116                 cls: 'glyphicon form-control-feedback'
10117             };
10118
10119             inputblock = {
10120                 cls : 'has-feedback',
10121                 cn :  [
10122                     input,
10123                     feedback
10124                 ] 
10125             };  
10126         }
10127         
10128         
10129         if (this.before || this.after) {
10130             
10131             inputblock = {
10132                 cls : 'input-group',
10133                 cn :  [] 
10134             };
10135             if (this.before) {
10136                 inputblock.cn.push({
10137                     tag :'span',
10138                     cls : 'input-group-addon',
10139                     html : this.before
10140                 });
10141             }
10142             
10143             inputblock.cn.push(input);
10144             
10145             if(this.hasFeedback && !this.allowBlank){
10146                 inputblock.cls += ' has-feedback';
10147                 inputblock.cn.push(feedback);
10148             }
10149             
10150             if (this.after) {
10151                 inputblock.cn.push({
10152                     tag :'span',
10153                     cls : 'input-group-addon',
10154                     html : this.after
10155                 });
10156             }
10157             
10158         }
10159         
10160         if (align ==='left' && this.fieldLabel.length) {
10161             cfg.cn = [
10162                 {
10163                     tag: 'label',
10164                     'for' :  id,
10165                     cls : 'control-label',
10166                     html : this.fieldLabel
10167                 },
10168                 {
10169                     cls : "",
10170                     cn: [
10171                         inputblock
10172                     ]
10173                 }
10174
10175             ];
10176             
10177             if(this.labelWidth > 12){
10178                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10179             }
10180
10181             if(this.labelWidth < 13 && this.labelmd == 0){
10182                 this.labelmd = this.labelWidth;
10183             }
10184
10185             if(this.labellg > 0){
10186                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10187                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10188             }
10189
10190             if(this.labelmd > 0){
10191                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10192                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10193             }
10194
10195             if(this.labelsm > 0){
10196                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10197                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10198             }
10199
10200             if(this.labelxs > 0){
10201                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10202                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10203             }
10204             
10205         } else if ( this.fieldLabel.length) {
10206             cfg.cn = [
10207
10208                {
10209                    tag: 'label',
10210                    //cls : 'input-group-addon',
10211                    html : this.fieldLabel
10212
10213                },
10214
10215                inputblock
10216
10217            ];
10218
10219         } else {
10220
10221             cfg.cn = [
10222
10223                 inputblock
10224
10225             ];
10226                 
10227         }
10228         
10229         if (this.disabled) {
10230             input.disabled=true;
10231         }
10232         
10233         return cfg;
10234         
10235     },
10236     /**
10237      * return the real textarea element.
10238      */
10239     inputEl: function ()
10240     {
10241         return this.el.select('textarea.form-control',true).first();
10242     },
10243     
10244     /**
10245      * Clear any invalid styles/messages for this field
10246      */
10247     clearInvalid : function()
10248     {
10249         
10250         if(!this.el || this.preventMark){ // not rendered
10251             return;
10252         }
10253         
10254         var label = this.el.select('label', true).first();
10255         var icon = this.el.select('i.fa-star', true).first();
10256         
10257         if(label && icon){
10258             icon.remove();
10259         }
10260         this.el.removeClass( this.validClass);
10261         this.inputEl().removeClass('is-invalid');
10262          
10263         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10264             
10265             var feedback = this.el.select('.form-control-feedback', true).first();
10266             
10267             if(feedback){
10268                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10269             }
10270             
10271         }
10272         
10273         this.fireEvent('valid', this);
10274     },
10275     
10276      /**
10277      * Mark this field as valid
10278      */
10279     markValid : function()
10280     {
10281         if(!this.el  || this.preventMark){ // not rendered
10282             return;
10283         }
10284         
10285         this.el.removeClass([this.invalidClass, this.validClass]);
10286         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10287         
10288         var feedback = this.el.select('.form-control-feedback', true).first();
10289             
10290         if(feedback){
10291             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10292         }
10293
10294         if(this.disabled || this.allowBlank){
10295             return;
10296         }
10297         
10298         var label = this.el.select('label', true).first();
10299         var icon = this.el.select('i.fa-star', true).first();
10300         
10301         if(label && icon){
10302             icon.remove();
10303         }
10304         if (Roo.bootstrap.version == 3) {
10305             this.el.addClass(this.validClass);
10306         } else {
10307             this.inputEl().addClass('is-valid');
10308         }
10309         
10310         
10311         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10312             
10313             var feedback = this.el.select('.form-control-feedback', true).first();
10314             
10315             if(feedback){
10316                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10317                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10318             }
10319             
10320         }
10321         
10322         this.fireEvent('valid', this);
10323     },
10324     
10325      /**
10326      * Mark this field as invalid
10327      * @param {String} msg The validation message
10328      */
10329     markInvalid : function(msg)
10330     {
10331         if(!this.el  || this.preventMark){ // not rendered
10332             return;
10333         }
10334         
10335         this.el.removeClass([this.invalidClass, this.validClass]);
10336         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10337         
10338         var feedback = this.el.select('.form-control-feedback', true).first();
10339             
10340         if(feedback){
10341             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10342         }
10343
10344         if(this.disabled || this.allowBlank){
10345             return;
10346         }
10347         
10348         var label = this.el.select('label', true).first();
10349         var icon = this.el.select('i.fa-star', true).first();
10350         
10351         if(!this.getValue().length && label && !icon){
10352             this.el.createChild({
10353                 tag : 'i',
10354                 cls : 'text-danger fa fa-lg fa-star',
10355                 tooltip : 'This field is required',
10356                 style : 'margin-right:5px;'
10357             }, label, true);
10358         }
10359         
10360         if (Roo.bootstrap.version == 3) {
10361             this.el.addClass(this.invalidClass);
10362         } else {
10363             this.inputEl().addClass('is-invalid');
10364         }
10365         
10366         // fixme ... this may be depricated need to test..
10367         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10368             
10369             var feedback = this.el.select('.form-control-feedback', true).first();
10370             
10371             if(feedback){
10372                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10373                 
10374                 if(this.getValue().length || this.forceFeedback){
10375                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10376                 }
10377                 
10378             }
10379             
10380         }
10381         
10382         this.fireEvent('invalid', this, msg);
10383     }
10384 });
10385
10386  
10387 /*
10388  * - LGPL
10389  *
10390  * trigger field - base class for combo..
10391  * 
10392  */
10393  
10394 /**
10395  * @class Roo.bootstrap.TriggerField
10396  * @extends Roo.bootstrap.Input
10397  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10398  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10399  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10400  * for which you can provide a custom implementation.  For example:
10401  * <pre><code>
10402 var trigger = new Roo.bootstrap.TriggerField();
10403 trigger.onTriggerClick = myTriggerFn;
10404 trigger.applyTo('my-field');
10405 </code></pre>
10406  *
10407  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10408  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10409  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10410  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10411  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10412
10413  * @constructor
10414  * Create a new TriggerField.
10415  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10416  * to the base TextField)
10417  */
10418 Roo.bootstrap.TriggerField = function(config){
10419     this.mimicing = false;
10420     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10421 };
10422
10423 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10424     /**
10425      * @cfg {String} triggerClass A CSS class to apply to the trigger
10426      */
10427      /**
10428      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10429      */
10430     hideTrigger:false,
10431
10432     /**
10433      * @cfg {Boolean} removable (true|false) special filter default false
10434      */
10435     removable : false,
10436     
10437     /** @cfg {Boolean} grow @hide */
10438     /** @cfg {Number} growMin @hide */
10439     /** @cfg {Number} growMax @hide */
10440
10441     /**
10442      * @hide 
10443      * @method
10444      */
10445     autoSize: Roo.emptyFn,
10446     // private
10447     monitorTab : true,
10448     // private
10449     deferHeight : true,
10450
10451     
10452     actionMode : 'wrap',
10453     
10454     caret : false,
10455     
10456     
10457     getAutoCreate : function(){
10458        
10459         var align = this.labelAlign || this.parentLabelAlign();
10460         
10461         var id = Roo.id();
10462         
10463         var cfg = {
10464             cls: 'form-group' //input-group
10465         };
10466         
10467         
10468         var input =  {
10469             tag: 'input',
10470             id : id,
10471             type : this.inputType,
10472             cls : 'form-control',
10473             autocomplete: 'new-password',
10474             placeholder : this.placeholder || '' 
10475             
10476         };
10477         if (this.name) {
10478             input.name = this.name;
10479         }
10480         if (this.size) {
10481             input.cls += ' input-' + this.size;
10482         }
10483         
10484         if (this.disabled) {
10485             input.disabled=true;
10486         }
10487         
10488         var inputblock = input;
10489         
10490         if(this.hasFeedback && !this.allowBlank){
10491             
10492             var feedback = {
10493                 tag: 'span',
10494                 cls: 'glyphicon form-control-feedback'
10495             };
10496             
10497             if(this.removable && !this.editable && !this.tickable){
10498                 inputblock = {
10499                     cls : 'has-feedback',
10500                     cn :  [
10501                         inputblock,
10502                         {
10503                             tag: 'button',
10504                             html : 'x',
10505                             cls : 'roo-combo-removable-btn close'
10506                         },
10507                         feedback
10508                     ] 
10509                 };
10510             } else {
10511                 inputblock = {
10512                     cls : 'has-feedback',
10513                     cn :  [
10514                         inputblock,
10515                         feedback
10516                     ] 
10517                 };
10518             }
10519
10520         } else {
10521             if(this.removable && !this.editable && !this.tickable){
10522                 inputblock = {
10523                     cls : 'roo-removable',
10524                     cn :  [
10525                         inputblock,
10526                         {
10527                             tag: 'button',
10528                             html : 'x',
10529                             cls : 'roo-combo-removable-btn close'
10530                         }
10531                     ] 
10532                 };
10533             }
10534         }
10535         
10536         if (this.before || this.after) {
10537             
10538             inputblock = {
10539                 cls : 'input-group',
10540                 cn :  [] 
10541             };
10542             if (this.before) {
10543                 inputblock.cn.push({
10544                     tag :'span',
10545                     cls : 'input-group-addon input-group-prepend input-group-text',
10546                     html : this.before
10547                 });
10548             }
10549             
10550             inputblock.cn.push(input);
10551             
10552             if(this.hasFeedback && !this.allowBlank){
10553                 inputblock.cls += ' has-feedback';
10554                 inputblock.cn.push(feedback);
10555             }
10556             
10557             if (this.after) {
10558                 inputblock.cn.push({
10559                     tag :'span',
10560                     cls : 'input-group-addon input-group-append input-group-text',
10561                     html : this.after
10562                 });
10563             }
10564             
10565         };
10566         
10567       
10568         
10569         var ibwrap = inputblock;
10570         
10571         if(this.multiple){
10572             ibwrap = {
10573                 tag: 'ul',
10574                 cls: 'roo-select2-choices',
10575                 cn:[
10576                     {
10577                         tag: 'li',
10578                         cls: 'roo-select2-search-field',
10579                         cn: [
10580
10581                             inputblock
10582                         ]
10583                     }
10584                 ]
10585             };
10586                 
10587         }
10588         
10589         var combobox = {
10590             cls: 'roo-select2-container input-group',
10591             cn: [
10592                  {
10593                     tag: 'input',
10594                     type : 'hidden',
10595                     cls: 'form-hidden-field'
10596                 },
10597                 ibwrap
10598             ]
10599         };
10600         
10601         if(!this.multiple && this.showToggleBtn){
10602             
10603             var caret = {
10604                         tag: 'span',
10605                         cls: 'caret'
10606              };
10607             if (this.caret != false) {
10608                 caret = {
10609                      tag: 'i',
10610                      cls: 'fa fa-' + this.caret
10611                 };
10612                 
10613             }
10614             
10615             combobox.cn.push({
10616                 tag :'span',
10617                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10618                 cn : [
10619                     caret,
10620                     {
10621                         tag: 'span',
10622                         cls: 'combobox-clear',
10623                         cn  : [
10624                             {
10625                                 tag : 'i',
10626                                 cls: 'icon-remove'
10627                             }
10628                         ]
10629                     }
10630                 ]
10631
10632             })
10633         }
10634         
10635         if(this.multiple){
10636             combobox.cls += ' roo-select2-container-multi';
10637         }
10638          var indicator = {
10639             tag : 'i',
10640             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10641             tooltip : 'This field is required'
10642         };
10643         if (Roo.bootstrap.version == 4) {
10644             indicator = {
10645                 tag : 'i',
10646                 style : 'display:none'
10647             };
10648         }
10649         
10650         
10651         if (align ==='left' && this.fieldLabel.length) {
10652             
10653             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10654
10655             cfg.cn = [
10656                 indicator,
10657                 {
10658                     tag: 'label',
10659                     'for' :  id,
10660                     cls : 'control-label',
10661                     html : this.fieldLabel
10662
10663                 },
10664                 {
10665                     cls : "", 
10666                     cn: [
10667                         combobox
10668                     ]
10669                 }
10670
10671             ];
10672             
10673             var labelCfg = cfg.cn[1];
10674             var contentCfg = cfg.cn[2];
10675             
10676             if(this.indicatorpos == 'right'){
10677                 cfg.cn = [
10678                     {
10679                         tag: 'label',
10680                         'for' :  id,
10681                         cls : 'control-label',
10682                         cn : [
10683                             {
10684                                 tag : 'span',
10685                                 html : this.fieldLabel
10686                             },
10687                             indicator
10688                         ]
10689                     },
10690                     {
10691                         cls : "", 
10692                         cn: [
10693                             combobox
10694                         ]
10695                     }
10696
10697                 ];
10698                 
10699                 labelCfg = cfg.cn[0];
10700                 contentCfg = cfg.cn[1];
10701             }
10702             
10703             if(this.labelWidth > 12){
10704                 labelCfg.style = "width: " + this.labelWidth + 'px';
10705             }
10706             
10707             if(this.labelWidth < 13 && this.labelmd == 0){
10708                 this.labelmd = this.labelWidth;
10709             }
10710             
10711             if(this.labellg > 0){
10712                 labelCfg.cls += ' col-lg-' + this.labellg;
10713                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10714             }
10715             
10716             if(this.labelmd > 0){
10717                 labelCfg.cls += ' col-md-' + this.labelmd;
10718                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10719             }
10720             
10721             if(this.labelsm > 0){
10722                 labelCfg.cls += ' col-sm-' + this.labelsm;
10723                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10724             }
10725             
10726             if(this.labelxs > 0){
10727                 labelCfg.cls += ' col-xs-' + this.labelxs;
10728                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10729             }
10730             
10731         } else if ( this.fieldLabel.length) {
10732 //                Roo.log(" label");
10733             cfg.cn = [
10734                 indicator,
10735                {
10736                    tag: 'label',
10737                    //cls : 'input-group-addon',
10738                    html : this.fieldLabel
10739
10740                },
10741
10742                combobox
10743
10744             ];
10745             
10746             if(this.indicatorpos == 'right'){
10747                 
10748                 cfg.cn = [
10749                     {
10750                        tag: 'label',
10751                        cn : [
10752                            {
10753                                tag : 'span',
10754                                html : this.fieldLabel
10755                            },
10756                            indicator
10757                        ]
10758
10759                     },
10760                     combobox
10761
10762                 ];
10763
10764             }
10765
10766         } else {
10767             
10768 //                Roo.log(" no label && no align");
10769                 cfg = combobox
10770                      
10771                 
10772         }
10773         
10774         var settings=this;
10775         ['xs','sm','md','lg'].map(function(size){
10776             if (settings[size]) {
10777                 cfg.cls += ' col-' + size + '-' + settings[size];
10778             }
10779         });
10780         
10781         return cfg;
10782         
10783     },
10784     
10785     
10786     
10787     // private
10788     onResize : function(w, h){
10789 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10790 //        if(typeof w == 'number'){
10791 //            var x = w - this.trigger.getWidth();
10792 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10793 //            this.trigger.setStyle('left', x+'px');
10794 //        }
10795     },
10796
10797     // private
10798     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10799
10800     // private
10801     getResizeEl : function(){
10802         return this.inputEl();
10803     },
10804
10805     // private
10806     getPositionEl : function(){
10807         return this.inputEl();
10808     },
10809
10810     // private
10811     alignErrorIcon : function(){
10812         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10813     },
10814
10815     // private
10816     initEvents : function(){
10817         
10818         this.createList();
10819         
10820         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10821         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10822         if(!this.multiple && this.showToggleBtn){
10823             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10824             if(this.hideTrigger){
10825                 this.trigger.setDisplayed(false);
10826             }
10827             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10828         }
10829         
10830         if(this.multiple){
10831             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10832         }
10833         
10834         if(this.removable && !this.editable && !this.tickable){
10835             var close = this.closeTriggerEl();
10836             
10837             if(close){
10838                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10839                 close.on('click', this.removeBtnClick, this, close);
10840             }
10841         }
10842         
10843         //this.trigger.addClassOnOver('x-form-trigger-over');
10844         //this.trigger.addClassOnClick('x-form-trigger-click');
10845         
10846         //if(!this.width){
10847         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10848         //}
10849     },
10850     
10851     closeTriggerEl : function()
10852     {
10853         var close = this.el.select('.roo-combo-removable-btn', true).first();
10854         return close ? close : false;
10855     },
10856     
10857     removeBtnClick : function(e, h, el)
10858     {
10859         e.preventDefault();
10860         
10861         if(this.fireEvent("remove", this) !== false){
10862             this.reset();
10863             this.fireEvent("afterremove", this)
10864         }
10865     },
10866     
10867     createList : function()
10868     {
10869         this.list = Roo.get(document.body).createChild({
10870             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10871             cls: 'typeahead typeahead-long dropdown-menu',
10872             style: 'display:none'
10873         });
10874         
10875         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10876         
10877     },
10878
10879     // private
10880     initTrigger : function(){
10881        
10882     },
10883
10884     // private
10885     onDestroy : function(){
10886         if(this.trigger){
10887             this.trigger.removeAllListeners();
10888           //  this.trigger.remove();
10889         }
10890         //if(this.wrap){
10891         //    this.wrap.remove();
10892         //}
10893         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10894     },
10895
10896     // private
10897     onFocus : function(){
10898         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10899         /*
10900         if(!this.mimicing){
10901             this.wrap.addClass('x-trigger-wrap-focus');
10902             this.mimicing = true;
10903             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10904             if(this.monitorTab){
10905                 this.el.on("keydown", this.checkTab, this);
10906             }
10907         }
10908         */
10909     },
10910
10911     // private
10912     checkTab : function(e){
10913         if(e.getKey() == e.TAB){
10914             this.triggerBlur();
10915         }
10916     },
10917
10918     // private
10919     onBlur : function(){
10920         // do nothing
10921     },
10922
10923     // private
10924     mimicBlur : function(e, t){
10925         /*
10926         if(!this.wrap.contains(t) && this.validateBlur()){
10927             this.triggerBlur();
10928         }
10929         */
10930     },
10931
10932     // private
10933     triggerBlur : function(){
10934         this.mimicing = false;
10935         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10936         if(this.monitorTab){
10937             this.el.un("keydown", this.checkTab, this);
10938         }
10939         //this.wrap.removeClass('x-trigger-wrap-focus');
10940         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10941     },
10942
10943     // private
10944     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10945     validateBlur : function(e, t){
10946         return true;
10947     },
10948
10949     // private
10950     onDisable : function(){
10951         this.inputEl().dom.disabled = true;
10952         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10953         //if(this.wrap){
10954         //    this.wrap.addClass('x-item-disabled');
10955         //}
10956     },
10957
10958     // private
10959     onEnable : function(){
10960         this.inputEl().dom.disabled = false;
10961         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10962         //if(this.wrap){
10963         //    this.el.removeClass('x-item-disabled');
10964         //}
10965     },
10966
10967     // private
10968     onShow : function(){
10969         var ae = this.getActionEl();
10970         
10971         if(ae){
10972             ae.dom.style.display = '';
10973             ae.dom.style.visibility = 'visible';
10974         }
10975     },
10976
10977     // private
10978     
10979     onHide : function(){
10980         var ae = this.getActionEl();
10981         ae.dom.style.display = 'none';
10982     },
10983
10984     /**
10985      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10986      * by an implementing function.
10987      * @method
10988      * @param {EventObject} e
10989      */
10990     onTriggerClick : Roo.emptyFn
10991 });
10992  /*
10993  * Based on:
10994  * Ext JS Library 1.1.1
10995  * Copyright(c) 2006-2007, Ext JS, LLC.
10996  *
10997  * Originally Released Under LGPL - original licence link has changed is not relivant.
10998  *
10999  * Fork - LGPL
11000  * <script type="text/javascript">
11001  */
11002
11003
11004 /**
11005  * @class Roo.data.SortTypes
11006  * @singleton
11007  * Defines the default sorting (casting?) comparison functions used when sorting data.
11008  */
11009 Roo.data.SortTypes = {
11010     /**
11011      * Default sort that does nothing
11012      * @param {Mixed} s The value being converted
11013      * @return {Mixed} The comparison value
11014      */
11015     none : function(s){
11016         return s;
11017     },
11018     
11019     /**
11020      * The regular expression used to strip tags
11021      * @type {RegExp}
11022      * @property
11023      */
11024     stripTagsRE : /<\/?[^>]+>/gi,
11025     
11026     /**
11027      * Strips all HTML tags to sort on text only
11028      * @param {Mixed} s The value being converted
11029      * @return {String} The comparison value
11030      */
11031     asText : function(s){
11032         return String(s).replace(this.stripTagsRE, "");
11033     },
11034     
11035     /**
11036      * Strips all HTML tags to sort on text only - Case insensitive
11037      * @param {Mixed} s The value being converted
11038      * @return {String} The comparison value
11039      */
11040     asUCText : function(s){
11041         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11042     },
11043     
11044     /**
11045      * Case insensitive string
11046      * @param {Mixed} s The value being converted
11047      * @return {String} The comparison value
11048      */
11049     asUCString : function(s) {
11050         return String(s).toUpperCase();
11051     },
11052     
11053     /**
11054      * Date sorting
11055      * @param {Mixed} s The value being converted
11056      * @return {Number} The comparison value
11057      */
11058     asDate : function(s) {
11059         if(!s){
11060             return 0;
11061         }
11062         if(s instanceof Date){
11063             return s.getTime();
11064         }
11065         return Date.parse(String(s));
11066     },
11067     
11068     /**
11069      * Float sorting
11070      * @param {Mixed} s The value being converted
11071      * @return {Float} The comparison value
11072      */
11073     asFloat : function(s) {
11074         var val = parseFloat(String(s).replace(/,/g, ""));
11075         if(isNaN(val)) {
11076             val = 0;
11077         }
11078         return val;
11079     },
11080     
11081     /**
11082      * Integer sorting
11083      * @param {Mixed} s The value being converted
11084      * @return {Number} The comparison value
11085      */
11086     asInt : function(s) {
11087         var val = parseInt(String(s).replace(/,/g, ""));
11088         if(isNaN(val)) {
11089             val = 0;
11090         }
11091         return val;
11092     }
11093 };/*
11094  * Based on:
11095  * Ext JS Library 1.1.1
11096  * Copyright(c) 2006-2007, Ext JS, LLC.
11097  *
11098  * Originally Released Under LGPL - original licence link has changed is not relivant.
11099  *
11100  * Fork - LGPL
11101  * <script type="text/javascript">
11102  */
11103
11104 /**
11105 * @class Roo.data.Record
11106  * Instances of this class encapsulate both record <em>definition</em> information, and record
11107  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11108  * to access Records cached in an {@link Roo.data.Store} object.<br>
11109  * <p>
11110  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11111  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11112  * objects.<br>
11113  * <p>
11114  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11115  * @constructor
11116  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11117  * {@link #create}. The parameters are the same.
11118  * @param {Array} data An associative Array of data values keyed by the field name.
11119  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11120  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11121  * not specified an integer id is generated.
11122  */
11123 Roo.data.Record = function(data, id){
11124     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11125     this.data = data;
11126 };
11127
11128 /**
11129  * Generate a constructor for a specific record layout.
11130  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11131  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11132  * Each field definition object may contain the following properties: <ul>
11133  * <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,
11134  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11135  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11136  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11137  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11138  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11139  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11140  * this may be omitted.</p></li>
11141  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11142  * <ul><li>auto (Default, implies no conversion)</li>
11143  * <li>string</li>
11144  * <li>int</li>
11145  * <li>float</li>
11146  * <li>boolean</li>
11147  * <li>date</li></ul></p></li>
11148  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11149  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11150  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11151  * by the Reader into an object that will be stored in the Record. It is passed the
11152  * following parameters:<ul>
11153  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11154  * </ul></p></li>
11155  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11156  * </ul>
11157  * <br>usage:<br><pre><code>
11158 var TopicRecord = Roo.data.Record.create(
11159     {name: 'title', mapping: 'topic_title'},
11160     {name: 'author', mapping: 'username'},
11161     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11162     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11163     {name: 'lastPoster', mapping: 'user2'},
11164     {name: 'excerpt', mapping: 'post_text'}
11165 );
11166
11167 var myNewRecord = new TopicRecord({
11168     title: 'Do my job please',
11169     author: 'noobie',
11170     totalPosts: 1,
11171     lastPost: new Date(),
11172     lastPoster: 'Animal',
11173     excerpt: 'No way dude!'
11174 });
11175 myStore.add(myNewRecord);
11176 </code></pre>
11177  * @method create
11178  * @static
11179  */
11180 Roo.data.Record.create = function(o){
11181     var f = function(){
11182         f.superclass.constructor.apply(this, arguments);
11183     };
11184     Roo.extend(f, Roo.data.Record);
11185     var p = f.prototype;
11186     p.fields = new Roo.util.MixedCollection(false, function(field){
11187         return field.name;
11188     });
11189     for(var i = 0, len = o.length; i < len; i++){
11190         p.fields.add(new Roo.data.Field(o[i]));
11191     }
11192     f.getField = function(name){
11193         return p.fields.get(name);  
11194     };
11195     return f;
11196 };
11197
11198 Roo.data.Record.AUTO_ID = 1000;
11199 Roo.data.Record.EDIT = 'edit';
11200 Roo.data.Record.REJECT = 'reject';
11201 Roo.data.Record.COMMIT = 'commit';
11202
11203 Roo.data.Record.prototype = {
11204     /**
11205      * Readonly flag - true if this record has been modified.
11206      * @type Boolean
11207      */
11208     dirty : false,
11209     editing : false,
11210     error: null,
11211     modified: null,
11212
11213     // private
11214     join : function(store){
11215         this.store = store;
11216     },
11217
11218     /**
11219      * Set the named field to the specified value.
11220      * @param {String} name The name of the field to set.
11221      * @param {Object} value The value to set the field to.
11222      */
11223     set : function(name, value){
11224         if(this.data[name] == value){
11225             return;
11226         }
11227         this.dirty = true;
11228         if(!this.modified){
11229             this.modified = {};
11230         }
11231         if(typeof this.modified[name] == 'undefined'){
11232             this.modified[name] = this.data[name];
11233         }
11234         this.data[name] = value;
11235         if(!this.editing && this.store){
11236             this.store.afterEdit(this);
11237         }       
11238     },
11239
11240     /**
11241      * Get the value of the named field.
11242      * @param {String} name The name of the field to get the value of.
11243      * @return {Object} The value of the field.
11244      */
11245     get : function(name){
11246         return this.data[name]; 
11247     },
11248
11249     // private
11250     beginEdit : function(){
11251         this.editing = true;
11252         this.modified = {}; 
11253     },
11254
11255     // private
11256     cancelEdit : function(){
11257         this.editing = false;
11258         delete this.modified;
11259     },
11260
11261     // private
11262     endEdit : function(){
11263         this.editing = false;
11264         if(this.dirty && this.store){
11265             this.store.afterEdit(this);
11266         }
11267     },
11268
11269     /**
11270      * Usually called by the {@link Roo.data.Store} which owns the Record.
11271      * Rejects all changes made to the Record since either creation, or the last commit operation.
11272      * Modified fields are reverted to their original values.
11273      * <p>
11274      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11275      * of reject operations.
11276      */
11277     reject : function(){
11278         var m = this.modified;
11279         for(var n in m){
11280             if(typeof m[n] != "function"){
11281                 this.data[n] = m[n];
11282             }
11283         }
11284         this.dirty = false;
11285         delete this.modified;
11286         this.editing = false;
11287         if(this.store){
11288             this.store.afterReject(this);
11289         }
11290     },
11291
11292     /**
11293      * Usually called by the {@link Roo.data.Store} which owns the Record.
11294      * Commits all changes made to the Record since either creation, or the last commit operation.
11295      * <p>
11296      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11297      * of commit operations.
11298      */
11299     commit : function(){
11300         this.dirty = false;
11301         delete this.modified;
11302         this.editing = false;
11303         if(this.store){
11304             this.store.afterCommit(this);
11305         }
11306     },
11307
11308     // private
11309     hasError : function(){
11310         return this.error != null;
11311     },
11312
11313     // private
11314     clearError : function(){
11315         this.error = null;
11316     },
11317
11318     /**
11319      * Creates a copy of this record.
11320      * @param {String} id (optional) A new record id if you don't want to use this record's id
11321      * @return {Record}
11322      */
11323     copy : function(newId) {
11324         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11325     }
11326 };/*
11327  * Based on:
11328  * Ext JS Library 1.1.1
11329  * Copyright(c) 2006-2007, Ext JS, LLC.
11330  *
11331  * Originally Released Under LGPL - original licence link has changed is not relivant.
11332  *
11333  * Fork - LGPL
11334  * <script type="text/javascript">
11335  */
11336
11337
11338
11339 /**
11340  * @class Roo.data.Store
11341  * @extends Roo.util.Observable
11342  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11343  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11344  * <p>
11345  * 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
11346  * has no knowledge of the format of the data returned by the Proxy.<br>
11347  * <p>
11348  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11349  * instances from the data object. These records are cached and made available through accessor functions.
11350  * @constructor
11351  * Creates a new Store.
11352  * @param {Object} config A config object containing the objects needed for the Store to access data,
11353  * and read the data into Records.
11354  */
11355 Roo.data.Store = function(config){
11356     this.data = new Roo.util.MixedCollection(false);
11357     this.data.getKey = function(o){
11358         return o.id;
11359     };
11360     this.baseParams = {};
11361     // private
11362     this.paramNames = {
11363         "start" : "start",
11364         "limit" : "limit",
11365         "sort" : "sort",
11366         "dir" : "dir",
11367         "multisort" : "_multisort"
11368     };
11369
11370     if(config && config.data){
11371         this.inlineData = config.data;
11372         delete config.data;
11373     }
11374
11375     Roo.apply(this, config);
11376     
11377     if(this.reader){ // reader passed
11378         this.reader = Roo.factory(this.reader, Roo.data);
11379         this.reader.xmodule = this.xmodule || false;
11380         if(!this.recordType){
11381             this.recordType = this.reader.recordType;
11382         }
11383         if(this.reader.onMetaChange){
11384             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11385         }
11386     }
11387
11388     if(this.recordType){
11389         this.fields = this.recordType.prototype.fields;
11390     }
11391     this.modified = [];
11392
11393     this.addEvents({
11394         /**
11395          * @event datachanged
11396          * Fires when the data cache has changed, and a widget which is using this Store
11397          * as a Record cache should refresh its view.
11398          * @param {Store} this
11399          */
11400         datachanged : true,
11401         /**
11402          * @event metachange
11403          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11404          * @param {Store} this
11405          * @param {Object} meta The JSON metadata
11406          */
11407         metachange : true,
11408         /**
11409          * @event add
11410          * Fires when Records have been added to the Store
11411          * @param {Store} this
11412          * @param {Roo.data.Record[]} records The array of Records added
11413          * @param {Number} index The index at which the record(s) were added
11414          */
11415         add : true,
11416         /**
11417          * @event remove
11418          * Fires when a Record has been removed from the Store
11419          * @param {Store} this
11420          * @param {Roo.data.Record} record The Record that was removed
11421          * @param {Number} index The index at which the record was removed
11422          */
11423         remove : true,
11424         /**
11425          * @event update
11426          * Fires when a Record has been updated
11427          * @param {Store} this
11428          * @param {Roo.data.Record} record The Record that was updated
11429          * @param {String} operation The update operation being performed.  Value may be one of:
11430          * <pre><code>
11431  Roo.data.Record.EDIT
11432  Roo.data.Record.REJECT
11433  Roo.data.Record.COMMIT
11434          * </code></pre>
11435          */
11436         update : true,
11437         /**
11438          * @event clear
11439          * Fires when the data cache has been cleared.
11440          * @param {Store} this
11441          */
11442         clear : true,
11443         /**
11444          * @event beforeload
11445          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11446          * the load action will be canceled.
11447          * @param {Store} this
11448          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11449          */
11450         beforeload : true,
11451         /**
11452          * @event beforeloadadd
11453          * Fires after a new set of Records has been loaded.
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          */
11458         beforeloadadd : true,
11459         /**
11460          * @event load
11461          * Fires after a new set of Records has been loaded, before they are added to the store.
11462          * @param {Store} this
11463          * @param {Roo.data.Record[]} records The Records that were loaded
11464          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11465          * @params {Object} return from reader
11466          */
11467         load : true,
11468         /**
11469          * @event loadexception
11470          * Fires if an exception occurs in the Proxy during loading.
11471          * Called with the signature of the Proxy's "loadexception" event.
11472          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11473          * 
11474          * @param {Proxy} 
11475          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11476          * @param {Object} load options 
11477          * @param {Object} jsonData from your request (normally this contains the Exception)
11478          */
11479         loadexception : true
11480     });
11481     
11482     if(this.proxy){
11483         this.proxy = Roo.factory(this.proxy, Roo.data);
11484         this.proxy.xmodule = this.xmodule || false;
11485         this.relayEvents(this.proxy,  ["loadexception"]);
11486     }
11487     this.sortToggle = {};
11488     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11489
11490     Roo.data.Store.superclass.constructor.call(this);
11491
11492     if(this.inlineData){
11493         this.loadData(this.inlineData);
11494         delete this.inlineData;
11495     }
11496 };
11497
11498 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11499      /**
11500     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11501     * without a remote query - used by combo/forms at present.
11502     */
11503     
11504     /**
11505     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11506     */
11507     /**
11508     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11509     */
11510     /**
11511     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11512     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11513     */
11514     /**
11515     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11516     * on any HTTP request
11517     */
11518     /**
11519     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11520     */
11521     /**
11522     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11523     */
11524     multiSort: false,
11525     /**
11526     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11527     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11528     */
11529     remoteSort : false,
11530
11531     /**
11532     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11533      * loaded or when a record is removed. (defaults to false).
11534     */
11535     pruneModifiedRecords : false,
11536
11537     // private
11538     lastOptions : null,
11539
11540     /**
11541      * Add Records to the Store and fires the add event.
11542      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11543      */
11544     add : function(records){
11545         records = [].concat(records);
11546         for(var i = 0, len = records.length; i < len; i++){
11547             records[i].join(this);
11548         }
11549         var index = this.data.length;
11550         this.data.addAll(records);
11551         this.fireEvent("add", this, records, index);
11552     },
11553
11554     /**
11555      * Remove a Record from the Store and fires the remove event.
11556      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11557      */
11558     remove : function(record){
11559         var index = this.data.indexOf(record);
11560         this.data.removeAt(index);
11561  
11562         if(this.pruneModifiedRecords){
11563             this.modified.remove(record);
11564         }
11565         this.fireEvent("remove", this, record, index);
11566     },
11567
11568     /**
11569      * Remove all Records from the Store and fires the clear event.
11570      */
11571     removeAll : function(){
11572         this.data.clear();
11573         if(this.pruneModifiedRecords){
11574             this.modified = [];
11575         }
11576         this.fireEvent("clear", this);
11577     },
11578
11579     /**
11580      * Inserts Records to the Store at the given index and fires the add event.
11581      * @param {Number} index The start index at which to insert the passed Records.
11582      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11583      */
11584     insert : function(index, records){
11585         records = [].concat(records);
11586         for(var i = 0, len = records.length; i < len; i++){
11587             this.data.insert(index, records[i]);
11588             records[i].join(this);
11589         }
11590         this.fireEvent("add", this, records, index);
11591     },
11592
11593     /**
11594      * Get the index within the cache of the passed Record.
11595      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11596      * @return {Number} The index of the passed Record. Returns -1 if not found.
11597      */
11598     indexOf : function(record){
11599         return this.data.indexOf(record);
11600     },
11601
11602     /**
11603      * Get the index within the cache of the Record with the passed id.
11604      * @param {String} id The id of the Record to find.
11605      * @return {Number} The index of the Record. Returns -1 if not found.
11606      */
11607     indexOfId : function(id){
11608         return this.data.indexOfKey(id);
11609     },
11610
11611     /**
11612      * Get the Record with the specified id.
11613      * @param {String} id The id of the Record to find.
11614      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11615      */
11616     getById : function(id){
11617         return this.data.key(id);
11618     },
11619
11620     /**
11621      * Get the Record at the specified index.
11622      * @param {Number} index The index of the Record to find.
11623      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11624      */
11625     getAt : function(index){
11626         return this.data.itemAt(index);
11627     },
11628
11629     /**
11630      * Returns a range of Records between specified indices.
11631      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11632      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11633      * @return {Roo.data.Record[]} An array of Records
11634      */
11635     getRange : function(start, end){
11636         return this.data.getRange(start, end);
11637     },
11638
11639     // private
11640     storeOptions : function(o){
11641         o = Roo.apply({}, o);
11642         delete o.callback;
11643         delete o.scope;
11644         this.lastOptions = o;
11645     },
11646
11647     /**
11648      * Loads the Record cache from the configured Proxy using the configured Reader.
11649      * <p>
11650      * If using remote paging, then the first load call must specify the <em>start</em>
11651      * and <em>limit</em> properties in the options.params property to establish the initial
11652      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11653      * <p>
11654      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11655      * and this call will return before the new data has been loaded. Perform any post-processing
11656      * in a callback function, or in a "load" event handler.</strong>
11657      * <p>
11658      * @param {Object} options An object containing properties which control loading options:<ul>
11659      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11660      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11661      * passed the following arguments:<ul>
11662      * <li>r : Roo.data.Record[]</li>
11663      * <li>options: Options object from the load call</li>
11664      * <li>success: Boolean success indicator</li></ul></li>
11665      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11666      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11667      * </ul>
11668      */
11669     load : function(options){
11670         options = options || {};
11671         if(this.fireEvent("beforeload", this, options) !== false){
11672             this.storeOptions(options);
11673             var p = Roo.apply(options.params || {}, this.baseParams);
11674             // if meta was not loaded from remote source.. try requesting it.
11675             if (!this.reader.metaFromRemote) {
11676                 p._requestMeta = 1;
11677             }
11678             if(this.sortInfo && this.remoteSort){
11679                 var pn = this.paramNames;
11680                 p[pn["sort"]] = this.sortInfo.field;
11681                 p[pn["dir"]] = this.sortInfo.direction;
11682             }
11683             if (this.multiSort) {
11684                 var pn = this.paramNames;
11685                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11686             }
11687             
11688             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11689         }
11690     },
11691
11692     /**
11693      * Reloads the Record cache from the configured Proxy using the configured Reader and
11694      * the options from the last load operation performed.
11695      * @param {Object} options (optional) An object containing properties which may override the options
11696      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11697      * the most recently used options are reused).
11698      */
11699     reload : function(options){
11700         this.load(Roo.applyIf(options||{}, this.lastOptions));
11701     },
11702
11703     // private
11704     // Called as a callback by the Reader during a load operation.
11705     loadRecords : function(o, options, success){
11706         if(!o || success === false){
11707             if(success !== false){
11708                 this.fireEvent("load", this, [], options, o);
11709             }
11710             if(options.callback){
11711                 options.callback.call(options.scope || this, [], options, false);
11712             }
11713             return;
11714         }
11715         // if data returned failure - throw an exception.
11716         if (o.success === false) {
11717             // show a message if no listener is registered.
11718             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11719                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11720             }
11721             // loadmask wil be hooked into this..
11722             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11723             return;
11724         }
11725         var r = o.records, t = o.totalRecords || r.length;
11726         
11727         this.fireEvent("beforeloadadd", this, r, options, o);
11728         
11729         if(!options || options.add !== true){
11730             if(this.pruneModifiedRecords){
11731                 this.modified = [];
11732             }
11733             for(var i = 0, len = r.length; i < len; i++){
11734                 r[i].join(this);
11735             }
11736             if(this.snapshot){
11737                 this.data = this.snapshot;
11738                 delete this.snapshot;
11739             }
11740             this.data.clear();
11741             this.data.addAll(r);
11742             this.totalLength = t;
11743             this.applySort();
11744             this.fireEvent("datachanged", this);
11745         }else{
11746             this.totalLength = Math.max(t, this.data.length+r.length);
11747             this.add(r);
11748         }
11749         
11750         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11751                 
11752             var e = new Roo.data.Record({});
11753
11754             e.set(this.parent.displayField, this.parent.emptyTitle);
11755             e.set(this.parent.valueField, '');
11756
11757             this.insert(0, e);
11758         }
11759             
11760         this.fireEvent("load", this, r, options, o);
11761         if(options.callback){
11762             options.callback.call(options.scope || this, r, options, true);
11763         }
11764     },
11765
11766
11767     /**
11768      * Loads data from a passed data block. A Reader which understands the format of the data
11769      * must have been configured in the constructor.
11770      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11771      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11772      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11773      */
11774     loadData : function(o, append){
11775         var r = this.reader.readRecords(o);
11776         this.loadRecords(r, {add: append}, true);
11777     },
11778
11779     /**
11780      * Gets the number of cached records.
11781      * <p>
11782      * <em>If using paging, this may not be the total size of the dataset. If the data object
11783      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11784      * the data set size</em>
11785      */
11786     getCount : function(){
11787         return this.data.length || 0;
11788     },
11789
11790     /**
11791      * Gets the total number of records in the dataset as returned by the server.
11792      * <p>
11793      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11794      * the dataset size</em>
11795      */
11796     getTotalCount : function(){
11797         return this.totalLength || 0;
11798     },
11799
11800     /**
11801      * Returns the sort state of the Store as an object with two properties:
11802      * <pre><code>
11803  field {String} The name of the field by which the Records are sorted
11804  direction {String} The sort order, "ASC" or "DESC"
11805      * </code></pre>
11806      */
11807     getSortState : function(){
11808         return this.sortInfo;
11809     },
11810
11811     // private
11812     applySort : function(){
11813         if(this.sortInfo && !this.remoteSort){
11814             var s = this.sortInfo, f = s.field;
11815             var st = this.fields.get(f).sortType;
11816             var fn = function(r1, r2){
11817                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11818                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11819             };
11820             this.data.sort(s.direction, fn);
11821             if(this.snapshot && this.snapshot != this.data){
11822                 this.snapshot.sort(s.direction, fn);
11823             }
11824         }
11825     },
11826
11827     /**
11828      * Sets the default sort column and order to be used by the next load operation.
11829      * @param {String} fieldName The name of the field to sort by.
11830      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11831      */
11832     setDefaultSort : function(field, dir){
11833         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11834     },
11835
11836     /**
11837      * Sort the Records.
11838      * If remote sorting is used, the sort is performed on the server, and the cache is
11839      * reloaded. If local sorting is used, the cache is sorted internally.
11840      * @param {String} fieldName The name of the field to sort by.
11841      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11842      */
11843     sort : function(fieldName, dir){
11844         var f = this.fields.get(fieldName);
11845         if(!dir){
11846             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11847             
11848             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11849                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11850             }else{
11851                 dir = f.sortDir;
11852             }
11853         }
11854         this.sortToggle[f.name] = dir;
11855         this.sortInfo = {field: f.name, direction: dir};
11856         if(!this.remoteSort){
11857             this.applySort();
11858             this.fireEvent("datachanged", this);
11859         }else{
11860             this.load(this.lastOptions);
11861         }
11862     },
11863
11864     /**
11865      * Calls the specified function for each of the Records in the cache.
11866      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11867      * Returning <em>false</em> aborts and exits the iteration.
11868      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11869      */
11870     each : function(fn, scope){
11871         this.data.each(fn, scope);
11872     },
11873
11874     /**
11875      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11876      * (e.g., during paging).
11877      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11878      */
11879     getModifiedRecords : function(){
11880         return this.modified;
11881     },
11882
11883     // private
11884     createFilterFn : function(property, value, anyMatch){
11885         if(!value.exec){ // not a regex
11886             value = String(value);
11887             if(value.length == 0){
11888                 return false;
11889             }
11890             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11891         }
11892         return function(r){
11893             return value.test(r.data[property]);
11894         };
11895     },
11896
11897     /**
11898      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11899      * @param {String} property A field on your records
11900      * @param {Number} start The record index to start at (defaults to 0)
11901      * @param {Number} end The last record index to include (defaults to length - 1)
11902      * @return {Number} The sum
11903      */
11904     sum : function(property, start, end){
11905         var rs = this.data.items, v = 0;
11906         start = start || 0;
11907         end = (end || end === 0) ? end : rs.length-1;
11908
11909         for(var i = start; i <= end; i++){
11910             v += (rs[i].data[property] || 0);
11911         }
11912         return v;
11913     },
11914
11915     /**
11916      * Filter the records by a specified property.
11917      * @param {String} field A field on your records
11918      * @param {String/RegExp} value Either a string that the field
11919      * should start with or a RegExp to test against the field
11920      * @param {Boolean} anyMatch True to match any part not just the beginning
11921      */
11922     filter : function(property, value, anyMatch){
11923         var fn = this.createFilterFn(property, value, anyMatch);
11924         return fn ? this.filterBy(fn) : this.clearFilter();
11925     },
11926
11927     /**
11928      * Filter by a function. The specified function will be called with each
11929      * record in this data source. If the function returns true the record is included,
11930      * otherwise it is filtered.
11931      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11932      * @param {Object} scope (optional) The scope of the function (defaults to this)
11933      */
11934     filterBy : function(fn, scope){
11935         this.snapshot = this.snapshot || this.data;
11936         this.data = this.queryBy(fn, scope||this);
11937         this.fireEvent("datachanged", this);
11938     },
11939
11940     /**
11941      * Query the records by a specified property.
11942      * @param {String} field A field on your records
11943      * @param {String/RegExp} value Either a string that the field
11944      * should start with or a RegExp to test against the field
11945      * @param {Boolean} anyMatch True to match any part not just the beginning
11946      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11947      */
11948     query : function(property, value, anyMatch){
11949         var fn = this.createFilterFn(property, value, anyMatch);
11950         return fn ? this.queryBy(fn) : this.data.clone();
11951     },
11952
11953     /**
11954      * Query by a function. The specified function will be called with each
11955      * record in this data source. If the function returns true the record is included
11956      * in the results.
11957      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11958      * @param {Object} scope (optional) The scope of the function (defaults to this)
11959       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11960      **/
11961     queryBy : function(fn, scope){
11962         var data = this.snapshot || this.data;
11963         return data.filterBy(fn, scope||this);
11964     },
11965
11966     /**
11967      * Collects unique values for a particular dataIndex from this store.
11968      * @param {String} dataIndex The property to collect
11969      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11970      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11971      * @return {Array} An array of the unique values
11972      **/
11973     collect : function(dataIndex, allowNull, bypassFilter){
11974         var d = (bypassFilter === true && this.snapshot) ?
11975                 this.snapshot.items : this.data.items;
11976         var v, sv, r = [], l = {};
11977         for(var i = 0, len = d.length; i < len; i++){
11978             v = d[i].data[dataIndex];
11979             sv = String(v);
11980             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11981                 l[sv] = true;
11982                 r[r.length] = v;
11983             }
11984         }
11985         return r;
11986     },
11987
11988     /**
11989      * Revert to a view of the Record cache with no filtering applied.
11990      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11991      */
11992     clearFilter : function(suppressEvent){
11993         if(this.snapshot && this.snapshot != this.data){
11994             this.data = this.snapshot;
11995             delete this.snapshot;
11996             if(suppressEvent !== true){
11997                 this.fireEvent("datachanged", this);
11998             }
11999         }
12000     },
12001
12002     // private
12003     afterEdit : function(record){
12004         if(this.modified.indexOf(record) == -1){
12005             this.modified.push(record);
12006         }
12007         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12008     },
12009     
12010     // private
12011     afterReject : function(record){
12012         this.modified.remove(record);
12013         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12014     },
12015
12016     // private
12017     afterCommit : function(record){
12018         this.modified.remove(record);
12019         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12020     },
12021
12022     /**
12023      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12024      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12025      */
12026     commitChanges : function(){
12027         var m = this.modified.slice(0);
12028         this.modified = [];
12029         for(var i = 0, len = m.length; i < len; i++){
12030             m[i].commit();
12031         }
12032     },
12033
12034     /**
12035      * Cancel outstanding changes on all changed records.
12036      */
12037     rejectChanges : function(){
12038         var m = this.modified.slice(0);
12039         this.modified = [];
12040         for(var i = 0, len = m.length; i < len; i++){
12041             m[i].reject();
12042         }
12043     },
12044
12045     onMetaChange : function(meta, rtype, o){
12046         this.recordType = rtype;
12047         this.fields = rtype.prototype.fields;
12048         delete this.snapshot;
12049         this.sortInfo = meta.sortInfo || this.sortInfo;
12050         this.modified = [];
12051         this.fireEvent('metachange', this, this.reader.meta);
12052     },
12053     
12054     moveIndex : function(data, type)
12055     {
12056         var index = this.indexOf(data);
12057         
12058         var newIndex = index + type;
12059         
12060         this.remove(data);
12061         
12062         this.insert(newIndex, data);
12063         
12064     }
12065 });/*
12066  * Based on:
12067  * Ext JS Library 1.1.1
12068  * Copyright(c) 2006-2007, Ext JS, LLC.
12069  *
12070  * Originally Released Under LGPL - original licence link has changed is not relivant.
12071  *
12072  * Fork - LGPL
12073  * <script type="text/javascript">
12074  */
12075
12076 /**
12077  * @class Roo.data.SimpleStore
12078  * @extends Roo.data.Store
12079  * Small helper class to make creating Stores from Array data easier.
12080  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12081  * @cfg {Array} fields An array of field definition objects, or field name strings.
12082  * @cfg {Array} data The multi-dimensional array of data
12083  * @constructor
12084  * @param {Object} config
12085  */
12086 Roo.data.SimpleStore = function(config){
12087     Roo.data.SimpleStore.superclass.constructor.call(this, {
12088         isLocal : true,
12089         reader: new Roo.data.ArrayReader({
12090                 id: config.id
12091             },
12092             Roo.data.Record.create(config.fields)
12093         ),
12094         proxy : new Roo.data.MemoryProxy(config.data)
12095     });
12096     this.load();
12097 };
12098 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12099  * Based on:
12100  * Ext JS Library 1.1.1
12101  * Copyright(c) 2006-2007, Ext JS, LLC.
12102  *
12103  * Originally Released Under LGPL - original licence link has changed is not relivant.
12104  *
12105  * Fork - LGPL
12106  * <script type="text/javascript">
12107  */
12108
12109 /**
12110 /**
12111  * @extends Roo.data.Store
12112  * @class Roo.data.JsonStore
12113  * Small helper class to make creating Stores for JSON data easier. <br/>
12114 <pre><code>
12115 var store = new Roo.data.JsonStore({
12116     url: 'get-images.php',
12117     root: 'images',
12118     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12119 });
12120 </code></pre>
12121  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12122  * JsonReader and HttpProxy (unless inline data is provided).</b>
12123  * @cfg {Array} fields An array of field definition objects, or field name strings.
12124  * @constructor
12125  * @param {Object} config
12126  */
12127 Roo.data.JsonStore = function(c){
12128     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12129         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12130         reader: new Roo.data.JsonReader(c, c.fields)
12131     }));
12132 };
12133 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12134  * Based on:
12135  * Ext JS Library 1.1.1
12136  * Copyright(c) 2006-2007, Ext JS, LLC.
12137  *
12138  * Originally Released Under LGPL - original licence link has changed is not relivant.
12139  *
12140  * Fork - LGPL
12141  * <script type="text/javascript">
12142  */
12143
12144  
12145 Roo.data.Field = function(config){
12146     if(typeof config == "string"){
12147         config = {name: config};
12148     }
12149     Roo.apply(this, config);
12150     
12151     if(!this.type){
12152         this.type = "auto";
12153     }
12154     
12155     var st = Roo.data.SortTypes;
12156     // named sortTypes are supported, here we look them up
12157     if(typeof this.sortType == "string"){
12158         this.sortType = st[this.sortType];
12159     }
12160     
12161     // set default sortType for strings and dates
12162     if(!this.sortType){
12163         switch(this.type){
12164             case "string":
12165                 this.sortType = st.asUCString;
12166                 break;
12167             case "date":
12168                 this.sortType = st.asDate;
12169                 break;
12170             default:
12171                 this.sortType = st.none;
12172         }
12173     }
12174
12175     // define once
12176     var stripRe = /[\$,%]/g;
12177
12178     // prebuilt conversion function for this field, instead of
12179     // switching every time we're reading a value
12180     if(!this.convert){
12181         var cv, dateFormat = this.dateFormat;
12182         switch(this.type){
12183             case "":
12184             case "auto":
12185             case undefined:
12186                 cv = function(v){ return v; };
12187                 break;
12188             case "string":
12189                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12190                 break;
12191             case "int":
12192                 cv = function(v){
12193                     return v !== undefined && v !== null && v !== '' ?
12194                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12195                     };
12196                 break;
12197             case "float":
12198                 cv = function(v){
12199                     return v !== undefined && v !== null && v !== '' ?
12200                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12201                     };
12202                 break;
12203             case "bool":
12204             case "boolean":
12205                 cv = function(v){ return v === true || v === "true" || v == 1; };
12206                 break;
12207             case "date":
12208                 cv = function(v){
12209                     if(!v){
12210                         return '';
12211                     }
12212                     if(v instanceof Date){
12213                         return v;
12214                     }
12215                     if(dateFormat){
12216                         if(dateFormat == "timestamp"){
12217                             return new Date(v*1000);
12218                         }
12219                         return Date.parseDate(v, dateFormat);
12220                     }
12221                     var parsed = Date.parse(v);
12222                     return parsed ? new Date(parsed) : null;
12223                 };
12224              break;
12225             
12226         }
12227         this.convert = cv;
12228     }
12229 };
12230
12231 Roo.data.Field.prototype = {
12232     dateFormat: null,
12233     defaultValue: "",
12234     mapping: null,
12235     sortType : null,
12236     sortDir : "ASC"
12237 };/*
12238  * Based on:
12239  * Ext JS Library 1.1.1
12240  * Copyright(c) 2006-2007, Ext JS, LLC.
12241  *
12242  * Originally Released Under LGPL - original licence link has changed is not relivant.
12243  *
12244  * Fork - LGPL
12245  * <script type="text/javascript">
12246  */
12247  
12248 // Base class for reading structured data from a data source.  This class is intended to be
12249 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12250
12251 /**
12252  * @class Roo.data.DataReader
12253  * Base class for reading structured data from a data source.  This class is intended to be
12254  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12255  */
12256
12257 Roo.data.DataReader = function(meta, recordType){
12258     
12259     this.meta = meta;
12260     
12261     this.recordType = recordType instanceof Array ? 
12262         Roo.data.Record.create(recordType) : recordType;
12263 };
12264
12265 Roo.data.DataReader.prototype = {
12266      /**
12267      * Create an empty record
12268      * @param {Object} data (optional) - overlay some values
12269      * @return {Roo.data.Record} record created.
12270      */
12271     newRow :  function(d) {
12272         var da =  {};
12273         this.recordType.prototype.fields.each(function(c) {
12274             switch( c.type) {
12275                 case 'int' : da[c.name] = 0; break;
12276                 case 'date' : da[c.name] = new Date(); break;
12277                 case 'float' : da[c.name] = 0.0; break;
12278                 case 'boolean' : da[c.name] = false; break;
12279                 default : da[c.name] = ""; break;
12280             }
12281             
12282         });
12283         return new this.recordType(Roo.apply(da, d));
12284     }
12285     
12286 };/*
12287  * Based on:
12288  * Ext JS Library 1.1.1
12289  * Copyright(c) 2006-2007, Ext JS, LLC.
12290  *
12291  * Originally Released Under LGPL - original licence link has changed is not relivant.
12292  *
12293  * Fork - LGPL
12294  * <script type="text/javascript">
12295  */
12296
12297 /**
12298  * @class Roo.data.DataProxy
12299  * @extends Roo.data.Observable
12300  * This class is an abstract base class for implementations which provide retrieval of
12301  * unformatted data objects.<br>
12302  * <p>
12303  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12304  * (of the appropriate type which knows how to parse the data object) to provide a block of
12305  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12306  * <p>
12307  * Custom implementations must implement the load method as described in
12308  * {@link Roo.data.HttpProxy#load}.
12309  */
12310 Roo.data.DataProxy = function(){
12311     this.addEvents({
12312         /**
12313          * @event beforeload
12314          * Fires before a network request is made to retrieve a data object.
12315          * @param {Object} This DataProxy object.
12316          * @param {Object} params The params parameter to the load function.
12317          */
12318         beforeload : true,
12319         /**
12320          * @event load
12321          * Fires before the load method's callback is called.
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          */
12326         load : true,
12327         /**
12328          * @event loadexception
12329          * Fires if an Exception occurs during data retrieval.
12330          * @param {Object} This DataProxy object.
12331          * @param {Object} o The data object.
12332          * @param {Object} arg The callback argument object passed to the load function.
12333          * @param {Object} e The Exception.
12334          */
12335         loadexception : true
12336     });
12337     Roo.data.DataProxy.superclass.constructor.call(this);
12338 };
12339
12340 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12341
12342     /**
12343      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12344      */
12345 /*
12346  * Based on:
12347  * Ext JS Library 1.1.1
12348  * Copyright(c) 2006-2007, Ext JS, LLC.
12349  *
12350  * Originally Released Under LGPL - original licence link has changed is not relivant.
12351  *
12352  * Fork - LGPL
12353  * <script type="text/javascript">
12354  */
12355 /**
12356  * @class Roo.data.MemoryProxy
12357  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12358  * to the Reader when its load method is called.
12359  * @constructor
12360  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12361  */
12362 Roo.data.MemoryProxy = function(data){
12363     if (data.data) {
12364         data = data.data;
12365     }
12366     Roo.data.MemoryProxy.superclass.constructor.call(this);
12367     this.data = data;
12368 };
12369
12370 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12371     
12372     /**
12373      * Load data from the requested source (in this case an in-memory
12374      * data object passed to the constructor), read the data object into
12375      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12376      * process that block using the passed callback.
12377      * @param {Object} params This parameter is not used by the MemoryProxy class.
12378      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12379      * object into a block of Roo.data.Records.
12380      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12381      * The function must be passed <ul>
12382      * <li>The Record block object</li>
12383      * <li>The "arg" argument from the load function</li>
12384      * <li>A boolean success indicator</li>
12385      * </ul>
12386      * @param {Object} scope The scope in which to call the callback
12387      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12388      */
12389     load : function(params, reader, callback, scope, arg){
12390         params = params || {};
12391         var result;
12392         try {
12393             result = reader.readRecords(this.data);
12394         }catch(e){
12395             this.fireEvent("loadexception", this, arg, null, e);
12396             callback.call(scope, null, arg, false);
12397             return;
12398         }
12399         callback.call(scope, result, arg, true);
12400     },
12401     
12402     // private
12403     update : function(params, records){
12404         
12405     }
12406 });/*
12407  * Based on:
12408  * Ext JS Library 1.1.1
12409  * Copyright(c) 2006-2007, Ext JS, LLC.
12410  *
12411  * Originally Released Under LGPL - original licence link has changed is not relivant.
12412  *
12413  * Fork - LGPL
12414  * <script type="text/javascript">
12415  */
12416 /**
12417  * @class Roo.data.HttpProxy
12418  * @extends Roo.data.DataProxy
12419  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12420  * configured to reference a certain URL.<br><br>
12421  * <p>
12422  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12423  * from which the running page was served.<br><br>
12424  * <p>
12425  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12426  * <p>
12427  * Be aware that to enable the browser to parse an XML document, the server must set
12428  * the Content-Type header in the HTTP response to "text/xml".
12429  * @constructor
12430  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12431  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12432  * will be used to make the request.
12433  */
12434 Roo.data.HttpProxy = function(conn){
12435     Roo.data.HttpProxy.superclass.constructor.call(this);
12436     // is conn a conn config or a real conn?
12437     this.conn = conn;
12438     this.useAjax = !conn || !conn.events;
12439   
12440 };
12441
12442 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12443     // thse are take from connection...
12444     
12445     /**
12446      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12447      */
12448     /**
12449      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12450      * extra parameters to each request made by this object. (defaults to undefined)
12451      */
12452     /**
12453      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12454      *  to each request made by this object. (defaults to undefined)
12455      */
12456     /**
12457      * @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)
12458      */
12459     /**
12460      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12461      */
12462      /**
12463      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12464      * @type Boolean
12465      */
12466   
12467
12468     /**
12469      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12470      * @type Boolean
12471      */
12472     /**
12473      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12474      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12475      * a finer-grained basis than the DataProxy events.
12476      */
12477     getConnection : function(){
12478         return this.useAjax ? Roo.Ajax : this.conn;
12479     },
12480
12481     /**
12482      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12483      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12484      * process that block using the passed callback.
12485      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12486      * for the request to the remote server.
12487      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12488      * object into a block of Roo.data.Records.
12489      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12490      * The function must be passed <ul>
12491      * <li>The Record block object</li>
12492      * <li>The "arg" argument from the load function</li>
12493      * <li>A boolean success indicator</li>
12494      * </ul>
12495      * @param {Object} scope The scope in which to call the callback
12496      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12497      */
12498     load : function(params, reader, callback, scope, arg){
12499         if(this.fireEvent("beforeload", this, params) !== false){
12500             var  o = {
12501                 params : params || {},
12502                 request: {
12503                     callback : callback,
12504                     scope : scope,
12505                     arg : arg
12506                 },
12507                 reader: reader,
12508                 callback : this.loadResponse,
12509                 scope: this
12510             };
12511             if(this.useAjax){
12512                 Roo.applyIf(o, this.conn);
12513                 if(this.activeRequest){
12514                     Roo.Ajax.abort(this.activeRequest);
12515                 }
12516                 this.activeRequest = Roo.Ajax.request(o);
12517             }else{
12518                 this.conn.request(o);
12519             }
12520         }else{
12521             callback.call(scope||this, null, arg, false);
12522         }
12523     },
12524
12525     // private
12526     loadResponse : function(o, success, response){
12527         delete this.activeRequest;
12528         if(!success){
12529             this.fireEvent("loadexception", this, o, response);
12530             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12531             return;
12532         }
12533         var result;
12534         try {
12535             result = o.reader.read(response);
12536         }catch(e){
12537             this.fireEvent("loadexception", this, o, response, e);
12538             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12539             return;
12540         }
12541         
12542         this.fireEvent("load", this, o, o.request.arg);
12543         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12544     },
12545
12546     // private
12547     update : function(dataSet){
12548
12549     },
12550
12551     // private
12552     updateResponse : function(dataSet){
12553
12554     }
12555 });/*
12556  * Based on:
12557  * Ext JS Library 1.1.1
12558  * Copyright(c) 2006-2007, Ext JS, LLC.
12559  *
12560  * Originally Released Under LGPL - original licence link has changed is not relivant.
12561  *
12562  * Fork - LGPL
12563  * <script type="text/javascript">
12564  */
12565
12566 /**
12567  * @class Roo.data.ScriptTagProxy
12568  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12569  * other than the originating domain of the running page.<br><br>
12570  * <p>
12571  * <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
12572  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12573  * <p>
12574  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12575  * source code that is used as the source inside a &lt;script> tag.<br><br>
12576  * <p>
12577  * In order for the browser to process the returned data, the server must wrap the data object
12578  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12579  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12580  * depending on whether the callback name was passed:
12581  * <p>
12582  * <pre><code>
12583 boolean scriptTag = false;
12584 String cb = request.getParameter("callback");
12585 if (cb != null) {
12586     scriptTag = true;
12587     response.setContentType("text/javascript");
12588 } else {
12589     response.setContentType("application/x-json");
12590 }
12591 Writer out = response.getWriter();
12592 if (scriptTag) {
12593     out.write(cb + "(");
12594 }
12595 out.print(dataBlock.toJsonString());
12596 if (scriptTag) {
12597     out.write(");");
12598 }
12599 </pre></code>
12600  *
12601  * @constructor
12602  * @param {Object} config A configuration object.
12603  */
12604 Roo.data.ScriptTagProxy = function(config){
12605     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12606     Roo.apply(this, config);
12607     this.head = document.getElementsByTagName("head")[0];
12608 };
12609
12610 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12611
12612 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12613     /**
12614      * @cfg {String} url The URL from which to request the data object.
12615      */
12616     /**
12617      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12618      */
12619     timeout : 30000,
12620     /**
12621      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12622      * the server the name of the callback function set up by the load call to process the returned data object.
12623      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12624      * javascript output which calls this named function passing the data object as its only parameter.
12625      */
12626     callbackParam : "callback",
12627     /**
12628      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12629      * name to the request.
12630      */
12631     nocache : true,
12632
12633     /**
12634      * Load data from the configured URL, read the data object into
12635      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12636      * process that block using the passed callback.
12637      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12638      * for the request to the remote server.
12639      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12640      * object into a block of Roo.data.Records.
12641      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12642      * The function must be passed <ul>
12643      * <li>The Record block object</li>
12644      * <li>The "arg" argument from the load function</li>
12645      * <li>A boolean success indicator</li>
12646      * </ul>
12647      * @param {Object} scope The scope in which to call the callback
12648      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12649      */
12650     load : function(params, reader, callback, scope, arg){
12651         if(this.fireEvent("beforeload", this, params) !== false){
12652
12653             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12654
12655             var url = this.url;
12656             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12657             if(this.nocache){
12658                 url += "&_dc=" + (new Date().getTime());
12659             }
12660             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12661             var trans = {
12662                 id : transId,
12663                 cb : "stcCallback"+transId,
12664                 scriptId : "stcScript"+transId,
12665                 params : params,
12666                 arg : arg,
12667                 url : url,
12668                 callback : callback,
12669                 scope : scope,
12670                 reader : reader
12671             };
12672             var conn = this;
12673
12674             window[trans.cb] = function(o){
12675                 conn.handleResponse(o, trans);
12676             };
12677
12678             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12679
12680             if(this.autoAbort !== false){
12681                 this.abort();
12682             }
12683
12684             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12685
12686             var script = document.createElement("script");
12687             script.setAttribute("src", url);
12688             script.setAttribute("type", "text/javascript");
12689             script.setAttribute("id", trans.scriptId);
12690             this.head.appendChild(script);
12691
12692             this.trans = trans;
12693         }else{
12694             callback.call(scope||this, null, arg, false);
12695         }
12696     },
12697
12698     // private
12699     isLoading : function(){
12700         return this.trans ? true : false;
12701     },
12702
12703     /**
12704      * Abort the current server request.
12705      */
12706     abort : function(){
12707         if(this.isLoading()){
12708             this.destroyTrans(this.trans);
12709         }
12710     },
12711
12712     // private
12713     destroyTrans : function(trans, isLoaded){
12714         this.head.removeChild(document.getElementById(trans.scriptId));
12715         clearTimeout(trans.timeoutId);
12716         if(isLoaded){
12717             window[trans.cb] = undefined;
12718             try{
12719                 delete window[trans.cb];
12720             }catch(e){}
12721         }else{
12722             // if hasn't been loaded, wait for load to remove it to prevent script error
12723             window[trans.cb] = function(){
12724                 window[trans.cb] = undefined;
12725                 try{
12726                     delete window[trans.cb];
12727                 }catch(e){}
12728             };
12729         }
12730     },
12731
12732     // private
12733     handleResponse : function(o, trans){
12734         this.trans = false;
12735         this.destroyTrans(trans, true);
12736         var result;
12737         try {
12738             result = trans.reader.readRecords(o);
12739         }catch(e){
12740             this.fireEvent("loadexception", this, o, trans.arg, e);
12741             trans.callback.call(trans.scope||window, null, trans.arg, false);
12742             return;
12743         }
12744         this.fireEvent("load", this, o, trans.arg);
12745         trans.callback.call(trans.scope||window, result, trans.arg, true);
12746     },
12747
12748     // private
12749     handleFailure : function(trans){
12750         this.trans = false;
12751         this.destroyTrans(trans, false);
12752         this.fireEvent("loadexception", this, null, trans.arg);
12753         trans.callback.call(trans.scope||window, null, trans.arg, false);
12754     }
12755 });/*
12756  * Based on:
12757  * Ext JS Library 1.1.1
12758  * Copyright(c) 2006-2007, Ext JS, LLC.
12759  *
12760  * Originally Released Under LGPL - original licence link has changed is not relivant.
12761  *
12762  * Fork - LGPL
12763  * <script type="text/javascript">
12764  */
12765
12766 /**
12767  * @class Roo.data.JsonReader
12768  * @extends Roo.data.DataReader
12769  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12770  * based on mappings in a provided Roo.data.Record constructor.
12771  * 
12772  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12773  * in the reply previously. 
12774  * 
12775  * <p>
12776  * Example code:
12777  * <pre><code>
12778 var RecordDef = Roo.data.Record.create([
12779     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12780     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12781 ]);
12782 var myReader = new Roo.data.JsonReader({
12783     totalProperty: "results",    // The property which contains the total dataset size (optional)
12784     root: "rows",                // The property which contains an Array of row objects
12785     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12786 }, RecordDef);
12787 </code></pre>
12788  * <p>
12789  * This would consume a JSON file like this:
12790  * <pre><code>
12791 { 'results': 2, 'rows': [
12792     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12793     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12794 }
12795 </code></pre>
12796  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12797  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12798  * paged from the remote server.
12799  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12800  * @cfg {String} root name of the property which contains the Array of row objects.
12801  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12802  * @cfg {Array} fields Array of field definition objects
12803  * @constructor
12804  * Create a new JsonReader
12805  * @param {Object} meta Metadata configuration options
12806  * @param {Object} recordType Either an Array of field definition objects,
12807  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12808  */
12809 Roo.data.JsonReader = function(meta, recordType){
12810     
12811     meta = meta || {};
12812     // set some defaults:
12813     Roo.applyIf(meta, {
12814         totalProperty: 'total',
12815         successProperty : 'success',
12816         root : 'data',
12817         id : 'id'
12818     });
12819     
12820     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12821 };
12822 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12823     
12824     /**
12825      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12826      * Used by Store query builder to append _requestMeta to params.
12827      * 
12828      */
12829     metaFromRemote : false,
12830     /**
12831      * This method is only used by a DataProxy which has retrieved data from a remote server.
12832      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12833      * @return {Object} data A data block which is used by an Roo.data.Store object as
12834      * a cache of Roo.data.Records.
12835      */
12836     read : function(response){
12837         var json = response.responseText;
12838        
12839         var o = /* eval:var:o */ eval("("+json+")");
12840         if(!o) {
12841             throw {message: "JsonReader.read: Json object not found"};
12842         }
12843         
12844         if(o.metaData){
12845             
12846             delete this.ef;
12847             this.metaFromRemote = true;
12848             this.meta = o.metaData;
12849             this.recordType = Roo.data.Record.create(o.metaData.fields);
12850             this.onMetaChange(this.meta, this.recordType, o);
12851         }
12852         return this.readRecords(o);
12853     },
12854
12855     // private function a store will implement
12856     onMetaChange : function(meta, recordType, o){
12857
12858     },
12859
12860     /**
12861          * @ignore
12862          */
12863     simpleAccess: function(obj, subsc) {
12864         return obj[subsc];
12865     },
12866
12867         /**
12868          * @ignore
12869          */
12870     getJsonAccessor: function(){
12871         var re = /[\[\.]/;
12872         return function(expr) {
12873             try {
12874                 return(re.test(expr))
12875                     ? new Function("obj", "return obj." + expr)
12876                     : function(obj){
12877                         return obj[expr];
12878                     };
12879             } catch(e){}
12880             return Roo.emptyFn;
12881         };
12882     }(),
12883
12884     /**
12885      * Create a data block containing Roo.data.Records from an XML document.
12886      * @param {Object} o An object which contains an Array of row objects in the property specified
12887      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12888      * which contains the total size of the dataset.
12889      * @return {Object} data A data block which is used by an Roo.data.Store object as
12890      * a cache of Roo.data.Records.
12891      */
12892     readRecords : function(o){
12893         /**
12894          * After any data loads, the raw JSON data is available for further custom processing.
12895          * @type Object
12896          */
12897         this.o = o;
12898         var s = this.meta, Record = this.recordType,
12899             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12900
12901 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12902         if (!this.ef) {
12903             if(s.totalProperty) {
12904                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12905                 }
12906                 if(s.successProperty) {
12907                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12908                 }
12909                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12910                 if (s.id) {
12911                         var g = this.getJsonAccessor(s.id);
12912                         this.getId = function(rec) {
12913                                 var r = g(rec);  
12914                                 return (r === undefined || r === "") ? null : r;
12915                         };
12916                 } else {
12917                         this.getId = function(){return null;};
12918                 }
12919             this.ef = [];
12920             for(var jj = 0; jj < fl; jj++){
12921                 f = fi[jj];
12922                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12923                 this.ef[jj] = this.getJsonAccessor(map);
12924             }
12925         }
12926
12927         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12928         if(s.totalProperty){
12929             var vt = parseInt(this.getTotal(o), 10);
12930             if(!isNaN(vt)){
12931                 totalRecords = vt;
12932             }
12933         }
12934         if(s.successProperty){
12935             var vs = this.getSuccess(o);
12936             if(vs === false || vs === 'false'){
12937                 success = false;
12938             }
12939         }
12940         var records = [];
12941         for(var i = 0; i < c; i++){
12942                 var n = root[i];
12943             var values = {};
12944             var id = this.getId(n);
12945             for(var j = 0; j < fl; j++){
12946                 f = fi[j];
12947             var v = this.ef[j](n);
12948             if (!f.convert) {
12949                 Roo.log('missing convert for ' + f.name);
12950                 Roo.log(f);
12951                 continue;
12952             }
12953             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12954             }
12955             var record = new Record(values, id);
12956             record.json = n;
12957             records[i] = record;
12958         }
12959         return {
12960             raw : o,
12961             success : success,
12962             records : records,
12963             totalRecords : totalRecords
12964         };
12965     }
12966 });/*
12967  * Based on:
12968  * Ext JS Library 1.1.1
12969  * Copyright(c) 2006-2007, Ext JS, LLC.
12970  *
12971  * Originally Released Under LGPL - original licence link has changed is not relivant.
12972  *
12973  * Fork - LGPL
12974  * <script type="text/javascript">
12975  */
12976
12977 /**
12978  * @class Roo.data.ArrayReader
12979  * @extends Roo.data.DataReader
12980  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12981  * Each element of that Array represents a row of data fields. The
12982  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12983  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12984  * <p>
12985  * Example code:.
12986  * <pre><code>
12987 var RecordDef = Roo.data.Record.create([
12988     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12989     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12990 ]);
12991 var myReader = new Roo.data.ArrayReader({
12992     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12993 }, RecordDef);
12994 </code></pre>
12995  * <p>
12996  * This would consume an Array like this:
12997  * <pre><code>
12998 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12999   </code></pre>
13000  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
13001  * @constructor
13002  * Create a new JsonReader
13003  * @param {Object} meta Metadata configuration options.
13004  * @param {Object} recordType Either an Array of field definition objects
13005  * as specified to {@link Roo.data.Record#create},
13006  * or an {@link Roo.data.Record} object
13007  * created using {@link Roo.data.Record#create}.
13008  */
13009 Roo.data.ArrayReader = function(meta, recordType){
13010     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
13011 };
13012
13013 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13014     /**
13015      * Create a data block containing Roo.data.Records from an XML document.
13016      * @param {Object} o An Array of row objects which represents the dataset.
13017      * @return {Object} data A data block which is used by an Roo.data.Store object as
13018      * a cache of Roo.data.Records.
13019      */
13020     readRecords : function(o){
13021         var sid = this.meta ? this.meta.id : null;
13022         var recordType = this.recordType, fields = recordType.prototype.fields;
13023         var records = [];
13024         var root = o;
13025             for(var i = 0; i < root.length; i++){
13026                     var n = root[i];
13027                 var values = {};
13028                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13029                 for(var j = 0, jlen = fields.length; j < jlen; j++){
13030                 var f = fields.items[j];
13031                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13032                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13033                 v = f.convert(v);
13034                 values[f.name] = v;
13035             }
13036                 var record = new recordType(values, id);
13037                 record.json = n;
13038                 records[records.length] = record;
13039             }
13040             return {
13041                 records : records,
13042                 totalRecords : records.length
13043             };
13044     }
13045 });/*
13046  * - LGPL
13047  * * 
13048  */
13049
13050 /**
13051  * @class Roo.bootstrap.ComboBox
13052  * @extends Roo.bootstrap.TriggerField
13053  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13054  * @cfg {Boolean} append (true|false) default false
13055  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13056  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13057  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13058  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13059  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13060  * @cfg {Boolean} animate default true
13061  * @cfg {Boolean} emptyResultText only for touch device
13062  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13063  * @cfg {String} emptyTitle default ''
13064  * @constructor
13065  * Create a new ComboBox.
13066  * @param {Object} config Configuration options
13067  */
13068 Roo.bootstrap.ComboBox = function(config){
13069     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13070     this.addEvents({
13071         /**
13072          * @event expand
13073          * Fires when the dropdown list is expanded
13074         * @param {Roo.bootstrap.ComboBox} combo This combo box
13075         */
13076         'expand' : true,
13077         /**
13078          * @event collapse
13079          * Fires when the dropdown list is collapsed
13080         * @param {Roo.bootstrap.ComboBox} combo This combo box
13081         */
13082         'collapse' : true,
13083         /**
13084          * @event beforeselect
13085          * Fires before a list item is selected. Return false to cancel the selection.
13086         * @param {Roo.bootstrap.ComboBox} combo This combo box
13087         * @param {Roo.data.Record} record The data record returned from the underlying store
13088         * @param {Number} index The index of the selected item in the dropdown list
13089         */
13090         'beforeselect' : true,
13091         /**
13092          * @event select
13093          * Fires when a list item is selected
13094         * @param {Roo.bootstrap.ComboBox} combo This combo box
13095         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13096         * @param {Number} index The index of the selected item in the dropdown list
13097         */
13098         'select' : true,
13099         /**
13100          * @event beforequery
13101          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13102          * The event object passed has these properties:
13103         * @param {Roo.bootstrap.ComboBox} combo This combo box
13104         * @param {String} query The query
13105         * @param {Boolean} forceAll true to force "all" query
13106         * @param {Boolean} cancel true to cancel the query
13107         * @param {Object} e The query event object
13108         */
13109         'beforequery': true,
13110          /**
13111          * @event add
13112          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13113         * @param {Roo.bootstrap.ComboBox} combo This combo box
13114         */
13115         'add' : true,
13116         /**
13117          * @event edit
13118          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13119         * @param {Roo.bootstrap.ComboBox} combo This combo box
13120         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13121         */
13122         'edit' : true,
13123         /**
13124          * @event remove
13125          * Fires when the remove value from the combobox array
13126         * @param {Roo.bootstrap.ComboBox} combo This combo box
13127         */
13128         'remove' : true,
13129         /**
13130          * @event afterremove
13131          * Fires when the remove value from the combobox array
13132         * @param {Roo.bootstrap.ComboBox} combo This combo box
13133         */
13134         'afterremove' : true,
13135         /**
13136          * @event specialfilter
13137          * Fires when specialfilter
13138             * @param {Roo.bootstrap.ComboBox} combo This combo box
13139             */
13140         'specialfilter' : true,
13141         /**
13142          * @event tick
13143          * Fires when tick the element
13144             * @param {Roo.bootstrap.ComboBox} combo This combo box
13145             */
13146         'tick' : true,
13147         /**
13148          * @event touchviewdisplay
13149          * Fires when touch view require special display (default is using displayField)
13150             * @param {Roo.bootstrap.ComboBox} combo This combo box
13151             * @param {Object} cfg set html .
13152             */
13153         'touchviewdisplay' : true
13154         
13155     });
13156     
13157     this.item = [];
13158     this.tickItems = [];
13159     
13160     this.selectedIndex = -1;
13161     if(this.mode == 'local'){
13162         if(config.queryDelay === undefined){
13163             this.queryDelay = 10;
13164         }
13165         if(config.minChars === undefined){
13166             this.minChars = 0;
13167         }
13168     }
13169 };
13170
13171 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13172      
13173     /**
13174      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13175      * rendering into an Roo.Editor, defaults to false)
13176      */
13177     /**
13178      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13179      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13180      */
13181     /**
13182      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13183      */
13184     /**
13185      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13186      * the dropdown list (defaults to undefined, with no header element)
13187      */
13188
13189      /**
13190      * @cfg {String/Roo.Template} tpl The template to use to render the output
13191      */
13192      
13193      /**
13194      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13195      */
13196     listWidth: undefined,
13197     /**
13198      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13199      * mode = 'remote' or 'text' if mode = 'local')
13200      */
13201     displayField: undefined,
13202     
13203     /**
13204      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13205      * mode = 'remote' or 'value' if mode = 'local'). 
13206      * Note: use of a valueField requires the user make a selection
13207      * in order for a value to be mapped.
13208      */
13209     valueField: undefined,
13210     /**
13211      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13212      */
13213     modalTitle : '',
13214     
13215     /**
13216      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13217      * field's data value (defaults to the underlying DOM element's name)
13218      */
13219     hiddenName: undefined,
13220     /**
13221      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13222      */
13223     listClass: '',
13224     /**
13225      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13226      */
13227     selectedClass: 'active',
13228     
13229     /**
13230      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13231      */
13232     shadow:'sides',
13233     /**
13234      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13235      * anchor positions (defaults to 'tl-bl')
13236      */
13237     listAlign: 'tl-bl?',
13238     /**
13239      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13240      */
13241     maxHeight: 300,
13242     /**
13243      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13244      * query specified by the allQuery config option (defaults to 'query')
13245      */
13246     triggerAction: 'query',
13247     /**
13248      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13249      * (defaults to 4, does not apply if editable = false)
13250      */
13251     minChars : 4,
13252     /**
13253      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13254      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13255      */
13256     typeAhead: false,
13257     /**
13258      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13259      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13260      */
13261     queryDelay: 500,
13262     /**
13263      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13264      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13265      */
13266     pageSize: 0,
13267     /**
13268      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13269      * when editable = true (defaults to false)
13270      */
13271     selectOnFocus:false,
13272     /**
13273      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13274      */
13275     queryParam: 'query',
13276     /**
13277      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13278      * when mode = 'remote' (defaults to 'Loading...')
13279      */
13280     loadingText: 'Loading...',
13281     /**
13282      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13283      */
13284     resizable: false,
13285     /**
13286      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13287      */
13288     handleHeight : 8,
13289     /**
13290      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13291      * traditional select (defaults to true)
13292      */
13293     editable: true,
13294     /**
13295      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13296      */
13297     allQuery: '',
13298     /**
13299      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13300      */
13301     mode: 'remote',
13302     /**
13303      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13304      * listWidth has a higher value)
13305      */
13306     minListWidth : 70,
13307     /**
13308      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13309      * allow the user to set arbitrary text into the field (defaults to false)
13310      */
13311     forceSelection:false,
13312     /**
13313      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13314      * if typeAhead = true (defaults to 250)
13315      */
13316     typeAheadDelay : 250,
13317     /**
13318      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13319      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13320      */
13321     valueNotFoundText : undefined,
13322     /**
13323      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13324      */
13325     blockFocus : false,
13326     
13327     /**
13328      * @cfg {Boolean} disableClear Disable showing of clear button.
13329      */
13330     disableClear : false,
13331     /**
13332      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13333      */
13334     alwaysQuery : false,
13335     
13336     /**
13337      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13338      */
13339     multiple : false,
13340     
13341     /**
13342      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13343      */
13344     invalidClass : "has-warning",
13345     
13346     /**
13347      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13348      */
13349     validClass : "has-success",
13350     
13351     /**
13352      * @cfg {Boolean} specialFilter (true|false) special filter default false
13353      */
13354     specialFilter : false,
13355     
13356     /**
13357      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13358      */
13359     mobileTouchView : true,
13360     
13361     /**
13362      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13363      */
13364     useNativeIOS : false,
13365     
13366     /**
13367      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13368      */
13369     mobile_restrict_height : false,
13370     
13371     ios_options : false,
13372     
13373     //private
13374     addicon : false,
13375     editicon: false,
13376     
13377     page: 0,
13378     hasQuery: false,
13379     append: false,
13380     loadNext: false,
13381     autoFocus : true,
13382     tickable : false,
13383     btnPosition : 'right',
13384     triggerList : true,
13385     showToggleBtn : true,
13386     animate : true,
13387     emptyResultText: 'Empty',
13388     triggerText : 'Select',
13389     emptyTitle : '',
13390     
13391     // element that contains real text value.. (when hidden is used..)
13392     
13393     getAutoCreate : function()
13394     {   
13395         var cfg = false;
13396         //render
13397         /*
13398          * Render classic select for iso
13399          */
13400         
13401         if(Roo.isIOS && this.useNativeIOS){
13402             cfg = this.getAutoCreateNativeIOS();
13403             return cfg;
13404         }
13405         
13406         /*
13407          * Touch Devices
13408          */
13409         
13410         if(Roo.isTouch && this.mobileTouchView){
13411             cfg = this.getAutoCreateTouchView();
13412             return cfg;;
13413         }
13414         
13415         /*
13416          *  Normal ComboBox
13417          */
13418         if(!this.tickable){
13419             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13420             return cfg;
13421         }
13422         
13423         /*
13424          *  ComboBox with tickable selections
13425          */
13426              
13427         var align = this.labelAlign || this.parentLabelAlign();
13428         
13429         cfg = {
13430             cls : 'form-group roo-combobox-tickable' //input-group
13431         };
13432         
13433         var btn_text_select = '';
13434         var btn_text_done = '';
13435         var btn_text_cancel = '';
13436         
13437         if (this.btn_text_show) {
13438             btn_text_select = 'Select';
13439             btn_text_done = 'Done';
13440             btn_text_cancel = 'Cancel'; 
13441         }
13442         
13443         var buttons = {
13444             tag : 'div',
13445             cls : 'tickable-buttons',
13446             cn : [
13447                 {
13448                     tag : 'button',
13449                     type : 'button',
13450                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13451                     //html : this.triggerText
13452                     html: btn_text_select
13453                 },
13454                 {
13455                     tag : 'button',
13456                     type : 'button',
13457                     name : 'ok',
13458                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13459                     //html : 'Done'
13460                     html: btn_text_done
13461                 },
13462                 {
13463                     tag : 'button',
13464                     type : 'button',
13465                     name : 'cancel',
13466                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13467                     //html : 'Cancel'
13468                     html: btn_text_cancel
13469                 }
13470             ]
13471         };
13472         
13473         if(this.editable){
13474             buttons.cn.unshift({
13475                 tag: 'input',
13476                 cls: 'roo-select2-search-field-input'
13477             });
13478         }
13479         
13480         var _this = this;
13481         
13482         Roo.each(buttons.cn, function(c){
13483             if (_this.size) {
13484                 c.cls += ' btn-' + _this.size;
13485             }
13486
13487             if (_this.disabled) {
13488                 c.disabled = true;
13489             }
13490         });
13491         
13492         var box = {
13493             tag: 'div',
13494             style : 'display: contents',
13495             cn: [
13496                 {
13497                     tag: 'input',
13498                     type : 'hidden',
13499                     cls: 'form-hidden-field'
13500                 },
13501                 {
13502                     tag: 'ul',
13503                     cls: 'roo-select2-choices',
13504                     cn:[
13505                         {
13506                             tag: 'li',
13507                             cls: 'roo-select2-search-field',
13508                             cn: [
13509                                 buttons
13510                             ]
13511                         }
13512                     ]
13513                 }
13514             ]
13515         };
13516         
13517         var combobox = {
13518             cls: 'roo-select2-container input-group roo-select2-container-multi',
13519             cn: [
13520                 
13521                 box
13522 //                {
13523 //                    tag: 'ul',
13524 //                    cls: 'typeahead typeahead-long dropdown-menu',
13525 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13526 //                }
13527             ]
13528         };
13529         
13530         if(this.hasFeedback && !this.allowBlank){
13531             
13532             var feedback = {
13533                 tag: 'span',
13534                 cls: 'glyphicon form-control-feedback'
13535             };
13536
13537             combobox.cn.push(feedback);
13538         }
13539         
13540         var indicator = {
13541             tag : 'i',
13542             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13543             tooltip : 'This field is required'
13544         };
13545         if (Roo.bootstrap.version == 4) {
13546             indicator = {
13547                 tag : 'i',
13548                 style : 'display:none'
13549             };
13550         }
13551         if (align ==='left' && this.fieldLabel.length) {
13552             
13553             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13554             
13555             cfg.cn = [
13556                 indicator,
13557                 {
13558                     tag: 'label',
13559                     'for' :  id,
13560                     cls : 'control-label col-form-label',
13561                     html : this.fieldLabel
13562
13563                 },
13564                 {
13565                     cls : "", 
13566                     cn: [
13567                         combobox
13568                     ]
13569                 }
13570
13571             ];
13572             
13573             var labelCfg = cfg.cn[1];
13574             var contentCfg = cfg.cn[2];
13575             
13576
13577             if(this.indicatorpos == 'right'){
13578                 
13579                 cfg.cn = [
13580                     {
13581                         tag: 'label',
13582                         'for' :  id,
13583                         cls : 'control-label col-form-label',
13584                         cn : [
13585                             {
13586                                 tag : 'span',
13587                                 html : this.fieldLabel
13588                             },
13589                             indicator
13590                         ]
13591                     },
13592                     {
13593                         cls : "",
13594                         cn: [
13595                             combobox
13596                         ]
13597                     }
13598
13599                 ];
13600                 
13601                 
13602                 
13603                 labelCfg = cfg.cn[0];
13604                 contentCfg = cfg.cn[1];
13605             
13606             }
13607             
13608             if(this.labelWidth > 12){
13609                 labelCfg.style = "width: " + this.labelWidth + 'px';
13610             }
13611             
13612             if(this.labelWidth < 13 && this.labelmd == 0){
13613                 this.labelmd = this.labelWidth;
13614             }
13615             
13616             if(this.labellg > 0){
13617                 labelCfg.cls += ' col-lg-' + this.labellg;
13618                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13619             }
13620             
13621             if(this.labelmd > 0){
13622                 labelCfg.cls += ' col-md-' + this.labelmd;
13623                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13624             }
13625             
13626             if(this.labelsm > 0){
13627                 labelCfg.cls += ' col-sm-' + this.labelsm;
13628                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13629             }
13630             
13631             if(this.labelxs > 0){
13632                 labelCfg.cls += ' col-xs-' + this.labelxs;
13633                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13634             }
13635                 
13636                 
13637         } else if ( this.fieldLabel.length) {
13638 //                Roo.log(" label");
13639                  cfg.cn = [
13640                    indicator,
13641                     {
13642                         tag: 'label',
13643                         //cls : 'input-group-addon',
13644                         html : this.fieldLabel
13645                     },
13646                     combobox
13647                 ];
13648                 
13649                 if(this.indicatorpos == 'right'){
13650                     cfg.cn = [
13651                         {
13652                             tag: 'label',
13653                             //cls : 'input-group-addon',
13654                             html : this.fieldLabel
13655                         },
13656                         indicator,
13657                         combobox
13658                     ];
13659                     
13660                 }
13661
13662         } else {
13663             
13664 //                Roo.log(" no label && no align");
13665                 cfg = combobox
13666                      
13667                 
13668         }
13669          
13670         var settings=this;
13671         ['xs','sm','md','lg'].map(function(size){
13672             if (settings[size]) {
13673                 cfg.cls += ' col-' + size + '-' + settings[size];
13674             }
13675         });
13676         
13677         return cfg;
13678         
13679     },
13680     
13681     _initEventsCalled : false,
13682     
13683     // private
13684     initEvents: function()
13685     {   
13686         if (this._initEventsCalled) { // as we call render... prevent looping...
13687             return;
13688         }
13689         this._initEventsCalled = true;
13690         
13691         if (!this.store) {
13692             throw "can not find store for combo";
13693         }
13694         
13695         this.indicator = this.indicatorEl();
13696         
13697         this.store = Roo.factory(this.store, Roo.data);
13698         this.store.parent = this;
13699         
13700         // if we are building from html. then this element is so complex, that we can not really
13701         // use the rendered HTML.
13702         // so we have to trash and replace the previous code.
13703         if (Roo.XComponent.build_from_html) {
13704             // remove this element....
13705             var e = this.el.dom, k=0;
13706             while (e ) { e = e.previousSibling;  ++k;}
13707
13708             this.el.remove();
13709             
13710             this.el=false;
13711             this.rendered = false;
13712             
13713             this.render(this.parent().getChildContainer(true), k);
13714         }
13715         
13716         if(Roo.isIOS && this.useNativeIOS){
13717             this.initIOSView();
13718             return;
13719         }
13720         
13721         /*
13722          * Touch Devices
13723          */
13724         
13725         if(Roo.isTouch && this.mobileTouchView){
13726             this.initTouchView();
13727             return;
13728         }
13729         
13730         if(this.tickable){
13731             this.initTickableEvents();
13732             return;
13733         }
13734         
13735         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13736         
13737         if(this.hiddenName){
13738             
13739             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13740             
13741             this.hiddenField.dom.value =
13742                 this.hiddenValue !== undefined ? this.hiddenValue :
13743                 this.value !== undefined ? this.value : '';
13744
13745             // prevent input submission
13746             this.el.dom.removeAttribute('name');
13747             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13748              
13749              
13750         }
13751         //if(Roo.isGecko){
13752         //    this.el.dom.setAttribute('autocomplete', 'off');
13753         //}
13754         
13755         var cls = 'x-combo-list';
13756         
13757         //this.list = new Roo.Layer({
13758         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13759         //});
13760         
13761         var _this = this;
13762         
13763         (function(){
13764             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13765             _this.list.setWidth(lw);
13766         }).defer(100);
13767         
13768         this.list.on('mouseover', this.onViewOver, this);
13769         this.list.on('mousemove', this.onViewMove, this);
13770         this.list.on('scroll', this.onViewScroll, this);
13771         
13772         /*
13773         this.list.swallowEvent('mousewheel');
13774         this.assetHeight = 0;
13775
13776         if(this.title){
13777             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13778             this.assetHeight += this.header.getHeight();
13779         }
13780
13781         this.innerList = this.list.createChild({cls:cls+'-inner'});
13782         this.innerList.on('mouseover', this.onViewOver, this);
13783         this.innerList.on('mousemove', this.onViewMove, this);
13784         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13785         
13786         if(this.allowBlank && !this.pageSize && !this.disableClear){
13787             this.footer = this.list.createChild({cls:cls+'-ft'});
13788             this.pageTb = new Roo.Toolbar(this.footer);
13789            
13790         }
13791         if(this.pageSize){
13792             this.footer = this.list.createChild({cls:cls+'-ft'});
13793             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13794                     {pageSize: this.pageSize});
13795             
13796         }
13797         
13798         if (this.pageTb && this.allowBlank && !this.disableClear) {
13799             var _this = this;
13800             this.pageTb.add(new Roo.Toolbar.Fill(), {
13801                 cls: 'x-btn-icon x-btn-clear',
13802                 text: '&#160;',
13803                 handler: function()
13804                 {
13805                     _this.collapse();
13806                     _this.clearValue();
13807                     _this.onSelect(false, -1);
13808                 }
13809             });
13810         }
13811         if (this.footer) {
13812             this.assetHeight += this.footer.getHeight();
13813         }
13814         */
13815             
13816         if(!this.tpl){
13817             this.tpl = Roo.bootstrap.version == 4 ?
13818                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13819                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13820         }
13821
13822         this.view = new Roo.View(this.list, this.tpl, {
13823             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13824         });
13825         //this.view.wrapEl.setDisplayed(false);
13826         this.view.on('click', this.onViewClick, this);
13827         
13828         
13829         this.store.on('beforeload', this.onBeforeLoad, this);
13830         this.store.on('load', this.onLoad, this);
13831         this.store.on('loadexception', this.onLoadException, this);
13832         /*
13833         if(this.resizable){
13834             this.resizer = new Roo.Resizable(this.list,  {
13835                pinned:true, handles:'se'
13836             });
13837             this.resizer.on('resize', function(r, w, h){
13838                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13839                 this.listWidth = w;
13840                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13841                 this.restrictHeight();
13842             }, this);
13843             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13844         }
13845         */
13846         if(!this.editable){
13847             this.editable = true;
13848             this.setEditable(false);
13849         }
13850         
13851         /*
13852         
13853         if (typeof(this.events.add.listeners) != 'undefined') {
13854             
13855             this.addicon = this.wrap.createChild(
13856                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13857        
13858             this.addicon.on('click', function(e) {
13859                 this.fireEvent('add', this);
13860             }, this);
13861         }
13862         if (typeof(this.events.edit.listeners) != 'undefined') {
13863             
13864             this.editicon = this.wrap.createChild(
13865                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13866             if (this.addicon) {
13867                 this.editicon.setStyle('margin-left', '40px');
13868             }
13869             this.editicon.on('click', function(e) {
13870                 
13871                 // we fire even  if inothing is selected..
13872                 this.fireEvent('edit', this, this.lastData );
13873                 
13874             }, this);
13875         }
13876         */
13877         
13878         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13879             "up" : function(e){
13880                 this.inKeyMode = true;
13881                 this.selectPrev();
13882             },
13883
13884             "down" : function(e){
13885                 if(!this.isExpanded()){
13886                     this.onTriggerClick();
13887                 }else{
13888                     this.inKeyMode = true;
13889                     this.selectNext();
13890                 }
13891             },
13892
13893             "enter" : function(e){
13894 //                this.onViewClick();
13895                 //return true;
13896                 this.collapse();
13897                 
13898                 if(this.fireEvent("specialkey", this, e)){
13899                     this.onViewClick(false);
13900                 }
13901                 
13902                 return true;
13903             },
13904
13905             "esc" : function(e){
13906                 this.collapse();
13907             },
13908
13909             "tab" : function(e){
13910                 this.collapse();
13911                 
13912                 if(this.fireEvent("specialkey", this, e)){
13913                     this.onViewClick(false);
13914                 }
13915                 
13916                 return true;
13917             },
13918
13919             scope : this,
13920
13921             doRelay : function(foo, bar, hname){
13922                 if(hname == 'down' || this.scope.isExpanded()){
13923                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13924                 }
13925                 return true;
13926             },
13927
13928             forceKeyDown: true
13929         });
13930         
13931         
13932         this.queryDelay = Math.max(this.queryDelay || 10,
13933                 this.mode == 'local' ? 10 : 250);
13934         
13935         
13936         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13937         
13938         if(this.typeAhead){
13939             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13940         }
13941         if(this.editable !== false){
13942             this.inputEl().on("keyup", this.onKeyUp, this);
13943         }
13944         if(this.forceSelection){
13945             this.inputEl().on('blur', this.doForce, this);
13946         }
13947         
13948         if(this.multiple){
13949             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13950             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13951         }
13952     },
13953     
13954     initTickableEvents: function()
13955     {   
13956         this.createList();
13957         
13958         if(this.hiddenName){
13959             
13960             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13961             
13962             this.hiddenField.dom.value =
13963                 this.hiddenValue !== undefined ? this.hiddenValue :
13964                 this.value !== undefined ? this.value : '';
13965
13966             // prevent input submission
13967             this.el.dom.removeAttribute('name');
13968             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13969              
13970              
13971         }
13972         
13973 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13974         
13975         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13976         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13977         if(this.triggerList){
13978             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13979         }
13980          
13981         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13982         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13983         
13984         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13985         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13986         
13987         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13988         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13989         
13990         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13991         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13992         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13993         
13994         this.okBtn.hide();
13995         this.cancelBtn.hide();
13996         
13997         var _this = this;
13998         
13999         (function(){
14000             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14001             _this.list.setWidth(lw);
14002         }).defer(100);
14003         
14004         this.list.on('mouseover', this.onViewOver, this);
14005         this.list.on('mousemove', this.onViewMove, this);
14006         
14007         this.list.on('scroll', this.onViewScroll, this);
14008         
14009         if(!this.tpl){
14010             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14011                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14012         }
14013
14014         this.view = new Roo.View(this.list, this.tpl, {
14015             singleSelect:true,
14016             tickable:true,
14017             parent:this,
14018             store: this.store,
14019             selectedClass: this.selectedClass
14020         });
14021         
14022         //this.view.wrapEl.setDisplayed(false);
14023         this.view.on('click', this.onViewClick, this);
14024         
14025         
14026         
14027         this.store.on('beforeload', this.onBeforeLoad, this);
14028         this.store.on('load', this.onLoad, this);
14029         this.store.on('loadexception', this.onLoadException, this);
14030         
14031         if(this.editable){
14032             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14033                 "up" : function(e){
14034                     this.inKeyMode = true;
14035                     this.selectPrev();
14036                 },
14037
14038                 "down" : function(e){
14039                     this.inKeyMode = true;
14040                     this.selectNext();
14041                 },
14042
14043                 "enter" : function(e){
14044                     if(this.fireEvent("specialkey", this, e)){
14045                         this.onViewClick(false);
14046                     }
14047                     
14048                     return true;
14049                 },
14050
14051                 "esc" : function(e){
14052                     this.onTickableFooterButtonClick(e, false, false);
14053                 },
14054
14055                 "tab" : function(e){
14056                     this.fireEvent("specialkey", this, e);
14057                     
14058                     this.onTickableFooterButtonClick(e, false, false);
14059                     
14060                     return true;
14061                 },
14062
14063                 scope : this,
14064
14065                 doRelay : function(e, fn, key){
14066                     if(this.scope.isExpanded()){
14067                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14068                     }
14069                     return true;
14070                 },
14071
14072                 forceKeyDown: true
14073             });
14074         }
14075         
14076         this.queryDelay = Math.max(this.queryDelay || 10,
14077                 this.mode == 'local' ? 10 : 250);
14078         
14079         
14080         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14081         
14082         if(this.typeAhead){
14083             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14084         }
14085         
14086         if(this.editable !== false){
14087             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14088         }
14089         
14090         this.indicator = this.indicatorEl();
14091         
14092         if(this.indicator){
14093             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14094             this.indicator.hide();
14095         }
14096         
14097     },
14098
14099     onDestroy : function(){
14100         if(this.view){
14101             this.view.setStore(null);
14102             this.view.el.removeAllListeners();
14103             this.view.el.remove();
14104             this.view.purgeListeners();
14105         }
14106         if(this.list){
14107             this.list.dom.innerHTML  = '';
14108         }
14109         
14110         if(this.store){
14111             this.store.un('beforeload', this.onBeforeLoad, this);
14112             this.store.un('load', this.onLoad, this);
14113             this.store.un('loadexception', this.onLoadException, this);
14114         }
14115         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14116     },
14117
14118     // private
14119     fireKey : function(e){
14120         if(e.isNavKeyPress() && !this.list.isVisible()){
14121             this.fireEvent("specialkey", this, e);
14122         }
14123     },
14124
14125     // private
14126     onResize: function(w, h){
14127 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14128 //        
14129 //        if(typeof w != 'number'){
14130 //            // we do not handle it!?!?
14131 //            return;
14132 //        }
14133 //        var tw = this.trigger.getWidth();
14134 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14135 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14136 //        var x = w - tw;
14137 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14138 //            
14139 //        //this.trigger.setStyle('left', x+'px');
14140 //        
14141 //        if(this.list && this.listWidth === undefined){
14142 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14143 //            this.list.setWidth(lw);
14144 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14145 //        }
14146         
14147     
14148         
14149     },
14150
14151     /**
14152      * Allow or prevent the user from directly editing the field text.  If false is passed,
14153      * the user will only be able to select from the items defined in the dropdown list.  This method
14154      * is the runtime equivalent of setting the 'editable' config option at config time.
14155      * @param {Boolean} value True to allow the user to directly edit the field text
14156      */
14157     setEditable : function(value){
14158         if(value == this.editable){
14159             return;
14160         }
14161         this.editable = value;
14162         if(!value){
14163             this.inputEl().dom.setAttribute('readOnly', true);
14164             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14165             this.inputEl().addClass('x-combo-noedit');
14166         }else{
14167             this.inputEl().dom.setAttribute('readOnly', false);
14168             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14169             this.inputEl().removeClass('x-combo-noedit');
14170         }
14171     },
14172
14173     // private
14174     
14175     onBeforeLoad : function(combo,opts){
14176         if(!this.hasFocus){
14177             return;
14178         }
14179          if (!opts.add) {
14180             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14181          }
14182         this.restrictHeight();
14183         this.selectedIndex = -1;
14184     },
14185
14186     // private
14187     onLoad : function(){
14188         
14189         this.hasQuery = false;
14190         
14191         if(!this.hasFocus){
14192             return;
14193         }
14194         
14195         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14196             this.loading.hide();
14197         }
14198         
14199         if(this.store.getCount() > 0){
14200             
14201             this.expand();
14202             this.restrictHeight();
14203             if(this.lastQuery == this.allQuery){
14204                 if(this.editable && !this.tickable){
14205                     this.inputEl().dom.select();
14206                 }
14207                 
14208                 if(
14209                     !this.selectByValue(this.value, true) &&
14210                     this.autoFocus && 
14211                     (
14212                         !this.store.lastOptions ||
14213                         typeof(this.store.lastOptions.add) == 'undefined' || 
14214                         this.store.lastOptions.add != true
14215                     )
14216                 ){
14217                     this.select(0, true);
14218                 }
14219             }else{
14220                 if(this.autoFocus){
14221                     this.selectNext();
14222                 }
14223                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14224                     this.taTask.delay(this.typeAheadDelay);
14225                 }
14226             }
14227         }else{
14228             this.onEmptyResults();
14229         }
14230         
14231         //this.el.focus();
14232     },
14233     // private
14234     onLoadException : function()
14235     {
14236         this.hasQuery = false;
14237         
14238         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14239             this.loading.hide();
14240         }
14241         
14242         if(this.tickable && this.editable){
14243             return;
14244         }
14245         
14246         this.collapse();
14247         // only causes errors at present
14248         //Roo.log(this.store.reader.jsonData);
14249         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14250             // fixme
14251             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14252         //}
14253         
14254         
14255     },
14256     // private
14257     onTypeAhead : function(){
14258         if(this.store.getCount() > 0){
14259             var r = this.store.getAt(0);
14260             var newValue = r.data[this.displayField];
14261             var len = newValue.length;
14262             var selStart = this.getRawValue().length;
14263             
14264             if(selStart != len){
14265                 this.setRawValue(newValue);
14266                 this.selectText(selStart, newValue.length);
14267             }
14268         }
14269     },
14270
14271     // private
14272     onSelect : function(record, index){
14273         
14274         if(this.fireEvent('beforeselect', this, record, index) !== false){
14275         
14276             this.setFromData(index > -1 ? record.data : false);
14277             
14278             this.collapse();
14279             this.fireEvent('select', this, record, index);
14280         }
14281     },
14282
14283     /**
14284      * Returns the currently selected field value or empty string if no value is set.
14285      * @return {String} value The selected value
14286      */
14287     getValue : function()
14288     {
14289         if(Roo.isIOS && this.useNativeIOS){
14290             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14291         }
14292         
14293         if(this.multiple){
14294             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14295         }
14296         
14297         if(this.valueField){
14298             return typeof this.value != 'undefined' ? this.value : '';
14299         }else{
14300             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14301         }
14302     },
14303     
14304     getRawValue : function()
14305     {
14306         if(Roo.isIOS && this.useNativeIOS){
14307             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14308         }
14309         
14310         var v = this.inputEl().getValue();
14311         
14312         return v;
14313     },
14314
14315     /**
14316      * Clears any text/value currently set in the field
14317      */
14318     clearValue : function(){
14319         
14320         if(this.hiddenField){
14321             this.hiddenField.dom.value = '';
14322         }
14323         this.value = '';
14324         this.setRawValue('');
14325         this.lastSelectionText = '';
14326         this.lastData = false;
14327         
14328         var close = this.closeTriggerEl();
14329         
14330         if(close){
14331             close.hide();
14332         }
14333         
14334         this.validate();
14335         
14336     },
14337
14338     /**
14339      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14340      * will be displayed in the field.  If the value does not match the data value of an existing item,
14341      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14342      * Otherwise the field will be blank (although the value will still be set).
14343      * @param {String} value The value to match
14344      */
14345     setValue : function(v)
14346     {
14347         if(Roo.isIOS && this.useNativeIOS){
14348             this.setIOSValue(v);
14349             return;
14350         }
14351         
14352         if(this.multiple){
14353             this.syncValue();
14354             return;
14355         }
14356         
14357         var text = v;
14358         if(this.valueField){
14359             var r = this.findRecord(this.valueField, v);
14360             if(r){
14361                 text = r.data[this.displayField];
14362             }else if(this.valueNotFoundText !== undefined){
14363                 text = this.valueNotFoundText;
14364             }
14365         }
14366         this.lastSelectionText = text;
14367         if(this.hiddenField){
14368             this.hiddenField.dom.value = v;
14369         }
14370         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14371         this.value = v;
14372         
14373         var close = this.closeTriggerEl();
14374         
14375         if(close){
14376             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14377         }
14378         
14379         this.validate();
14380     },
14381     /**
14382      * @property {Object} the last set data for the element
14383      */
14384     
14385     lastData : false,
14386     /**
14387      * Sets the value of the field based on a object which is related to the record format for the store.
14388      * @param {Object} value the value to set as. or false on reset?
14389      */
14390     setFromData : function(o){
14391         
14392         if(this.multiple){
14393             this.addItem(o);
14394             return;
14395         }
14396             
14397         var dv = ''; // display value
14398         var vv = ''; // value value..
14399         this.lastData = o;
14400         if (this.displayField) {
14401             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14402         } else {
14403             // this is an error condition!!!
14404             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14405         }
14406         
14407         if(this.valueField){
14408             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14409         }
14410         
14411         var close = this.closeTriggerEl();
14412         
14413         if(close){
14414             if(dv.length || vv * 1 > 0){
14415                 close.show() ;
14416                 this.blockFocus=true;
14417             } else {
14418                 close.hide();
14419             }             
14420         }
14421         
14422         if(this.hiddenField){
14423             this.hiddenField.dom.value = vv;
14424             
14425             this.lastSelectionText = dv;
14426             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14427             this.value = vv;
14428             return;
14429         }
14430         // no hidden field.. - we store the value in 'value', but still display
14431         // display field!!!!
14432         this.lastSelectionText = dv;
14433         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14434         this.value = vv;
14435         
14436         
14437         
14438     },
14439     // private
14440     reset : function(){
14441         // overridden so that last data is reset..
14442         
14443         if(this.multiple){
14444             this.clearItem();
14445             return;
14446         }
14447         
14448         this.setValue(this.originalValue);
14449         //this.clearInvalid();
14450         this.lastData = false;
14451         if (this.view) {
14452             this.view.clearSelections();
14453         }
14454         
14455         this.validate();
14456     },
14457     // private
14458     findRecord : function(prop, value){
14459         var record;
14460         if(this.store.getCount() > 0){
14461             this.store.each(function(r){
14462                 if(r.data[prop] == value){
14463                     record = r;
14464                     return false;
14465                 }
14466                 return true;
14467             });
14468         }
14469         return record;
14470     },
14471     
14472     getName: function()
14473     {
14474         // returns hidden if it's set..
14475         if (!this.rendered) {return ''};
14476         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14477         
14478     },
14479     // private
14480     onViewMove : function(e, t){
14481         this.inKeyMode = false;
14482     },
14483
14484     // private
14485     onViewOver : function(e, t){
14486         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14487             return;
14488         }
14489         var item = this.view.findItemFromChild(t);
14490         
14491         if(item){
14492             var index = this.view.indexOf(item);
14493             this.select(index, false);
14494         }
14495     },
14496
14497     // private
14498     onViewClick : function(view, doFocus, el, e)
14499     {
14500         var index = this.view.getSelectedIndexes()[0];
14501         
14502         var r = this.store.getAt(index);
14503         
14504         if(this.tickable){
14505             
14506             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14507                 return;
14508             }
14509             
14510             var rm = false;
14511             var _this = this;
14512             
14513             Roo.each(this.tickItems, function(v,k){
14514                 
14515                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14516                     Roo.log(v);
14517                     _this.tickItems.splice(k, 1);
14518                     
14519                     if(typeof(e) == 'undefined' && view == false){
14520                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14521                     }
14522                     
14523                     rm = true;
14524                     return;
14525                 }
14526             });
14527             
14528             if(rm){
14529                 return;
14530             }
14531             
14532             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14533                 this.tickItems.push(r.data);
14534             }
14535             
14536             if(typeof(e) == 'undefined' && view == false){
14537                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14538             }
14539                     
14540             return;
14541         }
14542         
14543         if(r){
14544             this.onSelect(r, index);
14545         }
14546         if(doFocus !== false && !this.blockFocus){
14547             this.inputEl().focus();
14548         }
14549     },
14550
14551     // private
14552     restrictHeight : function(){
14553         //this.innerList.dom.style.height = '';
14554         //var inner = this.innerList.dom;
14555         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14556         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14557         //this.list.beginUpdate();
14558         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14559         this.list.alignTo(this.inputEl(), this.listAlign);
14560         this.list.alignTo(this.inputEl(), this.listAlign);
14561         //this.list.endUpdate();
14562     },
14563
14564     // private
14565     onEmptyResults : function(){
14566         
14567         if(this.tickable && this.editable){
14568             this.hasFocus = false;
14569             this.restrictHeight();
14570             return;
14571         }
14572         
14573         this.collapse();
14574     },
14575
14576     /**
14577      * Returns true if the dropdown list is expanded, else false.
14578      */
14579     isExpanded : function(){
14580         return this.list.isVisible();
14581     },
14582
14583     /**
14584      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14585      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14586      * @param {String} value The data value of the item to select
14587      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14588      * selected item if it is not currently in view (defaults to true)
14589      * @return {Boolean} True if the value matched an item in the list, else false
14590      */
14591     selectByValue : function(v, scrollIntoView){
14592         if(v !== undefined && v !== null){
14593             var r = this.findRecord(this.valueField || this.displayField, v);
14594             if(r){
14595                 this.select(this.store.indexOf(r), scrollIntoView);
14596                 return true;
14597             }
14598         }
14599         return false;
14600     },
14601
14602     /**
14603      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14604      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14605      * @param {Number} index The zero-based index of the list item to select
14606      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14607      * selected item if it is not currently in view (defaults to true)
14608      */
14609     select : function(index, scrollIntoView){
14610         this.selectedIndex = index;
14611         this.view.select(index);
14612         if(scrollIntoView !== false){
14613             var el = this.view.getNode(index);
14614             /*
14615              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14616              */
14617             if(el){
14618                 this.list.scrollChildIntoView(el, false);
14619             }
14620         }
14621     },
14622
14623     // private
14624     selectNext : function(){
14625         var ct = this.store.getCount();
14626         if(ct > 0){
14627             if(this.selectedIndex == -1){
14628                 this.select(0);
14629             }else if(this.selectedIndex < ct-1){
14630                 this.select(this.selectedIndex+1);
14631             }
14632         }
14633     },
14634
14635     // private
14636     selectPrev : function(){
14637         var ct = this.store.getCount();
14638         if(ct > 0){
14639             if(this.selectedIndex == -1){
14640                 this.select(0);
14641             }else if(this.selectedIndex != 0){
14642                 this.select(this.selectedIndex-1);
14643             }
14644         }
14645     },
14646
14647     // private
14648     onKeyUp : function(e){
14649         if(this.editable !== false && !e.isSpecialKey()){
14650             this.lastKey = e.getKey();
14651             this.dqTask.delay(this.queryDelay);
14652         }
14653     },
14654
14655     // private
14656     validateBlur : function(){
14657         return !this.list || !this.list.isVisible();   
14658     },
14659
14660     // private
14661     initQuery : function(){
14662         
14663         var v = this.getRawValue();
14664         
14665         if(this.tickable && this.editable){
14666             v = this.tickableInputEl().getValue();
14667         }
14668         
14669         this.doQuery(v);
14670     },
14671
14672     // private
14673     doForce : function(){
14674         if(this.inputEl().dom.value.length > 0){
14675             this.inputEl().dom.value =
14676                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14677              
14678         }
14679     },
14680
14681     /**
14682      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14683      * query allowing the query action to be canceled if needed.
14684      * @param {String} query The SQL query to execute
14685      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14686      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14687      * saved in the current store (defaults to false)
14688      */
14689     doQuery : function(q, forceAll){
14690         
14691         if(q === undefined || q === null){
14692             q = '';
14693         }
14694         var qe = {
14695             query: q,
14696             forceAll: forceAll,
14697             combo: this,
14698             cancel:false
14699         };
14700         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14701             return false;
14702         }
14703         q = qe.query;
14704         
14705         forceAll = qe.forceAll;
14706         if(forceAll === true || (q.length >= this.minChars)){
14707             
14708             this.hasQuery = true;
14709             
14710             if(this.lastQuery != q || this.alwaysQuery){
14711                 this.lastQuery = q;
14712                 if(this.mode == 'local'){
14713                     this.selectedIndex = -1;
14714                     if(forceAll){
14715                         this.store.clearFilter();
14716                     }else{
14717                         
14718                         if(this.specialFilter){
14719                             this.fireEvent('specialfilter', this);
14720                             this.onLoad();
14721                             return;
14722                         }
14723                         
14724                         this.store.filter(this.displayField, q);
14725                     }
14726                     
14727                     this.store.fireEvent("datachanged", this.store);
14728                     
14729                     this.onLoad();
14730                     
14731                     
14732                 }else{
14733                     
14734                     this.store.baseParams[this.queryParam] = q;
14735                     
14736                     var options = {params : this.getParams(q)};
14737                     
14738                     if(this.loadNext){
14739                         options.add = true;
14740                         options.params.start = this.page * this.pageSize;
14741                     }
14742                     
14743                     this.store.load(options);
14744                     
14745                     /*
14746                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14747                      *  we should expand the list on onLoad
14748                      *  so command out it
14749                      */
14750 //                    this.expand();
14751                 }
14752             }else{
14753                 this.selectedIndex = -1;
14754                 this.onLoad();   
14755             }
14756         }
14757         
14758         this.loadNext = false;
14759     },
14760     
14761     // private
14762     getParams : function(q){
14763         var p = {};
14764         //p[this.queryParam] = q;
14765         
14766         if(this.pageSize){
14767             p.start = 0;
14768             p.limit = this.pageSize;
14769         }
14770         return p;
14771     },
14772
14773     /**
14774      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14775      */
14776     collapse : function(){
14777         if(!this.isExpanded()){
14778             return;
14779         }
14780         
14781         this.list.hide();
14782         
14783         this.hasFocus = false;
14784         
14785         if(this.tickable){
14786             this.okBtn.hide();
14787             this.cancelBtn.hide();
14788             this.trigger.show();
14789             
14790             if(this.editable){
14791                 this.tickableInputEl().dom.value = '';
14792                 this.tickableInputEl().blur();
14793             }
14794             
14795         }
14796         
14797         Roo.get(document).un('mousedown', this.collapseIf, this);
14798         Roo.get(document).un('mousewheel', this.collapseIf, this);
14799         if (!this.editable) {
14800             Roo.get(document).un('keydown', this.listKeyPress, this);
14801         }
14802         this.fireEvent('collapse', this);
14803         
14804         this.validate();
14805     },
14806
14807     // private
14808     collapseIf : function(e){
14809         var in_combo  = e.within(this.el);
14810         var in_list =  e.within(this.list);
14811         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14812         
14813         if (in_combo || in_list || is_list) {
14814             //e.stopPropagation();
14815             return;
14816         }
14817         
14818         if(this.tickable){
14819             this.onTickableFooterButtonClick(e, false, false);
14820         }
14821
14822         this.collapse();
14823         
14824     },
14825
14826     /**
14827      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14828      */
14829     expand : function(){
14830        
14831         if(this.isExpanded() || !this.hasFocus){
14832             return;
14833         }
14834         
14835         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14836         this.list.setWidth(lw);
14837         
14838         Roo.log('expand');
14839         
14840         this.list.show();
14841         
14842         this.restrictHeight();
14843         
14844         if(this.tickable){
14845             
14846             this.tickItems = Roo.apply([], this.item);
14847             
14848             this.okBtn.show();
14849             this.cancelBtn.show();
14850             this.trigger.hide();
14851             
14852             if(this.editable){
14853                 this.tickableInputEl().focus();
14854             }
14855             
14856         }
14857         
14858         Roo.get(document).on('mousedown', this.collapseIf, this);
14859         Roo.get(document).on('mousewheel', this.collapseIf, this);
14860         if (!this.editable) {
14861             Roo.get(document).on('keydown', this.listKeyPress, this);
14862         }
14863         
14864         this.fireEvent('expand', this);
14865     },
14866
14867     // private
14868     // Implements the default empty TriggerField.onTriggerClick function
14869     onTriggerClick : function(e)
14870     {
14871         Roo.log('trigger click');
14872         
14873         if(this.disabled || !this.triggerList){
14874             return;
14875         }
14876         
14877         this.page = 0;
14878         this.loadNext = false;
14879         
14880         if(this.isExpanded()){
14881             this.collapse();
14882             if (!this.blockFocus) {
14883                 this.inputEl().focus();
14884             }
14885             
14886         }else {
14887             this.hasFocus = true;
14888             if(this.triggerAction == 'all') {
14889                 this.doQuery(this.allQuery, true);
14890             } else {
14891                 this.doQuery(this.getRawValue());
14892             }
14893             if (!this.blockFocus) {
14894                 this.inputEl().focus();
14895             }
14896         }
14897     },
14898     
14899     onTickableTriggerClick : function(e)
14900     {
14901         if(this.disabled){
14902             return;
14903         }
14904         
14905         this.page = 0;
14906         this.loadNext = false;
14907         this.hasFocus = true;
14908         
14909         if(this.triggerAction == 'all') {
14910             this.doQuery(this.allQuery, true);
14911         } else {
14912             this.doQuery(this.getRawValue());
14913         }
14914     },
14915     
14916     onSearchFieldClick : function(e)
14917     {
14918         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14919             this.onTickableFooterButtonClick(e, false, false);
14920             return;
14921         }
14922         
14923         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14924             return;
14925         }
14926         
14927         this.page = 0;
14928         this.loadNext = false;
14929         this.hasFocus = true;
14930         
14931         if(this.triggerAction == 'all') {
14932             this.doQuery(this.allQuery, true);
14933         } else {
14934             this.doQuery(this.getRawValue());
14935         }
14936     },
14937     
14938     listKeyPress : function(e)
14939     {
14940         //Roo.log('listkeypress');
14941         // scroll to first matching element based on key pres..
14942         if (e.isSpecialKey()) {
14943             return false;
14944         }
14945         var k = String.fromCharCode(e.getKey()).toUpperCase();
14946         //Roo.log(k);
14947         var match  = false;
14948         var csel = this.view.getSelectedNodes();
14949         var cselitem = false;
14950         if (csel.length) {
14951             var ix = this.view.indexOf(csel[0]);
14952             cselitem  = this.store.getAt(ix);
14953             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14954                 cselitem = false;
14955             }
14956             
14957         }
14958         
14959         this.store.each(function(v) { 
14960             if (cselitem) {
14961                 // start at existing selection.
14962                 if (cselitem.id == v.id) {
14963                     cselitem = false;
14964                 }
14965                 return true;
14966             }
14967                 
14968             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14969                 match = this.store.indexOf(v);
14970                 return false;
14971             }
14972             return true;
14973         }, this);
14974         
14975         if (match === false) {
14976             return true; // no more action?
14977         }
14978         // scroll to?
14979         this.view.select(match);
14980         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14981         sn.scrollIntoView(sn.dom.parentNode, false);
14982     },
14983     
14984     onViewScroll : function(e, t){
14985         
14986         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){
14987             return;
14988         }
14989         
14990         this.hasQuery = true;
14991         
14992         this.loading = this.list.select('.loading', true).first();
14993         
14994         if(this.loading === null){
14995             this.list.createChild({
14996                 tag: 'div',
14997                 cls: 'loading roo-select2-more-results roo-select2-active',
14998                 html: 'Loading more results...'
14999             });
15000             
15001             this.loading = this.list.select('.loading', true).first();
15002             
15003             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15004             
15005             this.loading.hide();
15006         }
15007         
15008         this.loading.show();
15009         
15010         var _combo = this;
15011         
15012         this.page++;
15013         this.loadNext = true;
15014         
15015         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15016         
15017         return;
15018     },
15019     
15020     addItem : function(o)
15021     {   
15022         var dv = ''; // display value
15023         
15024         if (this.displayField) {
15025             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15026         } else {
15027             // this is an error condition!!!
15028             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15029         }
15030         
15031         if(!dv.length){
15032             return;
15033         }
15034         
15035         var choice = this.choices.createChild({
15036             tag: 'li',
15037             cls: 'roo-select2-search-choice',
15038             cn: [
15039                 {
15040                     tag: 'div',
15041                     html: dv
15042                 },
15043                 {
15044                     tag: 'a',
15045                     href: '#',
15046                     cls: 'roo-select2-search-choice-close fa fa-times',
15047                     tabindex: '-1'
15048                 }
15049             ]
15050             
15051         }, this.searchField);
15052         
15053         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15054         
15055         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15056         
15057         this.item.push(o);
15058         
15059         this.lastData = o;
15060         
15061         this.syncValue();
15062         
15063         this.inputEl().dom.value = '';
15064         
15065         this.validate();
15066     },
15067     
15068     onRemoveItem : function(e, _self, o)
15069     {
15070         e.preventDefault();
15071         
15072         this.lastItem = Roo.apply([], this.item);
15073         
15074         var index = this.item.indexOf(o.data) * 1;
15075         
15076         if( index < 0){
15077             Roo.log('not this item?!');
15078             return;
15079         }
15080         
15081         this.item.splice(index, 1);
15082         o.item.remove();
15083         
15084         this.syncValue();
15085         
15086         this.fireEvent('remove', this, e);
15087         
15088         this.validate();
15089         
15090     },
15091     
15092     syncValue : function()
15093     {
15094         if(!this.item.length){
15095             this.clearValue();
15096             return;
15097         }
15098             
15099         var value = [];
15100         var _this = this;
15101         Roo.each(this.item, function(i){
15102             if(_this.valueField){
15103                 value.push(i[_this.valueField]);
15104                 return;
15105             }
15106
15107             value.push(i);
15108         });
15109
15110         this.value = value.join(',');
15111
15112         if(this.hiddenField){
15113             this.hiddenField.dom.value = this.value;
15114         }
15115         
15116         this.store.fireEvent("datachanged", this.store);
15117         
15118         this.validate();
15119     },
15120     
15121     clearItem : function()
15122     {
15123         if(!this.multiple){
15124             return;
15125         }
15126         
15127         this.item = [];
15128         
15129         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15130            c.remove();
15131         });
15132         
15133         this.syncValue();
15134         
15135         this.validate();
15136         
15137         if(this.tickable && !Roo.isTouch){
15138             this.view.refresh();
15139         }
15140     },
15141     
15142     inputEl: function ()
15143     {
15144         if(Roo.isIOS && this.useNativeIOS){
15145             return this.el.select('select.roo-ios-select', true).first();
15146         }
15147         
15148         if(Roo.isTouch && this.mobileTouchView){
15149             return this.el.select('input.form-control',true).first();
15150         }
15151         
15152         if(this.tickable){
15153             return this.searchField;
15154         }
15155         
15156         return this.el.select('input.form-control',true).first();
15157     },
15158     
15159     onTickableFooterButtonClick : function(e, btn, el)
15160     {
15161         e.preventDefault();
15162         
15163         this.lastItem = Roo.apply([], this.item);
15164         
15165         if(btn && btn.name == 'cancel'){
15166             this.tickItems = Roo.apply([], this.item);
15167             this.collapse();
15168             return;
15169         }
15170         
15171         this.clearItem();
15172         
15173         var _this = this;
15174         
15175         Roo.each(this.tickItems, function(o){
15176             _this.addItem(o);
15177         });
15178         
15179         this.collapse();
15180         
15181     },
15182     
15183     validate : function()
15184     {
15185         if(this.getVisibilityEl().hasClass('hidden')){
15186             return true;
15187         }
15188         
15189         var v = this.getRawValue();
15190         
15191         if(this.multiple){
15192             v = this.getValue();
15193         }
15194         
15195         if(this.disabled || this.allowBlank || v.length){
15196             this.markValid();
15197             return true;
15198         }
15199         
15200         this.markInvalid();
15201         return false;
15202     },
15203     
15204     tickableInputEl : function()
15205     {
15206         if(!this.tickable || !this.editable){
15207             return this.inputEl();
15208         }
15209         
15210         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15211     },
15212     
15213     
15214     getAutoCreateTouchView : function()
15215     {
15216         var id = Roo.id();
15217         
15218         var cfg = {
15219             cls: 'form-group' //input-group
15220         };
15221         
15222         var input =  {
15223             tag: 'input',
15224             id : id,
15225             type : this.inputType,
15226             cls : 'form-control x-combo-noedit',
15227             autocomplete: 'new-password',
15228             placeholder : this.placeholder || '',
15229             readonly : true
15230         };
15231         
15232         if (this.name) {
15233             input.name = this.name;
15234         }
15235         
15236         if (this.size) {
15237             input.cls += ' input-' + this.size;
15238         }
15239         
15240         if (this.disabled) {
15241             input.disabled = true;
15242         }
15243         
15244         var inputblock = {
15245             cls : '',
15246             cn : [
15247                 input
15248             ]
15249         };
15250         
15251         if(this.before){
15252             inputblock.cls += ' input-group';
15253             
15254             inputblock.cn.unshift({
15255                 tag :'span',
15256                 cls : 'input-group-addon input-group-prepend input-group-text',
15257                 html : this.before
15258             });
15259         }
15260         
15261         if(this.removable && !this.multiple){
15262             inputblock.cls += ' roo-removable';
15263             
15264             inputblock.cn.push({
15265                 tag: 'button',
15266                 html : 'x',
15267                 cls : 'roo-combo-removable-btn close'
15268             });
15269         }
15270
15271         if(this.hasFeedback && !this.allowBlank){
15272             
15273             inputblock.cls += ' has-feedback';
15274             
15275             inputblock.cn.push({
15276                 tag: 'span',
15277                 cls: 'glyphicon form-control-feedback'
15278             });
15279             
15280         }
15281         
15282         if (this.after) {
15283             
15284             inputblock.cls += (this.before) ? '' : ' input-group';
15285             
15286             inputblock.cn.push({
15287                 tag :'span',
15288                 cls : 'input-group-addon input-group-append input-group-text',
15289                 html : this.after
15290             });
15291         }
15292
15293         
15294         var ibwrap = inputblock;
15295         
15296         if(this.multiple){
15297             ibwrap = {
15298                 tag: 'ul',
15299                 cls: 'roo-select2-choices',
15300                 cn:[
15301                     {
15302                         tag: 'li',
15303                         cls: 'roo-select2-search-field',
15304                         cn: [
15305
15306                             inputblock
15307                         ]
15308                     }
15309                 ]
15310             };
15311         
15312             
15313         }
15314         
15315         var combobox = {
15316             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15317             cn: [
15318                 {
15319                     tag: 'input',
15320                     type : 'hidden',
15321                     cls: 'form-hidden-field'
15322                 },
15323                 ibwrap
15324             ]
15325         };
15326         
15327         if(!this.multiple && this.showToggleBtn){
15328             
15329             var caret = {
15330                         tag: 'span',
15331                         cls: 'caret'
15332             };
15333             
15334             if (this.caret != false) {
15335                 caret = {
15336                      tag: 'i',
15337                      cls: 'fa fa-' + this.caret
15338                 };
15339                 
15340             }
15341             
15342             combobox.cn.push({
15343                 tag :'span',
15344                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15345                 cn : [
15346                     caret,
15347                     {
15348                         tag: 'span',
15349                         cls: 'combobox-clear',
15350                         cn  : [
15351                             {
15352                                 tag : 'i',
15353                                 cls: 'icon-remove'
15354                             }
15355                         ]
15356                     }
15357                 ]
15358
15359             })
15360         }
15361         
15362         if(this.multiple){
15363             combobox.cls += ' roo-select2-container-multi';
15364         }
15365         
15366         var align = this.labelAlign || this.parentLabelAlign();
15367         
15368         if (align ==='left' && this.fieldLabel.length) {
15369
15370             cfg.cn = [
15371                 {
15372                    tag : 'i',
15373                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15374                    tooltip : 'This field is required'
15375                 },
15376                 {
15377                     tag: 'label',
15378                     cls : 'control-label col-form-label',
15379                     html : this.fieldLabel
15380
15381                 },
15382                 {
15383                     cls : '', 
15384                     cn: [
15385                         combobox
15386                     ]
15387                 }
15388             ];
15389             
15390             var labelCfg = cfg.cn[1];
15391             var contentCfg = cfg.cn[2];
15392             
15393
15394             if(this.indicatorpos == 'right'){
15395                 cfg.cn = [
15396                     {
15397                         tag: 'label',
15398                         'for' :  id,
15399                         cls : 'control-label col-form-label',
15400                         cn : [
15401                             {
15402                                 tag : 'span',
15403                                 html : this.fieldLabel
15404                             },
15405                             {
15406                                 tag : 'i',
15407                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15408                                 tooltip : 'This field is required'
15409                             }
15410                         ]
15411                     },
15412                     {
15413                         cls : "",
15414                         cn: [
15415                             combobox
15416                         ]
15417                     }
15418
15419                 ];
15420                 
15421                 labelCfg = cfg.cn[0];
15422                 contentCfg = cfg.cn[1];
15423             }
15424             
15425            
15426             
15427             if(this.labelWidth > 12){
15428                 labelCfg.style = "width: " + this.labelWidth + 'px';
15429             }
15430             
15431             if(this.labelWidth < 13 && this.labelmd == 0){
15432                 this.labelmd = this.labelWidth;
15433             }
15434             
15435             if(this.labellg > 0){
15436                 labelCfg.cls += ' col-lg-' + this.labellg;
15437                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15438             }
15439             
15440             if(this.labelmd > 0){
15441                 labelCfg.cls += ' col-md-' + this.labelmd;
15442                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15443             }
15444             
15445             if(this.labelsm > 0){
15446                 labelCfg.cls += ' col-sm-' + this.labelsm;
15447                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15448             }
15449             
15450             if(this.labelxs > 0){
15451                 labelCfg.cls += ' col-xs-' + this.labelxs;
15452                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15453             }
15454                 
15455                 
15456         } else if ( this.fieldLabel.length) {
15457             cfg.cn = [
15458                 {
15459                    tag : 'i',
15460                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15461                    tooltip : 'This field is required'
15462                 },
15463                 {
15464                     tag: 'label',
15465                     cls : 'control-label',
15466                     html : this.fieldLabel
15467
15468                 },
15469                 {
15470                     cls : '', 
15471                     cn: [
15472                         combobox
15473                     ]
15474                 }
15475             ];
15476             
15477             if(this.indicatorpos == 'right'){
15478                 cfg.cn = [
15479                     {
15480                         tag: 'label',
15481                         cls : 'control-label',
15482                         html : this.fieldLabel,
15483                         cn : [
15484                             {
15485                                tag : 'i',
15486                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15487                                tooltip : 'This field is required'
15488                             }
15489                         ]
15490                     },
15491                     {
15492                         cls : '', 
15493                         cn: [
15494                             combobox
15495                         ]
15496                     }
15497                 ];
15498             }
15499         } else {
15500             cfg.cn = combobox;    
15501         }
15502         
15503         
15504         var settings = this;
15505         
15506         ['xs','sm','md','lg'].map(function(size){
15507             if (settings[size]) {
15508                 cfg.cls += ' col-' + size + '-' + settings[size];
15509             }
15510         });
15511         
15512         return cfg;
15513     },
15514     
15515     initTouchView : function()
15516     {
15517         this.renderTouchView();
15518         
15519         this.touchViewEl.on('scroll', function(){
15520             this.el.dom.scrollTop = 0;
15521         }, this);
15522         
15523         this.originalValue = this.getValue();
15524         
15525         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15526         
15527         this.inputEl().on("click", this.showTouchView, this);
15528         if (this.triggerEl) {
15529             this.triggerEl.on("click", this.showTouchView, this);
15530         }
15531         
15532         
15533         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15534         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15535         
15536         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15537         
15538         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15539         this.store.on('load', this.onTouchViewLoad, this);
15540         this.store.on('loadexception', this.onTouchViewLoadException, this);
15541         
15542         if(this.hiddenName){
15543             
15544             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15545             
15546             this.hiddenField.dom.value =
15547                 this.hiddenValue !== undefined ? this.hiddenValue :
15548                 this.value !== undefined ? this.value : '';
15549         
15550             this.el.dom.removeAttribute('name');
15551             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15552         }
15553         
15554         if(this.multiple){
15555             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15556             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15557         }
15558         
15559         if(this.removable && !this.multiple){
15560             var close = this.closeTriggerEl();
15561             if(close){
15562                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15563                 close.on('click', this.removeBtnClick, this, close);
15564             }
15565         }
15566         /*
15567          * fix the bug in Safari iOS8
15568          */
15569         this.inputEl().on("focus", function(e){
15570             document.activeElement.blur();
15571         }, this);
15572         
15573         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15574         
15575         return;
15576         
15577         
15578     },
15579     
15580     renderTouchView : function()
15581     {
15582         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15583         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15584         
15585         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15586         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15587         
15588         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15589         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15590         this.touchViewBodyEl.setStyle('overflow', 'auto');
15591         
15592         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15593         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15594         
15595         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15596         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15597         
15598     },
15599     
15600     showTouchView : function()
15601     {
15602         if(this.disabled){
15603             return;
15604         }
15605         
15606         this.touchViewHeaderEl.hide();
15607
15608         if(this.modalTitle.length){
15609             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15610             this.touchViewHeaderEl.show();
15611         }
15612
15613         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15614         this.touchViewEl.show();
15615
15616         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15617         
15618         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15619         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15620
15621         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15622
15623         if(this.modalTitle.length){
15624             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15625         }
15626         
15627         this.touchViewBodyEl.setHeight(bodyHeight);
15628
15629         if(this.animate){
15630             var _this = this;
15631             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15632         }else{
15633             this.touchViewEl.addClass('in');
15634         }
15635         
15636         if(this._touchViewMask){
15637             Roo.get(document.body).addClass("x-body-masked");
15638             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15639             this._touchViewMask.setStyle('z-index', 10000);
15640             this._touchViewMask.addClass('show');
15641         }
15642         
15643         this.doTouchViewQuery();
15644         
15645     },
15646     
15647     hideTouchView : function()
15648     {
15649         this.touchViewEl.removeClass('in');
15650
15651         if(this.animate){
15652             var _this = this;
15653             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15654         }else{
15655             this.touchViewEl.setStyle('display', 'none');
15656         }
15657         
15658         if(this._touchViewMask){
15659             this._touchViewMask.removeClass('show');
15660             Roo.get(document.body).removeClass("x-body-masked");
15661         }
15662     },
15663     
15664     setTouchViewValue : function()
15665     {
15666         if(this.multiple){
15667             this.clearItem();
15668         
15669             var _this = this;
15670
15671             Roo.each(this.tickItems, function(o){
15672                 this.addItem(o);
15673             }, this);
15674         }
15675         
15676         this.hideTouchView();
15677     },
15678     
15679     doTouchViewQuery : function()
15680     {
15681         var qe = {
15682             query: '',
15683             forceAll: true,
15684             combo: this,
15685             cancel:false
15686         };
15687         
15688         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15689             return false;
15690         }
15691         
15692         if(!this.alwaysQuery || this.mode == 'local'){
15693             this.onTouchViewLoad();
15694             return;
15695         }
15696         
15697         this.store.load();
15698     },
15699     
15700     onTouchViewBeforeLoad : function(combo,opts)
15701     {
15702         return;
15703     },
15704
15705     // private
15706     onTouchViewLoad : function()
15707     {
15708         if(this.store.getCount() < 1){
15709             this.onTouchViewEmptyResults();
15710             return;
15711         }
15712         
15713         this.clearTouchView();
15714         
15715         var rawValue = this.getRawValue();
15716         
15717         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15718         
15719         this.tickItems = [];
15720         
15721         this.store.data.each(function(d, rowIndex){
15722             var row = this.touchViewListGroup.createChild(template);
15723             
15724             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15725                 row.addClass(d.data.cls);
15726             }
15727             
15728             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15729                 var cfg = {
15730                     data : d.data,
15731                     html : d.data[this.displayField]
15732                 };
15733                 
15734                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15735                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15736                 }
15737             }
15738             row.removeClass('selected');
15739             if(!this.multiple && this.valueField &&
15740                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15741             {
15742                 // radio buttons..
15743                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15744                 row.addClass('selected');
15745             }
15746             
15747             if(this.multiple && this.valueField &&
15748                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15749             {
15750                 
15751                 // checkboxes...
15752                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15753                 this.tickItems.push(d.data);
15754             }
15755             
15756             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15757             
15758         }, this);
15759         
15760         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15761         
15762         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15763
15764         if(this.modalTitle.length){
15765             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15766         }
15767
15768         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15769         
15770         if(this.mobile_restrict_height && listHeight < bodyHeight){
15771             this.touchViewBodyEl.setHeight(listHeight);
15772         }
15773         
15774         var _this = this;
15775         
15776         if(firstChecked && listHeight > bodyHeight){
15777             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15778         }
15779         
15780     },
15781     
15782     onTouchViewLoadException : function()
15783     {
15784         this.hideTouchView();
15785     },
15786     
15787     onTouchViewEmptyResults : function()
15788     {
15789         this.clearTouchView();
15790         
15791         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15792         
15793         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15794         
15795     },
15796     
15797     clearTouchView : function()
15798     {
15799         this.touchViewListGroup.dom.innerHTML = '';
15800     },
15801     
15802     onTouchViewClick : function(e, el, o)
15803     {
15804         e.preventDefault();
15805         
15806         var row = o.row;
15807         var rowIndex = o.rowIndex;
15808         
15809         var r = this.store.getAt(rowIndex);
15810         
15811         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15812             
15813             if(!this.multiple){
15814                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15815                     c.dom.removeAttribute('checked');
15816                 }, this);
15817
15818                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15819
15820                 this.setFromData(r.data);
15821
15822                 var close = this.closeTriggerEl();
15823
15824                 if(close){
15825                     close.show();
15826                 }
15827
15828                 this.hideTouchView();
15829
15830                 this.fireEvent('select', this, r, rowIndex);
15831
15832                 return;
15833             }
15834
15835             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15836                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15837                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15838                 return;
15839             }
15840
15841             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15842             this.addItem(r.data);
15843             this.tickItems.push(r.data);
15844         }
15845     },
15846     
15847     getAutoCreateNativeIOS : function()
15848     {
15849         var cfg = {
15850             cls: 'form-group' //input-group,
15851         };
15852         
15853         var combobox =  {
15854             tag: 'select',
15855             cls : 'roo-ios-select'
15856         };
15857         
15858         if (this.name) {
15859             combobox.name = this.name;
15860         }
15861         
15862         if (this.disabled) {
15863             combobox.disabled = true;
15864         }
15865         
15866         var settings = this;
15867         
15868         ['xs','sm','md','lg'].map(function(size){
15869             if (settings[size]) {
15870                 cfg.cls += ' col-' + size + '-' + settings[size];
15871             }
15872         });
15873         
15874         cfg.cn = combobox;
15875         
15876         return cfg;
15877         
15878     },
15879     
15880     initIOSView : function()
15881     {
15882         this.store.on('load', this.onIOSViewLoad, this);
15883         
15884         return;
15885     },
15886     
15887     onIOSViewLoad : function()
15888     {
15889         if(this.store.getCount() < 1){
15890             return;
15891         }
15892         
15893         this.clearIOSView();
15894         
15895         if(this.allowBlank) {
15896             
15897             var default_text = '-- SELECT --';
15898             
15899             if(this.placeholder.length){
15900                 default_text = this.placeholder;
15901             }
15902             
15903             if(this.emptyTitle.length){
15904                 default_text += ' - ' + this.emptyTitle + ' -';
15905             }
15906             
15907             var opt = this.inputEl().createChild({
15908                 tag: 'option',
15909                 value : 0,
15910                 html : default_text
15911             });
15912             
15913             var o = {};
15914             o[this.valueField] = 0;
15915             o[this.displayField] = default_text;
15916             
15917             this.ios_options.push({
15918                 data : o,
15919                 el : opt
15920             });
15921             
15922         }
15923         
15924         this.store.data.each(function(d, rowIndex){
15925             
15926             var html = '';
15927             
15928             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15929                 html = d.data[this.displayField];
15930             }
15931             
15932             var value = '';
15933             
15934             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15935                 value = d.data[this.valueField];
15936             }
15937             
15938             var option = {
15939                 tag: 'option',
15940                 value : value,
15941                 html : html
15942             };
15943             
15944             if(this.value == d.data[this.valueField]){
15945                 option['selected'] = true;
15946             }
15947             
15948             var opt = this.inputEl().createChild(option);
15949             
15950             this.ios_options.push({
15951                 data : d.data,
15952                 el : opt
15953             });
15954             
15955         }, this);
15956         
15957         this.inputEl().on('change', function(){
15958            this.fireEvent('select', this);
15959         }, this);
15960         
15961     },
15962     
15963     clearIOSView: function()
15964     {
15965         this.inputEl().dom.innerHTML = '';
15966         
15967         this.ios_options = [];
15968     },
15969     
15970     setIOSValue: function(v)
15971     {
15972         this.value = v;
15973         
15974         if(!this.ios_options){
15975             return;
15976         }
15977         
15978         Roo.each(this.ios_options, function(opts){
15979            
15980            opts.el.dom.removeAttribute('selected');
15981            
15982            if(opts.data[this.valueField] != v){
15983                return;
15984            }
15985            
15986            opts.el.dom.setAttribute('selected', true);
15987            
15988         }, this);
15989     }
15990
15991     /** 
15992     * @cfg {Boolean} grow 
15993     * @hide 
15994     */
15995     /** 
15996     * @cfg {Number} growMin 
15997     * @hide 
15998     */
15999     /** 
16000     * @cfg {Number} growMax 
16001     * @hide 
16002     */
16003     /**
16004      * @hide
16005      * @method autoSize
16006      */
16007 });
16008
16009 Roo.apply(Roo.bootstrap.ComboBox,  {
16010     
16011     header : {
16012         tag: 'div',
16013         cls: 'modal-header',
16014         cn: [
16015             {
16016                 tag: 'h4',
16017                 cls: 'modal-title'
16018             }
16019         ]
16020     },
16021     
16022     body : {
16023         tag: 'div',
16024         cls: 'modal-body',
16025         cn: [
16026             {
16027                 tag: 'ul',
16028                 cls: 'list-group'
16029             }
16030         ]
16031     },
16032     
16033     listItemRadio : {
16034         tag: 'li',
16035         cls: 'list-group-item',
16036         cn: [
16037             {
16038                 tag: 'span',
16039                 cls: 'roo-combobox-list-group-item-value'
16040             },
16041             {
16042                 tag: 'div',
16043                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16044                 cn: [
16045                     {
16046                         tag: 'input',
16047                         type: 'radio'
16048                     },
16049                     {
16050                         tag: 'label'
16051                     }
16052                 ]
16053             }
16054         ]
16055     },
16056     
16057     listItemCheckbox : {
16058         tag: 'li',
16059         cls: 'list-group-item',
16060         cn: [
16061             {
16062                 tag: 'span',
16063                 cls: 'roo-combobox-list-group-item-value'
16064             },
16065             {
16066                 tag: 'div',
16067                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16068                 cn: [
16069                     {
16070                         tag: 'input',
16071                         type: 'checkbox'
16072                     },
16073                     {
16074                         tag: 'label'
16075                     }
16076                 ]
16077             }
16078         ]
16079     },
16080     
16081     emptyResult : {
16082         tag: 'div',
16083         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16084     },
16085     
16086     footer : {
16087         tag: 'div',
16088         cls: 'modal-footer',
16089         cn: [
16090             {
16091                 tag: 'div',
16092                 cls: 'row',
16093                 cn: [
16094                     {
16095                         tag: 'div',
16096                         cls: 'col-xs-6 text-left',
16097                         cn: {
16098                             tag: 'button',
16099                             cls: 'btn btn-danger roo-touch-view-cancel',
16100                             html: 'Cancel'
16101                         }
16102                     },
16103                     {
16104                         tag: 'div',
16105                         cls: 'col-xs-6 text-right',
16106                         cn: {
16107                             tag: 'button',
16108                             cls: 'btn btn-success roo-touch-view-ok',
16109                             html: 'OK'
16110                         }
16111                     }
16112                 ]
16113             }
16114         ]
16115         
16116     }
16117 });
16118
16119 Roo.apply(Roo.bootstrap.ComboBox,  {
16120     
16121     touchViewTemplate : {
16122         tag: 'div',
16123         cls: 'modal fade roo-combobox-touch-view',
16124         cn: [
16125             {
16126                 tag: 'div',
16127                 cls: 'modal-dialog',
16128                 style : 'position:fixed', // we have to fix position....
16129                 cn: [
16130                     {
16131                         tag: 'div',
16132                         cls: 'modal-content',
16133                         cn: [
16134                             Roo.bootstrap.ComboBox.header,
16135                             Roo.bootstrap.ComboBox.body,
16136                             Roo.bootstrap.ComboBox.footer
16137                         ]
16138                     }
16139                 ]
16140             }
16141         ]
16142     }
16143 });/*
16144  * Based on:
16145  * Ext JS Library 1.1.1
16146  * Copyright(c) 2006-2007, Ext JS, LLC.
16147  *
16148  * Originally Released Under LGPL - original licence link has changed is not relivant.
16149  *
16150  * Fork - LGPL
16151  * <script type="text/javascript">
16152  */
16153
16154 /**
16155  * @class Roo.View
16156  * @extends Roo.util.Observable
16157  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16158  * This class also supports single and multi selection modes. <br>
16159  * Create a data model bound view:
16160  <pre><code>
16161  var store = new Roo.data.Store(...);
16162
16163  var view = new Roo.View({
16164     el : "my-element",
16165     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16166  
16167     singleSelect: true,
16168     selectedClass: "ydataview-selected",
16169     store: store
16170  });
16171
16172  // listen for node click?
16173  view.on("click", function(vw, index, node, e){
16174  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16175  });
16176
16177  // load XML data
16178  dataModel.load("foobar.xml");
16179  </code></pre>
16180  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16181  * <br><br>
16182  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16183  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16184  * 
16185  * Note: old style constructor is still suported (container, template, config)
16186  * 
16187  * @constructor
16188  * Create a new View
16189  * @param {Object} config The config object
16190  * 
16191  */
16192 Roo.View = function(config, depreciated_tpl, depreciated_config){
16193     
16194     this.parent = false;
16195     
16196     if (typeof(depreciated_tpl) == 'undefined') {
16197         // new way.. - universal constructor.
16198         Roo.apply(this, config);
16199         this.el  = Roo.get(this.el);
16200     } else {
16201         // old format..
16202         this.el  = Roo.get(config);
16203         this.tpl = depreciated_tpl;
16204         Roo.apply(this, depreciated_config);
16205     }
16206     this.wrapEl  = this.el.wrap().wrap();
16207     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16208     
16209     
16210     if(typeof(this.tpl) == "string"){
16211         this.tpl = new Roo.Template(this.tpl);
16212     } else {
16213         // support xtype ctors..
16214         this.tpl = new Roo.factory(this.tpl, Roo);
16215     }
16216     
16217     
16218     this.tpl.compile();
16219     
16220     /** @private */
16221     this.addEvents({
16222         /**
16223          * @event beforeclick
16224          * Fires before a click is processed. Returns false to cancel the default action.
16225          * @param {Roo.View} this
16226          * @param {Number} index The index of the target node
16227          * @param {HTMLElement} node The target node
16228          * @param {Roo.EventObject} e The raw event object
16229          */
16230             "beforeclick" : true,
16231         /**
16232          * @event click
16233          * Fires when a template node is clicked.
16234          * @param {Roo.View} this
16235          * @param {Number} index The index of the target node
16236          * @param {HTMLElement} node The target node
16237          * @param {Roo.EventObject} e The raw event object
16238          */
16239             "click" : true,
16240         /**
16241          * @event dblclick
16242          * Fires when a template node is double clicked.
16243          * @param {Roo.View} this
16244          * @param {Number} index The index of the target node
16245          * @param {HTMLElement} node The target node
16246          * @param {Roo.EventObject} e The raw event object
16247          */
16248             "dblclick" : true,
16249         /**
16250          * @event contextmenu
16251          * Fires when a template node is right clicked.
16252          * @param {Roo.View} this
16253          * @param {Number} index The index of the target node
16254          * @param {HTMLElement} node The target node
16255          * @param {Roo.EventObject} e The raw event object
16256          */
16257             "contextmenu" : true,
16258         /**
16259          * @event selectionchange
16260          * Fires when the selected nodes change.
16261          * @param {Roo.View} this
16262          * @param {Array} selections Array of the selected nodes
16263          */
16264             "selectionchange" : true,
16265     
16266         /**
16267          * @event beforeselect
16268          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16269          * @param {Roo.View} this
16270          * @param {HTMLElement} node The node to be selected
16271          * @param {Array} selections Array of currently selected nodes
16272          */
16273             "beforeselect" : true,
16274         /**
16275          * @event preparedata
16276          * Fires on every row to render, to allow you to change the data.
16277          * @param {Roo.View} this
16278          * @param {Object} data to be rendered (change this)
16279          */
16280           "preparedata" : true
16281           
16282           
16283         });
16284
16285
16286
16287     this.el.on({
16288         "click": this.onClick,
16289         "dblclick": this.onDblClick,
16290         "contextmenu": this.onContextMenu,
16291         scope:this
16292     });
16293
16294     this.selections = [];
16295     this.nodes = [];
16296     this.cmp = new Roo.CompositeElementLite([]);
16297     if(this.store){
16298         this.store = Roo.factory(this.store, Roo.data);
16299         this.setStore(this.store, true);
16300     }
16301     
16302     if ( this.footer && this.footer.xtype) {
16303            
16304          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16305         
16306         this.footer.dataSource = this.store;
16307         this.footer.container = fctr;
16308         this.footer = Roo.factory(this.footer, Roo);
16309         fctr.insertFirst(this.el);
16310         
16311         // this is a bit insane - as the paging toolbar seems to detach the el..
16312 //        dom.parentNode.parentNode.parentNode
16313          // they get detached?
16314     }
16315     
16316     
16317     Roo.View.superclass.constructor.call(this);
16318     
16319     
16320 };
16321
16322 Roo.extend(Roo.View, Roo.util.Observable, {
16323     
16324      /**
16325      * @cfg {Roo.data.Store} store Data store to load data from.
16326      */
16327     store : false,
16328     
16329     /**
16330      * @cfg {String|Roo.Element} el The container element.
16331      */
16332     el : '',
16333     
16334     /**
16335      * @cfg {String|Roo.Template} tpl The template used by this View 
16336      */
16337     tpl : false,
16338     /**
16339      * @cfg {String} dataName the named area of the template to use as the data area
16340      *                          Works with domtemplates roo-name="name"
16341      */
16342     dataName: false,
16343     /**
16344      * @cfg {String} selectedClass The css class to add to selected nodes
16345      */
16346     selectedClass : "x-view-selected",
16347      /**
16348      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16349      */
16350     emptyText : "",
16351     
16352     /**
16353      * @cfg {String} text to display on mask (default Loading)
16354      */
16355     mask : false,
16356     /**
16357      * @cfg {Boolean} multiSelect Allow multiple selection
16358      */
16359     multiSelect : false,
16360     /**
16361      * @cfg {Boolean} singleSelect Allow single selection
16362      */
16363     singleSelect:  false,
16364     
16365     /**
16366      * @cfg {Boolean} toggleSelect - selecting 
16367      */
16368     toggleSelect : false,
16369     
16370     /**
16371      * @cfg {Boolean} tickable - selecting 
16372      */
16373     tickable : false,
16374     
16375     /**
16376      * Returns the element this view is bound to.
16377      * @return {Roo.Element}
16378      */
16379     getEl : function(){
16380         return this.wrapEl;
16381     },
16382     
16383     
16384
16385     /**
16386      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16387      */
16388     refresh : function(){
16389         //Roo.log('refresh');
16390         var t = this.tpl;
16391         
16392         // if we are using something like 'domtemplate', then
16393         // the what gets used is:
16394         // t.applySubtemplate(NAME, data, wrapping data..)
16395         // the outer template then get' applied with
16396         //     the store 'extra data'
16397         // and the body get's added to the
16398         //      roo-name="data" node?
16399         //      <span class='roo-tpl-{name}'></span> ?????
16400         
16401         
16402         
16403         this.clearSelections();
16404         this.el.update("");
16405         var html = [];
16406         var records = this.store.getRange();
16407         if(records.length < 1) {
16408             
16409             // is this valid??  = should it render a template??
16410             
16411             this.el.update(this.emptyText);
16412             return;
16413         }
16414         var el = this.el;
16415         if (this.dataName) {
16416             this.el.update(t.apply(this.store.meta)); //????
16417             el = this.el.child('.roo-tpl-' + this.dataName);
16418         }
16419         
16420         for(var i = 0, len = records.length; i < len; i++){
16421             var data = this.prepareData(records[i].data, i, records[i]);
16422             this.fireEvent("preparedata", this, data, i, records[i]);
16423             
16424             var d = Roo.apply({}, data);
16425             
16426             if(this.tickable){
16427                 Roo.apply(d, {'roo-id' : Roo.id()});
16428                 
16429                 var _this = this;
16430             
16431                 Roo.each(this.parent.item, function(item){
16432                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16433                         return;
16434                     }
16435                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16436                 });
16437             }
16438             
16439             html[html.length] = Roo.util.Format.trim(
16440                 this.dataName ?
16441                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16442                     t.apply(d)
16443             );
16444         }
16445         
16446         
16447         
16448         el.update(html.join(""));
16449         this.nodes = el.dom.childNodes;
16450         this.updateIndexes(0);
16451     },
16452     
16453
16454     /**
16455      * Function to override to reformat the data that is sent to
16456      * the template for each node.
16457      * DEPRICATED - use the preparedata event handler.
16458      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16459      * a JSON object for an UpdateManager bound view).
16460      */
16461     prepareData : function(data, index, record)
16462     {
16463         this.fireEvent("preparedata", this, data, index, record);
16464         return data;
16465     },
16466
16467     onUpdate : function(ds, record){
16468         // Roo.log('on update');   
16469         this.clearSelections();
16470         var index = this.store.indexOf(record);
16471         var n = this.nodes[index];
16472         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16473         n.parentNode.removeChild(n);
16474         this.updateIndexes(index, index);
16475     },
16476
16477     
16478     
16479 // --------- FIXME     
16480     onAdd : function(ds, records, index)
16481     {
16482         //Roo.log(['on Add', ds, records, index] );        
16483         this.clearSelections();
16484         if(this.nodes.length == 0){
16485             this.refresh();
16486             return;
16487         }
16488         var n = this.nodes[index];
16489         for(var i = 0, len = records.length; i < len; i++){
16490             var d = this.prepareData(records[i].data, i, records[i]);
16491             if(n){
16492                 this.tpl.insertBefore(n, d);
16493             }else{
16494                 
16495                 this.tpl.append(this.el, d);
16496             }
16497         }
16498         this.updateIndexes(index);
16499     },
16500
16501     onRemove : function(ds, record, index){
16502        // Roo.log('onRemove');
16503         this.clearSelections();
16504         var el = this.dataName  ?
16505             this.el.child('.roo-tpl-' + this.dataName) :
16506             this.el; 
16507         
16508         el.dom.removeChild(this.nodes[index]);
16509         this.updateIndexes(index);
16510     },
16511
16512     /**
16513      * Refresh an individual node.
16514      * @param {Number} index
16515      */
16516     refreshNode : function(index){
16517         this.onUpdate(this.store, this.store.getAt(index));
16518     },
16519
16520     updateIndexes : function(startIndex, endIndex){
16521         var ns = this.nodes;
16522         startIndex = startIndex || 0;
16523         endIndex = endIndex || ns.length - 1;
16524         for(var i = startIndex; i <= endIndex; i++){
16525             ns[i].nodeIndex = i;
16526         }
16527     },
16528
16529     /**
16530      * Changes the data store this view uses and refresh the view.
16531      * @param {Store} store
16532      */
16533     setStore : function(store, initial){
16534         if(!initial && this.store){
16535             this.store.un("datachanged", this.refresh);
16536             this.store.un("add", this.onAdd);
16537             this.store.un("remove", this.onRemove);
16538             this.store.un("update", this.onUpdate);
16539             this.store.un("clear", this.refresh);
16540             this.store.un("beforeload", this.onBeforeLoad);
16541             this.store.un("load", this.onLoad);
16542             this.store.un("loadexception", this.onLoad);
16543         }
16544         if(store){
16545           
16546             store.on("datachanged", this.refresh, this);
16547             store.on("add", this.onAdd, this);
16548             store.on("remove", this.onRemove, this);
16549             store.on("update", this.onUpdate, this);
16550             store.on("clear", this.refresh, this);
16551             store.on("beforeload", this.onBeforeLoad, this);
16552             store.on("load", this.onLoad, this);
16553             store.on("loadexception", this.onLoad, this);
16554         }
16555         
16556         if(store){
16557             this.refresh();
16558         }
16559     },
16560     /**
16561      * onbeforeLoad - masks the loading area.
16562      *
16563      */
16564     onBeforeLoad : function(store,opts)
16565     {
16566          //Roo.log('onBeforeLoad');   
16567         if (!opts.add) {
16568             this.el.update("");
16569         }
16570         this.el.mask(this.mask ? this.mask : "Loading" ); 
16571     },
16572     onLoad : function ()
16573     {
16574         this.el.unmask();
16575     },
16576     
16577
16578     /**
16579      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16580      * @param {HTMLElement} node
16581      * @return {HTMLElement} The template node
16582      */
16583     findItemFromChild : function(node){
16584         var el = this.dataName  ?
16585             this.el.child('.roo-tpl-' + this.dataName,true) :
16586             this.el.dom; 
16587         
16588         if(!node || node.parentNode == el){
16589                     return node;
16590             }
16591             var p = node.parentNode;
16592             while(p && p != el){
16593             if(p.parentNode == el){
16594                 return p;
16595             }
16596             p = p.parentNode;
16597         }
16598             return null;
16599     },
16600
16601     /** @ignore */
16602     onClick : function(e){
16603         var item = this.findItemFromChild(e.getTarget());
16604         if(item){
16605             var index = this.indexOf(item);
16606             if(this.onItemClick(item, index, e) !== false){
16607                 this.fireEvent("click", this, index, item, e);
16608             }
16609         }else{
16610             this.clearSelections();
16611         }
16612     },
16613
16614     /** @ignore */
16615     onContextMenu : function(e){
16616         var item = this.findItemFromChild(e.getTarget());
16617         if(item){
16618             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16619         }
16620     },
16621
16622     /** @ignore */
16623     onDblClick : function(e){
16624         var item = this.findItemFromChild(e.getTarget());
16625         if(item){
16626             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16627         }
16628     },
16629
16630     onItemClick : function(item, index, e)
16631     {
16632         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16633             return false;
16634         }
16635         if (this.toggleSelect) {
16636             var m = this.isSelected(item) ? 'unselect' : 'select';
16637             //Roo.log(m);
16638             var _t = this;
16639             _t[m](item, true, false);
16640             return true;
16641         }
16642         if(this.multiSelect || this.singleSelect){
16643             if(this.multiSelect && e.shiftKey && this.lastSelection){
16644                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16645             }else{
16646                 this.select(item, this.multiSelect && e.ctrlKey);
16647                 this.lastSelection = item;
16648             }
16649             
16650             if(!this.tickable){
16651                 e.preventDefault();
16652             }
16653             
16654         }
16655         return true;
16656     },
16657
16658     /**
16659      * Get the number of selected nodes.
16660      * @return {Number}
16661      */
16662     getSelectionCount : function(){
16663         return this.selections.length;
16664     },
16665
16666     /**
16667      * Get the currently selected nodes.
16668      * @return {Array} An array of HTMLElements
16669      */
16670     getSelectedNodes : function(){
16671         return this.selections;
16672     },
16673
16674     /**
16675      * Get the indexes of the selected nodes.
16676      * @return {Array}
16677      */
16678     getSelectedIndexes : function(){
16679         var indexes = [], s = this.selections;
16680         for(var i = 0, len = s.length; i < len; i++){
16681             indexes.push(s[i].nodeIndex);
16682         }
16683         return indexes;
16684     },
16685
16686     /**
16687      * Clear all selections
16688      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16689      */
16690     clearSelections : function(suppressEvent){
16691         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16692             this.cmp.elements = this.selections;
16693             this.cmp.removeClass(this.selectedClass);
16694             this.selections = [];
16695             if(!suppressEvent){
16696                 this.fireEvent("selectionchange", this, this.selections);
16697             }
16698         }
16699     },
16700
16701     /**
16702      * Returns true if the passed node is selected
16703      * @param {HTMLElement/Number} node The node or node index
16704      * @return {Boolean}
16705      */
16706     isSelected : function(node){
16707         var s = this.selections;
16708         if(s.length < 1){
16709             return false;
16710         }
16711         node = this.getNode(node);
16712         return s.indexOf(node) !== -1;
16713     },
16714
16715     /**
16716      * Selects nodes.
16717      * @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
16718      * @param {Boolean} keepExisting (optional) true to keep existing selections
16719      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16720      */
16721     select : function(nodeInfo, keepExisting, suppressEvent){
16722         if(nodeInfo instanceof Array){
16723             if(!keepExisting){
16724                 this.clearSelections(true);
16725             }
16726             for(var i = 0, len = nodeInfo.length; i < len; i++){
16727                 this.select(nodeInfo[i], true, true);
16728             }
16729             return;
16730         } 
16731         var node = this.getNode(nodeInfo);
16732         if(!node || this.isSelected(node)){
16733             return; // already selected.
16734         }
16735         if(!keepExisting){
16736             this.clearSelections(true);
16737         }
16738         
16739         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16740             Roo.fly(node).addClass(this.selectedClass);
16741             this.selections.push(node);
16742             if(!suppressEvent){
16743                 this.fireEvent("selectionchange", this, this.selections);
16744             }
16745         }
16746         
16747         
16748     },
16749       /**
16750      * Unselects nodes.
16751      * @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
16752      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16753      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16754      */
16755     unselect : function(nodeInfo, keepExisting, suppressEvent)
16756     {
16757         if(nodeInfo instanceof Array){
16758             Roo.each(this.selections, function(s) {
16759                 this.unselect(s, nodeInfo);
16760             }, this);
16761             return;
16762         }
16763         var node = this.getNode(nodeInfo);
16764         if(!node || !this.isSelected(node)){
16765             //Roo.log("not selected");
16766             return; // not selected.
16767         }
16768         // fireevent???
16769         var ns = [];
16770         Roo.each(this.selections, function(s) {
16771             if (s == node ) {
16772                 Roo.fly(node).removeClass(this.selectedClass);
16773
16774                 return;
16775             }
16776             ns.push(s);
16777         },this);
16778         
16779         this.selections= ns;
16780         this.fireEvent("selectionchange", this, this.selections);
16781     },
16782
16783     /**
16784      * Gets a template node.
16785      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16786      * @return {HTMLElement} The node or null if it wasn't found
16787      */
16788     getNode : function(nodeInfo){
16789         if(typeof nodeInfo == "string"){
16790             return document.getElementById(nodeInfo);
16791         }else if(typeof nodeInfo == "number"){
16792             return this.nodes[nodeInfo];
16793         }
16794         return nodeInfo;
16795     },
16796
16797     /**
16798      * Gets a range template nodes.
16799      * @param {Number} startIndex
16800      * @param {Number} endIndex
16801      * @return {Array} An array of nodes
16802      */
16803     getNodes : function(start, end){
16804         var ns = this.nodes;
16805         start = start || 0;
16806         end = typeof end == "undefined" ? ns.length - 1 : end;
16807         var nodes = [];
16808         if(start <= end){
16809             for(var i = start; i <= end; i++){
16810                 nodes.push(ns[i]);
16811             }
16812         } else{
16813             for(var i = start; i >= end; i--){
16814                 nodes.push(ns[i]);
16815             }
16816         }
16817         return nodes;
16818     },
16819
16820     /**
16821      * Finds the index of the passed node
16822      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16823      * @return {Number} The index of the node or -1
16824      */
16825     indexOf : function(node){
16826         node = this.getNode(node);
16827         if(typeof node.nodeIndex == "number"){
16828             return node.nodeIndex;
16829         }
16830         var ns = this.nodes;
16831         for(var i = 0, len = ns.length; i < len; i++){
16832             if(ns[i] == node){
16833                 return i;
16834             }
16835         }
16836         return -1;
16837     }
16838 });
16839 /*
16840  * - LGPL
16841  *
16842  * based on jquery fullcalendar
16843  * 
16844  */
16845
16846 Roo.bootstrap = Roo.bootstrap || {};
16847 /**
16848  * @class Roo.bootstrap.Calendar
16849  * @extends Roo.bootstrap.Component
16850  * Bootstrap Calendar class
16851  * @cfg {Boolean} loadMask (true|false) default false
16852  * @cfg {Object} header generate the user specific header of the calendar, default false
16853
16854  * @constructor
16855  * Create a new Container
16856  * @param {Object} config The config object
16857  */
16858
16859
16860
16861 Roo.bootstrap.Calendar = function(config){
16862     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16863      this.addEvents({
16864         /**
16865              * @event select
16866              * Fires when a date is selected
16867              * @param {DatePicker} this
16868              * @param {Date} date The selected date
16869              */
16870         'select': true,
16871         /**
16872              * @event monthchange
16873              * Fires when the displayed month changes 
16874              * @param {DatePicker} this
16875              * @param {Date} date The selected month
16876              */
16877         'monthchange': true,
16878         /**
16879              * @event evententer
16880              * Fires when mouse over an event
16881              * @param {Calendar} this
16882              * @param {event} Event
16883              */
16884         'evententer': true,
16885         /**
16886              * @event eventleave
16887              * Fires when the mouse leaves an
16888              * @param {Calendar} this
16889              * @param {event}
16890              */
16891         'eventleave': true,
16892         /**
16893              * @event eventclick
16894              * Fires when the mouse click an
16895              * @param {Calendar} this
16896              * @param {event}
16897              */
16898         'eventclick': true
16899         
16900     });
16901
16902 };
16903
16904 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16905     
16906      /**
16907      * @cfg {Number} startDay
16908      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16909      */
16910     startDay : 0,
16911     
16912     loadMask : false,
16913     
16914     header : false,
16915       
16916     getAutoCreate : function(){
16917         
16918         
16919         var fc_button = function(name, corner, style, content ) {
16920             return Roo.apply({},{
16921                 tag : 'span',
16922                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16923                          (corner.length ?
16924                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16925                             ''
16926                         ),
16927                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16928                 unselectable: 'on'
16929             });
16930         };
16931         
16932         var header = {};
16933         
16934         if(!this.header){
16935             header = {
16936                 tag : 'table',
16937                 cls : 'fc-header',
16938                 style : 'width:100%',
16939                 cn : [
16940                     {
16941                         tag: 'tr',
16942                         cn : [
16943                             {
16944                                 tag : 'td',
16945                                 cls : 'fc-header-left',
16946                                 cn : [
16947                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16948                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16949                                     { tag: 'span', cls: 'fc-header-space' },
16950                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16951
16952
16953                                 ]
16954                             },
16955
16956                             {
16957                                 tag : 'td',
16958                                 cls : 'fc-header-center',
16959                                 cn : [
16960                                     {
16961                                         tag: 'span',
16962                                         cls: 'fc-header-title',
16963                                         cn : {
16964                                             tag: 'H2',
16965                                             html : 'month / year'
16966                                         }
16967                                     }
16968
16969                                 ]
16970                             },
16971                             {
16972                                 tag : 'td',
16973                                 cls : 'fc-header-right',
16974                                 cn : [
16975                               /*      fc_button('month', 'left', '', 'month' ),
16976                                     fc_button('week', '', '', 'week' ),
16977                                     fc_button('day', 'right', '', 'day' )
16978                                 */    
16979
16980                                 ]
16981                             }
16982
16983                         ]
16984                     }
16985                 ]
16986             };
16987         }
16988         
16989         header = this.header;
16990         
16991        
16992         var cal_heads = function() {
16993             var ret = [];
16994             // fixme - handle this.
16995             
16996             for (var i =0; i < Date.dayNames.length; i++) {
16997                 var d = Date.dayNames[i];
16998                 ret.push({
16999                     tag: 'th',
17000                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17001                     html : d.substring(0,3)
17002                 });
17003                 
17004             }
17005             ret[0].cls += ' fc-first';
17006             ret[6].cls += ' fc-last';
17007             return ret;
17008         };
17009         var cal_cell = function(n) {
17010             return  {
17011                 tag: 'td',
17012                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17013                 cn : [
17014                     {
17015                         cn : [
17016                             {
17017                                 cls: 'fc-day-number',
17018                                 html: 'D'
17019                             },
17020                             {
17021                                 cls: 'fc-day-content',
17022                              
17023                                 cn : [
17024                                      {
17025                                         style: 'position: relative;' // height: 17px;
17026                                     }
17027                                 ]
17028                             }
17029                             
17030                             
17031                         ]
17032                     }
17033                 ]
17034                 
17035             }
17036         };
17037         var cal_rows = function() {
17038             
17039             var ret = [];
17040             for (var r = 0; r < 6; r++) {
17041                 var row= {
17042                     tag : 'tr',
17043                     cls : 'fc-week',
17044                     cn : []
17045                 };
17046                 
17047                 for (var i =0; i < Date.dayNames.length; i++) {
17048                     var d = Date.dayNames[i];
17049                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17050
17051                 }
17052                 row.cn[0].cls+=' fc-first';
17053                 row.cn[0].cn[0].style = 'min-height:90px';
17054                 row.cn[6].cls+=' fc-last';
17055                 ret.push(row);
17056                 
17057             }
17058             ret[0].cls += ' fc-first';
17059             ret[4].cls += ' fc-prev-last';
17060             ret[5].cls += ' fc-last';
17061             return ret;
17062             
17063         };
17064         
17065         var cal_table = {
17066             tag: 'table',
17067             cls: 'fc-border-separate',
17068             style : 'width:100%',
17069             cellspacing  : 0,
17070             cn : [
17071                 { 
17072                     tag: 'thead',
17073                     cn : [
17074                         { 
17075                             tag: 'tr',
17076                             cls : 'fc-first fc-last',
17077                             cn : cal_heads()
17078                         }
17079                     ]
17080                 },
17081                 { 
17082                     tag: 'tbody',
17083                     cn : cal_rows()
17084                 }
17085                   
17086             ]
17087         };
17088          
17089          var cfg = {
17090             cls : 'fc fc-ltr',
17091             cn : [
17092                 header,
17093                 {
17094                     cls : 'fc-content',
17095                     style : "position: relative;",
17096                     cn : [
17097                         {
17098                             cls : 'fc-view fc-view-month fc-grid',
17099                             style : 'position: relative',
17100                             unselectable : 'on',
17101                             cn : [
17102                                 {
17103                                     cls : 'fc-event-container',
17104                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17105                                 },
17106                                 cal_table
17107                             ]
17108                         }
17109                     ]
17110     
17111                 }
17112            ] 
17113             
17114         };
17115         
17116          
17117         
17118         return cfg;
17119     },
17120     
17121     
17122     initEvents : function()
17123     {
17124         if(!this.store){
17125             throw "can not find store for calendar";
17126         }
17127         
17128         var mark = {
17129             tag: "div",
17130             cls:"x-dlg-mask",
17131             style: "text-align:center",
17132             cn: [
17133                 {
17134                     tag: "div",
17135                     style: "background-color:white;width:50%;margin:250 auto",
17136                     cn: [
17137                         {
17138                             tag: "img",
17139                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17140                         },
17141                         {
17142                             tag: "span",
17143                             html: "Loading"
17144                         }
17145                         
17146                     ]
17147                 }
17148             ]
17149         };
17150         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17151         
17152         var size = this.el.select('.fc-content', true).first().getSize();
17153         this.maskEl.setSize(size.width, size.height);
17154         this.maskEl.enableDisplayMode("block");
17155         if(!this.loadMask){
17156             this.maskEl.hide();
17157         }
17158         
17159         this.store = Roo.factory(this.store, Roo.data);
17160         this.store.on('load', this.onLoad, this);
17161         this.store.on('beforeload', this.onBeforeLoad, this);
17162         
17163         this.resize();
17164         
17165         this.cells = this.el.select('.fc-day',true);
17166         //Roo.log(this.cells);
17167         this.textNodes = this.el.query('.fc-day-number');
17168         this.cells.addClassOnOver('fc-state-hover');
17169         
17170         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17171         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17172         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17173         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17174         
17175         this.on('monthchange', this.onMonthChange, this);
17176         
17177         this.update(new Date().clearTime());
17178     },
17179     
17180     resize : function() {
17181         var sz  = this.el.getSize();
17182         
17183         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17184         this.el.select('.fc-day-content div',true).setHeight(34);
17185     },
17186     
17187     
17188     // private
17189     showPrevMonth : function(e){
17190         this.update(this.activeDate.add("mo", -1));
17191     },
17192     showToday : function(e){
17193         this.update(new Date().clearTime());
17194     },
17195     // private
17196     showNextMonth : function(e){
17197         this.update(this.activeDate.add("mo", 1));
17198     },
17199
17200     // private
17201     showPrevYear : function(){
17202         this.update(this.activeDate.add("y", -1));
17203     },
17204
17205     // private
17206     showNextYear : function(){
17207         this.update(this.activeDate.add("y", 1));
17208     },
17209
17210     
17211    // private
17212     update : function(date)
17213     {
17214         var vd = this.activeDate;
17215         this.activeDate = date;
17216 //        if(vd && this.el){
17217 //            var t = date.getTime();
17218 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17219 //                Roo.log('using add remove');
17220 //                
17221 //                this.fireEvent('monthchange', this, date);
17222 //                
17223 //                this.cells.removeClass("fc-state-highlight");
17224 //                this.cells.each(function(c){
17225 //                   if(c.dateValue == t){
17226 //                       c.addClass("fc-state-highlight");
17227 //                       setTimeout(function(){
17228 //                            try{c.dom.firstChild.focus();}catch(e){}
17229 //                       }, 50);
17230 //                       return false;
17231 //                   }
17232 //                   return true;
17233 //                });
17234 //                return;
17235 //            }
17236 //        }
17237         
17238         var days = date.getDaysInMonth();
17239         
17240         var firstOfMonth = date.getFirstDateOfMonth();
17241         var startingPos = firstOfMonth.getDay()-this.startDay;
17242         
17243         if(startingPos < this.startDay){
17244             startingPos += 7;
17245         }
17246         
17247         var pm = date.add(Date.MONTH, -1);
17248         var prevStart = pm.getDaysInMonth()-startingPos;
17249 //        
17250         this.cells = this.el.select('.fc-day',true);
17251         this.textNodes = this.el.query('.fc-day-number');
17252         this.cells.addClassOnOver('fc-state-hover');
17253         
17254         var cells = this.cells.elements;
17255         var textEls = this.textNodes;
17256         
17257         Roo.each(cells, function(cell){
17258             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17259         });
17260         
17261         days += startingPos;
17262
17263         // convert everything to numbers so it's fast
17264         var day = 86400000;
17265         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17266         //Roo.log(d);
17267         //Roo.log(pm);
17268         //Roo.log(prevStart);
17269         
17270         var today = new Date().clearTime().getTime();
17271         var sel = date.clearTime().getTime();
17272         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17273         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17274         var ddMatch = this.disabledDatesRE;
17275         var ddText = this.disabledDatesText;
17276         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17277         var ddaysText = this.disabledDaysText;
17278         var format = this.format;
17279         
17280         var setCellClass = function(cal, cell){
17281             cell.row = 0;
17282             cell.events = [];
17283             cell.more = [];
17284             //Roo.log('set Cell Class');
17285             cell.title = "";
17286             var t = d.getTime();
17287             
17288             //Roo.log(d);
17289             
17290             cell.dateValue = t;
17291             if(t == today){
17292                 cell.className += " fc-today";
17293                 cell.className += " fc-state-highlight";
17294                 cell.title = cal.todayText;
17295             }
17296             if(t == sel){
17297                 // disable highlight in other month..
17298                 //cell.className += " fc-state-highlight";
17299                 
17300             }
17301             // disabling
17302             if(t < min) {
17303                 cell.className = " fc-state-disabled";
17304                 cell.title = cal.minText;
17305                 return;
17306             }
17307             if(t > max) {
17308                 cell.className = " fc-state-disabled";
17309                 cell.title = cal.maxText;
17310                 return;
17311             }
17312             if(ddays){
17313                 if(ddays.indexOf(d.getDay()) != -1){
17314                     cell.title = ddaysText;
17315                     cell.className = " fc-state-disabled";
17316                 }
17317             }
17318             if(ddMatch && format){
17319                 var fvalue = d.dateFormat(format);
17320                 if(ddMatch.test(fvalue)){
17321                     cell.title = ddText.replace("%0", fvalue);
17322                     cell.className = " fc-state-disabled";
17323                 }
17324             }
17325             
17326             if (!cell.initialClassName) {
17327                 cell.initialClassName = cell.dom.className;
17328             }
17329             
17330             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17331         };
17332
17333         var i = 0;
17334         
17335         for(; i < startingPos; i++) {
17336             textEls[i].innerHTML = (++prevStart);
17337             d.setDate(d.getDate()+1);
17338             
17339             cells[i].className = "fc-past fc-other-month";
17340             setCellClass(this, cells[i]);
17341         }
17342         
17343         var intDay = 0;
17344         
17345         for(; i < days; i++){
17346             intDay = i - startingPos + 1;
17347             textEls[i].innerHTML = (intDay);
17348             d.setDate(d.getDate()+1);
17349             
17350             cells[i].className = ''; // "x-date-active";
17351             setCellClass(this, cells[i]);
17352         }
17353         var extraDays = 0;
17354         
17355         for(; i < 42; i++) {
17356             textEls[i].innerHTML = (++extraDays);
17357             d.setDate(d.getDate()+1);
17358             
17359             cells[i].className = "fc-future fc-other-month";
17360             setCellClass(this, cells[i]);
17361         }
17362         
17363         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17364         
17365         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17366         
17367         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17368         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17369         
17370         if(totalRows != 6){
17371             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17372             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17373         }
17374         
17375         this.fireEvent('monthchange', this, date);
17376         
17377         
17378         /*
17379         if(!this.internalRender){
17380             var main = this.el.dom.firstChild;
17381             var w = main.offsetWidth;
17382             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17383             Roo.fly(main).setWidth(w);
17384             this.internalRender = true;
17385             // opera does not respect the auto grow header center column
17386             // then, after it gets a width opera refuses to recalculate
17387             // without a second pass
17388             if(Roo.isOpera && !this.secondPass){
17389                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17390                 this.secondPass = true;
17391                 this.update.defer(10, this, [date]);
17392             }
17393         }
17394         */
17395         
17396     },
17397     
17398     findCell : function(dt) {
17399         dt = dt.clearTime().getTime();
17400         var ret = false;
17401         this.cells.each(function(c){
17402             //Roo.log("check " +c.dateValue + '?=' + dt);
17403             if(c.dateValue == dt){
17404                 ret = c;
17405                 return false;
17406             }
17407             return true;
17408         });
17409         
17410         return ret;
17411     },
17412     
17413     findCells : function(ev) {
17414         var s = ev.start.clone().clearTime().getTime();
17415        // Roo.log(s);
17416         var e= ev.end.clone().clearTime().getTime();
17417        // Roo.log(e);
17418         var ret = [];
17419         this.cells.each(function(c){
17420              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17421             
17422             if(c.dateValue > e){
17423                 return ;
17424             }
17425             if(c.dateValue < s){
17426                 return ;
17427             }
17428             ret.push(c);
17429         });
17430         
17431         return ret;    
17432     },
17433     
17434 //    findBestRow: function(cells)
17435 //    {
17436 //        var ret = 0;
17437 //        
17438 //        for (var i =0 ; i < cells.length;i++) {
17439 //            ret  = Math.max(cells[i].rows || 0,ret);
17440 //        }
17441 //        return ret;
17442 //        
17443 //    },
17444     
17445     
17446     addItem : function(ev)
17447     {
17448         // look for vertical location slot in
17449         var cells = this.findCells(ev);
17450         
17451 //        ev.row = this.findBestRow(cells);
17452         
17453         // work out the location.
17454         
17455         var crow = false;
17456         var rows = [];
17457         for(var i =0; i < cells.length; i++) {
17458             
17459             cells[i].row = cells[0].row;
17460             
17461             if(i == 0){
17462                 cells[i].row = cells[i].row + 1;
17463             }
17464             
17465             if (!crow) {
17466                 crow = {
17467                     start : cells[i],
17468                     end :  cells[i]
17469                 };
17470                 continue;
17471             }
17472             if (crow.start.getY() == cells[i].getY()) {
17473                 // on same row.
17474                 crow.end = cells[i];
17475                 continue;
17476             }
17477             // different row.
17478             rows.push(crow);
17479             crow = {
17480                 start: cells[i],
17481                 end : cells[i]
17482             };
17483             
17484         }
17485         
17486         rows.push(crow);
17487         ev.els = [];
17488         ev.rows = rows;
17489         ev.cells = cells;
17490         
17491         cells[0].events.push(ev);
17492         
17493         this.calevents.push(ev);
17494     },
17495     
17496     clearEvents: function() {
17497         
17498         if(!this.calevents){
17499             return;
17500         }
17501         
17502         Roo.each(this.cells.elements, function(c){
17503             c.row = 0;
17504             c.events = [];
17505             c.more = [];
17506         });
17507         
17508         Roo.each(this.calevents, function(e) {
17509             Roo.each(e.els, function(el) {
17510                 el.un('mouseenter' ,this.onEventEnter, this);
17511                 el.un('mouseleave' ,this.onEventLeave, this);
17512                 el.remove();
17513             },this);
17514         },this);
17515         
17516         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17517             e.remove();
17518         });
17519         
17520     },
17521     
17522     renderEvents: function()
17523     {   
17524         var _this = this;
17525         
17526         this.cells.each(function(c) {
17527             
17528             if(c.row < 5){
17529                 return;
17530             }
17531             
17532             var ev = c.events;
17533             
17534             var r = 4;
17535             if(c.row != c.events.length){
17536                 r = 4 - (4 - (c.row - c.events.length));
17537             }
17538             
17539             c.events = ev.slice(0, r);
17540             c.more = ev.slice(r);
17541             
17542             if(c.more.length && c.more.length == 1){
17543                 c.events.push(c.more.pop());
17544             }
17545             
17546             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17547             
17548         });
17549             
17550         this.cells.each(function(c) {
17551             
17552             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17553             
17554             
17555             for (var e = 0; e < c.events.length; e++){
17556                 var ev = c.events[e];
17557                 var rows = ev.rows;
17558                 
17559                 for(var i = 0; i < rows.length; i++) {
17560                 
17561                     // how many rows should it span..
17562
17563                     var  cfg = {
17564                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17565                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17566
17567                         unselectable : "on",
17568                         cn : [
17569                             {
17570                                 cls: 'fc-event-inner',
17571                                 cn : [
17572     //                                {
17573     //                                  tag:'span',
17574     //                                  cls: 'fc-event-time',
17575     //                                  html : cells.length > 1 ? '' : ev.time
17576     //                                },
17577                                     {
17578                                       tag:'span',
17579                                       cls: 'fc-event-title',
17580                                       html : String.format('{0}', ev.title)
17581                                     }
17582
17583
17584                                 ]
17585                             },
17586                             {
17587                                 cls: 'ui-resizable-handle ui-resizable-e',
17588                                 html : '&nbsp;&nbsp;&nbsp'
17589                             }
17590
17591                         ]
17592                     };
17593
17594                     if (i == 0) {
17595                         cfg.cls += ' fc-event-start';
17596                     }
17597                     if ((i+1) == rows.length) {
17598                         cfg.cls += ' fc-event-end';
17599                     }
17600
17601                     var ctr = _this.el.select('.fc-event-container',true).first();
17602                     var cg = ctr.createChild(cfg);
17603
17604                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17605                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17606
17607                     var r = (c.more.length) ? 1 : 0;
17608                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17609                     cg.setWidth(ebox.right - sbox.x -2);
17610
17611                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17612                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17613                     cg.on('click', _this.onEventClick, _this, ev);
17614
17615                     ev.els.push(cg);
17616                     
17617                 }
17618                 
17619             }
17620             
17621             
17622             if(c.more.length){
17623                 var  cfg = {
17624                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17625                     style : 'position: absolute',
17626                     unselectable : "on",
17627                     cn : [
17628                         {
17629                             cls: 'fc-event-inner',
17630                             cn : [
17631                                 {
17632                                   tag:'span',
17633                                   cls: 'fc-event-title',
17634                                   html : 'More'
17635                                 }
17636
17637
17638                             ]
17639                         },
17640                         {
17641                             cls: 'ui-resizable-handle ui-resizable-e',
17642                             html : '&nbsp;&nbsp;&nbsp'
17643                         }
17644
17645                     ]
17646                 };
17647
17648                 var ctr = _this.el.select('.fc-event-container',true).first();
17649                 var cg = ctr.createChild(cfg);
17650
17651                 var sbox = c.select('.fc-day-content',true).first().getBox();
17652                 var ebox = c.select('.fc-day-content',true).first().getBox();
17653                 //Roo.log(cg);
17654                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17655                 cg.setWidth(ebox.right - sbox.x -2);
17656
17657                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17658                 
17659             }
17660             
17661         });
17662         
17663         
17664         
17665     },
17666     
17667     onEventEnter: function (e, el,event,d) {
17668         this.fireEvent('evententer', this, el, event);
17669     },
17670     
17671     onEventLeave: function (e, el,event,d) {
17672         this.fireEvent('eventleave', this, el, event);
17673     },
17674     
17675     onEventClick: function (e, el,event,d) {
17676         this.fireEvent('eventclick', this, el, event);
17677     },
17678     
17679     onMonthChange: function () {
17680         this.store.load();
17681     },
17682     
17683     onMoreEventClick: function(e, el, more)
17684     {
17685         var _this = this;
17686         
17687         this.calpopover.placement = 'right';
17688         this.calpopover.setTitle('More');
17689         
17690         this.calpopover.setContent('');
17691         
17692         var ctr = this.calpopover.el.select('.popover-content', true).first();
17693         
17694         Roo.each(more, function(m){
17695             var cfg = {
17696                 cls : 'fc-event-hori fc-event-draggable',
17697                 html : m.title
17698             };
17699             var cg = ctr.createChild(cfg);
17700             
17701             cg.on('click', _this.onEventClick, _this, m);
17702         });
17703         
17704         this.calpopover.show(el);
17705         
17706         
17707     },
17708     
17709     onLoad: function () 
17710     {   
17711         this.calevents = [];
17712         var cal = this;
17713         
17714         if(this.store.getCount() > 0){
17715             this.store.data.each(function(d){
17716                cal.addItem({
17717                     id : d.data.id,
17718                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17719                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17720                     time : d.data.start_time,
17721                     title : d.data.title,
17722                     description : d.data.description,
17723                     venue : d.data.venue
17724                 });
17725             });
17726         }
17727         
17728         this.renderEvents();
17729         
17730         if(this.calevents.length && this.loadMask){
17731             this.maskEl.hide();
17732         }
17733     },
17734     
17735     onBeforeLoad: function()
17736     {
17737         this.clearEvents();
17738         if(this.loadMask){
17739             this.maskEl.show();
17740         }
17741     }
17742 });
17743
17744  
17745  /*
17746  * - LGPL
17747  *
17748  * element
17749  * 
17750  */
17751
17752 /**
17753  * @class Roo.bootstrap.Popover
17754  * @extends Roo.bootstrap.Component
17755  * Bootstrap Popover class
17756  * @cfg {String} html contents of the popover   (or false to use children..)
17757  * @cfg {String} title of popover (or false to hide)
17758  * @cfg {String} placement how it is placed
17759  * @cfg {String} trigger click || hover (or false to trigger manually)
17760  * @cfg {String} over what (parent or false to trigger manually.)
17761  * @cfg {Number} delay - delay before showing
17762  
17763  * @constructor
17764  * Create a new Popover
17765  * @param {Object} config The config object
17766  */
17767
17768 Roo.bootstrap.Popover = function(config){
17769     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17770     
17771     this.addEvents({
17772         // raw events
17773          /**
17774          * @event show
17775          * After the popover show
17776          * 
17777          * @param {Roo.bootstrap.Popover} this
17778          */
17779         "show" : true,
17780         /**
17781          * @event hide
17782          * After the popover hide
17783          * 
17784          * @param {Roo.bootstrap.Popover} this
17785          */
17786         "hide" : true
17787     });
17788 };
17789
17790 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17791     
17792     title: 'Fill in a title',
17793     html: false,
17794     
17795     placement : 'right',
17796     trigger : 'hover', // hover
17797     
17798     delay : 0,
17799     
17800     over: 'parent',
17801     
17802     can_build_overlaid : false,
17803     
17804     getChildContainer : function()
17805     {
17806         return this.el.select('.popover-content',true).first();
17807     },
17808     
17809     getAutoCreate : function(){
17810          
17811         var cfg = {
17812            cls : 'popover roo-dynamic',
17813            style: 'display:block',
17814            cn : [
17815                 {
17816                     cls : 'arrow'
17817                 },
17818                 {
17819                     cls : 'popover-inner',
17820                     cn : [
17821                         {
17822                             tag: 'h3',
17823                             cls: 'popover-title popover-header',
17824                             html : this.title
17825                         },
17826                         {
17827                             cls : 'popover-content popover-body',
17828                             html : this.html
17829                         }
17830                     ]
17831                     
17832                 }
17833            ]
17834         };
17835         
17836         return cfg;
17837     },
17838     setTitle: function(str)
17839     {
17840         this.title = str;
17841         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17842     },
17843     setContent: function(str)
17844     {
17845         this.html = str;
17846         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17847     },
17848     // as it get's added to the bottom of the page.
17849     onRender : function(ct, position)
17850     {
17851         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17852         if(!this.el){
17853             var cfg = Roo.apply({},  this.getAutoCreate());
17854             cfg.id = Roo.id();
17855             
17856             if (this.cls) {
17857                 cfg.cls += ' ' + this.cls;
17858             }
17859             if (this.style) {
17860                 cfg.style = this.style;
17861             }
17862             //Roo.log("adding to ");
17863             this.el = Roo.get(document.body).createChild(cfg, position);
17864 //            Roo.log(this.el);
17865         }
17866         this.initEvents();
17867     },
17868     
17869     initEvents : function()
17870     {
17871         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17872         this.el.enableDisplayMode('block');
17873         this.el.hide();
17874         if (this.over === false) {
17875             return; 
17876         }
17877         if (this.triggers === false) {
17878             return;
17879         }
17880         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17881         var triggers = this.trigger ? this.trigger.split(' ') : [];
17882         Roo.each(triggers, function(trigger) {
17883         
17884             if (trigger == 'click') {
17885                 on_el.on('click', this.toggle, this);
17886             } else if (trigger != 'manual') {
17887                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17888                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17889       
17890                 on_el.on(eventIn  ,this.enter, this);
17891                 on_el.on(eventOut, this.leave, this);
17892             }
17893         }, this);
17894         
17895     },
17896     
17897     
17898     // private
17899     timeout : null,
17900     hoverState : null,
17901     
17902     toggle : function () {
17903         this.hoverState == 'in' ? this.leave() : this.enter();
17904     },
17905     
17906     enter : function () {
17907         
17908         clearTimeout(this.timeout);
17909     
17910         this.hoverState = 'in';
17911     
17912         if (!this.delay || !this.delay.show) {
17913             this.show();
17914             return;
17915         }
17916         var _t = this;
17917         this.timeout = setTimeout(function () {
17918             if (_t.hoverState == 'in') {
17919                 _t.show();
17920             }
17921         }, this.delay.show)
17922     },
17923     
17924     leave : function() {
17925         clearTimeout(this.timeout);
17926     
17927         this.hoverState = 'out';
17928     
17929         if (!this.delay || !this.delay.hide) {
17930             this.hide();
17931             return;
17932         }
17933         var _t = this;
17934         this.timeout = setTimeout(function () {
17935             if (_t.hoverState == 'out') {
17936                 _t.hide();
17937             }
17938         }, this.delay.hide)
17939     },
17940     
17941     show : function (on_el)
17942     {
17943         if (!on_el) {
17944             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17945         }
17946         
17947         // set content.
17948         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17949         if (this.html !== false) {
17950             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17951         }
17952         this.el.removeClass([
17953             'fade','top','bottom', 'left', 'right','in',
17954             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17955         ]);
17956         if (!this.title.length) {
17957             this.el.select('.popover-title',true).hide();
17958         }
17959         
17960         var placement = typeof this.placement == 'function' ?
17961             this.placement.call(this, this.el, on_el) :
17962             this.placement;
17963             
17964         var autoToken = /\s?auto?\s?/i;
17965         var autoPlace = autoToken.test(placement);
17966         if (autoPlace) {
17967             placement = placement.replace(autoToken, '') || 'top';
17968         }
17969         
17970         //this.el.detach()
17971         //this.el.setXY([0,0]);
17972         this.el.show();
17973         this.el.dom.style.display='block';
17974         this.el.addClass(placement);
17975         
17976         //this.el.appendTo(on_el);
17977         
17978         var p = this.getPosition();
17979         var box = this.el.getBox();
17980         
17981         if (autoPlace) {
17982             // fixme..
17983         }
17984         var align = Roo.bootstrap.Popover.alignment[placement];
17985         
17986 //        Roo.log(align);
17987         this.el.alignTo(on_el, align[0],align[1]);
17988         //var arrow = this.el.select('.arrow',true).first();
17989         //arrow.set(align[2], 
17990         
17991         this.el.addClass('in');
17992         
17993         
17994         if (this.el.hasClass('fade')) {
17995             // fade it?
17996         }
17997         
17998         this.hoverState = 'in';
17999         
18000         this.fireEvent('show', this);
18001         
18002     },
18003     hide : function()
18004     {
18005         this.el.setXY([0,0]);
18006         this.el.removeClass('in');
18007         this.el.hide();
18008         this.hoverState = null;
18009         
18010         this.fireEvent('hide', this);
18011     }
18012     
18013 });
18014
18015 Roo.bootstrap.Popover.alignment = {
18016     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18017     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18018     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18019     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18020 };
18021
18022  /*
18023  * - LGPL
18024  *
18025  * Progress
18026  * 
18027  */
18028
18029 /**
18030  * @class Roo.bootstrap.Progress
18031  * @extends Roo.bootstrap.Component
18032  * Bootstrap Progress class
18033  * @cfg {Boolean} striped striped of the progress bar
18034  * @cfg {Boolean} active animated of the progress bar
18035  * 
18036  * 
18037  * @constructor
18038  * Create a new Progress
18039  * @param {Object} config The config object
18040  */
18041
18042 Roo.bootstrap.Progress = function(config){
18043     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18044 };
18045
18046 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18047     
18048     striped : false,
18049     active: false,
18050     
18051     getAutoCreate : function(){
18052         var cfg = {
18053             tag: 'div',
18054             cls: 'progress'
18055         };
18056         
18057         
18058         if(this.striped){
18059             cfg.cls += ' progress-striped';
18060         }
18061       
18062         if(this.active){
18063             cfg.cls += ' active';
18064         }
18065         
18066         
18067         return cfg;
18068     }
18069    
18070 });
18071
18072  
18073
18074  /*
18075  * - LGPL
18076  *
18077  * ProgressBar
18078  * 
18079  */
18080
18081 /**
18082  * @class Roo.bootstrap.ProgressBar
18083  * @extends Roo.bootstrap.Component
18084  * Bootstrap ProgressBar class
18085  * @cfg {Number} aria_valuenow aria-value now
18086  * @cfg {Number} aria_valuemin aria-value min
18087  * @cfg {Number} aria_valuemax aria-value max
18088  * @cfg {String} label label for the progress bar
18089  * @cfg {String} panel (success | info | warning | danger )
18090  * @cfg {String} role role of the progress bar
18091  * @cfg {String} sr_only text
18092  * 
18093  * 
18094  * @constructor
18095  * Create a new ProgressBar
18096  * @param {Object} config The config object
18097  */
18098
18099 Roo.bootstrap.ProgressBar = function(config){
18100     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18101 };
18102
18103 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18104     
18105     aria_valuenow : 0,
18106     aria_valuemin : 0,
18107     aria_valuemax : 100,
18108     label : false,
18109     panel : false,
18110     role : false,
18111     sr_only: false,
18112     
18113     getAutoCreate : function()
18114     {
18115         
18116         var cfg = {
18117             tag: 'div',
18118             cls: 'progress-bar',
18119             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18120         };
18121         
18122         if(this.sr_only){
18123             cfg.cn = {
18124                 tag: 'span',
18125                 cls: 'sr-only',
18126                 html: this.sr_only
18127             }
18128         }
18129         
18130         if(this.role){
18131             cfg.role = this.role;
18132         }
18133         
18134         if(this.aria_valuenow){
18135             cfg['aria-valuenow'] = this.aria_valuenow;
18136         }
18137         
18138         if(this.aria_valuemin){
18139             cfg['aria-valuemin'] = this.aria_valuemin;
18140         }
18141         
18142         if(this.aria_valuemax){
18143             cfg['aria-valuemax'] = this.aria_valuemax;
18144         }
18145         
18146         if(this.label && !this.sr_only){
18147             cfg.html = this.label;
18148         }
18149         
18150         if(this.panel){
18151             cfg.cls += ' progress-bar-' + this.panel;
18152         }
18153         
18154         return cfg;
18155     },
18156     
18157     update : function(aria_valuenow)
18158     {
18159         this.aria_valuenow = aria_valuenow;
18160         
18161         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18162     }
18163    
18164 });
18165
18166  
18167
18168  /*
18169  * - LGPL
18170  *
18171  * column
18172  * 
18173  */
18174
18175 /**
18176  * @class Roo.bootstrap.TabGroup
18177  * @extends Roo.bootstrap.Column
18178  * Bootstrap Column class
18179  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18180  * @cfg {Boolean} carousel true to make the group behave like a carousel
18181  * @cfg {Boolean} bullets show bullets for the panels
18182  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18183  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18184  * @cfg {Boolean} showarrow (true|false) show arrow default true
18185  * 
18186  * @constructor
18187  * Create a new TabGroup
18188  * @param {Object} config The config object
18189  */
18190
18191 Roo.bootstrap.TabGroup = function(config){
18192     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18193     if (!this.navId) {
18194         this.navId = Roo.id();
18195     }
18196     this.tabs = [];
18197     Roo.bootstrap.TabGroup.register(this);
18198     
18199 };
18200
18201 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18202     
18203     carousel : false,
18204     transition : false,
18205     bullets : 0,
18206     timer : 0,
18207     autoslide : false,
18208     slideFn : false,
18209     slideOnTouch : false,
18210     showarrow : true,
18211     
18212     getAutoCreate : function()
18213     {
18214         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18215         
18216         cfg.cls += ' tab-content';
18217         
18218         if (this.carousel) {
18219             cfg.cls += ' carousel slide';
18220             
18221             cfg.cn = [{
18222                cls : 'carousel-inner',
18223                cn : []
18224             }];
18225         
18226             if(this.bullets  && !Roo.isTouch){
18227                 
18228                 var bullets = {
18229                     cls : 'carousel-bullets',
18230                     cn : []
18231                 };
18232                
18233                 if(this.bullets_cls){
18234                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18235                 }
18236                 
18237                 bullets.cn.push({
18238                     cls : 'clear'
18239                 });
18240                 
18241                 cfg.cn[0].cn.push(bullets);
18242             }
18243             
18244             if(this.showarrow){
18245                 cfg.cn[0].cn.push({
18246                     tag : 'div',
18247                     class : 'carousel-arrow',
18248                     cn : [
18249                         {
18250                             tag : 'div',
18251                             class : 'carousel-prev',
18252                             cn : [
18253                                 {
18254                                     tag : 'i',
18255                                     class : 'fa fa-chevron-left'
18256                                 }
18257                             ]
18258                         },
18259                         {
18260                             tag : 'div',
18261                             class : 'carousel-next',
18262                             cn : [
18263                                 {
18264                                     tag : 'i',
18265                                     class : 'fa fa-chevron-right'
18266                                 }
18267                             ]
18268                         }
18269                     ]
18270                 });
18271             }
18272             
18273         }
18274         
18275         return cfg;
18276     },
18277     
18278     initEvents:  function()
18279     {
18280 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18281 //            this.el.on("touchstart", this.onTouchStart, this);
18282 //        }
18283         
18284         if(this.autoslide){
18285             var _this = this;
18286             
18287             this.slideFn = window.setInterval(function() {
18288                 _this.showPanelNext();
18289             }, this.timer);
18290         }
18291         
18292         if(this.showarrow){
18293             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18294             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18295         }
18296         
18297         
18298     },
18299     
18300 //    onTouchStart : function(e, el, o)
18301 //    {
18302 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18303 //            return;
18304 //        }
18305 //        
18306 //        this.showPanelNext();
18307 //    },
18308     
18309     
18310     getChildContainer : function()
18311     {
18312         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18313     },
18314     
18315     /**
18316     * register a Navigation item
18317     * @param {Roo.bootstrap.NavItem} the navitem to add
18318     */
18319     register : function(item)
18320     {
18321         this.tabs.push( item);
18322         item.navId = this.navId; // not really needed..
18323         this.addBullet();
18324     
18325     },
18326     
18327     getActivePanel : function()
18328     {
18329         var r = false;
18330         Roo.each(this.tabs, function(t) {
18331             if (t.active) {
18332                 r = t;
18333                 return false;
18334             }
18335             return null;
18336         });
18337         return r;
18338         
18339     },
18340     getPanelByName : function(n)
18341     {
18342         var r = false;
18343         Roo.each(this.tabs, function(t) {
18344             if (t.tabId == n) {
18345                 r = t;
18346                 return false;
18347             }
18348             return null;
18349         });
18350         return r;
18351     },
18352     indexOfPanel : function(p)
18353     {
18354         var r = false;
18355         Roo.each(this.tabs, function(t,i) {
18356             if (t.tabId == p.tabId) {
18357                 r = i;
18358                 return false;
18359             }
18360             return null;
18361         });
18362         return r;
18363     },
18364     /**
18365      * show a specific panel
18366      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18367      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18368      */
18369     showPanel : function (pan)
18370     {
18371         if(this.transition || typeof(pan) == 'undefined'){
18372             Roo.log("waiting for the transitionend");
18373             return false;
18374         }
18375         
18376         if (typeof(pan) == 'number') {
18377             pan = this.tabs[pan];
18378         }
18379         
18380         if (typeof(pan) == 'string') {
18381             pan = this.getPanelByName(pan);
18382         }
18383         
18384         var cur = this.getActivePanel();
18385         
18386         if(!pan || !cur){
18387             Roo.log('pan or acitve pan is undefined');
18388             return false;
18389         }
18390         
18391         if (pan.tabId == this.getActivePanel().tabId) {
18392             return true;
18393         }
18394         
18395         if (false === cur.fireEvent('beforedeactivate')) {
18396             return false;
18397         }
18398         
18399         if(this.bullets > 0 && !Roo.isTouch){
18400             this.setActiveBullet(this.indexOfPanel(pan));
18401         }
18402         
18403         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18404             
18405             //class="carousel-item carousel-item-next carousel-item-left"
18406             
18407             this.transition = true;
18408             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18409             var lr = dir == 'next' ? 'left' : 'right';
18410             pan.el.addClass(dir); // or prev
18411             pan.el.addClass('carousel-item-' + dir); // or prev
18412             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18413             cur.el.addClass(lr); // or right
18414             pan.el.addClass(lr);
18415             cur.el.addClass('carousel-item-' +lr); // or right
18416             pan.el.addClass('carousel-item-' +lr);
18417             
18418             
18419             var _this = this;
18420             cur.el.on('transitionend', function() {
18421                 Roo.log("trans end?");
18422                 
18423                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18424                 pan.setActive(true);
18425                 
18426                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18427                 cur.setActive(false);
18428                 
18429                 _this.transition = false;
18430                 
18431             }, this, { single:  true } );
18432             
18433             return true;
18434         }
18435         
18436         cur.setActive(false);
18437         pan.setActive(true);
18438         
18439         return true;
18440         
18441     },
18442     showPanelNext : function()
18443     {
18444         var i = this.indexOfPanel(this.getActivePanel());
18445         
18446         if (i >= this.tabs.length - 1 && !this.autoslide) {
18447             return;
18448         }
18449         
18450         if (i >= this.tabs.length - 1 && this.autoslide) {
18451             i = -1;
18452         }
18453         
18454         this.showPanel(this.tabs[i+1]);
18455     },
18456     
18457     showPanelPrev : function()
18458     {
18459         var i = this.indexOfPanel(this.getActivePanel());
18460         
18461         if (i  < 1 && !this.autoslide) {
18462             return;
18463         }
18464         
18465         if (i < 1 && this.autoslide) {
18466             i = this.tabs.length;
18467         }
18468         
18469         this.showPanel(this.tabs[i-1]);
18470     },
18471     
18472     
18473     addBullet: function()
18474     {
18475         if(!this.bullets || Roo.isTouch){
18476             return;
18477         }
18478         var ctr = this.el.select('.carousel-bullets',true).first();
18479         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18480         var bullet = ctr.createChild({
18481             cls : 'bullet bullet-' + i
18482         },ctr.dom.lastChild);
18483         
18484         
18485         var _this = this;
18486         
18487         bullet.on('click', (function(e, el, o, ii, t){
18488
18489             e.preventDefault();
18490
18491             this.showPanel(ii);
18492
18493             if(this.autoslide && this.slideFn){
18494                 clearInterval(this.slideFn);
18495                 this.slideFn = window.setInterval(function() {
18496                     _this.showPanelNext();
18497                 }, this.timer);
18498             }
18499
18500         }).createDelegate(this, [i, bullet], true));
18501                 
18502         
18503     },
18504      
18505     setActiveBullet : function(i)
18506     {
18507         if(Roo.isTouch){
18508             return;
18509         }
18510         
18511         Roo.each(this.el.select('.bullet', true).elements, function(el){
18512             el.removeClass('selected');
18513         });
18514
18515         var bullet = this.el.select('.bullet-' + i, true).first();
18516         
18517         if(!bullet){
18518             return;
18519         }
18520         
18521         bullet.addClass('selected');
18522     }
18523     
18524     
18525   
18526 });
18527
18528  
18529
18530  
18531  
18532 Roo.apply(Roo.bootstrap.TabGroup, {
18533     
18534     groups: {},
18535      /**
18536     * register a Navigation Group
18537     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18538     */
18539     register : function(navgrp)
18540     {
18541         this.groups[navgrp.navId] = navgrp;
18542         
18543     },
18544     /**
18545     * fetch a Navigation Group based on the navigation ID
18546     * if one does not exist , it will get created.
18547     * @param {string} the navgroup to add
18548     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18549     */
18550     get: function(navId) {
18551         if (typeof(this.groups[navId]) == 'undefined') {
18552             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18553         }
18554         return this.groups[navId] ;
18555     }
18556     
18557     
18558     
18559 });
18560
18561  /*
18562  * - LGPL
18563  *
18564  * TabPanel
18565  * 
18566  */
18567
18568 /**
18569  * @class Roo.bootstrap.TabPanel
18570  * @extends Roo.bootstrap.Component
18571  * Bootstrap TabPanel class
18572  * @cfg {Boolean} active panel active
18573  * @cfg {String} html panel content
18574  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18575  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18576  * @cfg {String} href click to link..
18577  * 
18578  * 
18579  * @constructor
18580  * Create a new TabPanel
18581  * @param {Object} config The config object
18582  */
18583
18584 Roo.bootstrap.TabPanel = function(config){
18585     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18586     this.addEvents({
18587         /**
18588              * @event changed
18589              * Fires when the active status changes
18590              * @param {Roo.bootstrap.TabPanel} this
18591              * @param {Boolean} state the new state
18592             
18593          */
18594         'changed': true,
18595         /**
18596              * @event beforedeactivate
18597              * Fires before a tab is de-activated - can be used to do validation on a form.
18598              * @param {Roo.bootstrap.TabPanel} this
18599              * @return {Boolean} false if there is an error
18600             
18601          */
18602         'beforedeactivate': true
18603      });
18604     
18605     this.tabId = this.tabId || Roo.id();
18606   
18607 };
18608
18609 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18610     
18611     active: false,
18612     html: false,
18613     tabId: false,
18614     navId : false,
18615     href : '',
18616     
18617     getAutoCreate : function(){
18618         
18619         
18620         var cfg = {
18621             tag: 'div',
18622             // item is needed for carousel - not sure if it has any effect otherwise
18623             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18624             html: this.html || ''
18625         };
18626         
18627         if(this.active){
18628             cfg.cls += ' active';
18629         }
18630         
18631         if(this.tabId){
18632             cfg.tabId = this.tabId;
18633         }
18634         
18635         
18636         
18637         return cfg;
18638     },
18639     
18640     initEvents:  function()
18641     {
18642         var p = this.parent();
18643         
18644         this.navId = this.navId || p.navId;
18645         
18646         if (typeof(this.navId) != 'undefined') {
18647             // not really needed.. but just in case.. parent should be a NavGroup.
18648             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18649             
18650             tg.register(this);
18651             
18652             var i = tg.tabs.length - 1;
18653             
18654             if(this.active && tg.bullets > 0 && i < tg.bullets){
18655                 tg.setActiveBullet(i);
18656             }
18657         }
18658         
18659         this.el.on('click', this.onClick, this);
18660         
18661         if(Roo.isTouch){
18662             this.el.on("touchstart", this.onTouchStart, this);
18663             this.el.on("touchmove", this.onTouchMove, this);
18664             this.el.on("touchend", this.onTouchEnd, this);
18665         }
18666         
18667     },
18668     
18669     onRender : function(ct, position)
18670     {
18671         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18672     },
18673     
18674     setActive : function(state)
18675     {
18676         Roo.log("panel - set active " + this.tabId + "=" + state);
18677         
18678         this.active = state;
18679         if (!state) {
18680             this.el.removeClass('active');
18681             
18682         } else  if (!this.el.hasClass('active')) {
18683             this.el.addClass('active');
18684         }
18685         
18686         this.fireEvent('changed', this, state);
18687     },
18688     
18689     onClick : function(e)
18690     {
18691         e.preventDefault();
18692         
18693         if(!this.href.length){
18694             return;
18695         }
18696         
18697         window.location.href = this.href;
18698     },
18699     
18700     startX : 0,
18701     startY : 0,
18702     endX : 0,
18703     endY : 0,
18704     swiping : false,
18705     
18706     onTouchStart : function(e)
18707     {
18708         this.swiping = false;
18709         
18710         this.startX = e.browserEvent.touches[0].clientX;
18711         this.startY = e.browserEvent.touches[0].clientY;
18712     },
18713     
18714     onTouchMove : function(e)
18715     {
18716         this.swiping = true;
18717         
18718         this.endX = e.browserEvent.touches[0].clientX;
18719         this.endY = e.browserEvent.touches[0].clientY;
18720     },
18721     
18722     onTouchEnd : function(e)
18723     {
18724         if(!this.swiping){
18725             this.onClick(e);
18726             return;
18727         }
18728         
18729         var tabGroup = this.parent();
18730         
18731         if(this.endX > this.startX){ // swiping right
18732             tabGroup.showPanelPrev();
18733             return;
18734         }
18735         
18736         if(this.startX > this.endX){ // swiping left
18737             tabGroup.showPanelNext();
18738             return;
18739         }
18740     }
18741     
18742     
18743 });
18744  
18745
18746  
18747
18748  /*
18749  * - LGPL
18750  *
18751  * DateField
18752  * 
18753  */
18754
18755 /**
18756  * @class Roo.bootstrap.DateField
18757  * @extends Roo.bootstrap.Input
18758  * Bootstrap DateField class
18759  * @cfg {Number} weekStart default 0
18760  * @cfg {String} viewMode default empty, (months|years)
18761  * @cfg {String} minViewMode default empty, (months|years)
18762  * @cfg {Number} startDate default -Infinity
18763  * @cfg {Number} endDate default Infinity
18764  * @cfg {Boolean} todayHighlight default false
18765  * @cfg {Boolean} todayBtn default false
18766  * @cfg {Boolean} calendarWeeks default false
18767  * @cfg {Object} daysOfWeekDisabled default empty
18768  * @cfg {Boolean} singleMode default false (true | false)
18769  * 
18770  * @cfg {Boolean} keyboardNavigation default true
18771  * @cfg {String} language default en
18772  * 
18773  * @constructor
18774  * Create a new DateField
18775  * @param {Object} config The config object
18776  */
18777
18778 Roo.bootstrap.DateField = function(config){
18779     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18780      this.addEvents({
18781             /**
18782              * @event show
18783              * Fires when this field show.
18784              * @param {Roo.bootstrap.DateField} this
18785              * @param {Mixed} date The date value
18786              */
18787             show : true,
18788             /**
18789              * @event show
18790              * Fires when this field hide.
18791              * @param {Roo.bootstrap.DateField} this
18792              * @param {Mixed} date The date value
18793              */
18794             hide : true,
18795             /**
18796              * @event select
18797              * Fires when select a date.
18798              * @param {Roo.bootstrap.DateField} this
18799              * @param {Mixed} date The date value
18800              */
18801             select : true,
18802             /**
18803              * @event beforeselect
18804              * Fires when before select a date.
18805              * @param {Roo.bootstrap.DateField} this
18806              * @param {Mixed} date The date value
18807              */
18808             beforeselect : true
18809         });
18810 };
18811
18812 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18813     
18814     /**
18815      * @cfg {String} format
18816      * The default date format string which can be overriden for localization support.  The format must be
18817      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18818      */
18819     format : "m/d/y",
18820     /**
18821      * @cfg {String} altFormats
18822      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18823      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18824      */
18825     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18826     
18827     weekStart : 0,
18828     
18829     viewMode : '',
18830     
18831     minViewMode : '',
18832     
18833     todayHighlight : false,
18834     
18835     todayBtn: false,
18836     
18837     language: 'en',
18838     
18839     keyboardNavigation: true,
18840     
18841     calendarWeeks: false,
18842     
18843     startDate: -Infinity,
18844     
18845     endDate: Infinity,
18846     
18847     daysOfWeekDisabled: [],
18848     
18849     _events: [],
18850     
18851     singleMode : false,
18852     
18853     UTCDate: function()
18854     {
18855         return new Date(Date.UTC.apply(Date, arguments));
18856     },
18857     
18858     UTCToday: function()
18859     {
18860         var today = new Date();
18861         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18862     },
18863     
18864     getDate: function() {
18865             var d = this.getUTCDate();
18866             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18867     },
18868     
18869     getUTCDate: function() {
18870             return this.date;
18871     },
18872     
18873     setDate: function(d) {
18874             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18875     },
18876     
18877     setUTCDate: function(d) {
18878             this.date = d;
18879             this.setValue(this.formatDate(this.date));
18880     },
18881         
18882     onRender: function(ct, position)
18883     {
18884         
18885         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18886         
18887         this.language = this.language || 'en';
18888         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18889         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18890         
18891         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18892         this.format = this.format || 'm/d/y';
18893         this.isInline = false;
18894         this.isInput = true;
18895         this.component = this.el.select('.add-on', true).first() || false;
18896         this.component = (this.component && this.component.length === 0) ? false : this.component;
18897         this.hasInput = this.component && this.inputEl().length;
18898         
18899         if (typeof(this.minViewMode === 'string')) {
18900             switch (this.minViewMode) {
18901                 case 'months':
18902                     this.minViewMode = 1;
18903                     break;
18904                 case 'years':
18905                     this.minViewMode = 2;
18906                     break;
18907                 default:
18908                     this.minViewMode = 0;
18909                     break;
18910             }
18911         }
18912         
18913         if (typeof(this.viewMode === 'string')) {
18914             switch (this.viewMode) {
18915                 case 'months':
18916                     this.viewMode = 1;
18917                     break;
18918                 case 'years':
18919                     this.viewMode = 2;
18920                     break;
18921                 default:
18922                     this.viewMode = 0;
18923                     break;
18924             }
18925         }
18926                 
18927         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18928         
18929 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18930         
18931         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18932         
18933         this.picker().on('mousedown', this.onMousedown, this);
18934         this.picker().on('click', this.onClick, this);
18935         
18936         this.picker().addClass('datepicker-dropdown');
18937         
18938         this.startViewMode = this.viewMode;
18939         
18940         if(this.singleMode){
18941             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18942                 v.setVisibilityMode(Roo.Element.DISPLAY);
18943                 v.hide();
18944             });
18945             
18946             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18947                 v.setStyle('width', '189px');
18948             });
18949         }
18950         
18951         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18952             if(!this.calendarWeeks){
18953                 v.remove();
18954                 return;
18955             }
18956             
18957             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18958             v.attr('colspan', function(i, val){
18959                 return parseInt(val) + 1;
18960             });
18961         });
18962                         
18963         
18964         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18965         
18966         this.setStartDate(this.startDate);
18967         this.setEndDate(this.endDate);
18968         
18969         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18970         
18971         this.fillDow();
18972         this.fillMonths();
18973         this.update();
18974         this.showMode();
18975         
18976         if(this.isInline) {
18977             this.showPopup();
18978         }
18979     },
18980     
18981     picker : function()
18982     {
18983         return this.pickerEl;
18984 //        return this.el.select('.datepicker', true).first();
18985     },
18986     
18987     fillDow: function()
18988     {
18989         var dowCnt = this.weekStart;
18990         
18991         var dow = {
18992             tag: 'tr',
18993             cn: [
18994                 
18995             ]
18996         };
18997         
18998         if(this.calendarWeeks){
18999             dow.cn.push({
19000                 tag: 'th',
19001                 cls: 'cw',
19002                 html: '&nbsp;'
19003             })
19004         }
19005         
19006         while (dowCnt < this.weekStart + 7) {
19007             dow.cn.push({
19008                 tag: 'th',
19009                 cls: 'dow',
19010                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19011             });
19012         }
19013         
19014         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19015     },
19016     
19017     fillMonths: function()
19018     {    
19019         var i = 0;
19020         var months = this.picker().select('>.datepicker-months td', true).first();
19021         
19022         months.dom.innerHTML = '';
19023         
19024         while (i < 12) {
19025             var month = {
19026                 tag: 'span',
19027                 cls: 'month',
19028                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19029             };
19030             
19031             months.createChild(month);
19032         }
19033         
19034     },
19035     
19036     update: function()
19037     {
19038         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;
19039         
19040         if (this.date < this.startDate) {
19041             this.viewDate = new Date(this.startDate);
19042         } else if (this.date > this.endDate) {
19043             this.viewDate = new Date(this.endDate);
19044         } else {
19045             this.viewDate = new Date(this.date);
19046         }
19047         
19048         this.fill();
19049     },
19050     
19051     fill: function() 
19052     {
19053         var d = new Date(this.viewDate),
19054                 year = d.getUTCFullYear(),
19055                 month = d.getUTCMonth(),
19056                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19057                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19058                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19059                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19060                 currentDate = this.date && this.date.valueOf(),
19061                 today = this.UTCToday();
19062         
19063         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19064         
19065 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19066         
19067 //        this.picker.select('>tfoot th.today').
19068 //                                              .text(dates[this.language].today)
19069 //                                              .toggle(this.todayBtn !== false);
19070     
19071         this.updateNavArrows();
19072         this.fillMonths();
19073                                                 
19074         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19075         
19076         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19077          
19078         prevMonth.setUTCDate(day);
19079         
19080         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19081         
19082         var nextMonth = new Date(prevMonth);
19083         
19084         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19085         
19086         nextMonth = nextMonth.valueOf();
19087         
19088         var fillMonths = false;
19089         
19090         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19091         
19092         while(prevMonth.valueOf() <= nextMonth) {
19093             var clsName = '';
19094             
19095             if (prevMonth.getUTCDay() === this.weekStart) {
19096                 if(fillMonths){
19097                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19098                 }
19099                     
19100                 fillMonths = {
19101                     tag: 'tr',
19102                     cn: []
19103                 };
19104                 
19105                 if(this.calendarWeeks){
19106                     // ISO 8601: First week contains first thursday.
19107                     // ISO also states week starts on Monday, but we can be more abstract here.
19108                     var
19109                     // Start of current week: based on weekstart/current date
19110                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19111                     // Thursday of this week
19112                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19113                     // First Thursday of year, year from thursday
19114                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19115                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19116                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19117                     
19118                     fillMonths.cn.push({
19119                         tag: 'td',
19120                         cls: 'cw',
19121                         html: calWeek
19122                     });
19123                 }
19124             }
19125             
19126             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19127                 clsName += ' old';
19128             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19129                 clsName += ' new';
19130             }
19131             if (this.todayHighlight &&
19132                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19133                 prevMonth.getUTCMonth() == today.getMonth() &&
19134                 prevMonth.getUTCDate() == today.getDate()) {
19135                 clsName += ' today';
19136             }
19137             
19138             if (currentDate && prevMonth.valueOf() === currentDate) {
19139                 clsName += ' active';
19140             }
19141             
19142             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19143                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19144                     clsName += ' disabled';
19145             }
19146             
19147             fillMonths.cn.push({
19148                 tag: 'td',
19149                 cls: 'day ' + clsName,
19150                 html: prevMonth.getDate()
19151             });
19152             
19153             prevMonth.setDate(prevMonth.getDate()+1);
19154         }
19155           
19156         var currentYear = this.date && this.date.getUTCFullYear();
19157         var currentMonth = this.date && this.date.getUTCMonth();
19158         
19159         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19160         
19161         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19162             v.removeClass('active');
19163             
19164             if(currentYear === year && k === currentMonth){
19165                 v.addClass('active');
19166             }
19167             
19168             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19169                 v.addClass('disabled');
19170             }
19171             
19172         });
19173         
19174         
19175         year = parseInt(year/10, 10) * 10;
19176         
19177         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19178         
19179         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19180         
19181         year -= 1;
19182         for (var i = -1; i < 11; i++) {
19183             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19184                 tag: 'span',
19185                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19186                 html: year
19187             });
19188             
19189             year += 1;
19190         }
19191     },
19192     
19193     showMode: function(dir) 
19194     {
19195         if (dir) {
19196             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19197         }
19198         
19199         Roo.each(this.picker().select('>div',true).elements, function(v){
19200             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19201             v.hide();
19202         });
19203         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19204     },
19205     
19206     place: function()
19207     {
19208         if(this.isInline) {
19209             return;
19210         }
19211         
19212         this.picker().removeClass(['bottom', 'top']);
19213         
19214         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19215             /*
19216              * place to the top of element!
19217              *
19218              */
19219             
19220             this.picker().addClass('top');
19221             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19222             
19223             return;
19224         }
19225         
19226         this.picker().addClass('bottom');
19227         
19228         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19229     },
19230     
19231     parseDate : function(value)
19232     {
19233         if(!value || value instanceof Date){
19234             return value;
19235         }
19236         var v = Date.parseDate(value, this.format);
19237         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19238             v = Date.parseDate(value, 'Y-m-d');
19239         }
19240         if(!v && this.altFormats){
19241             if(!this.altFormatsArray){
19242                 this.altFormatsArray = this.altFormats.split("|");
19243             }
19244             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19245                 v = Date.parseDate(value, this.altFormatsArray[i]);
19246             }
19247         }
19248         return v;
19249     },
19250     
19251     formatDate : function(date, fmt)
19252     {   
19253         return (!date || !(date instanceof Date)) ?
19254         date : date.dateFormat(fmt || this.format);
19255     },
19256     
19257     onFocus : function()
19258     {
19259         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19260         this.showPopup();
19261     },
19262     
19263     onBlur : function()
19264     {
19265         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19266         
19267         var d = this.inputEl().getValue();
19268         
19269         this.setValue(d);
19270                 
19271         this.hidePopup();
19272     },
19273     
19274     showPopup : function()
19275     {
19276         this.picker().show();
19277         this.update();
19278         this.place();
19279         
19280         this.fireEvent('showpopup', this, this.date);
19281     },
19282     
19283     hidePopup : function()
19284     {
19285         if(this.isInline) {
19286             return;
19287         }
19288         this.picker().hide();
19289         this.viewMode = this.startViewMode;
19290         this.showMode();
19291         
19292         this.fireEvent('hidepopup', this, this.date);
19293         
19294     },
19295     
19296     onMousedown: function(e)
19297     {
19298         e.stopPropagation();
19299         e.preventDefault();
19300     },
19301     
19302     keyup: function(e)
19303     {
19304         Roo.bootstrap.DateField.superclass.keyup.call(this);
19305         this.update();
19306     },
19307
19308     setValue: function(v)
19309     {
19310         if(this.fireEvent('beforeselect', this, v) !== false){
19311             var d = new Date(this.parseDate(v) ).clearTime();
19312         
19313             if(isNaN(d.getTime())){
19314                 this.date = this.viewDate = '';
19315                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19316                 return;
19317             }
19318
19319             v = this.formatDate(d);
19320
19321             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19322
19323             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19324
19325             this.update();
19326
19327             this.fireEvent('select', this, this.date);
19328         }
19329     },
19330     
19331     getValue: function()
19332     {
19333         return this.formatDate(this.date);
19334     },
19335     
19336     fireKey: function(e)
19337     {
19338         if (!this.picker().isVisible()){
19339             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19340                 this.showPopup();
19341             }
19342             return;
19343         }
19344         
19345         var dateChanged = false,
19346         dir, day, month,
19347         newDate, newViewDate;
19348         
19349         switch(e.keyCode){
19350             case 27: // escape
19351                 this.hidePopup();
19352                 e.preventDefault();
19353                 break;
19354             case 37: // left
19355             case 39: // right
19356                 if (!this.keyboardNavigation) {
19357                     break;
19358                 }
19359                 dir = e.keyCode == 37 ? -1 : 1;
19360                 
19361                 if (e.ctrlKey){
19362                     newDate = this.moveYear(this.date, dir);
19363                     newViewDate = this.moveYear(this.viewDate, dir);
19364                 } else if (e.shiftKey){
19365                     newDate = this.moveMonth(this.date, dir);
19366                     newViewDate = this.moveMonth(this.viewDate, dir);
19367                 } else {
19368                     newDate = new Date(this.date);
19369                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19370                     newViewDate = new Date(this.viewDate);
19371                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19372                 }
19373                 if (this.dateWithinRange(newDate)){
19374                     this.date = newDate;
19375                     this.viewDate = newViewDate;
19376                     this.setValue(this.formatDate(this.date));
19377 //                    this.update();
19378                     e.preventDefault();
19379                     dateChanged = true;
19380                 }
19381                 break;
19382             case 38: // up
19383             case 40: // down
19384                 if (!this.keyboardNavigation) {
19385                     break;
19386                 }
19387                 dir = e.keyCode == 38 ? -1 : 1;
19388                 if (e.ctrlKey){
19389                     newDate = this.moveYear(this.date, dir);
19390                     newViewDate = this.moveYear(this.viewDate, dir);
19391                 } else if (e.shiftKey){
19392                     newDate = this.moveMonth(this.date, dir);
19393                     newViewDate = this.moveMonth(this.viewDate, dir);
19394                 } else {
19395                     newDate = new Date(this.date);
19396                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19397                     newViewDate = new Date(this.viewDate);
19398                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19399                 }
19400                 if (this.dateWithinRange(newDate)){
19401                     this.date = newDate;
19402                     this.viewDate = newViewDate;
19403                     this.setValue(this.formatDate(this.date));
19404 //                    this.update();
19405                     e.preventDefault();
19406                     dateChanged = true;
19407                 }
19408                 break;
19409             case 13: // enter
19410                 this.setValue(this.formatDate(this.date));
19411                 this.hidePopup();
19412                 e.preventDefault();
19413                 break;
19414             case 9: // tab
19415                 this.setValue(this.formatDate(this.date));
19416                 this.hidePopup();
19417                 break;
19418             case 16: // shift
19419             case 17: // ctrl
19420             case 18: // alt
19421                 break;
19422             default :
19423                 this.hidePopup();
19424                 
19425         }
19426     },
19427     
19428     
19429     onClick: function(e) 
19430     {
19431         e.stopPropagation();
19432         e.preventDefault();
19433         
19434         var target = e.getTarget();
19435         
19436         if(target.nodeName.toLowerCase() === 'i'){
19437             target = Roo.get(target).dom.parentNode;
19438         }
19439         
19440         var nodeName = target.nodeName;
19441         var className = target.className;
19442         var html = target.innerHTML;
19443         //Roo.log(nodeName);
19444         
19445         switch(nodeName.toLowerCase()) {
19446             case 'th':
19447                 switch(className) {
19448                     case 'switch':
19449                         this.showMode(1);
19450                         break;
19451                     case 'prev':
19452                     case 'next':
19453                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19454                         switch(this.viewMode){
19455                                 case 0:
19456                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19457                                         break;
19458                                 case 1:
19459                                 case 2:
19460                                         this.viewDate = this.moveYear(this.viewDate, dir);
19461                                         break;
19462                         }
19463                         this.fill();
19464                         break;
19465                     case 'today':
19466                         var date = new Date();
19467                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19468 //                        this.fill()
19469                         this.setValue(this.formatDate(this.date));
19470                         
19471                         this.hidePopup();
19472                         break;
19473                 }
19474                 break;
19475             case 'span':
19476                 if (className.indexOf('disabled') < 0) {
19477                     this.viewDate.setUTCDate(1);
19478                     if (className.indexOf('month') > -1) {
19479                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19480                     } else {
19481                         var year = parseInt(html, 10) || 0;
19482                         this.viewDate.setUTCFullYear(year);
19483                         
19484                     }
19485                     
19486                     if(this.singleMode){
19487                         this.setValue(this.formatDate(this.viewDate));
19488                         this.hidePopup();
19489                         return;
19490                     }
19491                     
19492                     this.showMode(-1);
19493                     this.fill();
19494                 }
19495                 break;
19496                 
19497             case 'td':
19498                 //Roo.log(className);
19499                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19500                     var day = parseInt(html, 10) || 1;
19501                     var year = this.viewDate.getUTCFullYear(),
19502                         month = this.viewDate.getUTCMonth();
19503
19504                     if (className.indexOf('old') > -1) {
19505                         if(month === 0 ){
19506                             month = 11;
19507                             year -= 1;
19508                         }else{
19509                             month -= 1;
19510                         }
19511                     } else if (className.indexOf('new') > -1) {
19512                         if (month == 11) {
19513                             month = 0;
19514                             year += 1;
19515                         } else {
19516                             month += 1;
19517                         }
19518                     }
19519                     //Roo.log([year,month,day]);
19520                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19521                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19522 //                    this.fill();
19523                     //Roo.log(this.formatDate(this.date));
19524                     this.setValue(this.formatDate(this.date));
19525                     this.hidePopup();
19526                 }
19527                 break;
19528         }
19529     },
19530     
19531     setStartDate: function(startDate)
19532     {
19533         this.startDate = startDate || -Infinity;
19534         if (this.startDate !== -Infinity) {
19535             this.startDate = this.parseDate(this.startDate);
19536         }
19537         this.update();
19538         this.updateNavArrows();
19539     },
19540
19541     setEndDate: function(endDate)
19542     {
19543         this.endDate = endDate || Infinity;
19544         if (this.endDate !== Infinity) {
19545             this.endDate = this.parseDate(this.endDate);
19546         }
19547         this.update();
19548         this.updateNavArrows();
19549     },
19550     
19551     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19552     {
19553         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19554         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19555             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19556         }
19557         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19558             return parseInt(d, 10);
19559         });
19560         this.update();
19561         this.updateNavArrows();
19562     },
19563     
19564     updateNavArrows: function() 
19565     {
19566         if(this.singleMode){
19567             return;
19568         }
19569         
19570         var d = new Date(this.viewDate),
19571         year = d.getUTCFullYear(),
19572         month = d.getUTCMonth();
19573         
19574         Roo.each(this.picker().select('.prev', true).elements, function(v){
19575             v.show();
19576             switch (this.viewMode) {
19577                 case 0:
19578
19579                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19580                         v.hide();
19581                     }
19582                     break;
19583                 case 1:
19584                 case 2:
19585                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19586                         v.hide();
19587                     }
19588                     break;
19589             }
19590         });
19591         
19592         Roo.each(this.picker().select('.next', true).elements, function(v){
19593             v.show();
19594             switch (this.viewMode) {
19595                 case 0:
19596
19597                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19598                         v.hide();
19599                     }
19600                     break;
19601                 case 1:
19602                 case 2:
19603                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19604                         v.hide();
19605                     }
19606                     break;
19607             }
19608         })
19609     },
19610     
19611     moveMonth: function(date, dir)
19612     {
19613         if (!dir) {
19614             return date;
19615         }
19616         var new_date = new Date(date.valueOf()),
19617         day = new_date.getUTCDate(),
19618         month = new_date.getUTCMonth(),
19619         mag = Math.abs(dir),
19620         new_month, test;
19621         dir = dir > 0 ? 1 : -1;
19622         if (mag == 1){
19623             test = dir == -1
19624             // If going back one month, make sure month is not current month
19625             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19626             ? function(){
19627                 return new_date.getUTCMonth() == month;
19628             }
19629             // If going forward one month, make sure month is as expected
19630             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19631             : function(){
19632                 return new_date.getUTCMonth() != new_month;
19633             };
19634             new_month = month + dir;
19635             new_date.setUTCMonth(new_month);
19636             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19637             if (new_month < 0 || new_month > 11) {
19638                 new_month = (new_month + 12) % 12;
19639             }
19640         } else {
19641             // For magnitudes >1, move one month at a time...
19642             for (var i=0; i<mag; i++) {
19643                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19644                 new_date = this.moveMonth(new_date, dir);
19645             }
19646             // ...then reset the day, keeping it in the new month
19647             new_month = new_date.getUTCMonth();
19648             new_date.setUTCDate(day);
19649             test = function(){
19650                 return new_month != new_date.getUTCMonth();
19651             };
19652         }
19653         // Common date-resetting loop -- if date is beyond end of month, make it
19654         // end of month
19655         while (test()){
19656             new_date.setUTCDate(--day);
19657             new_date.setUTCMonth(new_month);
19658         }
19659         return new_date;
19660     },
19661
19662     moveYear: function(date, dir)
19663     {
19664         return this.moveMonth(date, dir*12);
19665     },
19666
19667     dateWithinRange: function(date)
19668     {
19669         return date >= this.startDate && date <= this.endDate;
19670     },
19671
19672     
19673     remove: function() 
19674     {
19675         this.picker().remove();
19676     },
19677     
19678     validateValue : function(value)
19679     {
19680         if(this.getVisibilityEl().hasClass('hidden')){
19681             return true;
19682         }
19683         
19684         if(value.length < 1)  {
19685             if(this.allowBlank){
19686                 return true;
19687             }
19688             return false;
19689         }
19690         
19691         if(value.length < this.minLength){
19692             return false;
19693         }
19694         if(value.length > this.maxLength){
19695             return false;
19696         }
19697         if(this.vtype){
19698             var vt = Roo.form.VTypes;
19699             if(!vt[this.vtype](value, this)){
19700                 return false;
19701             }
19702         }
19703         if(typeof this.validator == "function"){
19704             var msg = this.validator(value);
19705             if(msg !== true){
19706                 return false;
19707             }
19708         }
19709         
19710         if(this.regex && !this.regex.test(value)){
19711             return false;
19712         }
19713         
19714         if(typeof(this.parseDate(value)) == 'undefined'){
19715             return false;
19716         }
19717         
19718         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19719             return false;
19720         }      
19721         
19722         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19723             return false;
19724         } 
19725         
19726         
19727         return true;
19728     },
19729     
19730     reset : function()
19731     {
19732         this.date = this.viewDate = '';
19733         
19734         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19735     }
19736    
19737 });
19738
19739 Roo.apply(Roo.bootstrap.DateField,  {
19740     
19741     head : {
19742         tag: 'thead',
19743         cn: [
19744         {
19745             tag: 'tr',
19746             cn: [
19747             {
19748                 tag: 'th',
19749                 cls: 'prev',
19750                 html: '<i class="fa fa-arrow-left"/>'
19751             },
19752             {
19753                 tag: 'th',
19754                 cls: 'switch',
19755                 colspan: '5'
19756             },
19757             {
19758                 tag: 'th',
19759                 cls: 'next',
19760                 html: '<i class="fa fa-arrow-right"/>'
19761             }
19762
19763             ]
19764         }
19765         ]
19766     },
19767     
19768     content : {
19769         tag: 'tbody',
19770         cn: [
19771         {
19772             tag: 'tr',
19773             cn: [
19774             {
19775                 tag: 'td',
19776                 colspan: '7'
19777             }
19778             ]
19779         }
19780         ]
19781     },
19782     
19783     footer : {
19784         tag: 'tfoot',
19785         cn: [
19786         {
19787             tag: 'tr',
19788             cn: [
19789             {
19790                 tag: 'th',
19791                 colspan: '7',
19792                 cls: 'today'
19793             }
19794                     
19795             ]
19796         }
19797         ]
19798     },
19799     
19800     dates:{
19801         en: {
19802             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19803             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19804             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19805             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19806             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19807             today: "Today"
19808         }
19809     },
19810     
19811     modes: [
19812     {
19813         clsName: 'days',
19814         navFnc: 'Month',
19815         navStep: 1
19816     },
19817     {
19818         clsName: 'months',
19819         navFnc: 'FullYear',
19820         navStep: 1
19821     },
19822     {
19823         clsName: 'years',
19824         navFnc: 'FullYear',
19825         navStep: 10
19826     }]
19827 });
19828
19829 Roo.apply(Roo.bootstrap.DateField,  {
19830   
19831     template : {
19832         tag: 'div',
19833         cls: 'datepicker dropdown-menu roo-dynamic',
19834         cn: [
19835         {
19836             tag: 'div',
19837             cls: 'datepicker-days',
19838             cn: [
19839             {
19840                 tag: 'table',
19841                 cls: 'table-condensed',
19842                 cn:[
19843                 Roo.bootstrap.DateField.head,
19844                 {
19845                     tag: 'tbody'
19846                 },
19847                 Roo.bootstrap.DateField.footer
19848                 ]
19849             }
19850             ]
19851         },
19852         {
19853             tag: 'div',
19854             cls: 'datepicker-months',
19855             cn: [
19856             {
19857                 tag: 'table',
19858                 cls: 'table-condensed',
19859                 cn:[
19860                 Roo.bootstrap.DateField.head,
19861                 Roo.bootstrap.DateField.content,
19862                 Roo.bootstrap.DateField.footer
19863                 ]
19864             }
19865             ]
19866         },
19867         {
19868             tag: 'div',
19869             cls: 'datepicker-years',
19870             cn: [
19871             {
19872                 tag: 'table',
19873                 cls: 'table-condensed',
19874                 cn:[
19875                 Roo.bootstrap.DateField.head,
19876                 Roo.bootstrap.DateField.content,
19877                 Roo.bootstrap.DateField.footer
19878                 ]
19879             }
19880             ]
19881         }
19882         ]
19883     }
19884 });
19885
19886  
19887
19888  /*
19889  * - LGPL
19890  *
19891  * TimeField
19892  * 
19893  */
19894
19895 /**
19896  * @class Roo.bootstrap.TimeField
19897  * @extends Roo.bootstrap.Input
19898  * Bootstrap DateField class
19899  * 
19900  * 
19901  * @constructor
19902  * Create a new TimeField
19903  * @param {Object} config The config object
19904  */
19905
19906 Roo.bootstrap.TimeField = function(config){
19907     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19908     this.addEvents({
19909             /**
19910              * @event show
19911              * Fires when this field show.
19912              * @param {Roo.bootstrap.DateField} thisthis
19913              * @param {Mixed} date The date value
19914              */
19915             show : true,
19916             /**
19917              * @event show
19918              * Fires when this field hide.
19919              * @param {Roo.bootstrap.DateField} this
19920              * @param {Mixed} date The date value
19921              */
19922             hide : true,
19923             /**
19924              * @event select
19925              * Fires when select a date.
19926              * @param {Roo.bootstrap.DateField} this
19927              * @param {Mixed} date The date value
19928              */
19929             select : true
19930         });
19931 };
19932
19933 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19934     
19935     /**
19936      * @cfg {String} format
19937      * The default time format string which can be overriden for localization support.  The format must be
19938      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19939      */
19940     format : "H:i",
19941        
19942     onRender: function(ct, position)
19943     {
19944         
19945         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19946                 
19947         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19948         
19949         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19950         
19951         this.pop = this.picker().select('>.datepicker-time',true).first();
19952         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19953         
19954         this.picker().on('mousedown', this.onMousedown, this);
19955         this.picker().on('click', this.onClick, this);
19956         
19957         this.picker().addClass('datepicker-dropdown');
19958     
19959         this.fillTime();
19960         this.update();
19961             
19962         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19963         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19964         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19965         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19966         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19967         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19968
19969     },
19970     
19971     fireKey: function(e){
19972         if (!this.picker().isVisible()){
19973             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19974                 this.show();
19975             }
19976             return;
19977         }
19978
19979         e.preventDefault();
19980         
19981         switch(e.keyCode){
19982             case 27: // escape
19983                 this.hide();
19984                 break;
19985             case 37: // left
19986             case 39: // right
19987                 this.onTogglePeriod();
19988                 break;
19989             case 38: // up
19990                 this.onIncrementMinutes();
19991                 break;
19992             case 40: // down
19993                 this.onDecrementMinutes();
19994                 break;
19995             case 13: // enter
19996             case 9: // tab
19997                 this.setTime();
19998                 break;
19999         }
20000     },
20001     
20002     onClick: function(e) {
20003         e.stopPropagation();
20004         e.preventDefault();
20005     },
20006     
20007     picker : function()
20008     {
20009         return this.el.select('.datepicker', true).first();
20010     },
20011     
20012     fillTime: function()
20013     {    
20014         var time = this.pop.select('tbody', true).first();
20015         
20016         time.dom.innerHTML = '';
20017         
20018         time.createChild({
20019             tag: 'tr',
20020             cn: [
20021                 {
20022                     tag: 'td',
20023                     cn: [
20024                         {
20025                             tag: 'a',
20026                             href: '#',
20027                             cls: 'btn',
20028                             cn: [
20029                                 {
20030                                     tag: 'span',
20031                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20032                                 }
20033                             ]
20034                         } 
20035                     ]
20036                 },
20037                 {
20038                     tag: 'td',
20039                     cls: 'separator'
20040                 },
20041                 {
20042                     tag: 'td',
20043                     cn: [
20044                         {
20045                             tag: 'a',
20046                             href: '#',
20047                             cls: 'btn',
20048                             cn: [
20049                                 {
20050                                     tag: 'span',
20051                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20052                                 }
20053                             ]
20054                         }
20055                     ]
20056                 },
20057                 {
20058                     tag: 'td',
20059                     cls: 'separator'
20060                 }
20061             ]
20062         });
20063         
20064         time.createChild({
20065             tag: 'tr',
20066             cn: [
20067                 {
20068                     tag: 'td',
20069                     cn: [
20070                         {
20071                             tag: 'span',
20072                             cls: 'timepicker-hour',
20073                             html: '00'
20074                         }  
20075                     ]
20076                 },
20077                 {
20078                     tag: 'td',
20079                     cls: 'separator',
20080                     html: ':'
20081                 },
20082                 {
20083                     tag: 'td',
20084                     cn: [
20085                         {
20086                             tag: 'span',
20087                             cls: 'timepicker-minute',
20088                             html: '00'
20089                         }  
20090                     ]
20091                 },
20092                 {
20093                     tag: 'td',
20094                     cls: 'separator'
20095                 },
20096                 {
20097                     tag: 'td',
20098                     cn: [
20099                         {
20100                             tag: 'button',
20101                             type: 'button',
20102                             cls: 'btn btn-primary period',
20103                             html: 'AM'
20104                             
20105                         }
20106                     ]
20107                 }
20108             ]
20109         });
20110         
20111         time.createChild({
20112             tag: 'tr',
20113             cn: [
20114                 {
20115                     tag: 'td',
20116                     cn: [
20117                         {
20118                             tag: 'a',
20119                             href: '#',
20120                             cls: 'btn',
20121                             cn: [
20122                                 {
20123                                     tag: 'span',
20124                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20125                                 }
20126                             ]
20127                         }
20128                     ]
20129                 },
20130                 {
20131                     tag: 'td',
20132                     cls: 'separator'
20133                 },
20134                 {
20135                     tag: 'td',
20136                     cn: [
20137                         {
20138                             tag: 'a',
20139                             href: '#',
20140                             cls: 'btn',
20141                             cn: [
20142                                 {
20143                                     tag: 'span',
20144                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20145                                 }
20146                             ]
20147                         }
20148                     ]
20149                 },
20150                 {
20151                     tag: 'td',
20152                     cls: 'separator'
20153                 }
20154             ]
20155         });
20156         
20157     },
20158     
20159     update: function()
20160     {
20161         
20162         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20163         
20164         this.fill();
20165     },
20166     
20167     fill: function() 
20168     {
20169         var hours = this.time.getHours();
20170         var minutes = this.time.getMinutes();
20171         var period = 'AM';
20172         
20173         if(hours > 11){
20174             period = 'PM';
20175         }
20176         
20177         if(hours == 0){
20178             hours = 12;
20179         }
20180         
20181         
20182         if(hours > 12){
20183             hours = hours - 12;
20184         }
20185         
20186         if(hours < 10){
20187             hours = '0' + hours;
20188         }
20189         
20190         if(minutes < 10){
20191             minutes = '0' + minutes;
20192         }
20193         
20194         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20195         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20196         this.pop.select('button', true).first().dom.innerHTML = period;
20197         
20198     },
20199     
20200     place: function()
20201     {   
20202         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20203         
20204         var cls = ['bottom'];
20205         
20206         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20207             cls.pop();
20208             cls.push('top');
20209         }
20210         
20211         cls.push('right');
20212         
20213         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20214             cls.pop();
20215             cls.push('left');
20216         }
20217         
20218         this.picker().addClass(cls.join('-'));
20219         
20220         var _this = this;
20221         
20222         Roo.each(cls, function(c){
20223             if(c == 'bottom'){
20224                 _this.picker().setTop(_this.inputEl().getHeight());
20225                 return;
20226             }
20227             if(c == 'top'){
20228                 _this.picker().setTop(0 - _this.picker().getHeight());
20229                 return;
20230             }
20231             
20232             if(c == 'left'){
20233                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20234                 return;
20235             }
20236             if(c == 'right'){
20237                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20238                 return;
20239             }
20240         });
20241         
20242     },
20243   
20244     onFocus : function()
20245     {
20246         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20247         this.show();
20248     },
20249     
20250     onBlur : function()
20251     {
20252         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20253         this.hide();
20254     },
20255     
20256     show : function()
20257     {
20258         this.picker().show();
20259         this.pop.show();
20260         this.update();
20261         this.place();
20262         
20263         this.fireEvent('show', this, this.date);
20264     },
20265     
20266     hide : function()
20267     {
20268         this.picker().hide();
20269         this.pop.hide();
20270         
20271         this.fireEvent('hide', this, this.date);
20272     },
20273     
20274     setTime : function()
20275     {
20276         this.hide();
20277         this.setValue(this.time.format(this.format));
20278         
20279         this.fireEvent('select', this, this.date);
20280         
20281         
20282     },
20283     
20284     onMousedown: function(e){
20285         e.stopPropagation();
20286         e.preventDefault();
20287     },
20288     
20289     onIncrementHours: function()
20290     {
20291         Roo.log('onIncrementHours');
20292         this.time = this.time.add(Date.HOUR, 1);
20293         this.update();
20294         
20295     },
20296     
20297     onDecrementHours: function()
20298     {
20299         Roo.log('onDecrementHours');
20300         this.time = this.time.add(Date.HOUR, -1);
20301         this.update();
20302     },
20303     
20304     onIncrementMinutes: function()
20305     {
20306         Roo.log('onIncrementMinutes');
20307         this.time = this.time.add(Date.MINUTE, 1);
20308         this.update();
20309     },
20310     
20311     onDecrementMinutes: function()
20312     {
20313         Roo.log('onDecrementMinutes');
20314         this.time = this.time.add(Date.MINUTE, -1);
20315         this.update();
20316     },
20317     
20318     onTogglePeriod: function()
20319     {
20320         Roo.log('onTogglePeriod');
20321         this.time = this.time.add(Date.HOUR, 12);
20322         this.update();
20323     }
20324     
20325    
20326 });
20327
20328 Roo.apply(Roo.bootstrap.TimeField,  {
20329     
20330     content : {
20331         tag: 'tbody',
20332         cn: [
20333             {
20334                 tag: 'tr',
20335                 cn: [
20336                 {
20337                     tag: 'td',
20338                     colspan: '7'
20339                 }
20340                 ]
20341             }
20342         ]
20343     },
20344     
20345     footer : {
20346         tag: 'tfoot',
20347         cn: [
20348             {
20349                 tag: 'tr',
20350                 cn: [
20351                 {
20352                     tag: 'th',
20353                     colspan: '7',
20354                     cls: '',
20355                     cn: [
20356                         {
20357                             tag: 'button',
20358                             cls: 'btn btn-info ok',
20359                             html: 'OK'
20360                         }
20361                     ]
20362                 }
20363
20364                 ]
20365             }
20366         ]
20367     }
20368 });
20369
20370 Roo.apply(Roo.bootstrap.TimeField,  {
20371   
20372     template : {
20373         tag: 'div',
20374         cls: 'datepicker dropdown-menu',
20375         cn: [
20376             {
20377                 tag: 'div',
20378                 cls: 'datepicker-time',
20379                 cn: [
20380                 {
20381                     tag: 'table',
20382                     cls: 'table-condensed',
20383                     cn:[
20384                     Roo.bootstrap.TimeField.content,
20385                     Roo.bootstrap.TimeField.footer
20386                     ]
20387                 }
20388                 ]
20389             }
20390         ]
20391     }
20392 });
20393
20394  
20395
20396  /*
20397  * - LGPL
20398  *
20399  * MonthField
20400  * 
20401  */
20402
20403 /**
20404  * @class Roo.bootstrap.MonthField
20405  * @extends Roo.bootstrap.Input
20406  * Bootstrap MonthField class
20407  * 
20408  * @cfg {String} language default en
20409  * 
20410  * @constructor
20411  * Create a new MonthField
20412  * @param {Object} config The config object
20413  */
20414
20415 Roo.bootstrap.MonthField = function(config){
20416     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20417     
20418     this.addEvents({
20419         /**
20420          * @event show
20421          * Fires when this field show.
20422          * @param {Roo.bootstrap.MonthField} this
20423          * @param {Mixed} date The date value
20424          */
20425         show : true,
20426         /**
20427          * @event show
20428          * Fires when this field hide.
20429          * @param {Roo.bootstrap.MonthField} this
20430          * @param {Mixed} date The date value
20431          */
20432         hide : true,
20433         /**
20434          * @event select
20435          * Fires when select a date.
20436          * @param {Roo.bootstrap.MonthField} this
20437          * @param {String} oldvalue The old value
20438          * @param {String} newvalue The new value
20439          */
20440         select : true
20441     });
20442 };
20443
20444 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20445     
20446     onRender: function(ct, position)
20447     {
20448         
20449         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20450         
20451         this.language = this.language || 'en';
20452         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20453         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20454         
20455         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20456         this.isInline = false;
20457         this.isInput = true;
20458         this.component = this.el.select('.add-on', true).first() || false;
20459         this.component = (this.component && this.component.length === 0) ? false : this.component;
20460         this.hasInput = this.component && this.inputEL().length;
20461         
20462         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20463         
20464         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20465         
20466         this.picker().on('mousedown', this.onMousedown, this);
20467         this.picker().on('click', this.onClick, this);
20468         
20469         this.picker().addClass('datepicker-dropdown');
20470         
20471         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20472             v.setStyle('width', '189px');
20473         });
20474         
20475         this.fillMonths();
20476         
20477         this.update();
20478         
20479         if(this.isInline) {
20480             this.show();
20481         }
20482         
20483     },
20484     
20485     setValue: function(v, suppressEvent)
20486     {   
20487         var o = this.getValue();
20488         
20489         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20490         
20491         this.update();
20492
20493         if(suppressEvent !== true){
20494             this.fireEvent('select', this, o, v);
20495         }
20496         
20497     },
20498     
20499     getValue: function()
20500     {
20501         return this.value;
20502     },
20503     
20504     onClick: function(e) 
20505     {
20506         e.stopPropagation();
20507         e.preventDefault();
20508         
20509         var target = e.getTarget();
20510         
20511         if(target.nodeName.toLowerCase() === 'i'){
20512             target = Roo.get(target).dom.parentNode;
20513         }
20514         
20515         var nodeName = target.nodeName;
20516         var className = target.className;
20517         var html = target.innerHTML;
20518         
20519         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20520             return;
20521         }
20522         
20523         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20524         
20525         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20526         
20527         this.hide();
20528                         
20529     },
20530     
20531     picker : function()
20532     {
20533         return this.pickerEl;
20534     },
20535     
20536     fillMonths: function()
20537     {    
20538         var i = 0;
20539         var months = this.picker().select('>.datepicker-months td', true).first();
20540         
20541         months.dom.innerHTML = '';
20542         
20543         while (i < 12) {
20544             var month = {
20545                 tag: 'span',
20546                 cls: 'month',
20547                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20548             };
20549             
20550             months.createChild(month);
20551         }
20552         
20553     },
20554     
20555     update: function()
20556     {
20557         var _this = this;
20558         
20559         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20560             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20561         }
20562         
20563         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20564             e.removeClass('active');
20565             
20566             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20567                 e.addClass('active');
20568             }
20569         })
20570     },
20571     
20572     place: function()
20573     {
20574         if(this.isInline) {
20575             return;
20576         }
20577         
20578         this.picker().removeClass(['bottom', 'top']);
20579         
20580         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20581             /*
20582              * place to the top of element!
20583              *
20584              */
20585             
20586             this.picker().addClass('top');
20587             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20588             
20589             return;
20590         }
20591         
20592         this.picker().addClass('bottom');
20593         
20594         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20595     },
20596     
20597     onFocus : function()
20598     {
20599         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20600         this.show();
20601     },
20602     
20603     onBlur : function()
20604     {
20605         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20606         
20607         var d = this.inputEl().getValue();
20608         
20609         this.setValue(d);
20610                 
20611         this.hide();
20612     },
20613     
20614     show : function()
20615     {
20616         this.picker().show();
20617         this.picker().select('>.datepicker-months', true).first().show();
20618         this.update();
20619         this.place();
20620         
20621         this.fireEvent('show', this, this.date);
20622     },
20623     
20624     hide : function()
20625     {
20626         if(this.isInline) {
20627             return;
20628         }
20629         this.picker().hide();
20630         this.fireEvent('hide', this, this.date);
20631         
20632     },
20633     
20634     onMousedown: function(e)
20635     {
20636         e.stopPropagation();
20637         e.preventDefault();
20638     },
20639     
20640     keyup: function(e)
20641     {
20642         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20643         this.update();
20644     },
20645
20646     fireKey: function(e)
20647     {
20648         if (!this.picker().isVisible()){
20649             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20650                 this.show();
20651             }
20652             return;
20653         }
20654         
20655         var dir;
20656         
20657         switch(e.keyCode){
20658             case 27: // escape
20659                 this.hide();
20660                 e.preventDefault();
20661                 break;
20662             case 37: // left
20663             case 39: // right
20664                 dir = e.keyCode == 37 ? -1 : 1;
20665                 
20666                 this.vIndex = this.vIndex + dir;
20667                 
20668                 if(this.vIndex < 0){
20669                     this.vIndex = 0;
20670                 }
20671                 
20672                 if(this.vIndex > 11){
20673                     this.vIndex = 11;
20674                 }
20675                 
20676                 if(isNaN(this.vIndex)){
20677                     this.vIndex = 0;
20678                 }
20679                 
20680                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20681                 
20682                 break;
20683             case 38: // up
20684             case 40: // down
20685                 
20686                 dir = e.keyCode == 38 ? -1 : 1;
20687                 
20688                 this.vIndex = this.vIndex + dir * 4;
20689                 
20690                 if(this.vIndex < 0){
20691                     this.vIndex = 0;
20692                 }
20693                 
20694                 if(this.vIndex > 11){
20695                     this.vIndex = 11;
20696                 }
20697                 
20698                 if(isNaN(this.vIndex)){
20699                     this.vIndex = 0;
20700                 }
20701                 
20702                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20703                 break;
20704                 
20705             case 13: // enter
20706                 
20707                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20708                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20709                 }
20710                 
20711                 this.hide();
20712                 e.preventDefault();
20713                 break;
20714             case 9: // tab
20715                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20716                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20717                 }
20718                 this.hide();
20719                 break;
20720             case 16: // shift
20721             case 17: // ctrl
20722             case 18: // alt
20723                 break;
20724             default :
20725                 this.hide();
20726                 
20727         }
20728     },
20729     
20730     remove: function() 
20731     {
20732         this.picker().remove();
20733     }
20734    
20735 });
20736
20737 Roo.apply(Roo.bootstrap.MonthField,  {
20738     
20739     content : {
20740         tag: 'tbody',
20741         cn: [
20742         {
20743             tag: 'tr',
20744             cn: [
20745             {
20746                 tag: 'td',
20747                 colspan: '7'
20748             }
20749             ]
20750         }
20751         ]
20752     },
20753     
20754     dates:{
20755         en: {
20756             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20757             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20758         }
20759     }
20760 });
20761
20762 Roo.apply(Roo.bootstrap.MonthField,  {
20763   
20764     template : {
20765         tag: 'div',
20766         cls: 'datepicker dropdown-menu roo-dynamic',
20767         cn: [
20768             {
20769                 tag: 'div',
20770                 cls: 'datepicker-months',
20771                 cn: [
20772                 {
20773                     tag: 'table',
20774                     cls: 'table-condensed',
20775                     cn:[
20776                         Roo.bootstrap.DateField.content
20777                     ]
20778                 }
20779                 ]
20780             }
20781         ]
20782     }
20783 });
20784
20785  
20786
20787  
20788  /*
20789  * - LGPL
20790  *
20791  * CheckBox
20792  * 
20793  */
20794
20795 /**
20796  * @class Roo.bootstrap.CheckBox
20797  * @extends Roo.bootstrap.Input
20798  * Bootstrap CheckBox class
20799  * 
20800  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20801  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20802  * @cfg {String} boxLabel The text that appears beside the checkbox
20803  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20804  * @cfg {Boolean} checked initnal the element
20805  * @cfg {Boolean} inline inline the element (default false)
20806  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20807  * @cfg {String} tooltip label tooltip
20808  * 
20809  * @constructor
20810  * Create a new CheckBox
20811  * @param {Object} config The config object
20812  */
20813
20814 Roo.bootstrap.CheckBox = function(config){
20815     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20816    
20817     this.addEvents({
20818         /**
20819         * @event check
20820         * Fires when the element is checked or unchecked.
20821         * @param {Roo.bootstrap.CheckBox} this This input
20822         * @param {Boolean} checked The new checked value
20823         */
20824        check : true,
20825        /**
20826         * @event click
20827         * Fires when the element is click.
20828         * @param {Roo.bootstrap.CheckBox} this This input
20829         */
20830        click : true
20831     });
20832     
20833 };
20834
20835 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20836   
20837     inputType: 'checkbox',
20838     inputValue: 1,
20839     valueOff: 0,
20840     boxLabel: false,
20841     checked: false,
20842     weight : false,
20843     inline: false,
20844     tooltip : '',
20845     
20846     getAutoCreate : function()
20847     {
20848         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20849         
20850         var id = Roo.id();
20851         
20852         var cfg = {};
20853         
20854         cfg.cls = 'form-group ' + this.inputType; //input-group
20855         
20856         if(this.inline){
20857             cfg.cls += ' ' + this.inputType + '-inline';
20858         }
20859         
20860         var input =  {
20861             tag: 'input',
20862             id : id,
20863             type : this.inputType,
20864             value : this.inputValue,
20865             cls : 'roo-' + this.inputType, //'form-box',
20866             placeholder : this.placeholder || ''
20867             
20868         };
20869         
20870         if(this.inputType != 'radio'){
20871             var hidden =  {
20872                 tag: 'input',
20873                 type : 'hidden',
20874                 cls : 'roo-hidden-value',
20875                 value : this.checked ? this.inputValue : this.valueOff
20876             };
20877         }
20878         
20879             
20880         if (this.weight) { // Validity check?
20881             cfg.cls += " " + this.inputType + "-" + this.weight;
20882         }
20883         
20884         if (this.disabled) {
20885             input.disabled=true;
20886         }
20887         
20888         if(this.checked){
20889             input.checked = this.checked;
20890         }
20891         
20892         if (this.name) {
20893             
20894             input.name = this.name;
20895             
20896             if(this.inputType != 'radio'){
20897                 hidden.name = this.name;
20898                 input.name = '_hidden_' + this.name;
20899             }
20900         }
20901         
20902         if (this.size) {
20903             input.cls += ' input-' + this.size;
20904         }
20905         
20906         var settings=this;
20907         
20908         ['xs','sm','md','lg'].map(function(size){
20909             if (settings[size]) {
20910                 cfg.cls += ' col-' + size + '-' + settings[size];
20911             }
20912         });
20913         
20914         var inputblock = input;
20915          
20916         if (this.before || this.after) {
20917             
20918             inputblock = {
20919                 cls : 'input-group',
20920                 cn :  [] 
20921             };
20922             
20923             if (this.before) {
20924                 inputblock.cn.push({
20925                     tag :'span',
20926                     cls : 'input-group-addon',
20927                     html : this.before
20928                 });
20929             }
20930             
20931             inputblock.cn.push(input);
20932             
20933             if(this.inputType != 'radio'){
20934                 inputblock.cn.push(hidden);
20935             }
20936             
20937             if (this.after) {
20938                 inputblock.cn.push({
20939                     tag :'span',
20940                     cls : 'input-group-addon',
20941                     html : this.after
20942                 });
20943             }
20944             
20945         }
20946         
20947         if (align ==='left' && this.fieldLabel.length) {
20948 //                Roo.log("left and has label");
20949             cfg.cn = [
20950                 {
20951                     tag: 'label',
20952                     'for' :  id,
20953                     cls : 'control-label',
20954                     html : this.fieldLabel
20955                 },
20956                 {
20957                     cls : "", 
20958                     cn: [
20959                         inputblock
20960                     ]
20961                 }
20962             ];
20963             
20964             if(this.labelWidth > 12){
20965                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20966             }
20967             
20968             if(this.labelWidth < 13 && this.labelmd == 0){
20969                 this.labelmd = this.labelWidth;
20970             }
20971             
20972             if(this.labellg > 0){
20973                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20974                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20975             }
20976             
20977             if(this.labelmd > 0){
20978                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20979                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20980             }
20981             
20982             if(this.labelsm > 0){
20983                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20984                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20985             }
20986             
20987             if(this.labelxs > 0){
20988                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20989                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20990             }
20991             
20992         } else if ( this.fieldLabel.length) {
20993 //                Roo.log(" label");
20994                 cfg.cn = [
20995                    
20996                     {
20997                         tag: this.boxLabel ? 'span' : 'label',
20998                         'for': id,
20999                         cls: 'control-label box-input-label',
21000                         //cls : 'input-group-addon',
21001                         html : this.fieldLabel
21002                     },
21003                     
21004                     inputblock
21005                     
21006                 ];
21007
21008         } else {
21009             
21010 //                Roo.log(" no label && no align");
21011                 cfg.cn = [  inputblock ] ;
21012                 
21013                 
21014         }
21015         
21016         if(this.boxLabel){
21017              var boxLabelCfg = {
21018                 tag: 'label',
21019                 //'for': id, // box label is handled by onclick - so no for...
21020                 cls: 'box-label',
21021                 html: this.boxLabel
21022             };
21023             
21024             if(this.tooltip){
21025                 boxLabelCfg.tooltip = this.tooltip;
21026             }
21027              
21028             cfg.cn.push(boxLabelCfg);
21029         }
21030         
21031         if(this.inputType != 'radio'){
21032             cfg.cn.push(hidden);
21033         }
21034         
21035         return cfg;
21036         
21037     },
21038     
21039     /**
21040      * return the real input element.
21041      */
21042     inputEl: function ()
21043     {
21044         return this.el.select('input.roo-' + this.inputType,true).first();
21045     },
21046     hiddenEl: function ()
21047     {
21048         return this.el.select('input.roo-hidden-value',true).first();
21049     },
21050     
21051     labelEl: function()
21052     {
21053         return this.el.select('label.control-label',true).first();
21054     },
21055     /* depricated... */
21056     
21057     label: function()
21058     {
21059         return this.labelEl();
21060     },
21061     
21062     boxLabelEl: function()
21063     {
21064         return this.el.select('label.box-label',true).first();
21065     },
21066     
21067     initEvents : function()
21068     {
21069 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21070         
21071         this.inputEl().on('click', this.onClick,  this);
21072         
21073         if (this.boxLabel) { 
21074             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21075         }
21076         
21077         this.startValue = this.getValue();
21078         
21079         if(this.groupId){
21080             Roo.bootstrap.CheckBox.register(this);
21081         }
21082     },
21083     
21084     onClick : function(e)
21085     {   
21086         if(this.fireEvent('click', this, e) !== false){
21087             this.setChecked(!this.checked);
21088         }
21089         
21090     },
21091     
21092     setChecked : function(state,suppressEvent)
21093     {
21094         this.startValue = this.getValue();
21095
21096         if(this.inputType == 'radio'){
21097             
21098             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21099                 e.dom.checked = false;
21100             });
21101             
21102             this.inputEl().dom.checked = true;
21103             
21104             this.inputEl().dom.value = this.inputValue;
21105             
21106             if(suppressEvent !== true){
21107                 this.fireEvent('check', this, true);
21108             }
21109             
21110             this.validate();
21111             
21112             return;
21113         }
21114         
21115         this.checked = state;
21116         
21117         this.inputEl().dom.checked = state;
21118         
21119         
21120         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21121         
21122         if(suppressEvent !== true){
21123             this.fireEvent('check', this, state);
21124         }
21125         
21126         this.validate();
21127     },
21128     
21129     getValue : function()
21130     {
21131         if(this.inputType == 'radio'){
21132             return this.getGroupValue();
21133         }
21134         
21135         return this.hiddenEl().dom.value;
21136         
21137     },
21138     
21139     getGroupValue : function()
21140     {
21141         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21142             return '';
21143         }
21144         
21145         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21146     },
21147     
21148     setValue : function(v,suppressEvent)
21149     {
21150         if(this.inputType == 'radio'){
21151             this.setGroupValue(v, suppressEvent);
21152             return;
21153         }
21154         
21155         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21156         
21157         this.validate();
21158     },
21159     
21160     setGroupValue : function(v, suppressEvent)
21161     {
21162         this.startValue = this.getValue();
21163         
21164         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21165             e.dom.checked = false;
21166             
21167             if(e.dom.value == v){
21168                 e.dom.checked = true;
21169             }
21170         });
21171         
21172         if(suppressEvent !== true){
21173             this.fireEvent('check', this, true);
21174         }
21175
21176         this.validate();
21177         
21178         return;
21179     },
21180     
21181     validate : function()
21182     {
21183         if(this.getVisibilityEl().hasClass('hidden')){
21184             return true;
21185         }
21186         
21187         if(
21188                 this.disabled || 
21189                 (this.inputType == 'radio' && this.validateRadio()) ||
21190                 (this.inputType == 'checkbox' && this.validateCheckbox())
21191         ){
21192             this.markValid();
21193             return true;
21194         }
21195         
21196         this.markInvalid();
21197         return false;
21198     },
21199     
21200     validateRadio : function()
21201     {
21202         if(this.getVisibilityEl().hasClass('hidden')){
21203             return true;
21204         }
21205         
21206         if(this.allowBlank){
21207             return true;
21208         }
21209         
21210         var valid = false;
21211         
21212         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21213             if(!e.dom.checked){
21214                 return;
21215             }
21216             
21217             valid = true;
21218             
21219             return false;
21220         });
21221         
21222         return valid;
21223     },
21224     
21225     validateCheckbox : function()
21226     {
21227         if(!this.groupId){
21228             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21229             //return (this.getValue() == this.inputValue) ? true : false;
21230         }
21231         
21232         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21233         
21234         if(!group){
21235             return false;
21236         }
21237         
21238         var r = false;
21239         
21240         for(var i in group){
21241             if(group[i].el.isVisible(true)){
21242                 r = false;
21243                 break;
21244             }
21245             
21246             r = true;
21247         }
21248         
21249         for(var i in group){
21250             if(r){
21251                 break;
21252             }
21253             
21254             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21255         }
21256         
21257         return r;
21258     },
21259     
21260     /**
21261      * Mark this field as valid
21262      */
21263     markValid : function()
21264     {
21265         var _this = this;
21266         
21267         this.fireEvent('valid', this);
21268         
21269         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21270         
21271         if(this.groupId){
21272             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21273         }
21274         
21275         if(label){
21276             label.markValid();
21277         }
21278
21279         if(this.inputType == 'radio'){
21280             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21281                 var fg = e.findParent('.form-group', false, true);
21282                 if (Roo.bootstrap.version == 3) {
21283                     fg.removeClass([_this.invalidClass, _this.validClass]);
21284                     fg.addClass(_this.validClass);
21285                 } else {
21286                     fg.removeClass(['is-valid', 'is-invalid']);
21287                     fg.addClass('is-valid');
21288                 }
21289             });
21290             
21291             return;
21292         }
21293
21294         if(!this.groupId){
21295             var fg = this.el.findParent('.form-group', false, true);
21296             if (Roo.bootstrap.version == 3) {
21297                 fg.removeClass([this.invalidClass, this.validClass]);
21298                 fg.addClass(this.validClass);
21299             } else {
21300                 fg.removeClass(['is-valid', 'is-invalid']);
21301                 fg.addClass('is-valid');
21302             }
21303             return;
21304         }
21305         
21306         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21307         
21308         if(!group){
21309             return;
21310         }
21311         
21312         for(var i in group){
21313             var fg = group[i].el.findParent('.form-group', false, true);
21314             if (Roo.bootstrap.version == 3) {
21315                 fg.removeClass([this.invalidClass, this.validClass]);
21316                 fg.addClass(this.validClass);
21317             } else {
21318                 fg.removeClass(['is-valid', 'is-invalid']);
21319                 fg.addClass('is-valid');
21320             }
21321         }
21322     },
21323     
21324      /**
21325      * Mark this field as invalid
21326      * @param {String} msg The validation message
21327      */
21328     markInvalid : function(msg)
21329     {
21330         if(this.allowBlank){
21331             return;
21332         }
21333         
21334         var _this = this;
21335         
21336         this.fireEvent('invalid', this, msg);
21337         
21338         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21339         
21340         if(this.groupId){
21341             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21342         }
21343         
21344         if(label){
21345             label.markInvalid();
21346         }
21347             
21348         if(this.inputType == 'radio'){
21349             
21350             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21351                 var fg = e.findParent('.form-group', false, true);
21352                 if (Roo.bootstrap.version == 3) {
21353                     fg.removeClass([_this.invalidClass, _this.validClass]);
21354                     fg.addClass(_this.invalidClass);
21355                 } else {
21356                     fg.removeClass(['is-invalid', 'is-valid']);
21357                     fg.addClass('is-invalid');
21358                 }
21359             });
21360             
21361             return;
21362         }
21363         
21364         if(!this.groupId){
21365             var fg = this.el.findParent('.form-group', false, true);
21366             if (Roo.bootstrap.version == 3) {
21367                 fg.removeClass([_this.invalidClass, _this.validClass]);
21368                 fg.addClass(_this.invalidClass);
21369             } else {
21370                 fg.removeClass(['is-invalid', 'is-valid']);
21371                 fg.addClass('is-invalid');
21372             }
21373             return;
21374         }
21375         
21376         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21377         
21378         if(!group){
21379             return;
21380         }
21381         
21382         for(var i in group){
21383             var fg = group[i].el.findParent('.form-group', false, true);
21384             if (Roo.bootstrap.version == 3) {
21385                 fg.removeClass([_this.invalidClass, _this.validClass]);
21386                 fg.addClass(_this.invalidClass);
21387             } else {
21388                 fg.removeClass(['is-invalid', 'is-valid']);
21389                 fg.addClass('is-invalid');
21390             }
21391         }
21392         
21393     },
21394     
21395     clearInvalid : function()
21396     {
21397         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21398         
21399         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21400         
21401         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21402         
21403         if (label && label.iconEl) {
21404             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21405             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21406         }
21407     },
21408     
21409     disable : function()
21410     {
21411         if(this.inputType != 'radio'){
21412             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21413             return;
21414         }
21415         
21416         var _this = this;
21417         
21418         if(this.rendered){
21419             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21420                 _this.getActionEl().addClass(this.disabledClass);
21421                 e.dom.disabled = true;
21422             });
21423         }
21424         
21425         this.disabled = true;
21426         this.fireEvent("disable", this);
21427         return this;
21428     },
21429
21430     enable : function()
21431     {
21432         if(this.inputType != 'radio'){
21433             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21434             return;
21435         }
21436         
21437         var _this = this;
21438         
21439         if(this.rendered){
21440             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21441                 _this.getActionEl().removeClass(this.disabledClass);
21442                 e.dom.disabled = false;
21443             });
21444         }
21445         
21446         this.disabled = false;
21447         this.fireEvent("enable", this);
21448         return this;
21449     },
21450     
21451     setBoxLabel : function(v)
21452     {
21453         this.boxLabel = v;
21454         
21455         if(this.rendered){
21456             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21457         }
21458     }
21459
21460 });
21461
21462 Roo.apply(Roo.bootstrap.CheckBox, {
21463     
21464     groups: {},
21465     
21466      /**
21467     * register a CheckBox Group
21468     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21469     */
21470     register : function(checkbox)
21471     {
21472         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21473             this.groups[checkbox.groupId] = {};
21474         }
21475         
21476         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21477             return;
21478         }
21479         
21480         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21481         
21482     },
21483     /**
21484     * fetch a CheckBox Group based on the group ID
21485     * @param {string} the group ID
21486     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21487     */
21488     get: function(groupId) {
21489         if (typeof(this.groups[groupId]) == 'undefined') {
21490             return false;
21491         }
21492         
21493         return this.groups[groupId] ;
21494     }
21495     
21496     
21497 });
21498 /*
21499  * - LGPL
21500  *
21501  * RadioItem
21502  * 
21503  */
21504
21505 /**
21506  * @class Roo.bootstrap.Radio
21507  * @extends Roo.bootstrap.Component
21508  * Bootstrap Radio class
21509  * @cfg {String} boxLabel - the label associated
21510  * @cfg {String} value - the value of radio
21511  * 
21512  * @constructor
21513  * Create a new Radio
21514  * @param {Object} config The config object
21515  */
21516 Roo.bootstrap.Radio = function(config){
21517     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21518     
21519 };
21520
21521 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21522     
21523     boxLabel : '',
21524     
21525     value : '',
21526     
21527     getAutoCreate : function()
21528     {
21529         var cfg = {
21530             tag : 'div',
21531             cls : 'form-group radio',
21532             cn : [
21533                 {
21534                     tag : 'label',
21535                     cls : 'box-label',
21536                     html : this.boxLabel
21537                 }
21538             ]
21539         };
21540         
21541         return cfg;
21542     },
21543     
21544     initEvents : function() 
21545     {
21546         this.parent().register(this);
21547         
21548         this.el.on('click', this.onClick, this);
21549         
21550     },
21551     
21552     onClick : function(e)
21553     {
21554         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21555             this.setChecked(true);
21556         }
21557     },
21558     
21559     setChecked : function(state, suppressEvent)
21560     {
21561         this.parent().setValue(this.value, suppressEvent);
21562         
21563     },
21564     
21565     setBoxLabel : function(v)
21566     {
21567         this.boxLabel = v;
21568         
21569         if(this.rendered){
21570             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21571         }
21572     }
21573     
21574 });
21575  
21576
21577  /*
21578  * - LGPL
21579  *
21580  * Input
21581  * 
21582  */
21583
21584 /**
21585  * @class Roo.bootstrap.SecurePass
21586  * @extends Roo.bootstrap.Input
21587  * Bootstrap SecurePass class
21588  *
21589  * 
21590  * @constructor
21591  * Create a new SecurePass
21592  * @param {Object} config The config object
21593  */
21594  
21595 Roo.bootstrap.SecurePass = function (config) {
21596     // these go here, so the translation tool can replace them..
21597     this.errors = {
21598         PwdEmpty: "Please type a password, and then retype it to confirm.",
21599         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21600         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21601         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21602         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21603         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21604         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21605         TooWeak: "Your password is Too Weak."
21606     },
21607     this.meterLabel = "Password strength:";
21608     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21609     this.meterClass = [
21610         "roo-password-meter-tooweak", 
21611         "roo-password-meter-weak", 
21612         "roo-password-meter-medium", 
21613         "roo-password-meter-strong", 
21614         "roo-password-meter-grey"
21615     ];
21616     
21617     this.errors = {};
21618     
21619     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21620 }
21621
21622 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21623     /**
21624      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21625      * {
21626      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21627      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21628      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21629      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21630      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21631      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21632      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21633      * })
21634      */
21635     // private
21636     
21637     meterWidth: 300,
21638     errorMsg :'',    
21639     errors: false,
21640     imageRoot: '/',
21641     /**
21642      * @cfg {String/Object} Label for the strength meter (defaults to
21643      * 'Password strength:')
21644      */
21645     // private
21646     meterLabel: '',
21647     /**
21648      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21649      * ['Weak', 'Medium', 'Strong'])
21650      */
21651     // private    
21652     pwdStrengths: false,    
21653     // private
21654     strength: 0,
21655     // private
21656     _lastPwd: null,
21657     // private
21658     kCapitalLetter: 0,
21659     kSmallLetter: 1,
21660     kDigit: 2,
21661     kPunctuation: 3,
21662     
21663     insecure: false,
21664     // private
21665     initEvents: function ()
21666     {
21667         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21668
21669         if (this.el.is('input[type=password]') && Roo.isSafari) {
21670             this.el.on('keydown', this.SafariOnKeyDown, this);
21671         }
21672
21673         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21674     },
21675     // private
21676     onRender: function (ct, position)
21677     {
21678         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21679         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21680         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21681
21682         this.trigger.createChild({
21683                    cn: [
21684                     {
21685                     //id: 'PwdMeter',
21686                     tag: 'div',
21687                     cls: 'roo-password-meter-grey col-xs-12',
21688                     style: {
21689                         //width: 0,
21690                         //width: this.meterWidth + 'px'                                                
21691                         }
21692                     },
21693                     {                            
21694                          cls: 'roo-password-meter-text'                          
21695                     }
21696                 ]            
21697         });
21698
21699          
21700         if (this.hideTrigger) {
21701             this.trigger.setDisplayed(false);
21702         }
21703         this.setSize(this.width || '', this.height || '');
21704     },
21705     // private
21706     onDestroy: function ()
21707     {
21708         if (this.trigger) {
21709             this.trigger.removeAllListeners();
21710             this.trigger.remove();
21711         }
21712         if (this.wrap) {
21713             this.wrap.remove();
21714         }
21715         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21716     },
21717     // private
21718     checkStrength: function ()
21719     {
21720         var pwd = this.inputEl().getValue();
21721         if (pwd == this._lastPwd) {
21722             return;
21723         }
21724
21725         var strength;
21726         if (this.ClientSideStrongPassword(pwd)) {
21727             strength = 3;
21728         } else if (this.ClientSideMediumPassword(pwd)) {
21729             strength = 2;
21730         } else if (this.ClientSideWeakPassword(pwd)) {
21731             strength = 1;
21732         } else {
21733             strength = 0;
21734         }
21735         
21736         Roo.log('strength1: ' + strength);
21737         
21738         //var pm = this.trigger.child('div/div/div').dom;
21739         var pm = this.trigger.child('div/div');
21740         pm.removeClass(this.meterClass);
21741         pm.addClass(this.meterClass[strength]);
21742                 
21743         
21744         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21745                 
21746         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21747         
21748         this._lastPwd = pwd;
21749     },
21750     reset: function ()
21751     {
21752         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21753         
21754         this._lastPwd = '';
21755         
21756         var pm = this.trigger.child('div/div');
21757         pm.removeClass(this.meterClass);
21758         pm.addClass('roo-password-meter-grey');        
21759         
21760         
21761         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21762         
21763         pt.innerHTML = '';
21764         this.inputEl().dom.type='password';
21765     },
21766     // private
21767     validateValue: function (value)
21768     {
21769         
21770         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21771             return false;
21772         }
21773         if (value.length == 0) {
21774             if (this.allowBlank) {
21775                 this.clearInvalid();
21776                 return true;
21777             }
21778
21779             this.markInvalid(this.errors.PwdEmpty);
21780             this.errorMsg = this.errors.PwdEmpty;
21781             return false;
21782         }
21783         
21784         if(this.insecure){
21785             return true;
21786         }
21787         
21788         if ('[\x21-\x7e]*'.match(value)) {
21789             this.markInvalid(this.errors.PwdBadChar);
21790             this.errorMsg = this.errors.PwdBadChar;
21791             return false;
21792         }
21793         if (value.length < 6) {
21794             this.markInvalid(this.errors.PwdShort);
21795             this.errorMsg = this.errors.PwdShort;
21796             return false;
21797         }
21798         if (value.length > 16) {
21799             this.markInvalid(this.errors.PwdLong);
21800             this.errorMsg = this.errors.PwdLong;
21801             return false;
21802         }
21803         var strength;
21804         if (this.ClientSideStrongPassword(value)) {
21805             strength = 3;
21806         } else if (this.ClientSideMediumPassword(value)) {
21807             strength = 2;
21808         } else if (this.ClientSideWeakPassword(value)) {
21809             strength = 1;
21810         } else {
21811             strength = 0;
21812         }
21813
21814         
21815         if (strength < 2) {
21816             //this.markInvalid(this.errors.TooWeak);
21817             this.errorMsg = this.errors.TooWeak;
21818             //return false;
21819         }
21820         
21821         
21822         console.log('strength2: ' + strength);
21823         
21824         //var pm = this.trigger.child('div/div/div').dom;
21825         
21826         var pm = this.trigger.child('div/div');
21827         pm.removeClass(this.meterClass);
21828         pm.addClass(this.meterClass[strength]);
21829                 
21830         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21831                 
21832         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21833         
21834         this.errorMsg = ''; 
21835         return true;
21836     },
21837     // private
21838     CharacterSetChecks: function (type)
21839     {
21840         this.type = type;
21841         this.fResult = false;
21842     },
21843     // private
21844     isctype: function (character, type)
21845     {
21846         switch (type) {  
21847             case this.kCapitalLetter:
21848                 if (character >= 'A' && character <= 'Z') {
21849                     return true;
21850                 }
21851                 break;
21852             
21853             case this.kSmallLetter:
21854                 if (character >= 'a' && character <= 'z') {
21855                     return true;
21856                 }
21857                 break;
21858             
21859             case this.kDigit:
21860                 if (character >= '0' && character <= '9') {
21861                     return true;
21862                 }
21863                 break;
21864             
21865             case this.kPunctuation:
21866                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21867                     return true;
21868                 }
21869                 break;
21870             
21871             default:
21872                 return false;
21873         }
21874
21875     },
21876     // private
21877     IsLongEnough: function (pwd, size)
21878     {
21879         return !(pwd == null || isNaN(size) || pwd.length < size);
21880     },
21881     // private
21882     SpansEnoughCharacterSets: function (word, nb)
21883     {
21884         if (!this.IsLongEnough(word, nb))
21885         {
21886             return false;
21887         }
21888
21889         var characterSetChecks = new Array(
21890             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21891             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21892         );
21893         
21894         for (var index = 0; index < word.length; ++index) {
21895             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21896                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21897                     characterSetChecks[nCharSet].fResult = true;
21898                     break;
21899                 }
21900             }
21901         }
21902
21903         var nCharSets = 0;
21904         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21905             if (characterSetChecks[nCharSet].fResult) {
21906                 ++nCharSets;
21907             }
21908         }
21909
21910         if (nCharSets < nb) {
21911             return false;
21912         }
21913         return true;
21914     },
21915     // private
21916     ClientSideStrongPassword: function (pwd)
21917     {
21918         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21919     },
21920     // private
21921     ClientSideMediumPassword: function (pwd)
21922     {
21923         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21924     },
21925     // private
21926     ClientSideWeakPassword: function (pwd)
21927     {
21928         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21929     }
21930           
21931 })//<script type="text/javascript">
21932
21933 /*
21934  * Based  Ext JS Library 1.1.1
21935  * Copyright(c) 2006-2007, Ext JS, LLC.
21936  * LGPL
21937  *
21938  */
21939  
21940 /**
21941  * @class Roo.HtmlEditorCore
21942  * @extends Roo.Component
21943  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21944  *
21945  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21946  */
21947
21948 Roo.HtmlEditorCore = function(config){
21949     
21950     
21951     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21952     
21953     
21954     this.addEvents({
21955         /**
21956          * @event initialize
21957          * Fires when the editor is fully initialized (including the iframe)
21958          * @param {Roo.HtmlEditorCore} this
21959          */
21960         initialize: true,
21961         /**
21962          * @event activate
21963          * Fires when the editor is first receives the focus. Any insertion must wait
21964          * until after this event.
21965          * @param {Roo.HtmlEditorCore} this
21966          */
21967         activate: true,
21968          /**
21969          * @event beforesync
21970          * Fires before the textarea is updated with content from the editor iframe. Return false
21971          * to cancel the sync.
21972          * @param {Roo.HtmlEditorCore} this
21973          * @param {String} html
21974          */
21975         beforesync: true,
21976          /**
21977          * @event beforepush
21978          * Fires before the iframe editor is updated with content from the textarea. Return false
21979          * to cancel the push.
21980          * @param {Roo.HtmlEditorCore} this
21981          * @param {String} html
21982          */
21983         beforepush: true,
21984          /**
21985          * @event sync
21986          * Fires when the textarea is updated with content from the editor iframe.
21987          * @param {Roo.HtmlEditorCore} this
21988          * @param {String} html
21989          */
21990         sync: true,
21991          /**
21992          * @event push
21993          * Fires when the iframe editor is updated with content from the textarea.
21994          * @param {Roo.HtmlEditorCore} this
21995          * @param {String} html
21996          */
21997         push: true,
21998         
21999         /**
22000          * @event editorevent
22001          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22002          * @param {Roo.HtmlEditorCore} this
22003          */
22004         editorevent: true
22005         
22006     });
22007     
22008     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22009     
22010     // defaults : white / black...
22011     this.applyBlacklists();
22012     
22013     
22014     
22015 };
22016
22017
22018 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22019
22020
22021      /**
22022      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22023      */
22024     
22025     owner : false,
22026     
22027      /**
22028      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22029      *                        Roo.resizable.
22030      */
22031     resizable : false,
22032      /**
22033      * @cfg {Number} height (in pixels)
22034      */   
22035     height: 300,
22036    /**
22037      * @cfg {Number} width (in pixels)
22038      */   
22039     width: 500,
22040     
22041     /**
22042      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22043      * 
22044      */
22045     stylesheets: false,
22046     
22047     // id of frame..
22048     frameId: false,
22049     
22050     // private properties
22051     validationEvent : false,
22052     deferHeight: true,
22053     initialized : false,
22054     activated : false,
22055     sourceEditMode : false,
22056     onFocus : Roo.emptyFn,
22057     iframePad:3,
22058     hideMode:'offsets',
22059     
22060     clearUp: true,
22061     
22062     // blacklist + whitelisted elements..
22063     black: false,
22064     white: false,
22065      
22066     bodyCls : '',
22067
22068     /**
22069      * Protected method that will not generally be called directly. It
22070      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22071      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22072      */
22073     getDocMarkup : function(){
22074         // body styles..
22075         var st = '';
22076         
22077         // inherit styels from page...?? 
22078         if (this.stylesheets === false) {
22079             
22080             Roo.get(document.head).select('style').each(function(node) {
22081                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22082             });
22083             
22084             Roo.get(document.head).select('link').each(function(node) { 
22085                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22086             });
22087             
22088         } else if (!this.stylesheets.length) {
22089                 // simple..
22090                 st = '<style type="text/css">' +
22091                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22092                    '</style>';
22093         } else { 
22094             st = '<style type="text/css">' +
22095                     this.stylesheets +
22096                 '</style>';
22097         }
22098         
22099         st +=  '<style type="text/css">' +
22100             'IMG { cursor: pointer } ' +
22101         '</style>';
22102
22103         var cls = 'roo-htmleditor-body';
22104         
22105         if(this.bodyCls.length){
22106             cls += ' ' + this.bodyCls;
22107         }
22108         
22109         return '<html><head>' + st  +
22110             //<style type="text/css">' +
22111             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22112             //'</style>' +
22113             ' </head><body class="' +  cls + '"></body></html>';
22114     },
22115
22116     // private
22117     onRender : function(ct, position)
22118     {
22119         var _t = this;
22120         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22121         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22122         
22123         
22124         this.el.dom.style.border = '0 none';
22125         this.el.dom.setAttribute('tabIndex', -1);
22126         this.el.addClass('x-hidden hide');
22127         
22128         
22129         
22130         if(Roo.isIE){ // fix IE 1px bogus margin
22131             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22132         }
22133        
22134         
22135         this.frameId = Roo.id();
22136         
22137          
22138         
22139         var iframe = this.owner.wrap.createChild({
22140             tag: 'iframe',
22141             cls: 'form-control', // bootstrap..
22142             id: this.frameId,
22143             name: this.frameId,
22144             frameBorder : 'no',
22145             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22146         }, this.el
22147         );
22148         
22149         
22150         this.iframe = iframe.dom;
22151
22152          this.assignDocWin();
22153         
22154         this.doc.designMode = 'on';
22155        
22156         this.doc.open();
22157         this.doc.write(this.getDocMarkup());
22158         this.doc.close();
22159
22160         
22161         var task = { // must defer to wait for browser to be ready
22162             run : function(){
22163                 //console.log("run task?" + this.doc.readyState);
22164                 this.assignDocWin();
22165                 if(this.doc.body || this.doc.readyState == 'complete'){
22166                     try {
22167                         this.doc.designMode="on";
22168                     } catch (e) {
22169                         return;
22170                     }
22171                     Roo.TaskMgr.stop(task);
22172                     this.initEditor.defer(10, this);
22173                 }
22174             },
22175             interval : 10,
22176             duration: 10000,
22177             scope: this
22178         };
22179         Roo.TaskMgr.start(task);
22180
22181     },
22182
22183     // private
22184     onResize : function(w, h)
22185     {
22186          Roo.log('resize: ' +w + ',' + h );
22187         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22188         if(!this.iframe){
22189             return;
22190         }
22191         if(typeof w == 'number'){
22192             
22193             this.iframe.style.width = w + 'px';
22194         }
22195         if(typeof h == 'number'){
22196             
22197             this.iframe.style.height = h + 'px';
22198             if(this.doc){
22199                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22200             }
22201         }
22202         
22203     },
22204
22205     /**
22206      * Toggles the editor between standard and source edit mode.
22207      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22208      */
22209     toggleSourceEdit : function(sourceEditMode){
22210         
22211         this.sourceEditMode = sourceEditMode === true;
22212         
22213         if(this.sourceEditMode){
22214  
22215             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22216             
22217         }else{
22218             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22219             //this.iframe.className = '';
22220             this.deferFocus();
22221         }
22222         //this.setSize(this.owner.wrap.getSize());
22223         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22224     },
22225
22226     
22227   
22228
22229     /**
22230      * Protected method that will not generally be called directly. If you need/want
22231      * custom HTML cleanup, this is the method you should override.
22232      * @param {String} html The HTML to be cleaned
22233      * return {String} The cleaned HTML
22234      */
22235     cleanHtml : function(html){
22236         html = String(html);
22237         if(html.length > 5){
22238             if(Roo.isSafari){ // strip safari nonsense
22239                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22240             }
22241         }
22242         if(html == '&nbsp;'){
22243             html = '';
22244         }
22245         return html;
22246     },
22247
22248     /**
22249      * HTML Editor -> Textarea
22250      * Protected method that will not generally be called directly. Syncs the contents
22251      * of the editor iframe with the textarea.
22252      */
22253     syncValue : function(){
22254         if(this.initialized){
22255             var bd = (this.doc.body || this.doc.documentElement);
22256             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22257             var html = bd.innerHTML;
22258             if(Roo.isSafari){
22259                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22260                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22261                 if(m && m[1]){
22262                     html = '<div style="'+m[0]+'">' + html + '</div>';
22263                 }
22264             }
22265             html = this.cleanHtml(html);
22266             // fix up the special chars.. normaly like back quotes in word...
22267             // however we do not want to do this with chinese..
22268             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22269                 var cc = b.charCodeAt();
22270                 if (
22271                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22272                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22273                     (cc >= 0xf900 && cc < 0xfb00 )
22274                 ) {
22275                         return b;
22276                 }
22277                 return "&#"+cc+";" 
22278             });
22279             if(this.owner.fireEvent('beforesync', this, html) !== false){
22280                 this.el.dom.value = html;
22281                 this.owner.fireEvent('sync', this, html);
22282             }
22283         }
22284     },
22285
22286     /**
22287      * Protected method that will not generally be called directly. Pushes the value of the textarea
22288      * into the iframe editor.
22289      */
22290     pushValue : function(){
22291         if(this.initialized){
22292             var v = this.el.dom.value.trim();
22293             
22294 //            if(v.length < 1){
22295 //                v = '&#160;';
22296 //            }
22297             
22298             if(this.owner.fireEvent('beforepush', this, v) !== false){
22299                 var d = (this.doc.body || this.doc.documentElement);
22300                 d.innerHTML = v;
22301                 this.cleanUpPaste();
22302                 this.el.dom.value = d.innerHTML;
22303                 this.owner.fireEvent('push', this, v);
22304             }
22305         }
22306     },
22307
22308     // private
22309     deferFocus : function(){
22310         this.focus.defer(10, this);
22311     },
22312
22313     // doc'ed in Field
22314     focus : function(){
22315         if(this.win && !this.sourceEditMode){
22316             this.win.focus();
22317         }else{
22318             this.el.focus();
22319         }
22320     },
22321     
22322     assignDocWin: function()
22323     {
22324         var iframe = this.iframe;
22325         
22326          if(Roo.isIE){
22327             this.doc = iframe.contentWindow.document;
22328             this.win = iframe.contentWindow;
22329         } else {
22330 //            if (!Roo.get(this.frameId)) {
22331 //                return;
22332 //            }
22333 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22334 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22335             
22336             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22337                 return;
22338             }
22339             
22340             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22341             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22342         }
22343     },
22344     
22345     // private
22346     initEditor : function(){
22347         //console.log("INIT EDITOR");
22348         this.assignDocWin();
22349         
22350         
22351         
22352         this.doc.designMode="on";
22353         this.doc.open();
22354         this.doc.write(this.getDocMarkup());
22355         this.doc.close();
22356         
22357         var dbody = (this.doc.body || this.doc.documentElement);
22358         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22359         // this copies styles from the containing element into thsi one..
22360         // not sure why we need all of this..
22361         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22362         
22363         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22364         //ss['background-attachment'] = 'fixed'; // w3c
22365         dbody.bgProperties = 'fixed'; // ie
22366         //Roo.DomHelper.applyStyles(dbody, ss);
22367         Roo.EventManager.on(this.doc, {
22368             //'mousedown': this.onEditorEvent,
22369             'mouseup': this.onEditorEvent,
22370             'dblclick': this.onEditorEvent,
22371             'click': this.onEditorEvent,
22372             'keyup': this.onEditorEvent,
22373             buffer:100,
22374             scope: this
22375         });
22376         if(Roo.isGecko){
22377             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22378         }
22379         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22380             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22381         }
22382         this.initialized = true;
22383
22384         this.owner.fireEvent('initialize', this);
22385         this.pushValue();
22386     },
22387
22388     // private
22389     onDestroy : function(){
22390         
22391         
22392         
22393         if(this.rendered){
22394             
22395             //for (var i =0; i < this.toolbars.length;i++) {
22396             //    // fixme - ask toolbars for heights?
22397             //    this.toolbars[i].onDestroy();
22398            // }
22399             
22400             //this.wrap.dom.innerHTML = '';
22401             //this.wrap.remove();
22402         }
22403     },
22404
22405     // private
22406     onFirstFocus : function(){
22407         
22408         this.assignDocWin();
22409         
22410         
22411         this.activated = true;
22412          
22413     
22414         if(Roo.isGecko){ // prevent silly gecko errors
22415             this.win.focus();
22416             var s = this.win.getSelection();
22417             if(!s.focusNode || s.focusNode.nodeType != 3){
22418                 var r = s.getRangeAt(0);
22419                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22420                 r.collapse(true);
22421                 this.deferFocus();
22422             }
22423             try{
22424                 this.execCmd('useCSS', true);
22425                 this.execCmd('styleWithCSS', false);
22426             }catch(e){}
22427         }
22428         this.owner.fireEvent('activate', this);
22429     },
22430
22431     // private
22432     adjustFont: function(btn){
22433         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22434         //if(Roo.isSafari){ // safari
22435         //    adjust *= 2;
22436        // }
22437         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22438         if(Roo.isSafari){ // safari
22439             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22440             v =  (v < 10) ? 10 : v;
22441             v =  (v > 48) ? 48 : v;
22442             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22443             
22444         }
22445         
22446         
22447         v = Math.max(1, v+adjust);
22448         
22449         this.execCmd('FontSize', v  );
22450     },
22451
22452     onEditorEvent : function(e)
22453     {
22454         this.owner.fireEvent('editorevent', this, e);
22455       //  this.updateToolbar();
22456         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22457     },
22458
22459     insertTag : function(tg)
22460     {
22461         // could be a bit smarter... -> wrap the current selected tRoo..
22462         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22463             
22464             range = this.createRange(this.getSelection());
22465             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22466             wrappingNode.appendChild(range.extractContents());
22467             range.insertNode(wrappingNode);
22468
22469             return;
22470             
22471             
22472             
22473         }
22474         this.execCmd("formatblock",   tg);
22475         
22476     },
22477     
22478     insertText : function(txt)
22479     {
22480         
22481         
22482         var range = this.createRange();
22483         range.deleteContents();
22484                //alert(Sender.getAttribute('label'));
22485                
22486         range.insertNode(this.doc.createTextNode(txt));
22487     } ,
22488     
22489      
22490
22491     /**
22492      * Executes a Midas editor command on the editor document and performs necessary focus and
22493      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22494      * @param {String} cmd The Midas command
22495      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22496      */
22497     relayCmd : function(cmd, value){
22498         this.win.focus();
22499         this.execCmd(cmd, value);
22500         this.owner.fireEvent('editorevent', this);
22501         //this.updateToolbar();
22502         this.owner.deferFocus();
22503     },
22504
22505     /**
22506      * Executes a Midas editor command directly on the editor document.
22507      * For visual commands, you should use {@link #relayCmd} instead.
22508      * <b>This should only be called after the editor is initialized.</b>
22509      * @param {String} cmd The Midas command
22510      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22511      */
22512     execCmd : function(cmd, value){
22513         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22514         this.syncValue();
22515     },
22516  
22517  
22518    
22519     /**
22520      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22521      * to insert tRoo.
22522      * @param {String} text | dom node.. 
22523      */
22524     insertAtCursor : function(text)
22525     {
22526         
22527         if(!this.activated){
22528             return;
22529         }
22530         /*
22531         if(Roo.isIE){
22532             this.win.focus();
22533             var r = this.doc.selection.createRange();
22534             if(r){
22535                 r.collapse(true);
22536                 r.pasteHTML(text);
22537                 this.syncValue();
22538                 this.deferFocus();
22539             
22540             }
22541             return;
22542         }
22543         */
22544         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22545             this.win.focus();
22546             
22547             
22548             // from jquery ui (MIT licenced)
22549             var range, node;
22550             var win = this.win;
22551             
22552             if (win.getSelection && win.getSelection().getRangeAt) {
22553                 range = win.getSelection().getRangeAt(0);
22554                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22555                 range.insertNode(node);
22556             } else if (win.document.selection && win.document.selection.createRange) {
22557                 // no firefox support
22558                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22559                 win.document.selection.createRange().pasteHTML(txt);
22560             } else {
22561                 // no firefox support
22562                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22563                 this.execCmd('InsertHTML', txt);
22564             } 
22565             
22566             this.syncValue();
22567             
22568             this.deferFocus();
22569         }
22570     },
22571  // private
22572     mozKeyPress : function(e){
22573         if(e.ctrlKey){
22574             var c = e.getCharCode(), cmd;
22575           
22576             if(c > 0){
22577                 c = String.fromCharCode(c).toLowerCase();
22578                 switch(c){
22579                     case 'b':
22580                         cmd = 'bold';
22581                         break;
22582                     case 'i':
22583                         cmd = 'italic';
22584                         break;
22585                     
22586                     case 'u':
22587                         cmd = 'underline';
22588                         break;
22589                     
22590                     case 'v':
22591                         this.cleanUpPaste.defer(100, this);
22592                         return;
22593                         
22594                 }
22595                 if(cmd){
22596                     this.win.focus();
22597                     this.execCmd(cmd);
22598                     this.deferFocus();
22599                     e.preventDefault();
22600                 }
22601                 
22602             }
22603         }
22604     },
22605
22606     // private
22607     fixKeys : function(){ // load time branching for fastest keydown performance
22608         if(Roo.isIE){
22609             return function(e){
22610                 var k = e.getKey(), r;
22611                 if(k == e.TAB){
22612                     e.stopEvent();
22613                     r = this.doc.selection.createRange();
22614                     if(r){
22615                         r.collapse(true);
22616                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22617                         this.deferFocus();
22618                     }
22619                     return;
22620                 }
22621                 
22622                 if(k == e.ENTER){
22623                     r = this.doc.selection.createRange();
22624                     if(r){
22625                         var target = r.parentElement();
22626                         if(!target || target.tagName.toLowerCase() != 'li'){
22627                             e.stopEvent();
22628                             r.pasteHTML('<br />');
22629                             r.collapse(false);
22630                             r.select();
22631                         }
22632                     }
22633                 }
22634                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22635                     this.cleanUpPaste.defer(100, this);
22636                     return;
22637                 }
22638                 
22639                 
22640             };
22641         }else if(Roo.isOpera){
22642             return function(e){
22643                 var k = e.getKey();
22644                 if(k == e.TAB){
22645                     e.stopEvent();
22646                     this.win.focus();
22647                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22648                     this.deferFocus();
22649                 }
22650                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22651                     this.cleanUpPaste.defer(100, this);
22652                     return;
22653                 }
22654                 
22655             };
22656         }else if(Roo.isSafari){
22657             return function(e){
22658                 var k = e.getKey();
22659                 
22660                 if(k == e.TAB){
22661                     e.stopEvent();
22662                     this.execCmd('InsertText','\t');
22663                     this.deferFocus();
22664                     return;
22665                 }
22666                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22667                     this.cleanUpPaste.defer(100, this);
22668                     return;
22669                 }
22670                 
22671              };
22672         }
22673     }(),
22674     
22675     getAllAncestors: function()
22676     {
22677         var p = this.getSelectedNode();
22678         var a = [];
22679         if (!p) {
22680             a.push(p); // push blank onto stack..
22681             p = this.getParentElement();
22682         }
22683         
22684         
22685         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22686             a.push(p);
22687             p = p.parentNode;
22688         }
22689         a.push(this.doc.body);
22690         return a;
22691     },
22692     lastSel : false,
22693     lastSelNode : false,
22694     
22695     
22696     getSelection : function() 
22697     {
22698         this.assignDocWin();
22699         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22700     },
22701     
22702     getSelectedNode: function() 
22703     {
22704         // this may only work on Gecko!!!
22705         
22706         // should we cache this!!!!
22707         
22708         
22709         
22710          
22711         var range = this.createRange(this.getSelection()).cloneRange();
22712         
22713         if (Roo.isIE) {
22714             var parent = range.parentElement();
22715             while (true) {
22716                 var testRange = range.duplicate();
22717                 testRange.moveToElementText(parent);
22718                 if (testRange.inRange(range)) {
22719                     break;
22720                 }
22721                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22722                     break;
22723                 }
22724                 parent = parent.parentElement;
22725             }
22726             return parent;
22727         }
22728         
22729         // is ancestor a text element.
22730         var ac =  range.commonAncestorContainer;
22731         if (ac.nodeType == 3) {
22732             ac = ac.parentNode;
22733         }
22734         
22735         var ar = ac.childNodes;
22736          
22737         var nodes = [];
22738         var other_nodes = [];
22739         var has_other_nodes = false;
22740         for (var i=0;i<ar.length;i++) {
22741             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22742                 continue;
22743             }
22744             // fullly contained node.
22745             
22746             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22747                 nodes.push(ar[i]);
22748                 continue;
22749             }
22750             
22751             // probably selected..
22752             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22753                 other_nodes.push(ar[i]);
22754                 continue;
22755             }
22756             // outer..
22757             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22758                 continue;
22759             }
22760             
22761             
22762             has_other_nodes = true;
22763         }
22764         if (!nodes.length && other_nodes.length) {
22765             nodes= other_nodes;
22766         }
22767         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22768             return false;
22769         }
22770         
22771         return nodes[0];
22772     },
22773     createRange: function(sel)
22774     {
22775         // this has strange effects when using with 
22776         // top toolbar - not sure if it's a great idea.
22777         //this.editor.contentWindow.focus();
22778         if (typeof sel != "undefined") {
22779             try {
22780                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22781             } catch(e) {
22782                 return this.doc.createRange();
22783             }
22784         } else {
22785             return this.doc.createRange();
22786         }
22787     },
22788     getParentElement: function()
22789     {
22790         
22791         this.assignDocWin();
22792         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22793         
22794         var range = this.createRange(sel);
22795          
22796         try {
22797             var p = range.commonAncestorContainer;
22798             while (p.nodeType == 3) { // text node
22799                 p = p.parentNode;
22800             }
22801             return p;
22802         } catch (e) {
22803             return null;
22804         }
22805     
22806     },
22807     /***
22808      *
22809      * Range intersection.. the hard stuff...
22810      *  '-1' = before
22811      *  '0' = hits..
22812      *  '1' = after.
22813      *         [ -- selected range --- ]
22814      *   [fail]                        [fail]
22815      *
22816      *    basically..
22817      *      if end is before start or  hits it. fail.
22818      *      if start is after end or hits it fail.
22819      *
22820      *   if either hits (but other is outside. - then it's not 
22821      *   
22822      *    
22823      **/
22824     
22825     
22826     // @see http://www.thismuchiknow.co.uk/?p=64.
22827     rangeIntersectsNode : function(range, node)
22828     {
22829         var nodeRange = node.ownerDocument.createRange();
22830         try {
22831             nodeRange.selectNode(node);
22832         } catch (e) {
22833             nodeRange.selectNodeContents(node);
22834         }
22835     
22836         var rangeStartRange = range.cloneRange();
22837         rangeStartRange.collapse(true);
22838     
22839         var rangeEndRange = range.cloneRange();
22840         rangeEndRange.collapse(false);
22841     
22842         var nodeStartRange = nodeRange.cloneRange();
22843         nodeStartRange.collapse(true);
22844     
22845         var nodeEndRange = nodeRange.cloneRange();
22846         nodeEndRange.collapse(false);
22847     
22848         return rangeStartRange.compareBoundaryPoints(
22849                  Range.START_TO_START, nodeEndRange) == -1 &&
22850                rangeEndRange.compareBoundaryPoints(
22851                  Range.START_TO_START, nodeStartRange) == 1;
22852         
22853          
22854     },
22855     rangeCompareNode : function(range, node)
22856     {
22857         var nodeRange = node.ownerDocument.createRange();
22858         try {
22859             nodeRange.selectNode(node);
22860         } catch (e) {
22861             nodeRange.selectNodeContents(node);
22862         }
22863         
22864         
22865         range.collapse(true);
22866     
22867         nodeRange.collapse(true);
22868      
22869         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22870         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22871          
22872         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22873         
22874         var nodeIsBefore   =  ss == 1;
22875         var nodeIsAfter    = ee == -1;
22876         
22877         if (nodeIsBefore && nodeIsAfter) {
22878             return 0; // outer
22879         }
22880         if (!nodeIsBefore && nodeIsAfter) {
22881             return 1; //right trailed.
22882         }
22883         
22884         if (nodeIsBefore && !nodeIsAfter) {
22885             return 2;  // left trailed.
22886         }
22887         // fully contined.
22888         return 3;
22889     },
22890
22891     // private? - in a new class?
22892     cleanUpPaste :  function()
22893     {
22894         // cleans up the whole document..
22895         Roo.log('cleanuppaste');
22896         
22897         this.cleanUpChildren(this.doc.body);
22898         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22899         if (clean != this.doc.body.innerHTML) {
22900             this.doc.body.innerHTML = clean;
22901         }
22902         
22903     },
22904     
22905     cleanWordChars : function(input) {// change the chars to hex code
22906         var he = Roo.HtmlEditorCore;
22907         
22908         var output = input;
22909         Roo.each(he.swapCodes, function(sw) { 
22910             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22911             
22912             output = output.replace(swapper, sw[1]);
22913         });
22914         
22915         return output;
22916     },
22917     
22918     
22919     cleanUpChildren : function (n)
22920     {
22921         if (!n.childNodes.length) {
22922             return;
22923         }
22924         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22925            this.cleanUpChild(n.childNodes[i]);
22926         }
22927     },
22928     
22929     
22930         
22931     
22932     cleanUpChild : function (node)
22933     {
22934         var ed = this;
22935         //console.log(node);
22936         if (node.nodeName == "#text") {
22937             // clean up silly Windows -- stuff?
22938             return; 
22939         }
22940         if (node.nodeName == "#comment") {
22941             node.parentNode.removeChild(node);
22942             // clean up silly Windows -- stuff?
22943             return; 
22944         }
22945         var lcname = node.tagName.toLowerCase();
22946         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22947         // whitelist of tags..
22948         
22949         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22950             // remove node.
22951             node.parentNode.removeChild(node);
22952             return;
22953             
22954         }
22955         
22956         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22957         
22958         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22959         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22960         
22961         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22962         //    remove_keep_children = true;
22963         //}
22964         
22965         if (remove_keep_children) {
22966             this.cleanUpChildren(node);
22967             // inserts everything just before this node...
22968             while (node.childNodes.length) {
22969                 var cn = node.childNodes[0];
22970                 node.removeChild(cn);
22971                 node.parentNode.insertBefore(cn, node);
22972             }
22973             node.parentNode.removeChild(node);
22974             return;
22975         }
22976         
22977         if (!node.attributes || !node.attributes.length) {
22978             this.cleanUpChildren(node);
22979             return;
22980         }
22981         
22982         function cleanAttr(n,v)
22983         {
22984             
22985             if (v.match(/^\./) || v.match(/^\//)) {
22986                 return;
22987             }
22988             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22989                 return;
22990             }
22991             if (v.match(/^#/)) {
22992                 return;
22993             }
22994 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22995             node.removeAttribute(n);
22996             
22997         }
22998         
22999         var cwhite = this.cwhite;
23000         var cblack = this.cblack;
23001             
23002         function cleanStyle(n,v)
23003         {
23004             if (v.match(/expression/)) { //XSS?? should we even bother..
23005                 node.removeAttribute(n);
23006                 return;
23007             }
23008             
23009             var parts = v.split(/;/);
23010             var clean = [];
23011             
23012             Roo.each(parts, function(p) {
23013                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23014                 if (!p.length) {
23015                     return true;
23016                 }
23017                 var l = p.split(':').shift().replace(/\s+/g,'');
23018                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23019                 
23020                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23021 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23022                     //node.removeAttribute(n);
23023                     return true;
23024                 }
23025                 //Roo.log()
23026                 // only allow 'c whitelisted system attributes'
23027                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23028 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23029                     //node.removeAttribute(n);
23030                     return true;
23031                 }
23032                 
23033                 
23034                  
23035                 
23036                 clean.push(p);
23037                 return true;
23038             });
23039             if (clean.length) { 
23040                 node.setAttribute(n, clean.join(';'));
23041             } else {
23042                 node.removeAttribute(n);
23043             }
23044             
23045         }
23046         
23047         
23048         for (var i = node.attributes.length-1; i > -1 ; i--) {
23049             var a = node.attributes[i];
23050             //console.log(a);
23051             
23052             if (a.name.toLowerCase().substr(0,2)=='on')  {
23053                 node.removeAttribute(a.name);
23054                 continue;
23055             }
23056             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23057                 node.removeAttribute(a.name);
23058                 continue;
23059             }
23060             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23061                 cleanAttr(a.name,a.value); // fixme..
23062                 continue;
23063             }
23064             if (a.name == 'style') {
23065                 cleanStyle(a.name,a.value);
23066                 continue;
23067             }
23068             /// clean up MS crap..
23069             // tecnically this should be a list of valid class'es..
23070             
23071             
23072             if (a.name == 'class') {
23073                 if (a.value.match(/^Mso/)) {
23074                     node.className = '';
23075                 }
23076                 
23077                 if (a.value.match(/^body$/)) {
23078                     node.className = '';
23079                 }
23080                 continue;
23081             }
23082             
23083             // style cleanup!?
23084             // class cleanup?
23085             
23086         }
23087         
23088         
23089         this.cleanUpChildren(node);
23090         
23091         
23092     },
23093     
23094     /**
23095      * Clean up MS wordisms...
23096      */
23097     cleanWord : function(node)
23098     {
23099         
23100         
23101         if (!node) {
23102             this.cleanWord(this.doc.body);
23103             return;
23104         }
23105         if (node.nodeName == "#text") {
23106             // clean up silly Windows -- stuff?
23107             return; 
23108         }
23109         if (node.nodeName == "#comment") {
23110             node.parentNode.removeChild(node);
23111             // clean up silly Windows -- stuff?
23112             return; 
23113         }
23114         
23115         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23116             node.parentNode.removeChild(node);
23117             return;
23118         }
23119         
23120         // remove - but keep children..
23121         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23122             while (node.childNodes.length) {
23123                 var cn = node.childNodes[0];
23124                 node.removeChild(cn);
23125                 node.parentNode.insertBefore(cn, node);
23126             }
23127             node.parentNode.removeChild(node);
23128             this.iterateChildren(node, this.cleanWord);
23129             return;
23130         }
23131         // clean styles
23132         if (node.className.length) {
23133             
23134             var cn = node.className.split(/\W+/);
23135             var cna = [];
23136             Roo.each(cn, function(cls) {
23137                 if (cls.match(/Mso[a-zA-Z]+/)) {
23138                     return;
23139                 }
23140                 cna.push(cls);
23141             });
23142             node.className = cna.length ? cna.join(' ') : '';
23143             if (!cna.length) {
23144                 node.removeAttribute("class");
23145             }
23146         }
23147         
23148         if (node.hasAttribute("lang")) {
23149             node.removeAttribute("lang");
23150         }
23151         
23152         if (node.hasAttribute("style")) {
23153             
23154             var styles = node.getAttribute("style").split(";");
23155             var nstyle = [];
23156             Roo.each(styles, function(s) {
23157                 if (!s.match(/:/)) {
23158                     return;
23159                 }
23160                 var kv = s.split(":");
23161                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23162                     return;
23163                 }
23164                 // what ever is left... we allow.
23165                 nstyle.push(s);
23166             });
23167             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23168             if (!nstyle.length) {
23169                 node.removeAttribute('style');
23170             }
23171         }
23172         this.iterateChildren(node, this.cleanWord);
23173         
23174         
23175         
23176     },
23177     /**
23178      * iterateChildren of a Node, calling fn each time, using this as the scole..
23179      * @param {DomNode} node node to iterate children of.
23180      * @param {Function} fn method of this class to call on each item.
23181      */
23182     iterateChildren : function(node, fn)
23183     {
23184         if (!node.childNodes.length) {
23185                 return;
23186         }
23187         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23188            fn.call(this, node.childNodes[i])
23189         }
23190     },
23191     
23192     
23193     /**
23194      * cleanTableWidths.
23195      *
23196      * Quite often pasting from word etc.. results in tables with column and widths.
23197      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23198      *
23199      */
23200     cleanTableWidths : function(node)
23201     {
23202          
23203          
23204         if (!node) {
23205             this.cleanTableWidths(this.doc.body);
23206             return;
23207         }
23208         
23209         // ignore list...
23210         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23211             return; 
23212         }
23213         Roo.log(node.tagName);
23214         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23215             this.iterateChildren(node, this.cleanTableWidths);
23216             return;
23217         }
23218         if (node.hasAttribute('width')) {
23219             node.removeAttribute('width');
23220         }
23221         
23222          
23223         if (node.hasAttribute("style")) {
23224             // pretty basic...
23225             
23226             var styles = node.getAttribute("style").split(";");
23227             var nstyle = [];
23228             Roo.each(styles, function(s) {
23229                 if (!s.match(/:/)) {
23230                     return;
23231                 }
23232                 var kv = s.split(":");
23233                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23234                     return;
23235                 }
23236                 // what ever is left... we allow.
23237                 nstyle.push(s);
23238             });
23239             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23240             if (!nstyle.length) {
23241                 node.removeAttribute('style');
23242             }
23243         }
23244         
23245         this.iterateChildren(node, this.cleanTableWidths);
23246         
23247         
23248     },
23249     
23250     
23251     
23252     
23253     domToHTML : function(currentElement, depth, nopadtext) {
23254         
23255         depth = depth || 0;
23256         nopadtext = nopadtext || false;
23257     
23258         if (!currentElement) {
23259             return this.domToHTML(this.doc.body);
23260         }
23261         
23262         //Roo.log(currentElement);
23263         var j;
23264         var allText = false;
23265         var nodeName = currentElement.nodeName;
23266         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23267         
23268         if  (nodeName == '#text') {
23269             
23270             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23271         }
23272         
23273         
23274         var ret = '';
23275         if (nodeName != 'BODY') {
23276              
23277             var i = 0;
23278             // Prints the node tagName, such as <A>, <IMG>, etc
23279             if (tagName) {
23280                 var attr = [];
23281                 for(i = 0; i < currentElement.attributes.length;i++) {
23282                     // quoting?
23283                     var aname = currentElement.attributes.item(i).name;
23284                     if (!currentElement.attributes.item(i).value.length) {
23285                         continue;
23286                     }
23287                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23288                 }
23289                 
23290                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23291             } 
23292             else {
23293                 
23294                 // eack
23295             }
23296         } else {
23297             tagName = false;
23298         }
23299         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23300             return ret;
23301         }
23302         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23303             nopadtext = true;
23304         }
23305         
23306         
23307         // Traverse the tree
23308         i = 0;
23309         var currentElementChild = currentElement.childNodes.item(i);
23310         var allText = true;
23311         var innerHTML  = '';
23312         lastnode = '';
23313         while (currentElementChild) {
23314             // Formatting code (indent the tree so it looks nice on the screen)
23315             var nopad = nopadtext;
23316             if (lastnode == 'SPAN') {
23317                 nopad  = true;
23318             }
23319             // text
23320             if  (currentElementChild.nodeName == '#text') {
23321                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23322                 toadd = nopadtext ? toadd : toadd.trim();
23323                 if (!nopad && toadd.length > 80) {
23324                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23325                 }
23326                 innerHTML  += toadd;
23327                 
23328                 i++;
23329                 currentElementChild = currentElement.childNodes.item(i);
23330                 lastNode = '';
23331                 continue;
23332             }
23333             allText = false;
23334             
23335             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23336                 
23337             // Recursively traverse the tree structure of the child node
23338             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23339             lastnode = currentElementChild.nodeName;
23340             i++;
23341             currentElementChild=currentElement.childNodes.item(i);
23342         }
23343         
23344         ret += innerHTML;
23345         
23346         if (!allText) {
23347                 // The remaining code is mostly for formatting the tree
23348             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23349         }
23350         
23351         
23352         if (tagName) {
23353             ret+= "</"+tagName+">";
23354         }
23355         return ret;
23356         
23357     },
23358         
23359     applyBlacklists : function()
23360     {
23361         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23362         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23363         
23364         this.white = [];
23365         this.black = [];
23366         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23367             if (b.indexOf(tag) > -1) {
23368                 return;
23369             }
23370             this.white.push(tag);
23371             
23372         }, this);
23373         
23374         Roo.each(w, function(tag) {
23375             if (b.indexOf(tag) > -1) {
23376                 return;
23377             }
23378             if (this.white.indexOf(tag) > -1) {
23379                 return;
23380             }
23381             this.white.push(tag);
23382             
23383         }, this);
23384         
23385         
23386         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23387             if (w.indexOf(tag) > -1) {
23388                 return;
23389             }
23390             this.black.push(tag);
23391             
23392         }, this);
23393         
23394         Roo.each(b, function(tag) {
23395             if (w.indexOf(tag) > -1) {
23396                 return;
23397             }
23398             if (this.black.indexOf(tag) > -1) {
23399                 return;
23400             }
23401             this.black.push(tag);
23402             
23403         }, this);
23404         
23405         
23406         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23407         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23408         
23409         this.cwhite = [];
23410         this.cblack = [];
23411         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23412             if (b.indexOf(tag) > -1) {
23413                 return;
23414             }
23415             this.cwhite.push(tag);
23416             
23417         }, this);
23418         
23419         Roo.each(w, function(tag) {
23420             if (b.indexOf(tag) > -1) {
23421                 return;
23422             }
23423             if (this.cwhite.indexOf(tag) > -1) {
23424                 return;
23425             }
23426             this.cwhite.push(tag);
23427             
23428         }, this);
23429         
23430         
23431         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23432             if (w.indexOf(tag) > -1) {
23433                 return;
23434             }
23435             this.cblack.push(tag);
23436             
23437         }, this);
23438         
23439         Roo.each(b, function(tag) {
23440             if (w.indexOf(tag) > -1) {
23441                 return;
23442             }
23443             if (this.cblack.indexOf(tag) > -1) {
23444                 return;
23445             }
23446             this.cblack.push(tag);
23447             
23448         }, this);
23449     },
23450     
23451     setStylesheets : function(stylesheets)
23452     {
23453         if(typeof(stylesheets) == 'string'){
23454             Roo.get(this.iframe.contentDocument.head).createChild({
23455                 tag : 'link',
23456                 rel : 'stylesheet',
23457                 type : 'text/css',
23458                 href : stylesheets
23459             });
23460             
23461             return;
23462         }
23463         var _this = this;
23464      
23465         Roo.each(stylesheets, function(s) {
23466             if(!s.length){
23467                 return;
23468             }
23469             
23470             Roo.get(_this.iframe.contentDocument.head).createChild({
23471                 tag : 'link',
23472                 rel : 'stylesheet',
23473                 type : 'text/css',
23474                 href : s
23475             });
23476         });
23477
23478         
23479     },
23480     
23481     removeStylesheets : function()
23482     {
23483         var _this = this;
23484         
23485         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23486             s.remove();
23487         });
23488     },
23489     
23490     setStyle : function(style)
23491     {
23492         Roo.get(this.iframe.contentDocument.head).createChild({
23493             tag : 'style',
23494             type : 'text/css',
23495             html : style
23496         });
23497
23498         return;
23499     }
23500     
23501     // hide stuff that is not compatible
23502     /**
23503      * @event blur
23504      * @hide
23505      */
23506     /**
23507      * @event change
23508      * @hide
23509      */
23510     /**
23511      * @event focus
23512      * @hide
23513      */
23514     /**
23515      * @event specialkey
23516      * @hide
23517      */
23518     /**
23519      * @cfg {String} fieldClass @hide
23520      */
23521     /**
23522      * @cfg {String} focusClass @hide
23523      */
23524     /**
23525      * @cfg {String} autoCreate @hide
23526      */
23527     /**
23528      * @cfg {String} inputType @hide
23529      */
23530     /**
23531      * @cfg {String} invalidClass @hide
23532      */
23533     /**
23534      * @cfg {String} invalidText @hide
23535      */
23536     /**
23537      * @cfg {String} msgFx @hide
23538      */
23539     /**
23540      * @cfg {String} validateOnBlur @hide
23541      */
23542 });
23543
23544 Roo.HtmlEditorCore.white = [
23545         'area', 'br', 'img', 'input', 'hr', 'wbr',
23546         
23547        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23548        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23549        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23550        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23551        'table',   'ul',         'xmp', 
23552        
23553        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23554       'thead',   'tr', 
23555      
23556       'dir', 'menu', 'ol', 'ul', 'dl',
23557        
23558       'embed',  'object'
23559 ];
23560
23561
23562 Roo.HtmlEditorCore.black = [
23563     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23564         'applet', // 
23565         'base',   'basefont', 'bgsound', 'blink',  'body', 
23566         'frame',  'frameset', 'head',    'html',   'ilayer', 
23567         'iframe', 'layer',  'link',     'meta',    'object',   
23568         'script', 'style' ,'title',  'xml' // clean later..
23569 ];
23570 Roo.HtmlEditorCore.clean = [
23571     'script', 'style', 'title', 'xml'
23572 ];
23573 Roo.HtmlEditorCore.remove = [
23574     'font'
23575 ];
23576 // attributes..
23577
23578 Roo.HtmlEditorCore.ablack = [
23579     'on'
23580 ];
23581     
23582 Roo.HtmlEditorCore.aclean = [ 
23583     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23584 ];
23585
23586 // protocols..
23587 Roo.HtmlEditorCore.pwhite= [
23588         'http',  'https',  'mailto'
23589 ];
23590
23591 // white listed style attributes.
23592 Roo.HtmlEditorCore.cwhite= [
23593       //  'text-align', /// default is to allow most things..
23594       
23595          
23596 //        'font-size'//??
23597 ];
23598
23599 // black listed style attributes.
23600 Roo.HtmlEditorCore.cblack= [
23601       //  'font-size' -- this can be set by the project 
23602 ];
23603
23604
23605 Roo.HtmlEditorCore.swapCodes   =[ 
23606     [    8211, "--" ], 
23607     [    8212, "--" ], 
23608     [    8216,  "'" ],  
23609     [    8217, "'" ],  
23610     [    8220, '"' ],  
23611     [    8221, '"' ],  
23612     [    8226, "*" ],  
23613     [    8230, "..." ]
23614 ]; 
23615
23616     /*
23617  * - LGPL
23618  *
23619  * HtmlEditor
23620  * 
23621  */
23622
23623 /**
23624  * @class Roo.bootstrap.HtmlEditor
23625  * @extends Roo.bootstrap.TextArea
23626  * Bootstrap HtmlEditor class
23627
23628  * @constructor
23629  * Create a new HtmlEditor
23630  * @param {Object} config The config object
23631  */
23632
23633 Roo.bootstrap.HtmlEditor = function(config){
23634     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23635     if (!this.toolbars) {
23636         this.toolbars = [];
23637     }
23638     
23639     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23640     this.addEvents({
23641             /**
23642              * @event initialize
23643              * Fires when the editor is fully initialized (including the iframe)
23644              * @param {HtmlEditor} this
23645              */
23646             initialize: true,
23647             /**
23648              * @event activate
23649              * Fires when the editor is first receives the focus. Any insertion must wait
23650              * until after this event.
23651              * @param {HtmlEditor} this
23652              */
23653             activate: true,
23654              /**
23655              * @event beforesync
23656              * Fires before the textarea is updated with content from the editor iframe. Return false
23657              * to cancel the sync.
23658              * @param {HtmlEditor} this
23659              * @param {String} html
23660              */
23661             beforesync: true,
23662              /**
23663              * @event beforepush
23664              * Fires before the iframe editor is updated with content from the textarea. Return false
23665              * to cancel the push.
23666              * @param {HtmlEditor} this
23667              * @param {String} html
23668              */
23669             beforepush: true,
23670              /**
23671              * @event sync
23672              * Fires when the textarea is updated with content from the editor iframe.
23673              * @param {HtmlEditor} this
23674              * @param {String} html
23675              */
23676             sync: true,
23677              /**
23678              * @event push
23679              * Fires when the iframe editor is updated with content from the textarea.
23680              * @param {HtmlEditor} this
23681              * @param {String} html
23682              */
23683             push: true,
23684              /**
23685              * @event editmodechange
23686              * Fires when the editor switches edit modes
23687              * @param {HtmlEditor} this
23688              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23689              */
23690             editmodechange: true,
23691             /**
23692              * @event editorevent
23693              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23694              * @param {HtmlEditor} this
23695              */
23696             editorevent: true,
23697             /**
23698              * @event firstfocus
23699              * Fires when on first focus - needed by toolbars..
23700              * @param {HtmlEditor} this
23701              */
23702             firstfocus: true,
23703             /**
23704              * @event autosave
23705              * Auto save the htmlEditor value as a file into Events
23706              * @param {HtmlEditor} this
23707              */
23708             autosave: true,
23709             /**
23710              * @event savedpreview
23711              * preview the saved version of htmlEditor
23712              * @param {HtmlEditor} this
23713              */
23714             savedpreview: true
23715         });
23716 };
23717
23718
23719 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23720     
23721     
23722       /**
23723      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23724      */
23725     toolbars : false,
23726     
23727      /**
23728     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23729     */
23730     btns : [],
23731    
23732      /**
23733      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23734      *                        Roo.resizable.
23735      */
23736     resizable : false,
23737      /**
23738      * @cfg {Number} height (in pixels)
23739      */   
23740     height: 300,
23741    /**
23742      * @cfg {Number} width (in pixels)
23743      */   
23744     width: false,
23745     
23746     /**
23747      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23748      * 
23749      */
23750     stylesheets: false,
23751     
23752     // id of frame..
23753     frameId: false,
23754     
23755     // private properties
23756     validationEvent : false,
23757     deferHeight: true,
23758     initialized : false,
23759     activated : false,
23760     
23761     onFocus : Roo.emptyFn,
23762     iframePad:3,
23763     hideMode:'offsets',
23764     
23765     tbContainer : false,
23766     
23767     bodyCls : '',
23768     
23769     toolbarContainer :function() {
23770         return this.wrap.select('.x-html-editor-tb',true).first();
23771     },
23772
23773     /**
23774      * Protected method that will not generally be called directly. It
23775      * is called when the editor creates its toolbar. Override this method if you need to
23776      * add custom toolbar buttons.
23777      * @param {HtmlEditor} editor
23778      */
23779     createToolbar : function(){
23780         Roo.log('renewing');
23781         Roo.log("create toolbars");
23782         
23783         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23784         this.toolbars[0].render(this.toolbarContainer());
23785         
23786         return;
23787         
23788 //        if (!editor.toolbars || !editor.toolbars.length) {
23789 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23790 //        }
23791 //        
23792 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23793 //            editor.toolbars[i] = Roo.factory(
23794 //                    typeof(editor.toolbars[i]) == 'string' ?
23795 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23796 //                Roo.bootstrap.HtmlEditor);
23797 //            editor.toolbars[i].init(editor);
23798 //        }
23799     },
23800
23801      
23802     // private
23803     onRender : function(ct, position)
23804     {
23805        // Roo.log("Call onRender: " + this.xtype);
23806         var _t = this;
23807         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23808       
23809         this.wrap = this.inputEl().wrap({
23810             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23811         });
23812         
23813         this.editorcore.onRender(ct, position);
23814          
23815         if (this.resizable) {
23816             this.resizeEl = new Roo.Resizable(this.wrap, {
23817                 pinned : true,
23818                 wrap: true,
23819                 dynamic : true,
23820                 minHeight : this.height,
23821                 height: this.height,
23822                 handles : this.resizable,
23823                 width: this.width,
23824                 listeners : {
23825                     resize : function(r, w, h) {
23826                         _t.onResize(w,h); // -something
23827                     }
23828                 }
23829             });
23830             
23831         }
23832         this.createToolbar(this);
23833        
23834         
23835         if(!this.width && this.resizable){
23836             this.setSize(this.wrap.getSize());
23837         }
23838         if (this.resizeEl) {
23839             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23840             // should trigger onReize..
23841         }
23842         
23843     },
23844
23845     // private
23846     onResize : function(w, h)
23847     {
23848         Roo.log('resize: ' +w + ',' + h );
23849         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23850         var ew = false;
23851         var eh = false;
23852         
23853         if(this.inputEl() ){
23854             if(typeof w == 'number'){
23855                 var aw = w - this.wrap.getFrameWidth('lr');
23856                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23857                 ew = aw;
23858             }
23859             if(typeof h == 'number'){
23860                  var tbh = -11;  // fixme it needs to tool bar size!
23861                 for (var i =0; i < this.toolbars.length;i++) {
23862                     // fixme - ask toolbars for heights?
23863                     tbh += this.toolbars[i].el.getHeight();
23864                     //if (this.toolbars[i].footer) {
23865                     //    tbh += this.toolbars[i].footer.el.getHeight();
23866                     //}
23867                 }
23868               
23869                 
23870                 
23871                 
23872                 
23873                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23874                 ah -= 5; // knock a few pixes off for look..
23875                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23876                 var eh = ah;
23877             }
23878         }
23879         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23880         this.editorcore.onResize(ew,eh);
23881         
23882     },
23883
23884     /**
23885      * Toggles the editor between standard and source edit mode.
23886      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23887      */
23888     toggleSourceEdit : function(sourceEditMode)
23889     {
23890         this.editorcore.toggleSourceEdit(sourceEditMode);
23891         
23892         if(this.editorcore.sourceEditMode){
23893             Roo.log('editor - showing textarea');
23894             
23895 //            Roo.log('in');
23896 //            Roo.log(this.syncValue());
23897             this.syncValue();
23898             this.inputEl().removeClass(['hide', 'x-hidden']);
23899             this.inputEl().dom.removeAttribute('tabIndex');
23900             this.inputEl().focus();
23901         }else{
23902             Roo.log('editor - hiding textarea');
23903 //            Roo.log('out')
23904 //            Roo.log(this.pushValue()); 
23905             this.pushValue();
23906             
23907             this.inputEl().addClass(['hide', 'x-hidden']);
23908             this.inputEl().dom.setAttribute('tabIndex', -1);
23909             //this.deferFocus();
23910         }
23911          
23912         if(this.resizable){
23913             this.setSize(this.wrap.getSize());
23914         }
23915         
23916         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23917     },
23918  
23919     // private (for BoxComponent)
23920     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23921
23922     // private (for BoxComponent)
23923     getResizeEl : function(){
23924         return this.wrap;
23925     },
23926
23927     // private (for BoxComponent)
23928     getPositionEl : function(){
23929         return this.wrap;
23930     },
23931
23932     // private
23933     initEvents : function(){
23934         this.originalValue = this.getValue();
23935     },
23936
23937 //    /**
23938 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23939 //     * @method
23940 //     */
23941 //    markInvalid : Roo.emptyFn,
23942 //    /**
23943 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23944 //     * @method
23945 //     */
23946 //    clearInvalid : Roo.emptyFn,
23947
23948     setValue : function(v){
23949         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23950         this.editorcore.pushValue();
23951     },
23952
23953      
23954     // private
23955     deferFocus : function(){
23956         this.focus.defer(10, this);
23957     },
23958
23959     // doc'ed in Field
23960     focus : function(){
23961         this.editorcore.focus();
23962         
23963     },
23964       
23965
23966     // private
23967     onDestroy : function(){
23968         
23969         
23970         
23971         if(this.rendered){
23972             
23973             for (var i =0; i < this.toolbars.length;i++) {
23974                 // fixme - ask toolbars for heights?
23975                 this.toolbars[i].onDestroy();
23976             }
23977             
23978             this.wrap.dom.innerHTML = '';
23979             this.wrap.remove();
23980         }
23981     },
23982
23983     // private
23984     onFirstFocus : function(){
23985         //Roo.log("onFirstFocus");
23986         this.editorcore.onFirstFocus();
23987          for (var i =0; i < this.toolbars.length;i++) {
23988             this.toolbars[i].onFirstFocus();
23989         }
23990         
23991     },
23992     
23993     // private
23994     syncValue : function()
23995     {   
23996         this.editorcore.syncValue();
23997     },
23998     
23999     pushValue : function()
24000     {   
24001         this.editorcore.pushValue();
24002     }
24003      
24004     
24005     // hide stuff that is not compatible
24006     /**
24007      * @event blur
24008      * @hide
24009      */
24010     /**
24011      * @event change
24012      * @hide
24013      */
24014     /**
24015      * @event focus
24016      * @hide
24017      */
24018     /**
24019      * @event specialkey
24020      * @hide
24021      */
24022     /**
24023      * @cfg {String} fieldClass @hide
24024      */
24025     /**
24026      * @cfg {String} focusClass @hide
24027      */
24028     /**
24029      * @cfg {String} autoCreate @hide
24030      */
24031     /**
24032      * @cfg {String} inputType @hide
24033      */
24034      
24035     /**
24036      * @cfg {String} invalidText @hide
24037      */
24038     /**
24039      * @cfg {String} msgFx @hide
24040      */
24041     /**
24042      * @cfg {String} validateOnBlur @hide
24043      */
24044 });
24045  
24046     
24047    
24048    
24049    
24050       
24051 Roo.namespace('Roo.bootstrap.htmleditor');
24052 /**
24053  * @class Roo.bootstrap.HtmlEditorToolbar1
24054  * Basic Toolbar
24055  * 
24056  * Usage:
24057  *
24058  new Roo.bootstrap.HtmlEditor({
24059     ....
24060     toolbars : [
24061         new Roo.bootstrap.HtmlEditorToolbar1({
24062             disable : { fonts: 1 , format: 1, ..., ... , ...],
24063             btns : [ .... ]
24064         })
24065     }
24066      
24067  * 
24068  * @cfg {Object} disable List of elements to disable..
24069  * @cfg {Array} btns List of additional buttons.
24070  * 
24071  * 
24072  * NEEDS Extra CSS? 
24073  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24074  */
24075  
24076 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24077 {
24078     
24079     Roo.apply(this, config);
24080     
24081     // default disabled, based on 'good practice'..
24082     this.disable = this.disable || {};
24083     Roo.applyIf(this.disable, {
24084         fontSize : true,
24085         colors : true,
24086         specialElements : true
24087     });
24088     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24089     
24090     this.editor = config.editor;
24091     this.editorcore = config.editor.editorcore;
24092     
24093     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24094     
24095     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24096     // dont call parent... till later.
24097 }
24098 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24099      
24100     bar : true,
24101     
24102     editor : false,
24103     editorcore : false,
24104     
24105     
24106     formats : [
24107         "p" ,  
24108         "h1","h2","h3","h4","h5","h6", 
24109         "pre", "code", 
24110         "abbr", "acronym", "address", "cite", "samp", "var",
24111         'div','span'
24112     ],
24113     
24114     onRender : function(ct, position)
24115     {
24116        // Roo.log("Call onRender: " + this.xtype);
24117         
24118        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24119        Roo.log(this.el);
24120        this.el.dom.style.marginBottom = '0';
24121        var _this = this;
24122        var editorcore = this.editorcore;
24123        var editor= this.editor;
24124        
24125        var children = [];
24126        var btn = function(id,cmd , toggle, handler, html){
24127        
24128             var  event = toggle ? 'toggle' : 'click';
24129        
24130             var a = {
24131                 size : 'sm',
24132                 xtype: 'Button',
24133                 xns: Roo.bootstrap,
24134                 //glyphicon : id,
24135                 fa: id,
24136                 cmd : id || cmd,
24137                 enableToggle:toggle !== false,
24138                 html : html || '',
24139                 pressed : toggle ? false : null,
24140                 listeners : {}
24141             };
24142             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24143                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24144             };
24145             children.push(a);
24146             return a;
24147        }
24148        
24149     //    var cb_box = function...
24150         
24151         var style = {
24152                 xtype: 'Button',
24153                 size : 'sm',
24154                 xns: Roo.bootstrap,
24155                 fa : 'font',
24156                 //html : 'submit'
24157                 menu : {
24158                     xtype: 'Menu',
24159                     xns: Roo.bootstrap,
24160                     items:  []
24161                 }
24162         };
24163         Roo.each(this.formats, function(f) {
24164             style.menu.items.push({
24165                 xtype :'MenuItem',
24166                 xns: Roo.bootstrap,
24167                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24168                 tagname : f,
24169                 listeners : {
24170                     click : function()
24171                     {
24172                         editorcore.insertTag(this.tagname);
24173                         editor.focus();
24174                     }
24175                 }
24176                 
24177             });
24178         });
24179         children.push(style);   
24180         
24181         btn('bold',false,true);
24182         btn('italic',false,true);
24183         btn('align-left', 'justifyleft',true);
24184         btn('align-center', 'justifycenter',true);
24185         btn('align-right' , 'justifyright',true);
24186         btn('link', false, false, function(btn) {
24187             //Roo.log("create link?");
24188             var url = prompt(this.createLinkText, this.defaultLinkValue);
24189             if(url && url != 'http:/'+'/'){
24190                 this.editorcore.relayCmd('createlink', url);
24191             }
24192         }),
24193         btn('list','insertunorderedlist',true);
24194         btn('pencil', false,true, function(btn){
24195                 Roo.log(this);
24196                 this.toggleSourceEdit(btn.pressed);
24197         });
24198         
24199         if (this.editor.btns.length > 0) {
24200             for (var i = 0; i<this.editor.btns.length; i++) {
24201                 children.push(this.editor.btns[i]);
24202             }
24203         }
24204         
24205         /*
24206         var cog = {
24207                 xtype: 'Button',
24208                 size : 'sm',
24209                 xns: Roo.bootstrap,
24210                 glyphicon : 'cog',
24211                 //html : 'submit'
24212                 menu : {
24213                     xtype: 'Menu',
24214                     xns: Roo.bootstrap,
24215                     items:  []
24216                 }
24217         };
24218         
24219         cog.menu.items.push({
24220             xtype :'MenuItem',
24221             xns: Roo.bootstrap,
24222             html : Clean styles,
24223             tagname : f,
24224             listeners : {
24225                 click : function()
24226                 {
24227                     editorcore.insertTag(this.tagname);
24228                     editor.focus();
24229                 }
24230             }
24231             
24232         });
24233        */
24234         
24235          
24236        this.xtype = 'NavSimplebar';
24237         
24238         for(var i=0;i< children.length;i++) {
24239             
24240             this.buttons.add(this.addxtypeChild(children[i]));
24241             
24242         }
24243         
24244         editor.on('editorevent', this.updateToolbar, this);
24245     },
24246     onBtnClick : function(id)
24247     {
24248        this.editorcore.relayCmd(id);
24249        this.editorcore.focus();
24250     },
24251     
24252     /**
24253      * Protected method that will not generally be called directly. It triggers
24254      * a toolbar update by reading the markup state of the current selection in the editor.
24255      */
24256     updateToolbar: function(){
24257
24258         if(!this.editorcore.activated){
24259             this.editor.onFirstFocus(); // is this neeed?
24260             return;
24261         }
24262
24263         var btns = this.buttons; 
24264         var doc = this.editorcore.doc;
24265         btns.get('bold').setActive(doc.queryCommandState('bold'));
24266         btns.get('italic').setActive(doc.queryCommandState('italic'));
24267         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24268         
24269         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24270         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24271         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24272         
24273         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24274         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24275          /*
24276         
24277         var ans = this.editorcore.getAllAncestors();
24278         if (this.formatCombo) {
24279             
24280             
24281             var store = this.formatCombo.store;
24282             this.formatCombo.setValue("");
24283             for (var i =0; i < ans.length;i++) {
24284                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24285                     // select it..
24286                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24287                     break;
24288                 }
24289             }
24290         }
24291         
24292         
24293         
24294         // hides menus... - so this cant be on a menu...
24295         Roo.bootstrap.MenuMgr.hideAll();
24296         */
24297         Roo.bootstrap.MenuMgr.hideAll();
24298         //this.editorsyncValue();
24299     },
24300     onFirstFocus: function() {
24301         this.buttons.each(function(item){
24302            item.enable();
24303         });
24304     },
24305     toggleSourceEdit : function(sourceEditMode){
24306         
24307           
24308         if(sourceEditMode){
24309             Roo.log("disabling buttons");
24310            this.buttons.each( function(item){
24311                 if(item.cmd != 'pencil'){
24312                     item.disable();
24313                 }
24314             });
24315           
24316         }else{
24317             Roo.log("enabling buttons");
24318             if(this.editorcore.initialized){
24319                 this.buttons.each( function(item){
24320                     item.enable();
24321                 });
24322             }
24323             
24324         }
24325         Roo.log("calling toggole on editor");
24326         // tell the editor that it's been pressed..
24327         this.editor.toggleSourceEdit(sourceEditMode);
24328        
24329     }
24330 });
24331
24332
24333
24334
24335
24336 /**
24337  * @class Roo.bootstrap.Table.AbstractSelectionModel
24338  * @extends Roo.util.Observable
24339  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24340  * implemented by descendant classes.  This class should not be directly instantiated.
24341  * @constructor
24342  */
24343 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24344     this.locked = false;
24345     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24346 };
24347
24348
24349 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24350     /** @ignore Called by the grid automatically. Do not call directly. */
24351     init : function(grid){
24352         this.grid = grid;
24353         this.initEvents();
24354     },
24355
24356     /**
24357      * Locks the selections.
24358      */
24359     lock : function(){
24360         this.locked = true;
24361     },
24362
24363     /**
24364      * Unlocks the selections.
24365      */
24366     unlock : function(){
24367         this.locked = false;
24368     },
24369
24370     /**
24371      * Returns true if the selections are locked.
24372      * @return {Boolean}
24373      */
24374     isLocked : function(){
24375         return this.locked;
24376     }
24377 });
24378 /**
24379  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24380  * @class Roo.bootstrap.Table.RowSelectionModel
24381  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24382  * It supports multiple selections and keyboard selection/navigation. 
24383  * @constructor
24384  * @param {Object} config
24385  */
24386
24387 Roo.bootstrap.Table.RowSelectionModel = function(config){
24388     Roo.apply(this, config);
24389     this.selections = new Roo.util.MixedCollection(false, function(o){
24390         return o.id;
24391     });
24392
24393     this.last = false;
24394     this.lastActive = false;
24395
24396     this.addEvents({
24397         /**
24398              * @event selectionchange
24399              * Fires when the selection changes
24400              * @param {SelectionModel} this
24401              */
24402             "selectionchange" : true,
24403         /**
24404              * @event afterselectionchange
24405              * Fires after the selection changes (eg. by key press or clicking)
24406              * @param {SelectionModel} this
24407              */
24408             "afterselectionchange" : true,
24409         /**
24410              * @event beforerowselect
24411              * Fires when a row is selected being selected, return false to cancel.
24412              * @param {SelectionModel} this
24413              * @param {Number} rowIndex The selected index
24414              * @param {Boolean} keepExisting False if other selections will be cleared
24415              */
24416             "beforerowselect" : true,
24417         /**
24418              * @event rowselect
24419              * Fires when a row is selected.
24420              * @param {SelectionModel} this
24421              * @param {Number} rowIndex The selected index
24422              * @param {Roo.data.Record} r The record
24423              */
24424             "rowselect" : true,
24425         /**
24426              * @event rowdeselect
24427              * Fires when a row is deselected.
24428              * @param {SelectionModel} this
24429              * @param {Number} rowIndex The selected index
24430              */
24431         "rowdeselect" : true
24432     });
24433     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24434     this.locked = false;
24435  };
24436
24437 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24438     /**
24439      * @cfg {Boolean} singleSelect
24440      * True to allow selection of only one row at a time (defaults to false)
24441      */
24442     singleSelect : false,
24443
24444     // private
24445     initEvents : function()
24446     {
24447
24448         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24449         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24450         //}else{ // allow click to work like normal
24451          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24452         //}
24453         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24454         this.grid.on("rowclick", this.handleMouseDown, this);
24455         
24456         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24457             "up" : function(e){
24458                 if(!e.shiftKey){
24459                     this.selectPrevious(e.shiftKey);
24460                 }else if(this.last !== false && this.lastActive !== false){
24461                     var last = this.last;
24462                     this.selectRange(this.last,  this.lastActive-1);
24463                     this.grid.getView().focusRow(this.lastActive);
24464                     if(last !== false){
24465                         this.last = last;
24466                     }
24467                 }else{
24468                     this.selectFirstRow();
24469                 }
24470                 this.fireEvent("afterselectionchange", this);
24471             },
24472             "down" : function(e){
24473                 if(!e.shiftKey){
24474                     this.selectNext(e.shiftKey);
24475                 }else if(this.last !== false && this.lastActive !== false){
24476                     var last = this.last;
24477                     this.selectRange(this.last,  this.lastActive+1);
24478                     this.grid.getView().focusRow(this.lastActive);
24479                     if(last !== false){
24480                         this.last = last;
24481                     }
24482                 }else{
24483                     this.selectFirstRow();
24484                 }
24485                 this.fireEvent("afterselectionchange", this);
24486             },
24487             scope: this
24488         });
24489         this.grid.store.on('load', function(){
24490             this.selections.clear();
24491         },this);
24492         /*
24493         var view = this.grid.view;
24494         view.on("refresh", this.onRefresh, this);
24495         view.on("rowupdated", this.onRowUpdated, this);
24496         view.on("rowremoved", this.onRemove, this);
24497         */
24498     },
24499
24500     // private
24501     onRefresh : function()
24502     {
24503         var ds = this.grid.store, i, v = this.grid.view;
24504         var s = this.selections;
24505         s.each(function(r){
24506             if((i = ds.indexOfId(r.id)) != -1){
24507                 v.onRowSelect(i);
24508             }else{
24509                 s.remove(r);
24510             }
24511         });
24512     },
24513
24514     // private
24515     onRemove : function(v, index, r){
24516         this.selections.remove(r);
24517     },
24518
24519     // private
24520     onRowUpdated : function(v, index, r){
24521         if(this.isSelected(r)){
24522             v.onRowSelect(index);
24523         }
24524     },
24525
24526     /**
24527      * Select records.
24528      * @param {Array} records The records to select
24529      * @param {Boolean} keepExisting (optional) True to keep existing selections
24530      */
24531     selectRecords : function(records, keepExisting)
24532     {
24533         if(!keepExisting){
24534             this.clearSelections();
24535         }
24536             var ds = this.grid.store;
24537         for(var i = 0, len = records.length; i < len; i++){
24538             this.selectRow(ds.indexOf(records[i]), true);
24539         }
24540     },
24541
24542     /**
24543      * Gets the number of selected rows.
24544      * @return {Number}
24545      */
24546     getCount : function(){
24547         return this.selections.length;
24548     },
24549
24550     /**
24551      * Selects the first row in the grid.
24552      */
24553     selectFirstRow : function(){
24554         this.selectRow(0);
24555     },
24556
24557     /**
24558      * Select the last row.
24559      * @param {Boolean} keepExisting (optional) True to keep existing selections
24560      */
24561     selectLastRow : function(keepExisting){
24562         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24563         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24564     },
24565
24566     /**
24567      * Selects the row immediately following the last selected row.
24568      * @param {Boolean} keepExisting (optional) True to keep existing selections
24569      */
24570     selectNext : function(keepExisting)
24571     {
24572             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24573             this.selectRow(this.last+1, keepExisting);
24574             this.grid.getView().focusRow(this.last);
24575         }
24576     },
24577
24578     /**
24579      * Selects the row that precedes the last selected row.
24580      * @param {Boolean} keepExisting (optional) True to keep existing selections
24581      */
24582     selectPrevious : function(keepExisting){
24583         if(this.last){
24584             this.selectRow(this.last-1, keepExisting);
24585             this.grid.getView().focusRow(this.last);
24586         }
24587     },
24588
24589     /**
24590      * Returns the selected records
24591      * @return {Array} Array of selected records
24592      */
24593     getSelections : function(){
24594         return [].concat(this.selections.items);
24595     },
24596
24597     /**
24598      * Returns the first selected record.
24599      * @return {Record}
24600      */
24601     getSelected : function(){
24602         return this.selections.itemAt(0);
24603     },
24604
24605
24606     /**
24607      * Clears all selections.
24608      */
24609     clearSelections : function(fast)
24610     {
24611         if(this.locked) {
24612             return;
24613         }
24614         if(fast !== true){
24615                 var ds = this.grid.store;
24616             var s = this.selections;
24617             s.each(function(r){
24618                 this.deselectRow(ds.indexOfId(r.id));
24619             }, this);
24620             s.clear();
24621         }else{
24622             this.selections.clear();
24623         }
24624         this.last = false;
24625     },
24626
24627
24628     /**
24629      * Selects all rows.
24630      */
24631     selectAll : function(){
24632         if(this.locked) {
24633             return;
24634         }
24635         this.selections.clear();
24636         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24637             this.selectRow(i, true);
24638         }
24639     },
24640
24641     /**
24642      * Returns True if there is a selection.
24643      * @return {Boolean}
24644      */
24645     hasSelection : function(){
24646         return this.selections.length > 0;
24647     },
24648
24649     /**
24650      * Returns True if the specified row is selected.
24651      * @param {Number/Record} record The record or index of the record to check
24652      * @return {Boolean}
24653      */
24654     isSelected : function(index){
24655             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24656         return (r && this.selections.key(r.id) ? true : false);
24657     },
24658
24659     /**
24660      * Returns True if the specified record id is selected.
24661      * @param {String} id The id of record to check
24662      * @return {Boolean}
24663      */
24664     isIdSelected : function(id){
24665         return (this.selections.key(id) ? true : false);
24666     },
24667
24668
24669     // private
24670     handleMouseDBClick : function(e, t){
24671         
24672     },
24673     // private
24674     handleMouseDown : function(e, t)
24675     {
24676             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24677         if(this.isLocked() || rowIndex < 0 ){
24678             return;
24679         };
24680         if(e.shiftKey && this.last !== false){
24681             var last = this.last;
24682             this.selectRange(last, rowIndex, e.ctrlKey);
24683             this.last = last; // reset the last
24684             t.focus();
24685     
24686         }else{
24687             var isSelected = this.isSelected(rowIndex);
24688             //Roo.log("select row:" + rowIndex);
24689             if(isSelected){
24690                 this.deselectRow(rowIndex);
24691             } else {
24692                         this.selectRow(rowIndex, true);
24693             }
24694     
24695             /*
24696                 if(e.button !== 0 && isSelected){
24697                 alert('rowIndex 2: ' + rowIndex);
24698                     view.focusRow(rowIndex);
24699                 }else if(e.ctrlKey && isSelected){
24700                     this.deselectRow(rowIndex);
24701                 }else if(!isSelected){
24702                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24703                     view.focusRow(rowIndex);
24704                 }
24705             */
24706         }
24707         this.fireEvent("afterselectionchange", this);
24708     },
24709     // private
24710     handleDragableRowClick :  function(grid, rowIndex, e) 
24711     {
24712         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24713             this.selectRow(rowIndex, false);
24714             grid.view.focusRow(rowIndex);
24715              this.fireEvent("afterselectionchange", this);
24716         }
24717     },
24718     
24719     /**
24720      * Selects multiple rows.
24721      * @param {Array} rows Array of the indexes of the row to select
24722      * @param {Boolean} keepExisting (optional) True to keep existing selections
24723      */
24724     selectRows : function(rows, keepExisting){
24725         if(!keepExisting){
24726             this.clearSelections();
24727         }
24728         for(var i = 0, len = rows.length; i < len; i++){
24729             this.selectRow(rows[i], true);
24730         }
24731     },
24732
24733     /**
24734      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24735      * @param {Number} startRow The index of the first row in the range
24736      * @param {Number} endRow The index of the last row in the range
24737      * @param {Boolean} keepExisting (optional) True to retain existing selections
24738      */
24739     selectRange : function(startRow, endRow, keepExisting){
24740         if(this.locked) {
24741             return;
24742         }
24743         if(!keepExisting){
24744             this.clearSelections();
24745         }
24746         if(startRow <= endRow){
24747             for(var i = startRow; i <= endRow; i++){
24748                 this.selectRow(i, true);
24749             }
24750         }else{
24751             for(var i = startRow; i >= endRow; i--){
24752                 this.selectRow(i, true);
24753             }
24754         }
24755     },
24756
24757     /**
24758      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24759      * @param {Number} startRow The index of the first row in the range
24760      * @param {Number} endRow The index of the last row in the range
24761      */
24762     deselectRange : function(startRow, endRow, preventViewNotify){
24763         if(this.locked) {
24764             return;
24765         }
24766         for(var i = startRow; i <= endRow; i++){
24767             this.deselectRow(i, preventViewNotify);
24768         }
24769     },
24770
24771     /**
24772      * Selects a row.
24773      * @param {Number} row The index of the row to select
24774      * @param {Boolean} keepExisting (optional) True to keep existing selections
24775      */
24776     selectRow : function(index, keepExisting, preventViewNotify)
24777     {
24778             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24779             return;
24780         }
24781         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24782             if(!keepExisting || this.singleSelect){
24783                 this.clearSelections();
24784             }
24785             
24786             var r = this.grid.store.getAt(index);
24787             //console.log('selectRow - record id :' + r.id);
24788             
24789             this.selections.add(r);
24790             this.last = this.lastActive = index;
24791             if(!preventViewNotify){
24792                 var proxy = new Roo.Element(
24793                                 this.grid.getRowDom(index)
24794                 );
24795                 proxy.addClass('bg-info info');
24796             }
24797             this.fireEvent("rowselect", this, index, r);
24798             this.fireEvent("selectionchange", this);
24799         }
24800     },
24801
24802     /**
24803      * Deselects a row.
24804      * @param {Number} row The index of the row to deselect
24805      */
24806     deselectRow : function(index, preventViewNotify)
24807     {
24808         if(this.locked) {
24809             return;
24810         }
24811         if(this.last == index){
24812             this.last = false;
24813         }
24814         if(this.lastActive == index){
24815             this.lastActive = false;
24816         }
24817         
24818         var r = this.grid.store.getAt(index);
24819         if (!r) {
24820             return;
24821         }
24822         
24823         this.selections.remove(r);
24824         //.console.log('deselectRow - record id :' + r.id);
24825         if(!preventViewNotify){
24826         
24827             var proxy = new Roo.Element(
24828                 this.grid.getRowDom(index)
24829             );
24830             proxy.removeClass('bg-info info');
24831         }
24832         this.fireEvent("rowdeselect", this, index);
24833         this.fireEvent("selectionchange", this);
24834     },
24835
24836     // private
24837     restoreLast : function(){
24838         if(this._last){
24839             this.last = this._last;
24840         }
24841     },
24842
24843     // private
24844     acceptsNav : function(row, col, cm){
24845         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24846     },
24847
24848     // private
24849     onEditorKey : function(field, e){
24850         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24851         if(k == e.TAB){
24852             e.stopEvent();
24853             ed.completeEdit();
24854             if(e.shiftKey){
24855                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24856             }else{
24857                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24858             }
24859         }else if(k == e.ENTER && !e.ctrlKey){
24860             e.stopEvent();
24861             ed.completeEdit();
24862             if(e.shiftKey){
24863                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24864             }else{
24865                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24866             }
24867         }else if(k == e.ESC){
24868             ed.cancelEdit();
24869         }
24870         if(newCell){
24871             g.startEditing(newCell[0], newCell[1]);
24872         }
24873     }
24874 });
24875 /*
24876  * Based on:
24877  * Ext JS Library 1.1.1
24878  * Copyright(c) 2006-2007, Ext JS, LLC.
24879  *
24880  * Originally Released Under LGPL - original licence link has changed is not relivant.
24881  *
24882  * Fork - LGPL
24883  * <script type="text/javascript">
24884  */
24885  
24886 /**
24887  * @class Roo.bootstrap.PagingToolbar
24888  * @extends Roo.bootstrap.NavSimplebar
24889  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24890  * @constructor
24891  * Create a new PagingToolbar
24892  * @param {Object} config The config object
24893  * @param {Roo.data.Store} store
24894  */
24895 Roo.bootstrap.PagingToolbar = function(config)
24896 {
24897     // old args format still supported... - xtype is prefered..
24898         // created from xtype...
24899     
24900     this.ds = config.dataSource;
24901     
24902     if (config.store && !this.ds) {
24903         this.store= Roo.factory(config.store, Roo.data);
24904         this.ds = this.store;
24905         this.ds.xmodule = this.xmodule || false;
24906     }
24907     
24908     this.toolbarItems = [];
24909     if (config.items) {
24910         this.toolbarItems = config.items;
24911     }
24912     
24913     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24914     
24915     this.cursor = 0;
24916     
24917     if (this.ds) { 
24918         this.bind(this.ds);
24919     }
24920     
24921     if (Roo.bootstrap.version == 4) {
24922         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24923     } else {
24924         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24925     }
24926     
24927 };
24928
24929 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24930     /**
24931      * @cfg {Roo.data.Store} dataSource
24932      * The underlying data store providing the paged data
24933      */
24934     /**
24935      * @cfg {String/HTMLElement/Element} container
24936      * container The id or element that will contain the toolbar
24937      */
24938     /**
24939      * @cfg {Boolean} displayInfo
24940      * True to display the displayMsg (defaults to false)
24941      */
24942     /**
24943      * @cfg {Number} pageSize
24944      * The number of records to display per page (defaults to 20)
24945      */
24946     pageSize: 20,
24947     /**
24948      * @cfg {String} displayMsg
24949      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24950      */
24951     displayMsg : 'Displaying {0} - {1} of {2}',
24952     /**
24953      * @cfg {String} emptyMsg
24954      * The message to display when no records are found (defaults to "No data to display")
24955      */
24956     emptyMsg : 'No data to display',
24957     /**
24958      * Customizable piece of the default paging text (defaults to "Page")
24959      * @type String
24960      */
24961     beforePageText : "Page",
24962     /**
24963      * Customizable piece of the default paging text (defaults to "of %0")
24964      * @type String
24965      */
24966     afterPageText : "of {0}",
24967     /**
24968      * Customizable piece of the default paging text (defaults to "First Page")
24969      * @type String
24970      */
24971     firstText : "First Page",
24972     /**
24973      * Customizable piece of the default paging text (defaults to "Previous Page")
24974      * @type String
24975      */
24976     prevText : "Previous Page",
24977     /**
24978      * Customizable piece of the default paging text (defaults to "Next Page")
24979      * @type String
24980      */
24981     nextText : "Next Page",
24982     /**
24983      * Customizable piece of the default paging text (defaults to "Last Page")
24984      * @type String
24985      */
24986     lastText : "Last Page",
24987     /**
24988      * Customizable piece of the default paging text (defaults to "Refresh")
24989      * @type String
24990      */
24991     refreshText : "Refresh",
24992
24993     buttons : false,
24994     // private
24995     onRender : function(ct, position) 
24996     {
24997         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24998         this.navgroup.parentId = this.id;
24999         this.navgroup.onRender(this.el, null);
25000         // add the buttons to the navgroup
25001         
25002         if(this.displayInfo){
25003             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25004             this.displayEl = this.el.select('.x-paging-info', true).first();
25005 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25006 //            this.displayEl = navel.el.select('span',true).first();
25007         }
25008         
25009         var _this = this;
25010         
25011         if(this.buttons){
25012             Roo.each(_this.buttons, function(e){ // this might need to use render????
25013                Roo.factory(e).render(_this.el);
25014             });
25015         }
25016             
25017         Roo.each(_this.toolbarItems, function(e) {
25018             _this.navgroup.addItem(e);
25019         });
25020         
25021         
25022         this.first = this.navgroup.addItem({
25023             tooltip: this.firstText,
25024             cls: "prev btn-outline-secondary",
25025             html : ' <i class="fa fa-step-backward"></i>',
25026             disabled: true,
25027             preventDefault: true,
25028             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25029         });
25030         
25031         this.prev =  this.navgroup.addItem({
25032             tooltip: this.prevText,
25033             cls: "prev btn-outline-secondary",
25034             html : ' <i class="fa fa-backward"></i>',
25035             disabled: true,
25036             preventDefault: true,
25037             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25038         });
25039     //this.addSeparator();
25040         
25041         
25042         var field = this.navgroup.addItem( {
25043             tagtype : 'span',
25044             cls : 'x-paging-position  btn-outline-secondary',
25045              disabled: true,
25046             html : this.beforePageText  +
25047                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25048                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25049          } ); //?? escaped?
25050         
25051         this.field = field.el.select('input', true).first();
25052         this.field.on("keydown", this.onPagingKeydown, this);
25053         this.field.on("focus", function(){this.dom.select();});
25054     
25055     
25056         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25057         //this.field.setHeight(18);
25058         //this.addSeparator();
25059         this.next = this.navgroup.addItem({
25060             tooltip: this.nextText,
25061             cls: "next btn-outline-secondary",
25062             html : ' <i class="fa fa-forward"></i>',
25063             disabled: true,
25064             preventDefault: true,
25065             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25066         });
25067         this.last = this.navgroup.addItem({
25068             tooltip: this.lastText,
25069             html : ' <i class="fa fa-step-forward"></i>',
25070             cls: "next btn-outline-secondary",
25071             disabled: true,
25072             preventDefault: true,
25073             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25074         });
25075     //this.addSeparator();
25076         this.loading = this.navgroup.addItem({
25077             tooltip: this.refreshText,
25078             cls: "btn-outline-secondary",
25079             html : ' <i class="fa fa-refresh"></i>',
25080             preventDefault: true,
25081             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25082         });
25083         
25084     },
25085
25086     // private
25087     updateInfo : function(){
25088         if(this.displayEl){
25089             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25090             var msg = count == 0 ?
25091                 this.emptyMsg :
25092                 String.format(
25093                     this.displayMsg,
25094                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25095                 );
25096             this.displayEl.update(msg);
25097         }
25098     },
25099
25100     // private
25101     onLoad : function(ds, r, o)
25102     {
25103         this.cursor = o.params.start ? o.params.start : 0;
25104         
25105         var d = this.getPageData(),
25106             ap = d.activePage,
25107             ps = d.pages;
25108         
25109         
25110         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25111         this.field.dom.value = ap;
25112         this.first.setDisabled(ap == 1);
25113         this.prev.setDisabled(ap == 1);
25114         this.next.setDisabled(ap == ps);
25115         this.last.setDisabled(ap == ps);
25116         this.loading.enable();
25117         this.updateInfo();
25118     },
25119
25120     // private
25121     getPageData : function(){
25122         var total = this.ds.getTotalCount();
25123         return {
25124             total : total,
25125             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25126             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25127         };
25128     },
25129
25130     // private
25131     onLoadError : function(){
25132         this.loading.enable();
25133     },
25134
25135     // private
25136     onPagingKeydown : function(e){
25137         var k = e.getKey();
25138         var d = this.getPageData();
25139         if(k == e.RETURN){
25140             var v = this.field.dom.value, pageNum;
25141             if(!v || isNaN(pageNum = parseInt(v, 10))){
25142                 this.field.dom.value = d.activePage;
25143                 return;
25144             }
25145             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25146             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25147             e.stopEvent();
25148         }
25149         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))
25150         {
25151           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25152           this.field.dom.value = pageNum;
25153           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25154           e.stopEvent();
25155         }
25156         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25157         {
25158           var v = this.field.dom.value, pageNum; 
25159           var increment = (e.shiftKey) ? 10 : 1;
25160           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25161                 increment *= -1;
25162           }
25163           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25164             this.field.dom.value = d.activePage;
25165             return;
25166           }
25167           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25168           {
25169             this.field.dom.value = parseInt(v, 10) + increment;
25170             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25171             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25172           }
25173           e.stopEvent();
25174         }
25175     },
25176
25177     // private
25178     beforeLoad : function(){
25179         if(this.loading){
25180             this.loading.disable();
25181         }
25182     },
25183
25184     // private
25185     onClick : function(which){
25186         
25187         var ds = this.ds;
25188         if (!ds) {
25189             return;
25190         }
25191         
25192         switch(which){
25193             case "first":
25194                 ds.load({params:{start: 0, limit: this.pageSize}});
25195             break;
25196             case "prev":
25197                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25198             break;
25199             case "next":
25200                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25201             break;
25202             case "last":
25203                 var total = ds.getTotalCount();
25204                 var extra = total % this.pageSize;
25205                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25206                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25207             break;
25208             case "refresh":
25209                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25210             break;
25211         }
25212     },
25213
25214     /**
25215      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25216      * @param {Roo.data.Store} store The data store to unbind
25217      */
25218     unbind : function(ds){
25219         ds.un("beforeload", this.beforeLoad, this);
25220         ds.un("load", this.onLoad, this);
25221         ds.un("loadexception", this.onLoadError, this);
25222         ds.un("remove", this.updateInfo, this);
25223         ds.un("add", this.updateInfo, this);
25224         this.ds = undefined;
25225     },
25226
25227     /**
25228      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25229      * @param {Roo.data.Store} store The data store to bind
25230      */
25231     bind : function(ds){
25232         ds.on("beforeload", this.beforeLoad, this);
25233         ds.on("load", this.onLoad, this);
25234         ds.on("loadexception", this.onLoadError, this);
25235         ds.on("remove", this.updateInfo, this);
25236         ds.on("add", this.updateInfo, this);
25237         this.ds = ds;
25238     }
25239 });/*
25240  * - LGPL
25241  *
25242  * element
25243  * 
25244  */
25245
25246 /**
25247  * @class Roo.bootstrap.MessageBar
25248  * @extends Roo.bootstrap.Component
25249  * Bootstrap MessageBar class
25250  * @cfg {String} html contents of the MessageBar
25251  * @cfg {String} weight (info | success | warning | danger) default info
25252  * @cfg {String} beforeClass insert the bar before the given class
25253  * @cfg {Boolean} closable (true | false) default false
25254  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25255  * 
25256  * @constructor
25257  * Create a new Element
25258  * @param {Object} config The config object
25259  */
25260
25261 Roo.bootstrap.MessageBar = function(config){
25262     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25263 };
25264
25265 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25266     
25267     html: '',
25268     weight: 'info',
25269     closable: false,
25270     fixed: false,
25271     beforeClass: 'bootstrap-sticky-wrap',
25272     
25273     getAutoCreate : function(){
25274         
25275         var cfg = {
25276             tag: 'div',
25277             cls: 'alert alert-dismissable alert-' + this.weight,
25278             cn: [
25279                 {
25280                     tag: 'span',
25281                     cls: 'message',
25282                     html: this.html || ''
25283                 }
25284             ]
25285         };
25286         
25287         if(this.fixed){
25288             cfg.cls += ' alert-messages-fixed';
25289         }
25290         
25291         if(this.closable){
25292             cfg.cn.push({
25293                 tag: 'button',
25294                 cls: 'close',
25295                 html: 'x'
25296             });
25297         }
25298         
25299         return cfg;
25300     },
25301     
25302     onRender : function(ct, position)
25303     {
25304         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25305         
25306         if(!this.el){
25307             var cfg = Roo.apply({},  this.getAutoCreate());
25308             cfg.id = Roo.id();
25309             
25310             if (this.cls) {
25311                 cfg.cls += ' ' + this.cls;
25312             }
25313             if (this.style) {
25314                 cfg.style = this.style;
25315             }
25316             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25317             
25318             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25319         }
25320         
25321         this.el.select('>button.close').on('click', this.hide, this);
25322         
25323     },
25324     
25325     show : function()
25326     {
25327         if (!this.rendered) {
25328             this.render();
25329         }
25330         
25331         this.el.show();
25332         
25333         this.fireEvent('show', this);
25334         
25335     },
25336     
25337     hide : function()
25338     {
25339         if (!this.rendered) {
25340             this.render();
25341         }
25342         
25343         this.el.hide();
25344         
25345         this.fireEvent('hide', this);
25346     },
25347     
25348     update : function()
25349     {
25350 //        var e = this.el.dom.firstChild;
25351 //        
25352 //        if(this.closable){
25353 //            e = e.nextSibling;
25354 //        }
25355 //        
25356 //        e.data = this.html || '';
25357
25358         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25359     }
25360    
25361 });
25362
25363  
25364
25365      /*
25366  * - LGPL
25367  *
25368  * Graph
25369  * 
25370  */
25371
25372
25373 /**
25374  * @class Roo.bootstrap.Graph
25375  * @extends Roo.bootstrap.Component
25376  * Bootstrap Graph class
25377 > Prameters
25378  -sm {number} sm 4
25379  -md {number} md 5
25380  @cfg {String} graphtype  bar | vbar | pie
25381  @cfg {number} g_x coodinator | centre x (pie)
25382  @cfg {number} g_y coodinator | centre y (pie)
25383  @cfg {number} g_r radius (pie)
25384  @cfg {number} g_height height of the chart (respected by all elements in the set)
25385  @cfg {number} g_width width of the chart (respected by all elements in the set)
25386  @cfg {Object} title The title of the chart
25387     
25388  -{Array}  values
25389  -opts (object) options for the chart 
25390      o {
25391      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25392      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25393      o vgutter (number)
25394      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.
25395      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25396      o to
25397      o stretch (boolean)
25398      o }
25399  -opts (object) options for the pie
25400      o{
25401      o cut
25402      o startAngle (number)
25403      o endAngle (number)
25404      } 
25405  *
25406  * @constructor
25407  * Create a new Input
25408  * @param {Object} config The config object
25409  */
25410
25411 Roo.bootstrap.Graph = function(config){
25412     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25413     
25414     this.addEvents({
25415         // img events
25416         /**
25417          * @event click
25418          * The img click event for the img.
25419          * @param {Roo.EventObject} e
25420          */
25421         "click" : true
25422     });
25423 };
25424
25425 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25426     
25427     sm: 4,
25428     md: 5,
25429     graphtype: 'bar',
25430     g_height: 250,
25431     g_width: 400,
25432     g_x: 50,
25433     g_y: 50,
25434     g_r: 30,
25435     opts:{
25436         //g_colors: this.colors,
25437         g_type: 'soft',
25438         g_gutter: '20%'
25439
25440     },
25441     title : false,
25442
25443     getAutoCreate : function(){
25444         
25445         var cfg = {
25446             tag: 'div',
25447             html : null
25448         };
25449         
25450         
25451         return  cfg;
25452     },
25453
25454     onRender : function(ct,position){
25455         
25456         
25457         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25458         
25459         if (typeof(Raphael) == 'undefined') {
25460             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25461             return;
25462         }
25463         
25464         this.raphael = Raphael(this.el.dom);
25465         
25466                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25467                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25468                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25469                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25470                 /*
25471                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25472                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25473                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25474                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25475                 
25476                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25477                 r.barchart(330, 10, 300, 220, data1);
25478                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25479                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25480                 */
25481                 
25482                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25483                 // r.barchart(30, 30, 560, 250,  xdata, {
25484                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25485                 //     axis : "0 0 1 1",
25486                 //     axisxlabels :  xdata
25487                 //     //yvalues : cols,
25488                    
25489                 // });
25490 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25491 //        
25492 //        this.load(null,xdata,{
25493 //                axis : "0 0 1 1",
25494 //                axisxlabels :  xdata
25495 //                });
25496
25497     },
25498
25499     load : function(graphtype,xdata,opts)
25500     {
25501         this.raphael.clear();
25502         if(!graphtype) {
25503             graphtype = this.graphtype;
25504         }
25505         if(!opts){
25506             opts = this.opts;
25507         }
25508         var r = this.raphael,
25509             fin = function () {
25510                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25511             },
25512             fout = function () {
25513                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25514             },
25515             pfin = function() {
25516                 this.sector.stop();
25517                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25518
25519                 if (this.label) {
25520                     this.label[0].stop();
25521                     this.label[0].attr({ r: 7.5 });
25522                     this.label[1].attr({ "font-weight": 800 });
25523                 }
25524             },
25525             pfout = function() {
25526                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25527
25528                 if (this.label) {
25529                     this.label[0].animate({ r: 5 }, 500, "bounce");
25530                     this.label[1].attr({ "font-weight": 400 });
25531                 }
25532             };
25533
25534         switch(graphtype){
25535             case 'bar':
25536                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25537                 break;
25538             case 'hbar':
25539                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25540                 break;
25541             case 'pie':
25542 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25543 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25544 //            
25545                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25546                 
25547                 break;
25548
25549         }
25550         
25551         if(this.title){
25552             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25553         }
25554         
25555     },
25556     
25557     setTitle: function(o)
25558     {
25559         this.title = o;
25560     },
25561     
25562     initEvents: function() {
25563         
25564         if(!this.href){
25565             this.el.on('click', this.onClick, this);
25566         }
25567     },
25568     
25569     onClick : function(e)
25570     {
25571         Roo.log('img onclick');
25572         this.fireEvent('click', this, e);
25573     }
25574    
25575 });
25576
25577  
25578 /*
25579  * - LGPL
25580  *
25581  * numberBox
25582  * 
25583  */
25584 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25585
25586 /**
25587  * @class Roo.bootstrap.dash.NumberBox
25588  * @extends Roo.bootstrap.Component
25589  * Bootstrap NumberBox class
25590  * @cfg {String} headline Box headline
25591  * @cfg {String} content Box content
25592  * @cfg {String} icon Box icon
25593  * @cfg {String} footer Footer text
25594  * @cfg {String} fhref Footer href
25595  * 
25596  * @constructor
25597  * Create a new NumberBox
25598  * @param {Object} config The config object
25599  */
25600
25601
25602 Roo.bootstrap.dash.NumberBox = function(config){
25603     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25604     
25605 };
25606
25607 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25608     
25609     headline : '',
25610     content : '',
25611     icon : '',
25612     footer : '',
25613     fhref : '',
25614     ficon : '',
25615     
25616     getAutoCreate : function(){
25617         
25618         var cfg = {
25619             tag : 'div',
25620             cls : 'small-box ',
25621             cn : [
25622                 {
25623                     tag : 'div',
25624                     cls : 'inner',
25625                     cn :[
25626                         {
25627                             tag : 'h3',
25628                             cls : 'roo-headline',
25629                             html : this.headline
25630                         },
25631                         {
25632                             tag : 'p',
25633                             cls : 'roo-content',
25634                             html : this.content
25635                         }
25636                     ]
25637                 }
25638             ]
25639         };
25640         
25641         if(this.icon){
25642             cfg.cn.push({
25643                 tag : 'div',
25644                 cls : 'icon',
25645                 cn :[
25646                     {
25647                         tag : 'i',
25648                         cls : 'ion ' + this.icon
25649                     }
25650                 ]
25651             });
25652         }
25653         
25654         if(this.footer){
25655             var footer = {
25656                 tag : 'a',
25657                 cls : 'small-box-footer',
25658                 href : this.fhref || '#',
25659                 html : this.footer
25660             };
25661             
25662             cfg.cn.push(footer);
25663             
25664         }
25665         
25666         return  cfg;
25667     },
25668
25669     onRender : function(ct,position){
25670         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25671
25672
25673        
25674                 
25675     },
25676
25677     setHeadline: function (value)
25678     {
25679         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25680     },
25681     
25682     setFooter: function (value, href)
25683     {
25684         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25685         
25686         if(href){
25687             this.el.select('a.small-box-footer',true).first().attr('href', href);
25688         }
25689         
25690     },
25691
25692     setContent: function (value)
25693     {
25694         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25695     },
25696
25697     initEvents: function() 
25698     {   
25699         
25700     }
25701     
25702 });
25703
25704  
25705 /*
25706  * - LGPL
25707  *
25708  * TabBox
25709  * 
25710  */
25711 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25712
25713 /**
25714  * @class Roo.bootstrap.dash.TabBox
25715  * @extends Roo.bootstrap.Component
25716  * Bootstrap TabBox class
25717  * @cfg {String} title Title of the TabBox
25718  * @cfg {String} icon Icon of the TabBox
25719  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25720  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25721  * 
25722  * @constructor
25723  * Create a new TabBox
25724  * @param {Object} config The config object
25725  */
25726
25727
25728 Roo.bootstrap.dash.TabBox = function(config){
25729     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25730     this.addEvents({
25731         // raw events
25732         /**
25733          * @event addpane
25734          * When a pane is added
25735          * @param {Roo.bootstrap.dash.TabPane} pane
25736          */
25737         "addpane" : true,
25738         /**
25739          * @event activatepane
25740          * When a pane is activated
25741          * @param {Roo.bootstrap.dash.TabPane} pane
25742          */
25743         "activatepane" : true
25744         
25745          
25746     });
25747     
25748     this.panes = [];
25749 };
25750
25751 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25752
25753     title : '',
25754     icon : false,
25755     showtabs : true,
25756     tabScrollable : false,
25757     
25758     getChildContainer : function()
25759     {
25760         return this.el.select('.tab-content', true).first();
25761     },
25762     
25763     getAutoCreate : function(){
25764         
25765         var header = {
25766             tag: 'li',
25767             cls: 'pull-left header',
25768             html: this.title,
25769             cn : []
25770         };
25771         
25772         if(this.icon){
25773             header.cn.push({
25774                 tag: 'i',
25775                 cls: 'fa ' + this.icon
25776             });
25777         }
25778         
25779         var h = {
25780             tag: 'ul',
25781             cls: 'nav nav-tabs pull-right',
25782             cn: [
25783                 header
25784             ]
25785         };
25786         
25787         if(this.tabScrollable){
25788             h = {
25789                 tag: 'div',
25790                 cls: 'tab-header',
25791                 cn: [
25792                     {
25793                         tag: 'ul',
25794                         cls: 'nav nav-tabs pull-right',
25795                         cn: [
25796                             header
25797                         ]
25798                     }
25799                 ]
25800             };
25801         }
25802         
25803         var cfg = {
25804             tag: 'div',
25805             cls: 'nav-tabs-custom',
25806             cn: [
25807                 h,
25808                 {
25809                     tag: 'div',
25810                     cls: 'tab-content no-padding',
25811                     cn: []
25812                 }
25813             ]
25814         };
25815
25816         return  cfg;
25817     },
25818     initEvents : function()
25819     {
25820         //Roo.log('add add pane handler');
25821         this.on('addpane', this.onAddPane, this);
25822     },
25823      /**
25824      * Updates the box title
25825      * @param {String} html to set the title to.
25826      */
25827     setTitle : function(value)
25828     {
25829         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25830     },
25831     onAddPane : function(pane)
25832     {
25833         this.panes.push(pane);
25834         //Roo.log('addpane');
25835         //Roo.log(pane);
25836         // tabs are rendere left to right..
25837         if(!this.showtabs){
25838             return;
25839         }
25840         
25841         var ctr = this.el.select('.nav-tabs', true).first();
25842          
25843          
25844         var existing = ctr.select('.nav-tab',true);
25845         var qty = existing.getCount();;
25846         
25847         
25848         var tab = ctr.createChild({
25849             tag : 'li',
25850             cls : 'nav-tab' + (qty ? '' : ' active'),
25851             cn : [
25852                 {
25853                     tag : 'a',
25854                     href:'#',
25855                     html : pane.title
25856                 }
25857             ]
25858         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25859         pane.tab = tab;
25860         
25861         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25862         if (!qty) {
25863             pane.el.addClass('active');
25864         }
25865         
25866                 
25867     },
25868     onTabClick : function(ev,un,ob,pane)
25869     {
25870         //Roo.log('tab - prev default');
25871         ev.preventDefault();
25872         
25873         
25874         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25875         pane.tab.addClass('active');
25876         //Roo.log(pane.title);
25877         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25878         // technically we should have a deactivate event.. but maybe add later.
25879         // and it should not de-activate the selected tab...
25880         this.fireEvent('activatepane', pane);
25881         pane.el.addClass('active');
25882         pane.fireEvent('activate');
25883         
25884         
25885     },
25886     
25887     getActivePane : function()
25888     {
25889         var r = false;
25890         Roo.each(this.panes, function(p) {
25891             if(p.el.hasClass('active')){
25892                 r = p;
25893                 return false;
25894             }
25895             
25896             return;
25897         });
25898         
25899         return r;
25900     }
25901     
25902     
25903 });
25904
25905  
25906 /*
25907  * - LGPL
25908  *
25909  * Tab pane
25910  * 
25911  */
25912 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25913 /**
25914  * @class Roo.bootstrap.TabPane
25915  * @extends Roo.bootstrap.Component
25916  * Bootstrap TabPane class
25917  * @cfg {Boolean} active (false | true) Default false
25918  * @cfg {String} title title of panel
25919
25920  * 
25921  * @constructor
25922  * Create a new TabPane
25923  * @param {Object} config The config object
25924  */
25925
25926 Roo.bootstrap.dash.TabPane = function(config){
25927     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25928     
25929     this.addEvents({
25930         // raw events
25931         /**
25932          * @event activate
25933          * When a pane is activated
25934          * @param {Roo.bootstrap.dash.TabPane} pane
25935          */
25936         "activate" : true
25937          
25938     });
25939 };
25940
25941 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25942     
25943     active : false,
25944     title : '',
25945     
25946     // the tabBox that this is attached to.
25947     tab : false,
25948      
25949     getAutoCreate : function() 
25950     {
25951         var cfg = {
25952             tag: 'div',
25953             cls: 'tab-pane'
25954         };
25955         
25956         if(this.active){
25957             cfg.cls += ' active';
25958         }
25959         
25960         return cfg;
25961     },
25962     initEvents  : function()
25963     {
25964         //Roo.log('trigger add pane handler');
25965         this.parent().fireEvent('addpane', this)
25966     },
25967     
25968      /**
25969      * Updates the tab title 
25970      * @param {String} html to set the title to.
25971      */
25972     setTitle: function(str)
25973     {
25974         if (!this.tab) {
25975             return;
25976         }
25977         this.title = str;
25978         this.tab.select('a', true).first().dom.innerHTML = str;
25979         
25980     }
25981     
25982     
25983     
25984 });
25985
25986  
25987
25988
25989  /*
25990  * - LGPL
25991  *
25992  * menu
25993  * 
25994  */
25995 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25996
25997 /**
25998  * @class Roo.bootstrap.menu.Menu
25999  * @extends Roo.bootstrap.Component
26000  * Bootstrap Menu class - container for Menu
26001  * @cfg {String} html Text of the menu
26002  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26003  * @cfg {String} icon Font awesome icon
26004  * @cfg {String} pos Menu align to (top | bottom) default bottom
26005  * 
26006  * 
26007  * @constructor
26008  * Create a new Menu
26009  * @param {Object} config The config object
26010  */
26011
26012
26013 Roo.bootstrap.menu.Menu = function(config){
26014     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26015     
26016     this.addEvents({
26017         /**
26018          * @event beforeshow
26019          * Fires before this menu is displayed
26020          * @param {Roo.bootstrap.menu.Menu} this
26021          */
26022         beforeshow : true,
26023         /**
26024          * @event beforehide
26025          * Fires before this menu is hidden
26026          * @param {Roo.bootstrap.menu.Menu} this
26027          */
26028         beforehide : true,
26029         /**
26030          * @event show
26031          * Fires after this menu is displayed
26032          * @param {Roo.bootstrap.menu.Menu} this
26033          */
26034         show : true,
26035         /**
26036          * @event hide
26037          * Fires after this menu is hidden
26038          * @param {Roo.bootstrap.menu.Menu} this
26039          */
26040         hide : true,
26041         /**
26042          * @event click
26043          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26044          * @param {Roo.bootstrap.menu.Menu} this
26045          * @param {Roo.EventObject} e
26046          */
26047         click : true
26048     });
26049     
26050 };
26051
26052 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26053     
26054     submenu : false,
26055     html : '',
26056     weight : 'default',
26057     icon : false,
26058     pos : 'bottom',
26059     
26060     
26061     getChildContainer : function() {
26062         if(this.isSubMenu){
26063             return this.el;
26064         }
26065         
26066         return this.el.select('ul.dropdown-menu', true).first();  
26067     },
26068     
26069     getAutoCreate : function()
26070     {
26071         var text = [
26072             {
26073                 tag : 'span',
26074                 cls : 'roo-menu-text',
26075                 html : this.html
26076             }
26077         ];
26078         
26079         if(this.icon){
26080             text.unshift({
26081                 tag : 'i',
26082                 cls : 'fa ' + this.icon
26083             })
26084         }
26085         
26086         
26087         var cfg = {
26088             tag : 'div',
26089             cls : 'btn-group',
26090             cn : [
26091                 {
26092                     tag : 'button',
26093                     cls : 'dropdown-button btn btn-' + this.weight,
26094                     cn : text
26095                 },
26096                 {
26097                     tag : 'button',
26098                     cls : 'dropdown-toggle btn btn-' + this.weight,
26099                     cn : [
26100                         {
26101                             tag : 'span',
26102                             cls : 'caret'
26103                         }
26104                     ]
26105                 },
26106                 {
26107                     tag : 'ul',
26108                     cls : 'dropdown-menu'
26109                 }
26110             ]
26111             
26112         };
26113         
26114         if(this.pos == 'top'){
26115             cfg.cls += ' dropup';
26116         }
26117         
26118         if(this.isSubMenu){
26119             cfg = {
26120                 tag : 'ul',
26121                 cls : 'dropdown-menu'
26122             }
26123         }
26124         
26125         return cfg;
26126     },
26127     
26128     onRender : function(ct, position)
26129     {
26130         this.isSubMenu = ct.hasClass('dropdown-submenu');
26131         
26132         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26133     },
26134     
26135     initEvents : function() 
26136     {
26137         if(this.isSubMenu){
26138             return;
26139         }
26140         
26141         this.hidden = true;
26142         
26143         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26144         this.triggerEl.on('click', this.onTriggerPress, this);
26145         
26146         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26147         this.buttonEl.on('click', this.onClick, this);
26148         
26149     },
26150     
26151     list : function()
26152     {
26153         if(this.isSubMenu){
26154             return this.el;
26155         }
26156         
26157         return this.el.select('ul.dropdown-menu', true).first();
26158     },
26159     
26160     onClick : function(e)
26161     {
26162         this.fireEvent("click", this, e);
26163     },
26164     
26165     onTriggerPress  : function(e)
26166     {   
26167         if (this.isVisible()) {
26168             this.hide();
26169         } else {
26170             this.show();
26171         }
26172     },
26173     
26174     isVisible : function(){
26175         return !this.hidden;
26176     },
26177     
26178     show : function()
26179     {
26180         this.fireEvent("beforeshow", this);
26181         
26182         this.hidden = false;
26183         this.el.addClass('open');
26184         
26185         Roo.get(document).on("mouseup", this.onMouseUp, this);
26186         
26187         this.fireEvent("show", this);
26188         
26189         
26190     },
26191     
26192     hide : function()
26193     {
26194         this.fireEvent("beforehide", this);
26195         
26196         this.hidden = true;
26197         this.el.removeClass('open');
26198         
26199         Roo.get(document).un("mouseup", this.onMouseUp);
26200         
26201         this.fireEvent("hide", this);
26202     },
26203     
26204     onMouseUp : function()
26205     {
26206         this.hide();
26207     }
26208     
26209 });
26210
26211  
26212  /*
26213  * - LGPL
26214  *
26215  * menu item
26216  * 
26217  */
26218 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26219
26220 /**
26221  * @class Roo.bootstrap.menu.Item
26222  * @extends Roo.bootstrap.Component
26223  * Bootstrap MenuItem class
26224  * @cfg {Boolean} submenu (true | false) default false
26225  * @cfg {String} html text of the item
26226  * @cfg {String} href the link
26227  * @cfg {Boolean} disable (true | false) default false
26228  * @cfg {Boolean} preventDefault (true | false) default true
26229  * @cfg {String} icon Font awesome icon
26230  * @cfg {String} pos Submenu align to (left | right) default right 
26231  * 
26232  * 
26233  * @constructor
26234  * Create a new Item
26235  * @param {Object} config The config object
26236  */
26237
26238
26239 Roo.bootstrap.menu.Item = function(config){
26240     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26241     this.addEvents({
26242         /**
26243          * @event mouseover
26244          * Fires when the mouse is hovering over this menu
26245          * @param {Roo.bootstrap.menu.Item} this
26246          * @param {Roo.EventObject} e
26247          */
26248         mouseover : true,
26249         /**
26250          * @event mouseout
26251          * Fires when the mouse exits this menu
26252          * @param {Roo.bootstrap.menu.Item} this
26253          * @param {Roo.EventObject} e
26254          */
26255         mouseout : true,
26256         // raw events
26257         /**
26258          * @event click
26259          * The raw click event for the entire grid.
26260          * @param {Roo.EventObject} e
26261          */
26262         click : true
26263     });
26264 };
26265
26266 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26267     
26268     submenu : false,
26269     href : '',
26270     html : '',
26271     preventDefault: true,
26272     disable : false,
26273     icon : false,
26274     pos : 'right',
26275     
26276     getAutoCreate : function()
26277     {
26278         var text = [
26279             {
26280                 tag : 'span',
26281                 cls : 'roo-menu-item-text',
26282                 html : this.html
26283             }
26284         ];
26285         
26286         if(this.icon){
26287             text.unshift({
26288                 tag : 'i',
26289                 cls : 'fa ' + this.icon
26290             })
26291         }
26292         
26293         var cfg = {
26294             tag : 'li',
26295             cn : [
26296                 {
26297                     tag : 'a',
26298                     href : this.href || '#',
26299                     cn : text
26300                 }
26301             ]
26302         };
26303         
26304         if(this.disable){
26305             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26306         }
26307         
26308         if(this.submenu){
26309             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26310             
26311             if(this.pos == 'left'){
26312                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26313             }
26314         }
26315         
26316         return cfg;
26317     },
26318     
26319     initEvents : function() 
26320     {
26321         this.el.on('mouseover', this.onMouseOver, this);
26322         this.el.on('mouseout', this.onMouseOut, this);
26323         
26324         this.el.select('a', true).first().on('click', this.onClick, this);
26325         
26326     },
26327     
26328     onClick : function(e)
26329     {
26330         if(this.preventDefault){
26331             e.preventDefault();
26332         }
26333         
26334         this.fireEvent("click", this, e);
26335     },
26336     
26337     onMouseOver : function(e)
26338     {
26339         if(this.submenu && this.pos == 'left'){
26340             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26341         }
26342         
26343         this.fireEvent("mouseover", this, e);
26344     },
26345     
26346     onMouseOut : function(e)
26347     {
26348         this.fireEvent("mouseout", this, e);
26349     }
26350 });
26351
26352  
26353
26354  /*
26355  * - LGPL
26356  *
26357  * menu separator
26358  * 
26359  */
26360 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26361
26362 /**
26363  * @class Roo.bootstrap.menu.Separator
26364  * @extends Roo.bootstrap.Component
26365  * Bootstrap Separator class
26366  * 
26367  * @constructor
26368  * Create a new Separator
26369  * @param {Object} config The config object
26370  */
26371
26372
26373 Roo.bootstrap.menu.Separator = function(config){
26374     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26375 };
26376
26377 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26378     
26379     getAutoCreate : function(){
26380         var cfg = {
26381             tag : 'li',
26382             cls: 'divider'
26383         };
26384         
26385         return cfg;
26386     }
26387    
26388 });
26389
26390  
26391
26392  /*
26393  * - LGPL
26394  *
26395  * Tooltip
26396  * 
26397  */
26398
26399 /**
26400  * @class Roo.bootstrap.Tooltip
26401  * Bootstrap Tooltip class
26402  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26403  * to determine which dom element triggers the tooltip.
26404  * 
26405  * It needs to add support for additional attributes like tooltip-position
26406  * 
26407  * @constructor
26408  * Create a new Toolti
26409  * @param {Object} config The config object
26410  */
26411
26412 Roo.bootstrap.Tooltip = function(config){
26413     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26414     
26415     this.alignment = Roo.bootstrap.Tooltip.alignment;
26416     
26417     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26418         this.alignment = config.alignment;
26419     }
26420     
26421 };
26422
26423 Roo.apply(Roo.bootstrap.Tooltip, {
26424     /**
26425      * @function init initialize tooltip monitoring.
26426      * @static
26427      */
26428     currentEl : false,
26429     currentTip : false,
26430     currentRegion : false,
26431     
26432     //  init : delay?
26433     
26434     init : function()
26435     {
26436         Roo.get(document).on('mouseover', this.enter ,this);
26437         Roo.get(document).on('mouseout', this.leave, this);
26438          
26439         
26440         this.currentTip = new Roo.bootstrap.Tooltip();
26441     },
26442     
26443     enter : function(ev)
26444     {
26445         var dom = ev.getTarget();
26446         
26447         //Roo.log(['enter',dom]);
26448         var el = Roo.fly(dom);
26449         if (this.currentEl) {
26450             //Roo.log(dom);
26451             //Roo.log(this.currentEl);
26452             //Roo.log(this.currentEl.contains(dom));
26453             if (this.currentEl == el) {
26454                 return;
26455             }
26456             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26457                 return;
26458             }
26459
26460         }
26461         
26462         if (this.currentTip.el) {
26463             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26464         }    
26465         //Roo.log(ev);
26466         
26467         if(!el || el.dom == document){
26468             return;
26469         }
26470         
26471         var bindEl = el;
26472         
26473         // you can not look for children, as if el is the body.. then everythign is the child..
26474         if (!el.attr('tooltip')) { //
26475             if (!el.select("[tooltip]").elements.length) {
26476                 return;
26477             }
26478             // is the mouse over this child...?
26479             bindEl = el.select("[tooltip]").first();
26480             var xy = ev.getXY();
26481             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26482                 //Roo.log("not in region.");
26483                 return;
26484             }
26485             //Roo.log("child element over..");
26486             
26487         }
26488         this.currentEl = bindEl;
26489         this.currentTip.bind(bindEl);
26490         this.currentRegion = Roo.lib.Region.getRegion(dom);
26491         this.currentTip.enter();
26492         
26493     },
26494     leave : function(ev)
26495     {
26496         var dom = ev.getTarget();
26497         //Roo.log(['leave',dom]);
26498         if (!this.currentEl) {
26499             return;
26500         }
26501         
26502         
26503         if (dom != this.currentEl.dom) {
26504             return;
26505         }
26506         var xy = ev.getXY();
26507         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26508             return;
26509         }
26510         // only activate leave if mouse cursor is outside... bounding box..
26511         
26512         
26513         
26514         
26515         if (this.currentTip) {
26516             this.currentTip.leave();
26517         }
26518         //Roo.log('clear currentEl');
26519         this.currentEl = false;
26520         
26521         
26522     },
26523     alignment : {
26524         'left' : ['r-l', [-2,0], 'right'],
26525         'right' : ['l-r', [2,0], 'left'],
26526         'bottom' : ['t-b', [0,2], 'top'],
26527         'top' : [ 'b-t', [0,-2], 'bottom']
26528     }
26529     
26530 });
26531
26532
26533 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26534     
26535     
26536     bindEl : false,
26537     
26538     delay : null, // can be { show : 300 , hide: 500}
26539     
26540     timeout : null,
26541     
26542     hoverState : null, //???
26543     
26544     placement : 'bottom', 
26545     
26546     alignment : false,
26547     
26548     getAutoCreate : function(){
26549     
26550         var cfg = {
26551            cls : 'tooltip',
26552            role : 'tooltip',
26553            cn : [
26554                 {
26555                     cls : 'tooltip-arrow'
26556                 },
26557                 {
26558                     cls : 'tooltip-inner'
26559                 }
26560            ]
26561         };
26562         
26563         return cfg;
26564     },
26565     bind : function(el)
26566     {
26567         this.bindEl = el;
26568     },
26569       
26570     
26571     enter : function () {
26572        
26573         if (this.timeout != null) {
26574             clearTimeout(this.timeout);
26575         }
26576         
26577         this.hoverState = 'in';
26578          //Roo.log("enter - show");
26579         if (!this.delay || !this.delay.show) {
26580             this.show();
26581             return;
26582         }
26583         var _t = this;
26584         this.timeout = setTimeout(function () {
26585             if (_t.hoverState == 'in') {
26586                 _t.show();
26587             }
26588         }, this.delay.show);
26589     },
26590     leave : function()
26591     {
26592         clearTimeout(this.timeout);
26593     
26594         this.hoverState = 'out';
26595          if (!this.delay || !this.delay.hide) {
26596             this.hide();
26597             return;
26598         }
26599        
26600         var _t = this;
26601         this.timeout = setTimeout(function () {
26602             //Roo.log("leave - timeout");
26603             
26604             if (_t.hoverState == 'out') {
26605                 _t.hide();
26606                 Roo.bootstrap.Tooltip.currentEl = false;
26607             }
26608         }, delay);
26609     },
26610     
26611     show : function (msg)
26612     {
26613         if (!this.el) {
26614             this.render(document.body);
26615         }
26616         // set content.
26617         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26618         
26619         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26620         
26621         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26622         
26623         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26624         
26625         var placement = typeof this.placement == 'function' ?
26626             this.placement.call(this, this.el, on_el) :
26627             this.placement;
26628             
26629         var autoToken = /\s?auto?\s?/i;
26630         var autoPlace = autoToken.test(placement);
26631         if (autoPlace) {
26632             placement = placement.replace(autoToken, '') || 'top';
26633         }
26634         
26635         //this.el.detach()
26636         //this.el.setXY([0,0]);
26637         this.el.show();
26638         //this.el.dom.style.display='block';
26639         
26640         //this.el.appendTo(on_el);
26641         
26642         var p = this.getPosition();
26643         var box = this.el.getBox();
26644         
26645         if (autoPlace) {
26646             // fixme..
26647         }
26648         
26649         var align = this.alignment[placement];
26650         
26651         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26652         
26653         if(placement == 'top' || placement == 'bottom'){
26654             if(xy[0] < 0){
26655                 placement = 'right';
26656             }
26657             
26658             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26659                 placement = 'left';
26660             }
26661             
26662             var scroll = Roo.select('body', true).first().getScroll();
26663             
26664             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26665                 placement = 'top';
26666             }
26667             
26668             align = this.alignment[placement];
26669         }
26670         
26671         this.el.alignTo(this.bindEl, align[0],align[1]);
26672         //var arrow = this.el.select('.arrow',true).first();
26673         //arrow.set(align[2], 
26674         
26675         this.el.addClass(placement);
26676         
26677         this.el.addClass('in fade');
26678         
26679         this.hoverState = null;
26680         
26681         if (this.el.hasClass('fade')) {
26682             // fade it?
26683         }
26684         
26685     },
26686     hide : function()
26687     {
26688          
26689         if (!this.el) {
26690             return;
26691         }
26692         //this.el.setXY([0,0]);
26693         this.el.removeClass('in');
26694         //this.el.hide();
26695         
26696     }
26697     
26698 });
26699  
26700
26701  /*
26702  * - LGPL
26703  *
26704  * Location Picker
26705  * 
26706  */
26707
26708 /**
26709  * @class Roo.bootstrap.LocationPicker
26710  * @extends Roo.bootstrap.Component
26711  * Bootstrap LocationPicker class
26712  * @cfg {Number} latitude Position when init default 0
26713  * @cfg {Number} longitude Position when init default 0
26714  * @cfg {Number} zoom default 15
26715  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26716  * @cfg {Boolean} mapTypeControl default false
26717  * @cfg {Boolean} disableDoubleClickZoom default false
26718  * @cfg {Boolean} scrollwheel default true
26719  * @cfg {Boolean} streetViewControl default false
26720  * @cfg {Number} radius default 0
26721  * @cfg {String} locationName
26722  * @cfg {Boolean} draggable default true
26723  * @cfg {Boolean} enableAutocomplete default false
26724  * @cfg {Boolean} enableReverseGeocode default true
26725  * @cfg {String} markerTitle
26726  * 
26727  * @constructor
26728  * Create a new LocationPicker
26729  * @param {Object} config The config object
26730  */
26731
26732
26733 Roo.bootstrap.LocationPicker = function(config){
26734     
26735     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26736     
26737     this.addEvents({
26738         /**
26739          * @event initial
26740          * Fires when the picker initialized.
26741          * @param {Roo.bootstrap.LocationPicker} this
26742          * @param {Google Location} location
26743          */
26744         initial : true,
26745         /**
26746          * @event positionchanged
26747          * Fires when the picker position changed.
26748          * @param {Roo.bootstrap.LocationPicker} this
26749          * @param {Google Location} location
26750          */
26751         positionchanged : true,
26752         /**
26753          * @event resize
26754          * Fires when the map resize.
26755          * @param {Roo.bootstrap.LocationPicker} this
26756          */
26757         resize : true,
26758         /**
26759          * @event show
26760          * Fires when the map show.
26761          * @param {Roo.bootstrap.LocationPicker} this
26762          */
26763         show : true,
26764         /**
26765          * @event hide
26766          * Fires when the map hide.
26767          * @param {Roo.bootstrap.LocationPicker} this
26768          */
26769         hide : true,
26770         /**
26771          * @event mapClick
26772          * Fires when click the map.
26773          * @param {Roo.bootstrap.LocationPicker} this
26774          * @param {Map event} e
26775          */
26776         mapClick : true,
26777         /**
26778          * @event mapRightClick
26779          * Fires when right click the map.
26780          * @param {Roo.bootstrap.LocationPicker} this
26781          * @param {Map event} e
26782          */
26783         mapRightClick : true,
26784         /**
26785          * @event markerClick
26786          * Fires when click the marker.
26787          * @param {Roo.bootstrap.LocationPicker} this
26788          * @param {Map event} e
26789          */
26790         markerClick : true,
26791         /**
26792          * @event markerRightClick
26793          * Fires when right click the marker.
26794          * @param {Roo.bootstrap.LocationPicker} this
26795          * @param {Map event} e
26796          */
26797         markerRightClick : true,
26798         /**
26799          * @event OverlayViewDraw
26800          * Fires when OverlayView Draw
26801          * @param {Roo.bootstrap.LocationPicker} this
26802          */
26803         OverlayViewDraw : true,
26804         /**
26805          * @event OverlayViewOnAdd
26806          * Fires when OverlayView Draw
26807          * @param {Roo.bootstrap.LocationPicker} this
26808          */
26809         OverlayViewOnAdd : true,
26810         /**
26811          * @event OverlayViewOnRemove
26812          * Fires when OverlayView Draw
26813          * @param {Roo.bootstrap.LocationPicker} this
26814          */
26815         OverlayViewOnRemove : true,
26816         /**
26817          * @event OverlayViewShow
26818          * Fires when OverlayView Draw
26819          * @param {Roo.bootstrap.LocationPicker} this
26820          * @param {Pixel} cpx
26821          */
26822         OverlayViewShow : true,
26823         /**
26824          * @event OverlayViewHide
26825          * Fires when OverlayView Draw
26826          * @param {Roo.bootstrap.LocationPicker} this
26827          */
26828         OverlayViewHide : true,
26829         /**
26830          * @event loadexception
26831          * Fires when load google lib failed.
26832          * @param {Roo.bootstrap.LocationPicker} this
26833          */
26834         loadexception : true
26835     });
26836         
26837 };
26838
26839 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26840     
26841     gMapContext: false,
26842     
26843     latitude: 0,
26844     longitude: 0,
26845     zoom: 15,
26846     mapTypeId: false,
26847     mapTypeControl: false,
26848     disableDoubleClickZoom: false,
26849     scrollwheel: true,
26850     streetViewControl: false,
26851     radius: 0,
26852     locationName: '',
26853     draggable: true,
26854     enableAutocomplete: false,
26855     enableReverseGeocode: true,
26856     markerTitle: '',
26857     
26858     getAutoCreate: function()
26859     {
26860
26861         var cfg = {
26862             tag: 'div',
26863             cls: 'roo-location-picker'
26864         };
26865         
26866         return cfg
26867     },
26868     
26869     initEvents: function(ct, position)
26870     {       
26871         if(!this.el.getWidth() || this.isApplied()){
26872             return;
26873         }
26874         
26875         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26876         
26877         this.initial();
26878     },
26879     
26880     initial: function()
26881     {
26882         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26883             this.fireEvent('loadexception', this);
26884             return;
26885         }
26886         
26887         if(!this.mapTypeId){
26888             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26889         }
26890         
26891         this.gMapContext = this.GMapContext();
26892         
26893         this.initOverlayView();
26894         
26895         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26896         
26897         var _this = this;
26898                 
26899         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26900             _this.setPosition(_this.gMapContext.marker.position);
26901         });
26902         
26903         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26904             _this.fireEvent('mapClick', this, event);
26905             
26906         });
26907
26908         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26909             _this.fireEvent('mapRightClick', this, event);
26910             
26911         });
26912         
26913         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26914             _this.fireEvent('markerClick', this, event);
26915             
26916         });
26917
26918         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26919             _this.fireEvent('markerRightClick', this, event);
26920             
26921         });
26922         
26923         this.setPosition(this.gMapContext.location);
26924         
26925         this.fireEvent('initial', this, this.gMapContext.location);
26926     },
26927     
26928     initOverlayView: function()
26929     {
26930         var _this = this;
26931         
26932         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26933             
26934             draw: function()
26935             {
26936                 _this.fireEvent('OverlayViewDraw', _this);
26937             },
26938             
26939             onAdd: function()
26940             {
26941                 _this.fireEvent('OverlayViewOnAdd', _this);
26942             },
26943             
26944             onRemove: function()
26945             {
26946                 _this.fireEvent('OverlayViewOnRemove', _this);
26947             },
26948             
26949             show: function(cpx)
26950             {
26951                 _this.fireEvent('OverlayViewShow', _this, cpx);
26952             },
26953             
26954             hide: function()
26955             {
26956                 _this.fireEvent('OverlayViewHide', _this);
26957             }
26958             
26959         });
26960     },
26961     
26962     fromLatLngToContainerPixel: function(event)
26963     {
26964         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26965     },
26966     
26967     isApplied: function() 
26968     {
26969         return this.getGmapContext() == false ? false : true;
26970     },
26971     
26972     getGmapContext: function() 
26973     {
26974         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26975     },
26976     
26977     GMapContext: function() 
26978     {
26979         var position = new google.maps.LatLng(this.latitude, this.longitude);
26980         
26981         var _map = new google.maps.Map(this.el.dom, {
26982             center: position,
26983             zoom: this.zoom,
26984             mapTypeId: this.mapTypeId,
26985             mapTypeControl: this.mapTypeControl,
26986             disableDoubleClickZoom: this.disableDoubleClickZoom,
26987             scrollwheel: this.scrollwheel,
26988             streetViewControl: this.streetViewControl,
26989             locationName: this.locationName,
26990             draggable: this.draggable,
26991             enableAutocomplete: this.enableAutocomplete,
26992             enableReverseGeocode: this.enableReverseGeocode
26993         });
26994         
26995         var _marker = new google.maps.Marker({
26996             position: position,
26997             map: _map,
26998             title: this.markerTitle,
26999             draggable: this.draggable
27000         });
27001         
27002         return {
27003             map: _map,
27004             marker: _marker,
27005             circle: null,
27006             location: position,
27007             radius: this.radius,
27008             locationName: this.locationName,
27009             addressComponents: {
27010                 formatted_address: null,
27011                 addressLine1: null,
27012                 addressLine2: null,
27013                 streetName: null,
27014                 streetNumber: null,
27015                 city: null,
27016                 district: null,
27017                 state: null,
27018                 stateOrProvince: null
27019             },
27020             settings: this,
27021             domContainer: this.el.dom,
27022             geodecoder: new google.maps.Geocoder()
27023         };
27024     },
27025     
27026     drawCircle: function(center, radius, options) 
27027     {
27028         if (this.gMapContext.circle != null) {
27029             this.gMapContext.circle.setMap(null);
27030         }
27031         if (radius > 0) {
27032             radius *= 1;
27033             options = Roo.apply({}, options, {
27034                 strokeColor: "#0000FF",
27035                 strokeOpacity: .35,
27036                 strokeWeight: 2,
27037                 fillColor: "#0000FF",
27038                 fillOpacity: .2
27039             });
27040             
27041             options.map = this.gMapContext.map;
27042             options.radius = radius;
27043             options.center = center;
27044             this.gMapContext.circle = new google.maps.Circle(options);
27045             return this.gMapContext.circle;
27046         }
27047         
27048         return null;
27049     },
27050     
27051     setPosition: function(location) 
27052     {
27053         this.gMapContext.location = location;
27054         this.gMapContext.marker.setPosition(location);
27055         this.gMapContext.map.panTo(location);
27056         this.drawCircle(location, this.gMapContext.radius, {});
27057         
27058         var _this = this;
27059         
27060         if (this.gMapContext.settings.enableReverseGeocode) {
27061             this.gMapContext.geodecoder.geocode({
27062                 latLng: this.gMapContext.location
27063             }, function(results, status) {
27064                 
27065                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27066                     _this.gMapContext.locationName = results[0].formatted_address;
27067                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27068                     
27069                     _this.fireEvent('positionchanged', this, location);
27070                 }
27071             });
27072             
27073             return;
27074         }
27075         
27076         this.fireEvent('positionchanged', this, location);
27077     },
27078     
27079     resize: function()
27080     {
27081         google.maps.event.trigger(this.gMapContext.map, "resize");
27082         
27083         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27084         
27085         this.fireEvent('resize', this);
27086     },
27087     
27088     setPositionByLatLng: function(latitude, longitude)
27089     {
27090         this.setPosition(new google.maps.LatLng(latitude, longitude));
27091     },
27092     
27093     getCurrentPosition: function() 
27094     {
27095         return {
27096             latitude: this.gMapContext.location.lat(),
27097             longitude: this.gMapContext.location.lng()
27098         };
27099     },
27100     
27101     getAddressName: function() 
27102     {
27103         return this.gMapContext.locationName;
27104     },
27105     
27106     getAddressComponents: function() 
27107     {
27108         return this.gMapContext.addressComponents;
27109     },
27110     
27111     address_component_from_google_geocode: function(address_components) 
27112     {
27113         var result = {};
27114         
27115         for (var i = 0; i < address_components.length; i++) {
27116             var component = address_components[i];
27117             if (component.types.indexOf("postal_code") >= 0) {
27118                 result.postalCode = component.short_name;
27119             } else if (component.types.indexOf("street_number") >= 0) {
27120                 result.streetNumber = component.short_name;
27121             } else if (component.types.indexOf("route") >= 0) {
27122                 result.streetName = component.short_name;
27123             } else if (component.types.indexOf("neighborhood") >= 0) {
27124                 result.city = component.short_name;
27125             } else if (component.types.indexOf("locality") >= 0) {
27126                 result.city = component.short_name;
27127             } else if (component.types.indexOf("sublocality") >= 0) {
27128                 result.district = component.short_name;
27129             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27130                 result.stateOrProvince = component.short_name;
27131             } else if (component.types.indexOf("country") >= 0) {
27132                 result.country = component.short_name;
27133             }
27134         }
27135         
27136         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27137         result.addressLine2 = "";
27138         return result;
27139     },
27140     
27141     setZoomLevel: function(zoom)
27142     {
27143         this.gMapContext.map.setZoom(zoom);
27144     },
27145     
27146     show: function()
27147     {
27148         if(!this.el){
27149             return;
27150         }
27151         
27152         this.el.show();
27153         
27154         this.resize();
27155         
27156         this.fireEvent('show', this);
27157     },
27158     
27159     hide: function()
27160     {
27161         if(!this.el){
27162             return;
27163         }
27164         
27165         this.el.hide();
27166         
27167         this.fireEvent('hide', this);
27168     }
27169     
27170 });
27171
27172 Roo.apply(Roo.bootstrap.LocationPicker, {
27173     
27174     OverlayView : function(map, options)
27175     {
27176         options = options || {};
27177         
27178         this.setMap(map);
27179     }
27180     
27181     
27182 });/*
27183  * - LGPL
27184  *
27185  * Alert
27186  * 
27187  */
27188
27189 /**
27190  * @class Roo.bootstrap.Alert
27191  * @extends Roo.bootstrap.Component
27192  * Bootstrap Alert class
27193  * @cfg {String} title The title of alert
27194  * @cfg {String} html The content of alert
27195  * @cfg {String} weight (  success | info | warning | danger )
27196  * @cfg {String} faicon font-awesomeicon
27197  * 
27198  * @constructor
27199  * Create a new alert
27200  * @param {Object} config The config object
27201  */
27202
27203
27204 Roo.bootstrap.Alert = function(config){
27205     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27206     
27207 };
27208
27209 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27210     
27211     title: '',
27212     html: '',
27213     weight: false,
27214     faicon: false,
27215     
27216     getAutoCreate : function()
27217     {
27218         
27219         var cfg = {
27220             tag : 'div',
27221             cls : 'alert',
27222             cn : [
27223                 {
27224                     tag : 'i',
27225                     cls : 'roo-alert-icon'
27226                     
27227                 },
27228                 {
27229                     tag : 'b',
27230                     cls : 'roo-alert-title',
27231                     html : this.title
27232                 },
27233                 {
27234                     tag : 'span',
27235                     cls : 'roo-alert-text',
27236                     html : this.html
27237                 }
27238             ]
27239         };
27240         
27241         if(this.faicon){
27242             cfg.cn[0].cls += ' fa ' + this.faicon;
27243         }
27244         
27245         if(this.weight){
27246             cfg.cls += ' alert-' + this.weight;
27247         }
27248         
27249         return cfg;
27250     },
27251     
27252     initEvents: function() 
27253     {
27254         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27255     },
27256     
27257     setTitle : function(str)
27258     {
27259         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27260     },
27261     
27262     setText : function(str)
27263     {
27264         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27265     },
27266     
27267     setWeight : function(weight)
27268     {
27269         if(this.weight){
27270             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27271         }
27272         
27273         this.weight = weight;
27274         
27275         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27276     },
27277     
27278     setIcon : function(icon)
27279     {
27280         if(this.faicon){
27281             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27282         }
27283         
27284         this.faicon = icon;
27285         
27286         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27287     },
27288     
27289     hide: function() 
27290     {
27291         this.el.hide();   
27292     },
27293     
27294     show: function() 
27295     {  
27296         this.el.show();   
27297     }
27298     
27299 });
27300
27301  
27302 /*
27303 * Licence: LGPL
27304 */
27305
27306 /**
27307  * @class Roo.bootstrap.UploadCropbox
27308  * @extends Roo.bootstrap.Component
27309  * Bootstrap UploadCropbox class
27310  * @cfg {String} emptyText show when image has been loaded
27311  * @cfg {String} rotateNotify show when image too small to rotate
27312  * @cfg {Number} errorTimeout default 3000
27313  * @cfg {Number} minWidth default 300
27314  * @cfg {Number} minHeight default 300
27315  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27316  * @cfg {Boolean} isDocument (true|false) default false
27317  * @cfg {String} url action url
27318  * @cfg {String} paramName default 'imageUpload'
27319  * @cfg {String} method default POST
27320  * @cfg {Boolean} loadMask (true|false) default true
27321  * @cfg {Boolean} loadingText default 'Loading...'
27322  * 
27323  * @constructor
27324  * Create a new UploadCropbox
27325  * @param {Object} config The config object
27326  */
27327
27328 Roo.bootstrap.UploadCropbox = function(config){
27329     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27330     
27331     this.addEvents({
27332         /**
27333          * @event beforeselectfile
27334          * Fire before select file
27335          * @param {Roo.bootstrap.UploadCropbox} this
27336          */
27337         "beforeselectfile" : true,
27338         /**
27339          * @event initial
27340          * Fire after initEvent
27341          * @param {Roo.bootstrap.UploadCropbox} this
27342          */
27343         "initial" : true,
27344         /**
27345          * @event crop
27346          * Fire after initEvent
27347          * @param {Roo.bootstrap.UploadCropbox} this
27348          * @param {String} data
27349          */
27350         "crop" : true,
27351         /**
27352          * @event prepare
27353          * Fire when preparing the file data
27354          * @param {Roo.bootstrap.UploadCropbox} this
27355          * @param {Object} file
27356          */
27357         "prepare" : true,
27358         /**
27359          * @event exception
27360          * Fire when get exception
27361          * @param {Roo.bootstrap.UploadCropbox} this
27362          * @param {XMLHttpRequest} xhr
27363          */
27364         "exception" : true,
27365         /**
27366          * @event beforeloadcanvas
27367          * Fire before load the canvas
27368          * @param {Roo.bootstrap.UploadCropbox} this
27369          * @param {String} src
27370          */
27371         "beforeloadcanvas" : true,
27372         /**
27373          * @event trash
27374          * Fire when trash image
27375          * @param {Roo.bootstrap.UploadCropbox} this
27376          */
27377         "trash" : true,
27378         /**
27379          * @event download
27380          * Fire when download the image
27381          * @param {Roo.bootstrap.UploadCropbox} this
27382          */
27383         "download" : true,
27384         /**
27385          * @event footerbuttonclick
27386          * Fire when footerbuttonclick
27387          * @param {Roo.bootstrap.UploadCropbox} this
27388          * @param {String} type
27389          */
27390         "footerbuttonclick" : true,
27391         /**
27392          * @event resize
27393          * Fire when resize
27394          * @param {Roo.bootstrap.UploadCropbox} this
27395          */
27396         "resize" : true,
27397         /**
27398          * @event rotate
27399          * Fire when rotate the image
27400          * @param {Roo.bootstrap.UploadCropbox} this
27401          * @param {String} pos
27402          */
27403         "rotate" : true,
27404         /**
27405          * @event inspect
27406          * Fire when inspect the file
27407          * @param {Roo.bootstrap.UploadCropbox} this
27408          * @param {Object} file
27409          */
27410         "inspect" : true,
27411         /**
27412          * @event upload
27413          * Fire when xhr upload the file
27414          * @param {Roo.bootstrap.UploadCropbox} this
27415          * @param {Object} data
27416          */
27417         "upload" : true,
27418         /**
27419          * @event arrange
27420          * Fire when arrange the file data
27421          * @param {Roo.bootstrap.UploadCropbox} this
27422          * @param {Object} formData
27423          */
27424         "arrange" : true
27425     });
27426     
27427     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27428 };
27429
27430 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27431     
27432     emptyText : 'Click to upload image',
27433     rotateNotify : 'Image is too small to rotate',
27434     errorTimeout : 3000,
27435     scale : 0,
27436     baseScale : 1,
27437     rotate : 0,
27438     dragable : false,
27439     pinching : false,
27440     mouseX : 0,
27441     mouseY : 0,
27442     cropData : false,
27443     minWidth : 300,
27444     minHeight : 300,
27445     file : false,
27446     exif : {},
27447     baseRotate : 1,
27448     cropType : 'image/jpeg',
27449     buttons : false,
27450     canvasLoaded : false,
27451     isDocument : false,
27452     method : 'POST',
27453     paramName : 'imageUpload',
27454     loadMask : true,
27455     loadingText : 'Loading...',
27456     maskEl : false,
27457     
27458     getAutoCreate : function()
27459     {
27460         var cfg = {
27461             tag : 'div',
27462             cls : 'roo-upload-cropbox',
27463             cn : [
27464                 {
27465                     tag : 'input',
27466                     cls : 'roo-upload-cropbox-selector',
27467                     type : 'file'
27468                 },
27469                 {
27470                     tag : 'div',
27471                     cls : 'roo-upload-cropbox-body',
27472                     style : 'cursor:pointer',
27473                     cn : [
27474                         {
27475                             tag : 'div',
27476                             cls : 'roo-upload-cropbox-preview'
27477                         },
27478                         {
27479                             tag : 'div',
27480                             cls : 'roo-upload-cropbox-thumb'
27481                         },
27482                         {
27483                             tag : 'div',
27484                             cls : 'roo-upload-cropbox-empty-notify',
27485                             html : this.emptyText
27486                         },
27487                         {
27488                             tag : 'div',
27489                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27490                             html : this.rotateNotify
27491                         }
27492                     ]
27493                 },
27494                 {
27495                     tag : 'div',
27496                     cls : 'roo-upload-cropbox-footer',
27497                     cn : {
27498                         tag : 'div',
27499                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27500                         cn : []
27501                     }
27502                 }
27503             ]
27504         };
27505         
27506         return cfg;
27507     },
27508     
27509     onRender : function(ct, position)
27510     {
27511         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27512         
27513         if (this.buttons.length) {
27514             
27515             Roo.each(this.buttons, function(bb) {
27516                 
27517                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27518                 
27519                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27520                 
27521             }, this);
27522         }
27523         
27524         if(this.loadMask){
27525             this.maskEl = this.el;
27526         }
27527     },
27528     
27529     initEvents : function()
27530     {
27531         this.urlAPI = (window.createObjectURL && window) || 
27532                                 (window.URL && URL.revokeObjectURL && URL) || 
27533                                 (window.webkitURL && webkitURL);
27534                         
27535         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27536         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27537         
27538         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27539         this.selectorEl.hide();
27540         
27541         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27542         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27543         
27544         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27545         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27546         this.thumbEl.hide();
27547         
27548         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27549         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27550         
27551         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27552         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27553         this.errorEl.hide();
27554         
27555         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27556         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27557         this.footerEl.hide();
27558         
27559         this.setThumbBoxSize();
27560         
27561         this.bind();
27562         
27563         this.resize();
27564         
27565         this.fireEvent('initial', this);
27566     },
27567
27568     bind : function()
27569     {
27570         var _this = this;
27571         
27572         window.addEventListener("resize", function() { _this.resize(); } );
27573         
27574         this.bodyEl.on('click', this.beforeSelectFile, this);
27575         
27576         if(Roo.isTouch){
27577             this.bodyEl.on('touchstart', this.onTouchStart, this);
27578             this.bodyEl.on('touchmove', this.onTouchMove, this);
27579             this.bodyEl.on('touchend', this.onTouchEnd, this);
27580         }
27581         
27582         if(!Roo.isTouch){
27583             this.bodyEl.on('mousedown', this.onMouseDown, this);
27584             this.bodyEl.on('mousemove', this.onMouseMove, this);
27585             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27586             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27587             Roo.get(document).on('mouseup', this.onMouseUp, this);
27588         }
27589         
27590         this.selectorEl.on('change', this.onFileSelected, this);
27591     },
27592     
27593     reset : function()
27594     {    
27595         this.scale = 0;
27596         this.baseScale = 1;
27597         this.rotate = 0;
27598         this.baseRotate = 1;
27599         this.dragable = false;
27600         this.pinching = false;
27601         this.mouseX = 0;
27602         this.mouseY = 0;
27603         this.cropData = false;
27604         this.notifyEl.dom.innerHTML = this.emptyText;
27605         
27606         this.selectorEl.dom.value = '';
27607         
27608     },
27609     
27610     resize : function()
27611     {
27612         if(this.fireEvent('resize', this) != false){
27613             this.setThumbBoxPosition();
27614             this.setCanvasPosition();
27615         }
27616     },
27617     
27618     onFooterButtonClick : function(e, el, o, type)
27619     {
27620         switch (type) {
27621             case 'rotate-left' :
27622                 this.onRotateLeft(e);
27623                 break;
27624             case 'rotate-right' :
27625                 this.onRotateRight(e);
27626                 break;
27627             case 'picture' :
27628                 this.beforeSelectFile(e);
27629                 break;
27630             case 'trash' :
27631                 this.trash(e);
27632                 break;
27633             case 'crop' :
27634                 this.crop(e);
27635                 break;
27636             case 'download' :
27637                 this.download(e);
27638                 break;
27639             default :
27640                 break;
27641         }
27642         
27643         this.fireEvent('footerbuttonclick', this, type);
27644     },
27645     
27646     beforeSelectFile : function(e)
27647     {
27648         e.preventDefault();
27649         
27650         if(this.fireEvent('beforeselectfile', this) != false){
27651             this.selectorEl.dom.click();
27652         }
27653     },
27654     
27655     onFileSelected : function(e)
27656     {
27657         e.preventDefault();
27658         
27659         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27660             return;
27661         }
27662         
27663         var file = this.selectorEl.dom.files[0];
27664         
27665         if(this.fireEvent('inspect', this, file) != false){
27666             this.prepare(file);
27667         }
27668         
27669     },
27670     
27671     trash : function(e)
27672     {
27673         this.fireEvent('trash', this);
27674     },
27675     
27676     download : function(e)
27677     {
27678         this.fireEvent('download', this);
27679     },
27680     
27681     loadCanvas : function(src)
27682     {   
27683         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27684             
27685             this.reset();
27686             
27687             this.imageEl = document.createElement('img');
27688             
27689             var _this = this;
27690             
27691             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27692             
27693             this.imageEl.src = src;
27694         }
27695     },
27696     
27697     onLoadCanvas : function()
27698     {   
27699         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27700         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27701         
27702         this.bodyEl.un('click', this.beforeSelectFile, this);
27703         
27704         this.notifyEl.hide();
27705         this.thumbEl.show();
27706         this.footerEl.show();
27707         
27708         this.baseRotateLevel();
27709         
27710         if(this.isDocument){
27711             this.setThumbBoxSize();
27712         }
27713         
27714         this.setThumbBoxPosition();
27715         
27716         this.baseScaleLevel();
27717         
27718         this.draw();
27719         
27720         this.resize();
27721         
27722         this.canvasLoaded = true;
27723         
27724         if(this.loadMask){
27725             this.maskEl.unmask();
27726         }
27727         
27728     },
27729     
27730     setCanvasPosition : function()
27731     {   
27732         if(!this.canvasEl){
27733             return;
27734         }
27735         
27736         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27737         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27738         
27739         this.previewEl.setLeft(pw);
27740         this.previewEl.setTop(ph);
27741         
27742     },
27743     
27744     onMouseDown : function(e)
27745     {   
27746         e.stopEvent();
27747         
27748         this.dragable = true;
27749         this.pinching = false;
27750         
27751         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27752             this.dragable = false;
27753             return;
27754         }
27755         
27756         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27757         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27758         
27759     },
27760     
27761     onMouseMove : function(e)
27762     {   
27763         e.stopEvent();
27764         
27765         if(!this.canvasLoaded){
27766             return;
27767         }
27768         
27769         if (!this.dragable){
27770             return;
27771         }
27772         
27773         var minX = Math.ceil(this.thumbEl.getLeft(true));
27774         var minY = Math.ceil(this.thumbEl.getTop(true));
27775         
27776         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27777         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27778         
27779         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27780         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27781         
27782         x = x - this.mouseX;
27783         y = y - this.mouseY;
27784         
27785         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27786         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27787         
27788         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27789         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27790         
27791         this.previewEl.setLeft(bgX);
27792         this.previewEl.setTop(bgY);
27793         
27794         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27795         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27796     },
27797     
27798     onMouseUp : function(e)
27799     {   
27800         e.stopEvent();
27801         
27802         this.dragable = false;
27803     },
27804     
27805     onMouseWheel : function(e)
27806     {   
27807         e.stopEvent();
27808         
27809         this.startScale = this.scale;
27810         
27811         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27812         
27813         if(!this.zoomable()){
27814             this.scale = this.startScale;
27815             return;
27816         }
27817         
27818         this.draw();
27819         
27820         return;
27821     },
27822     
27823     zoomable : function()
27824     {
27825         var minScale = this.thumbEl.getWidth() / this.minWidth;
27826         
27827         if(this.minWidth < this.minHeight){
27828             minScale = this.thumbEl.getHeight() / this.minHeight;
27829         }
27830         
27831         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27832         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27833         
27834         if(
27835                 this.isDocument &&
27836                 (this.rotate == 0 || this.rotate == 180) && 
27837                 (
27838                     width > this.imageEl.OriginWidth || 
27839                     height > this.imageEl.OriginHeight ||
27840                     (width < this.minWidth && height < this.minHeight)
27841                 )
27842         ){
27843             return false;
27844         }
27845         
27846         if(
27847                 this.isDocument &&
27848                 (this.rotate == 90 || this.rotate == 270) && 
27849                 (
27850                     width > this.imageEl.OriginWidth || 
27851                     height > this.imageEl.OriginHeight ||
27852                     (width < this.minHeight && height < this.minWidth)
27853                 )
27854         ){
27855             return false;
27856         }
27857         
27858         if(
27859                 !this.isDocument &&
27860                 (this.rotate == 0 || this.rotate == 180) && 
27861                 (
27862                     width < this.minWidth || 
27863                     width > this.imageEl.OriginWidth || 
27864                     height < this.minHeight || 
27865                     height > this.imageEl.OriginHeight
27866                 )
27867         ){
27868             return false;
27869         }
27870         
27871         if(
27872                 !this.isDocument &&
27873                 (this.rotate == 90 || this.rotate == 270) && 
27874                 (
27875                     width < this.minHeight || 
27876                     width > this.imageEl.OriginWidth || 
27877                     height < this.minWidth || 
27878                     height > this.imageEl.OriginHeight
27879                 )
27880         ){
27881             return false;
27882         }
27883         
27884         return true;
27885         
27886     },
27887     
27888     onRotateLeft : function(e)
27889     {   
27890         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27891             
27892             var minScale = this.thumbEl.getWidth() / this.minWidth;
27893             
27894             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27895             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27896             
27897             this.startScale = this.scale;
27898             
27899             while (this.getScaleLevel() < minScale){
27900             
27901                 this.scale = this.scale + 1;
27902                 
27903                 if(!this.zoomable()){
27904                     break;
27905                 }
27906                 
27907                 if(
27908                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27909                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27910                 ){
27911                     continue;
27912                 }
27913                 
27914                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27915
27916                 this.draw();
27917                 
27918                 return;
27919             }
27920             
27921             this.scale = this.startScale;
27922             
27923             this.onRotateFail();
27924             
27925             return false;
27926         }
27927         
27928         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27929
27930         if(this.isDocument){
27931             this.setThumbBoxSize();
27932             this.setThumbBoxPosition();
27933             this.setCanvasPosition();
27934         }
27935         
27936         this.draw();
27937         
27938         this.fireEvent('rotate', this, 'left');
27939         
27940     },
27941     
27942     onRotateRight : function(e)
27943     {
27944         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27945             
27946             var minScale = this.thumbEl.getWidth() / this.minWidth;
27947         
27948             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27949             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27950             
27951             this.startScale = this.scale;
27952             
27953             while (this.getScaleLevel() < minScale){
27954             
27955                 this.scale = this.scale + 1;
27956                 
27957                 if(!this.zoomable()){
27958                     break;
27959                 }
27960                 
27961                 if(
27962                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27963                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27964                 ){
27965                     continue;
27966                 }
27967                 
27968                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27969
27970                 this.draw();
27971                 
27972                 return;
27973             }
27974             
27975             this.scale = this.startScale;
27976             
27977             this.onRotateFail();
27978             
27979             return false;
27980         }
27981         
27982         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27983
27984         if(this.isDocument){
27985             this.setThumbBoxSize();
27986             this.setThumbBoxPosition();
27987             this.setCanvasPosition();
27988         }
27989         
27990         this.draw();
27991         
27992         this.fireEvent('rotate', this, 'right');
27993     },
27994     
27995     onRotateFail : function()
27996     {
27997         this.errorEl.show(true);
27998         
27999         var _this = this;
28000         
28001         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28002     },
28003     
28004     draw : function()
28005     {
28006         this.previewEl.dom.innerHTML = '';
28007         
28008         var canvasEl = document.createElement("canvas");
28009         
28010         var contextEl = canvasEl.getContext("2d");
28011         
28012         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28013         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28014         var center = this.imageEl.OriginWidth / 2;
28015         
28016         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28017             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28018             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28019             center = this.imageEl.OriginHeight / 2;
28020         }
28021         
28022         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28023         
28024         contextEl.translate(center, center);
28025         contextEl.rotate(this.rotate * Math.PI / 180);
28026
28027         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28028         
28029         this.canvasEl = document.createElement("canvas");
28030         
28031         this.contextEl = this.canvasEl.getContext("2d");
28032         
28033         switch (this.rotate) {
28034             case 0 :
28035                 
28036                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28037                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28038                 
28039                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28040                 
28041                 break;
28042             case 90 : 
28043                 
28044                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28045                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28046                 
28047                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28048                     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);
28049                     break;
28050                 }
28051                 
28052                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28053                 
28054                 break;
28055             case 180 :
28056                 
28057                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28058                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28059                 
28060                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28061                     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);
28062                     break;
28063                 }
28064                 
28065                 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);
28066                 
28067                 break;
28068             case 270 :
28069                 
28070                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28071                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28072         
28073                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28074                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28075                     break;
28076                 }
28077                 
28078                 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);
28079                 
28080                 break;
28081             default : 
28082                 break;
28083         }
28084         
28085         this.previewEl.appendChild(this.canvasEl);
28086         
28087         this.setCanvasPosition();
28088     },
28089     
28090     crop : function()
28091     {
28092         if(!this.canvasLoaded){
28093             return;
28094         }
28095         
28096         var imageCanvas = document.createElement("canvas");
28097         
28098         var imageContext = imageCanvas.getContext("2d");
28099         
28100         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28101         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28102         
28103         var center = imageCanvas.width / 2;
28104         
28105         imageContext.translate(center, center);
28106         
28107         imageContext.rotate(this.rotate * Math.PI / 180);
28108         
28109         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28110         
28111         var canvas = document.createElement("canvas");
28112         
28113         var context = canvas.getContext("2d");
28114                 
28115         canvas.width = this.minWidth;
28116         canvas.height = this.minHeight;
28117
28118         switch (this.rotate) {
28119             case 0 :
28120                 
28121                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28122                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28123                 
28124                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28125                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28126                 
28127                 var targetWidth = this.minWidth - 2 * x;
28128                 var targetHeight = this.minHeight - 2 * y;
28129                 
28130                 var scale = 1;
28131                 
28132                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28133                     scale = targetWidth / width;
28134                 }
28135                 
28136                 if(x > 0 && y == 0){
28137                     scale = targetHeight / height;
28138                 }
28139                 
28140                 if(x > 0 && y > 0){
28141                     scale = targetWidth / width;
28142                     
28143                     if(width < height){
28144                         scale = targetHeight / height;
28145                     }
28146                 }
28147                 
28148                 context.scale(scale, scale);
28149                 
28150                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28151                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28152
28153                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28154                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28155
28156                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28157                 
28158                 break;
28159             case 90 : 
28160                 
28161                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28162                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28163                 
28164                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28165                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28166                 
28167                 var targetWidth = this.minWidth - 2 * x;
28168                 var targetHeight = this.minHeight - 2 * y;
28169                 
28170                 var scale = 1;
28171                 
28172                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28173                     scale = targetWidth / width;
28174                 }
28175                 
28176                 if(x > 0 && y == 0){
28177                     scale = targetHeight / height;
28178                 }
28179                 
28180                 if(x > 0 && y > 0){
28181                     scale = targetWidth / width;
28182                     
28183                     if(width < height){
28184                         scale = targetHeight / height;
28185                     }
28186                 }
28187                 
28188                 context.scale(scale, scale);
28189                 
28190                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28191                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28192
28193                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28194                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28195                 
28196                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28197                 
28198                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28199                 
28200                 break;
28201             case 180 :
28202                 
28203                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28204                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28205                 
28206                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28207                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28208                 
28209                 var targetWidth = this.minWidth - 2 * x;
28210                 var targetHeight = this.minHeight - 2 * y;
28211                 
28212                 var scale = 1;
28213                 
28214                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28215                     scale = targetWidth / width;
28216                 }
28217                 
28218                 if(x > 0 && y == 0){
28219                     scale = targetHeight / height;
28220                 }
28221                 
28222                 if(x > 0 && y > 0){
28223                     scale = targetWidth / width;
28224                     
28225                     if(width < height){
28226                         scale = targetHeight / height;
28227                     }
28228                 }
28229                 
28230                 context.scale(scale, scale);
28231                 
28232                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28233                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28234
28235                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28236                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28237
28238                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28239                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28240                 
28241                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28242                 
28243                 break;
28244             case 270 :
28245                 
28246                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28247                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28248                 
28249                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28250                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28251                 
28252                 var targetWidth = this.minWidth - 2 * x;
28253                 var targetHeight = this.minHeight - 2 * y;
28254                 
28255                 var scale = 1;
28256                 
28257                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28258                     scale = targetWidth / width;
28259                 }
28260                 
28261                 if(x > 0 && y == 0){
28262                     scale = targetHeight / height;
28263                 }
28264                 
28265                 if(x > 0 && y > 0){
28266                     scale = targetWidth / width;
28267                     
28268                     if(width < height){
28269                         scale = targetHeight / height;
28270                     }
28271                 }
28272                 
28273                 context.scale(scale, scale);
28274                 
28275                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28276                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28277
28278                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28279                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28280                 
28281                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28282                 
28283                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28284                 
28285                 break;
28286             default : 
28287                 break;
28288         }
28289         
28290         this.cropData = canvas.toDataURL(this.cropType);
28291         
28292         if(this.fireEvent('crop', this, this.cropData) !== false){
28293             this.process(this.file, this.cropData);
28294         }
28295         
28296         return;
28297         
28298     },
28299     
28300     setThumbBoxSize : function()
28301     {
28302         var width, height;
28303         
28304         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28305             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28306             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28307             
28308             this.minWidth = width;
28309             this.minHeight = height;
28310             
28311             if(this.rotate == 90 || this.rotate == 270){
28312                 this.minWidth = height;
28313                 this.minHeight = width;
28314             }
28315         }
28316         
28317         height = 300;
28318         width = Math.ceil(this.minWidth * height / this.minHeight);
28319         
28320         if(this.minWidth > this.minHeight){
28321             width = 300;
28322             height = Math.ceil(this.minHeight * width / this.minWidth);
28323         }
28324         
28325         this.thumbEl.setStyle({
28326             width : width + 'px',
28327             height : height + 'px'
28328         });
28329
28330         return;
28331             
28332     },
28333     
28334     setThumbBoxPosition : function()
28335     {
28336         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28337         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28338         
28339         this.thumbEl.setLeft(x);
28340         this.thumbEl.setTop(y);
28341         
28342     },
28343     
28344     baseRotateLevel : function()
28345     {
28346         this.baseRotate = 1;
28347         
28348         if(
28349                 typeof(this.exif) != 'undefined' &&
28350                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28351                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28352         ){
28353             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28354         }
28355         
28356         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28357         
28358     },
28359     
28360     baseScaleLevel : function()
28361     {
28362         var width, height;
28363         
28364         if(this.isDocument){
28365             
28366             if(this.baseRotate == 6 || this.baseRotate == 8){
28367             
28368                 height = this.thumbEl.getHeight();
28369                 this.baseScale = height / this.imageEl.OriginWidth;
28370
28371                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28372                     width = this.thumbEl.getWidth();
28373                     this.baseScale = width / this.imageEl.OriginHeight;
28374                 }
28375
28376                 return;
28377             }
28378
28379             height = this.thumbEl.getHeight();
28380             this.baseScale = height / this.imageEl.OriginHeight;
28381
28382             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28383                 width = this.thumbEl.getWidth();
28384                 this.baseScale = width / this.imageEl.OriginWidth;
28385             }
28386
28387             return;
28388         }
28389         
28390         if(this.baseRotate == 6 || this.baseRotate == 8){
28391             
28392             width = this.thumbEl.getHeight();
28393             this.baseScale = width / this.imageEl.OriginHeight;
28394             
28395             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28396                 height = this.thumbEl.getWidth();
28397                 this.baseScale = height / this.imageEl.OriginHeight;
28398             }
28399             
28400             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28401                 height = this.thumbEl.getWidth();
28402                 this.baseScale = height / this.imageEl.OriginHeight;
28403                 
28404                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28405                     width = this.thumbEl.getHeight();
28406                     this.baseScale = width / this.imageEl.OriginWidth;
28407                 }
28408             }
28409             
28410             return;
28411         }
28412         
28413         width = this.thumbEl.getWidth();
28414         this.baseScale = width / this.imageEl.OriginWidth;
28415         
28416         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28417             height = this.thumbEl.getHeight();
28418             this.baseScale = height / this.imageEl.OriginHeight;
28419         }
28420         
28421         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28422             
28423             height = this.thumbEl.getHeight();
28424             this.baseScale = height / this.imageEl.OriginHeight;
28425             
28426             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28427                 width = this.thumbEl.getWidth();
28428                 this.baseScale = width / this.imageEl.OriginWidth;
28429             }
28430             
28431         }
28432         
28433         return;
28434     },
28435     
28436     getScaleLevel : function()
28437     {
28438         return this.baseScale * Math.pow(1.1, this.scale);
28439     },
28440     
28441     onTouchStart : function(e)
28442     {
28443         if(!this.canvasLoaded){
28444             this.beforeSelectFile(e);
28445             return;
28446         }
28447         
28448         var touches = e.browserEvent.touches;
28449         
28450         if(!touches){
28451             return;
28452         }
28453         
28454         if(touches.length == 1){
28455             this.onMouseDown(e);
28456             return;
28457         }
28458         
28459         if(touches.length != 2){
28460             return;
28461         }
28462         
28463         var coords = [];
28464         
28465         for(var i = 0, finger; finger = touches[i]; i++){
28466             coords.push(finger.pageX, finger.pageY);
28467         }
28468         
28469         var x = Math.pow(coords[0] - coords[2], 2);
28470         var y = Math.pow(coords[1] - coords[3], 2);
28471         
28472         this.startDistance = Math.sqrt(x + y);
28473         
28474         this.startScale = this.scale;
28475         
28476         this.pinching = true;
28477         this.dragable = false;
28478         
28479     },
28480     
28481     onTouchMove : function(e)
28482     {
28483         if(!this.pinching && !this.dragable){
28484             return;
28485         }
28486         
28487         var touches = e.browserEvent.touches;
28488         
28489         if(!touches){
28490             return;
28491         }
28492         
28493         if(this.dragable){
28494             this.onMouseMove(e);
28495             return;
28496         }
28497         
28498         var coords = [];
28499         
28500         for(var i = 0, finger; finger = touches[i]; i++){
28501             coords.push(finger.pageX, finger.pageY);
28502         }
28503         
28504         var x = Math.pow(coords[0] - coords[2], 2);
28505         var y = Math.pow(coords[1] - coords[3], 2);
28506         
28507         this.endDistance = Math.sqrt(x + y);
28508         
28509         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28510         
28511         if(!this.zoomable()){
28512             this.scale = this.startScale;
28513             return;
28514         }
28515         
28516         this.draw();
28517         
28518     },
28519     
28520     onTouchEnd : function(e)
28521     {
28522         this.pinching = false;
28523         this.dragable = false;
28524         
28525     },
28526     
28527     process : function(file, crop)
28528     {
28529         if(this.loadMask){
28530             this.maskEl.mask(this.loadingText);
28531         }
28532         
28533         this.xhr = new XMLHttpRequest();
28534         
28535         file.xhr = this.xhr;
28536
28537         this.xhr.open(this.method, this.url, true);
28538         
28539         var headers = {
28540             "Accept": "application/json",
28541             "Cache-Control": "no-cache",
28542             "X-Requested-With": "XMLHttpRequest"
28543         };
28544         
28545         for (var headerName in headers) {
28546             var headerValue = headers[headerName];
28547             if (headerValue) {
28548                 this.xhr.setRequestHeader(headerName, headerValue);
28549             }
28550         }
28551         
28552         var _this = this;
28553         
28554         this.xhr.onload = function()
28555         {
28556             _this.xhrOnLoad(_this.xhr);
28557         }
28558         
28559         this.xhr.onerror = function()
28560         {
28561             _this.xhrOnError(_this.xhr);
28562         }
28563         
28564         var formData = new FormData();
28565
28566         formData.append('returnHTML', 'NO');
28567         
28568         if(crop){
28569             formData.append('crop', crop);
28570         }
28571         
28572         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28573             formData.append(this.paramName, file, file.name);
28574         }
28575         
28576         if(typeof(file.filename) != 'undefined'){
28577             formData.append('filename', file.filename);
28578         }
28579         
28580         if(typeof(file.mimetype) != 'undefined'){
28581             formData.append('mimetype', file.mimetype);
28582         }
28583         
28584         if(this.fireEvent('arrange', this, formData) != false){
28585             this.xhr.send(formData);
28586         };
28587     },
28588     
28589     xhrOnLoad : function(xhr)
28590     {
28591         if(this.loadMask){
28592             this.maskEl.unmask();
28593         }
28594         
28595         if (xhr.readyState !== 4) {
28596             this.fireEvent('exception', this, xhr);
28597             return;
28598         }
28599
28600         var response = Roo.decode(xhr.responseText);
28601         
28602         if(!response.success){
28603             this.fireEvent('exception', this, xhr);
28604             return;
28605         }
28606         
28607         var response = Roo.decode(xhr.responseText);
28608         
28609         this.fireEvent('upload', this, response);
28610         
28611     },
28612     
28613     xhrOnError : function()
28614     {
28615         if(this.loadMask){
28616             this.maskEl.unmask();
28617         }
28618         
28619         Roo.log('xhr on error');
28620         
28621         var response = Roo.decode(xhr.responseText);
28622           
28623         Roo.log(response);
28624         
28625     },
28626     
28627     prepare : function(file)
28628     {   
28629         if(this.loadMask){
28630             this.maskEl.mask(this.loadingText);
28631         }
28632         
28633         this.file = false;
28634         this.exif = {};
28635         
28636         if(typeof(file) === 'string'){
28637             this.loadCanvas(file);
28638             return;
28639         }
28640         
28641         if(!file || !this.urlAPI){
28642             return;
28643         }
28644         
28645         this.file = file;
28646         this.cropType = file.type;
28647         
28648         var _this = this;
28649         
28650         if(this.fireEvent('prepare', this, this.file) != false){
28651             
28652             var reader = new FileReader();
28653             
28654             reader.onload = function (e) {
28655                 if (e.target.error) {
28656                     Roo.log(e.target.error);
28657                     return;
28658                 }
28659                 
28660                 var buffer = e.target.result,
28661                     dataView = new DataView(buffer),
28662                     offset = 2,
28663                     maxOffset = dataView.byteLength - 4,
28664                     markerBytes,
28665                     markerLength;
28666                 
28667                 if (dataView.getUint16(0) === 0xffd8) {
28668                     while (offset < maxOffset) {
28669                         markerBytes = dataView.getUint16(offset);
28670                         
28671                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28672                             markerLength = dataView.getUint16(offset + 2) + 2;
28673                             if (offset + markerLength > dataView.byteLength) {
28674                                 Roo.log('Invalid meta data: Invalid segment size.');
28675                                 break;
28676                             }
28677                             
28678                             if(markerBytes == 0xffe1){
28679                                 _this.parseExifData(
28680                                     dataView,
28681                                     offset,
28682                                     markerLength
28683                                 );
28684                             }
28685                             
28686                             offset += markerLength;
28687                             
28688                             continue;
28689                         }
28690                         
28691                         break;
28692                     }
28693                     
28694                 }
28695                 
28696                 var url = _this.urlAPI.createObjectURL(_this.file);
28697                 
28698                 _this.loadCanvas(url);
28699                 
28700                 return;
28701             }
28702             
28703             reader.readAsArrayBuffer(this.file);
28704             
28705         }
28706         
28707     },
28708     
28709     parseExifData : function(dataView, offset, length)
28710     {
28711         var tiffOffset = offset + 10,
28712             littleEndian,
28713             dirOffset;
28714     
28715         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28716             // No Exif data, might be XMP data instead
28717             return;
28718         }
28719         
28720         // Check for the ASCII code for "Exif" (0x45786966):
28721         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28722             // No Exif data, might be XMP data instead
28723             return;
28724         }
28725         if (tiffOffset + 8 > dataView.byteLength) {
28726             Roo.log('Invalid Exif data: Invalid segment size.');
28727             return;
28728         }
28729         // Check for the two null bytes:
28730         if (dataView.getUint16(offset + 8) !== 0x0000) {
28731             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28732             return;
28733         }
28734         // Check the byte alignment:
28735         switch (dataView.getUint16(tiffOffset)) {
28736         case 0x4949:
28737             littleEndian = true;
28738             break;
28739         case 0x4D4D:
28740             littleEndian = false;
28741             break;
28742         default:
28743             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28744             return;
28745         }
28746         // Check for the TIFF tag marker (0x002A):
28747         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28748             Roo.log('Invalid Exif data: Missing TIFF marker.');
28749             return;
28750         }
28751         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28752         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28753         
28754         this.parseExifTags(
28755             dataView,
28756             tiffOffset,
28757             tiffOffset + dirOffset,
28758             littleEndian
28759         );
28760     },
28761     
28762     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28763     {
28764         var tagsNumber,
28765             dirEndOffset,
28766             i;
28767         if (dirOffset + 6 > dataView.byteLength) {
28768             Roo.log('Invalid Exif data: Invalid directory offset.');
28769             return;
28770         }
28771         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28772         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28773         if (dirEndOffset + 4 > dataView.byteLength) {
28774             Roo.log('Invalid Exif data: Invalid directory size.');
28775             return;
28776         }
28777         for (i = 0; i < tagsNumber; i += 1) {
28778             this.parseExifTag(
28779                 dataView,
28780                 tiffOffset,
28781                 dirOffset + 2 + 12 * i, // tag offset
28782                 littleEndian
28783             );
28784         }
28785         // Return the offset to the next directory:
28786         return dataView.getUint32(dirEndOffset, littleEndian);
28787     },
28788     
28789     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28790     {
28791         var tag = dataView.getUint16(offset, littleEndian);
28792         
28793         this.exif[tag] = this.getExifValue(
28794             dataView,
28795             tiffOffset,
28796             offset,
28797             dataView.getUint16(offset + 2, littleEndian), // tag type
28798             dataView.getUint32(offset + 4, littleEndian), // tag length
28799             littleEndian
28800         );
28801     },
28802     
28803     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28804     {
28805         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28806             tagSize,
28807             dataOffset,
28808             values,
28809             i,
28810             str,
28811             c;
28812     
28813         if (!tagType) {
28814             Roo.log('Invalid Exif data: Invalid tag type.');
28815             return;
28816         }
28817         
28818         tagSize = tagType.size * length;
28819         // Determine if the value is contained in the dataOffset bytes,
28820         // or if the value at the dataOffset is a pointer to the actual data:
28821         dataOffset = tagSize > 4 ?
28822                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28823         if (dataOffset + tagSize > dataView.byteLength) {
28824             Roo.log('Invalid Exif data: Invalid data offset.');
28825             return;
28826         }
28827         if (length === 1) {
28828             return tagType.getValue(dataView, dataOffset, littleEndian);
28829         }
28830         values = [];
28831         for (i = 0; i < length; i += 1) {
28832             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28833         }
28834         
28835         if (tagType.ascii) {
28836             str = '';
28837             // Concatenate the chars:
28838             for (i = 0; i < values.length; i += 1) {
28839                 c = values[i];
28840                 // Ignore the terminating NULL byte(s):
28841                 if (c === '\u0000') {
28842                     break;
28843                 }
28844                 str += c;
28845             }
28846             return str;
28847         }
28848         return values;
28849     }
28850     
28851 });
28852
28853 Roo.apply(Roo.bootstrap.UploadCropbox, {
28854     tags : {
28855         'Orientation': 0x0112
28856     },
28857     
28858     Orientation: {
28859             1: 0, //'top-left',
28860 //            2: 'top-right',
28861             3: 180, //'bottom-right',
28862 //            4: 'bottom-left',
28863 //            5: 'left-top',
28864             6: 90, //'right-top',
28865 //            7: 'right-bottom',
28866             8: 270 //'left-bottom'
28867     },
28868     
28869     exifTagTypes : {
28870         // byte, 8-bit unsigned int:
28871         1: {
28872             getValue: function (dataView, dataOffset) {
28873                 return dataView.getUint8(dataOffset);
28874             },
28875             size: 1
28876         },
28877         // ascii, 8-bit byte:
28878         2: {
28879             getValue: function (dataView, dataOffset) {
28880                 return String.fromCharCode(dataView.getUint8(dataOffset));
28881             },
28882             size: 1,
28883             ascii: true
28884         },
28885         // short, 16 bit int:
28886         3: {
28887             getValue: function (dataView, dataOffset, littleEndian) {
28888                 return dataView.getUint16(dataOffset, littleEndian);
28889             },
28890             size: 2
28891         },
28892         // long, 32 bit int:
28893         4: {
28894             getValue: function (dataView, dataOffset, littleEndian) {
28895                 return dataView.getUint32(dataOffset, littleEndian);
28896             },
28897             size: 4
28898         },
28899         // rational = two long values, first is numerator, second is denominator:
28900         5: {
28901             getValue: function (dataView, dataOffset, littleEndian) {
28902                 return dataView.getUint32(dataOffset, littleEndian) /
28903                     dataView.getUint32(dataOffset + 4, littleEndian);
28904             },
28905             size: 8
28906         },
28907         // slong, 32 bit signed int:
28908         9: {
28909             getValue: function (dataView, dataOffset, littleEndian) {
28910                 return dataView.getInt32(dataOffset, littleEndian);
28911             },
28912             size: 4
28913         },
28914         // srational, two slongs, first is numerator, second is denominator:
28915         10: {
28916             getValue: function (dataView, dataOffset, littleEndian) {
28917                 return dataView.getInt32(dataOffset, littleEndian) /
28918                     dataView.getInt32(dataOffset + 4, littleEndian);
28919             },
28920             size: 8
28921         }
28922     },
28923     
28924     footer : {
28925         STANDARD : [
28926             {
28927                 tag : 'div',
28928                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28929                 action : 'rotate-left',
28930                 cn : [
28931                     {
28932                         tag : 'button',
28933                         cls : 'btn btn-default',
28934                         html : '<i class="fa fa-undo"></i>'
28935                     }
28936                 ]
28937             },
28938             {
28939                 tag : 'div',
28940                 cls : 'btn-group roo-upload-cropbox-picture',
28941                 action : 'picture',
28942                 cn : [
28943                     {
28944                         tag : 'button',
28945                         cls : 'btn btn-default',
28946                         html : '<i class="fa fa-picture-o"></i>'
28947                     }
28948                 ]
28949             },
28950             {
28951                 tag : 'div',
28952                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28953                 action : 'rotate-right',
28954                 cn : [
28955                     {
28956                         tag : 'button',
28957                         cls : 'btn btn-default',
28958                         html : '<i class="fa fa-repeat"></i>'
28959                     }
28960                 ]
28961             }
28962         ],
28963         DOCUMENT : [
28964             {
28965                 tag : 'div',
28966                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28967                 action : 'rotate-left',
28968                 cn : [
28969                     {
28970                         tag : 'button',
28971                         cls : 'btn btn-default',
28972                         html : '<i class="fa fa-undo"></i>'
28973                     }
28974                 ]
28975             },
28976             {
28977                 tag : 'div',
28978                 cls : 'btn-group roo-upload-cropbox-download',
28979                 action : 'download',
28980                 cn : [
28981                     {
28982                         tag : 'button',
28983                         cls : 'btn btn-default',
28984                         html : '<i class="fa fa-download"></i>'
28985                     }
28986                 ]
28987             },
28988             {
28989                 tag : 'div',
28990                 cls : 'btn-group roo-upload-cropbox-crop',
28991                 action : 'crop',
28992                 cn : [
28993                     {
28994                         tag : 'button',
28995                         cls : 'btn btn-default',
28996                         html : '<i class="fa fa-crop"></i>'
28997                     }
28998                 ]
28999             },
29000             {
29001                 tag : 'div',
29002                 cls : 'btn-group roo-upload-cropbox-trash',
29003                 action : 'trash',
29004                 cn : [
29005                     {
29006                         tag : 'button',
29007                         cls : 'btn btn-default',
29008                         html : '<i class="fa fa-trash"></i>'
29009                     }
29010                 ]
29011             },
29012             {
29013                 tag : 'div',
29014                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29015                 action : 'rotate-right',
29016                 cn : [
29017                     {
29018                         tag : 'button',
29019                         cls : 'btn btn-default',
29020                         html : '<i class="fa fa-repeat"></i>'
29021                     }
29022                 ]
29023             }
29024         ],
29025         ROTATOR : [
29026             {
29027                 tag : 'div',
29028                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29029                 action : 'rotate-left',
29030                 cn : [
29031                     {
29032                         tag : 'button',
29033                         cls : 'btn btn-default',
29034                         html : '<i class="fa fa-undo"></i>'
29035                     }
29036                 ]
29037             },
29038             {
29039                 tag : 'div',
29040                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29041                 action : 'rotate-right',
29042                 cn : [
29043                     {
29044                         tag : 'button',
29045                         cls : 'btn btn-default',
29046                         html : '<i class="fa fa-repeat"></i>'
29047                     }
29048                 ]
29049             }
29050         ]
29051     }
29052 });
29053
29054 /*
29055 * Licence: LGPL
29056 */
29057
29058 /**
29059  * @class Roo.bootstrap.DocumentManager
29060  * @extends Roo.bootstrap.Component
29061  * Bootstrap DocumentManager class
29062  * @cfg {String} paramName default 'imageUpload'
29063  * @cfg {String} toolTipName default 'filename'
29064  * @cfg {String} method default POST
29065  * @cfg {String} url action url
29066  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29067  * @cfg {Boolean} multiple multiple upload default true
29068  * @cfg {Number} thumbSize default 300
29069  * @cfg {String} fieldLabel
29070  * @cfg {Number} labelWidth default 4
29071  * @cfg {String} labelAlign (left|top) default left
29072  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29073 * @cfg {Number} labellg set the width of label (1-12)
29074  * @cfg {Number} labelmd set the width of label (1-12)
29075  * @cfg {Number} labelsm set the width of label (1-12)
29076  * @cfg {Number} labelxs set the width of label (1-12)
29077  * 
29078  * @constructor
29079  * Create a new DocumentManager
29080  * @param {Object} config The config object
29081  */
29082
29083 Roo.bootstrap.DocumentManager = function(config){
29084     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29085     
29086     this.files = [];
29087     this.delegates = [];
29088     
29089     this.addEvents({
29090         /**
29091          * @event initial
29092          * Fire when initial the DocumentManager
29093          * @param {Roo.bootstrap.DocumentManager} this
29094          */
29095         "initial" : true,
29096         /**
29097          * @event inspect
29098          * inspect selected file
29099          * @param {Roo.bootstrap.DocumentManager} this
29100          * @param {File} file
29101          */
29102         "inspect" : true,
29103         /**
29104          * @event exception
29105          * Fire when xhr load exception
29106          * @param {Roo.bootstrap.DocumentManager} this
29107          * @param {XMLHttpRequest} xhr
29108          */
29109         "exception" : true,
29110         /**
29111          * @event afterupload
29112          * Fire when xhr load exception
29113          * @param {Roo.bootstrap.DocumentManager} this
29114          * @param {XMLHttpRequest} xhr
29115          */
29116         "afterupload" : true,
29117         /**
29118          * @event prepare
29119          * prepare the form data
29120          * @param {Roo.bootstrap.DocumentManager} this
29121          * @param {Object} formData
29122          */
29123         "prepare" : true,
29124         /**
29125          * @event remove
29126          * Fire when remove the file
29127          * @param {Roo.bootstrap.DocumentManager} this
29128          * @param {Object} file
29129          */
29130         "remove" : true,
29131         /**
29132          * @event refresh
29133          * Fire after refresh the file
29134          * @param {Roo.bootstrap.DocumentManager} this
29135          */
29136         "refresh" : true,
29137         /**
29138          * @event click
29139          * Fire after click the image
29140          * @param {Roo.bootstrap.DocumentManager} this
29141          * @param {Object} file
29142          */
29143         "click" : true,
29144         /**
29145          * @event edit
29146          * Fire when upload a image and editable set to true
29147          * @param {Roo.bootstrap.DocumentManager} this
29148          * @param {Object} file
29149          */
29150         "edit" : true,
29151         /**
29152          * @event beforeselectfile
29153          * Fire before select file
29154          * @param {Roo.bootstrap.DocumentManager} this
29155          */
29156         "beforeselectfile" : true,
29157         /**
29158          * @event process
29159          * Fire before process file
29160          * @param {Roo.bootstrap.DocumentManager} this
29161          * @param {Object} file
29162          */
29163         "process" : true,
29164         /**
29165          * @event previewrendered
29166          * Fire when preview rendered
29167          * @param {Roo.bootstrap.DocumentManager} this
29168          * @param {Object} file
29169          */
29170         "previewrendered" : true,
29171         /**
29172          */
29173         "previewResize" : true
29174         
29175     });
29176 };
29177
29178 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29179     
29180     boxes : 0,
29181     inputName : '',
29182     thumbSize : 300,
29183     multiple : true,
29184     files : false,
29185     method : 'POST',
29186     url : '',
29187     paramName : 'imageUpload',
29188     toolTipName : 'filename',
29189     fieldLabel : '',
29190     labelWidth : 4,
29191     labelAlign : 'left',
29192     editable : true,
29193     delegates : false,
29194     xhr : false, 
29195     
29196     labellg : 0,
29197     labelmd : 0,
29198     labelsm : 0,
29199     labelxs : 0,
29200     
29201     getAutoCreate : function()
29202     {   
29203         var managerWidget = {
29204             tag : 'div',
29205             cls : 'roo-document-manager',
29206             cn : [
29207                 {
29208                     tag : 'input',
29209                     cls : 'roo-document-manager-selector',
29210                     type : 'file'
29211                 },
29212                 {
29213                     tag : 'div',
29214                     cls : 'roo-document-manager-uploader',
29215                     cn : [
29216                         {
29217                             tag : 'div',
29218                             cls : 'roo-document-manager-upload-btn',
29219                             html : '<i class="fa fa-plus"></i>'
29220                         }
29221                     ]
29222                     
29223                 }
29224             ]
29225         };
29226         
29227         var content = [
29228             {
29229                 tag : 'div',
29230                 cls : 'column col-md-12',
29231                 cn : managerWidget
29232             }
29233         ];
29234         
29235         if(this.fieldLabel.length){
29236             
29237             content = [
29238                 {
29239                     tag : 'div',
29240                     cls : 'column col-md-12',
29241                     html : this.fieldLabel
29242                 },
29243                 {
29244                     tag : 'div',
29245                     cls : 'column col-md-12',
29246                     cn : managerWidget
29247                 }
29248             ];
29249
29250             if(this.labelAlign == 'left'){
29251                 content = [
29252                     {
29253                         tag : 'div',
29254                         cls : 'column',
29255                         html : this.fieldLabel
29256                     },
29257                     {
29258                         tag : 'div',
29259                         cls : 'column',
29260                         cn : managerWidget
29261                     }
29262                 ];
29263                 
29264                 if(this.labelWidth > 12){
29265                     content[0].style = "width: " + this.labelWidth + 'px';
29266                 }
29267
29268                 if(this.labelWidth < 13 && this.labelmd == 0){
29269                     this.labelmd = this.labelWidth;
29270                 }
29271
29272                 if(this.labellg > 0){
29273                     content[0].cls += ' col-lg-' + this.labellg;
29274                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29275                 }
29276
29277                 if(this.labelmd > 0){
29278                     content[0].cls += ' col-md-' + this.labelmd;
29279                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29280                 }
29281
29282                 if(this.labelsm > 0){
29283                     content[0].cls += ' col-sm-' + this.labelsm;
29284                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29285                 }
29286
29287                 if(this.labelxs > 0){
29288                     content[0].cls += ' col-xs-' + this.labelxs;
29289                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29290                 }
29291                 
29292             }
29293         }
29294         
29295         var cfg = {
29296             tag : 'div',
29297             cls : 'row clearfix',
29298             cn : content
29299         };
29300         
29301         return cfg;
29302         
29303     },
29304     
29305     initEvents : function()
29306     {
29307         this.managerEl = this.el.select('.roo-document-manager', true).first();
29308         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29309         
29310         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29311         this.selectorEl.hide();
29312         
29313         if(this.multiple){
29314             this.selectorEl.attr('multiple', 'multiple');
29315         }
29316         
29317         this.selectorEl.on('change', this.onFileSelected, this);
29318         
29319         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29320         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29321         
29322         this.uploader.on('click', this.onUploaderClick, this);
29323         
29324         this.renderProgressDialog();
29325         
29326         var _this = this;
29327         
29328         window.addEventListener("resize", function() { _this.refresh(); } );
29329         
29330         this.fireEvent('initial', this);
29331     },
29332     
29333     renderProgressDialog : function()
29334     {
29335         var _this = this;
29336         
29337         this.progressDialog = new Roo.bootstrap.Modal({
29338             cls : 'roo-document-manager-progress-dialog',
29339             allow_close : false,
29340             animate : false,
29341             title : '',
29342             buttons : [
29343                 {
29344                     name  :'cancel',
29345                     weight : 'danger',
29346                     html : 'Cancel'
29347                 }
29348             ], 
29349             listeners : { 
29350                 btnclick : function() {
29351                     _this.uploadCancel();
29352                     this.hide();
29353                 }
29354             }
29355         });
29356          
29357         this.progressDialog.render(Roo.get(document.body));
29358          
29359         this.progress = new Roo.bootstrap.Progress({
29360             cls : 'roo-document-manager-progress',
29361             active : true,
29362             striped : true
29363         });
29364         
29365         this.progress.render(this.progressDialog.getChildContainer());
29366         
29367         this.progressBar = new Roo.bootstrap.ProgressBar({
29368             cls : 'roo-document-manager-progress-bar',
29369             aria_valuenow : 0,
29370             aria_valuemin : 0,
29371             aria_valuemax : 12,
29372             panel : 'success'
29373         });
29374         
29375         this.progressBar.render(this.progress.getChildContainer());
29376     },
29377     
29378     onUploaderClick : function(e)
29379     {
29380         e.preventDefault();
29381      
29382         if(this.fireEvent('beforeselectfile', this) != false){
29383             this.selectorEl.dom.click();
29384         }
29385         
29386     },
29387     
29388     onFileSelected : function(e)
29389     {
29390         e.preventDefault();
29391         
29392         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29393             return;
29394         }
29395         
29396         Roo.each(this.selectorEl.dom.files, function(file){
29397             if(this.fireEvent('inspect', this, file) != false){
29398                 this.files.push(file);
29399             }
29400         }, this);
29401         
29402         this.queue();
29403         
29404     },
29405     
29406     queue : function()
29407     {
29408         this.selectorEl.dom.value = '';
29409         
29410         if(!this.files || !this.files.length){
29411             return;
29412         }
29413         
29414         if(this.boxes > 0 && this.files.length > this.boxes){
29415             this.files = this.files.slice(0, this.boxes);
29416         }
29417         
29418         this.uploader.show();
29419         
29420         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29421             this.uploader.hide();
29422         }
29423         
29424         var _this = this;
29425         
29426         var files = [];
29427         
29428         var docs = [];
29429         
29430         Roo.each(this.files, function(file){
29431             
29432             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29433                 var f = this.renderPreview(file);
29434                 files.push(f);
29435                 return;
29436             }
29437             
29438             if(file.type.indexOf('image') != -1){
29439                 this.delegates.push(
29440                     (function(){
29441                         _this.process(file);
29442                     }).createDelegate(this)
29443                 );
29444         
29445                 return;
29446             }
29447             
29448             docs.push(
29449                 (function(){
29450                     _this.process(file);
29451                 }).createDelegate(this)
29452             );
29453             
29454         }, this);
29455         
29456         this.files = files;
29457         
29458         this.delegates = this.delegates.concat(docs);
29459         
29460         if(!this.delegates.length){
29461             this.refresh();
29462             return;
29463         }
29464         
29465         this.progressBar.aria_valuemax = this.delegates.length;
29466         
29467         this.arrange();
29468         
29469         return;
29470     },
29471     
29472     arrange : function()
29473     {
29474         if(!this.delegates.length){
29475             this.progressDialog.hide();
29476             this.refresh();
29477             return;
29478         }
29479         
29480         var delegate = this.delegates.shift();
29481         
29482         this.progressDialog.show();
29483         
29484         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29485         
29486         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29487         
29488         delegate();
29489     },
29490     
29491     refresh : function()
29492     {
29493         this.uploader.show();
29494         
29495         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29496             this.uploader.hide();
29497         }
29498         
29499         Roo.isTouch ? this.closable(false) : this.closable(true);
29500         
29501         this.fireEvent('refresh', this);
29502     },
29503     
29504     onRemove : function(e, el, o)
29505     {
29506         e.preventDefault();
29507         
29508         this.fireEvent('remove', this, o);
29509         
29510     },
29511     
29512     remove : function(o)
29513     {
29514         var files = [];
29515         
29516         Roo.each(this.files, function(file){
29517             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29518                 files.push(file);
29519                 return;
29520             }
29521
29522             o.target.remove();
29523
29524         }, this);
29525         
29526         this.files = files;
29527         
29528         this.refresh();
29529     },
29530     
29531     clear : function()
29532     {
29533         Roo.each(this.files, function(file){
29534             if(!file.target){
29535                 return;
29536             }
29537             
29538             file.target.remove();
29539
29540         }, this);
29541         
29542         this.files = [];
29543         
29544         this.refresh();
29545     },
29546     
29547     onClick : function(e, el, o)
29548     {
29549         e.preventDefault();
29550         
29551         this.fireEvent('click', this, o);
29552         
29553     },
29554     
29555     closable : function(closable)
29556     {
29557         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29558             
29559             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29560             
29561             if(closable){
29562                 el.show();
29563                 return;
29564             }
29565             
29566             el.hide();
29567             
29568         }, this);
29569     },
29570     
29571     xhrOnLoad : function(xhr)
29572     {
29573         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29574             el.remove();
29575         }, this);
29576         
29577         if (xhr.readyState !== 4) {
29578             this.arrange();
29579             this.fireEvent('exception', this, xhr);
29580             return;
29581         }
29582
29583         var response = Roo.decode(xhr.responseText);
29584         
29585         if(!response.success){
29586             this.arrange();
29587             this.fireEvent('exception', this, xhr);
29588             return;
29589         }
29590         
29591         var file = this.renderPreview(response.data);
29592         
29593         this.files.push(file);
29594         
29595         this.arrange();
29596         
29597         this.fireEvent('afterupload', this, xhr);
29598         
29599     },
29600     
29601     xhrOnError : function(xhr)
29602     {
29603         Roo.log('xhr on error');
29604         
29605         var response = Roo.decode(xhr.responseText);
29606           
29607         Roo.log(response);
29608         
29609         this.arrange();
29610     },
29611     
29612     process : function(file)
29613     {
29614         if(this.fireEvent('process', this, file) !== false){
29615             if(this.editable && file.type.indexOf('image') != -1){
29616                 this.fireEvent('edit', this, file);
29617                 return;
29618             }
29619
29620             this.uploadStart(file, false);
29621
29622             return;
29623         }
29624         
29625     },
29626     
29627     uploadStart : function(file, crop)
29628     {
29629         this.xhr = new XMLHttpRequest();
29630         
29631         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29632             this.arrange();
29633             return;
29634         }
29635         
29636         file.xhr = this.xhr;
29637             
29638         this.managerEl.createChild({
29639             tag : 'div',
29640             cls : 'roo-document-manager-loading',
29641             cn : [
29642                 {
29643                     tag : 'div',
29644                     tooltip : file.name,
29645                     cls : 'roo-document-manager-thumb',
29646                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29647                 }
29648             ]
29649
29650         });
29651
29652         this.xhr.open(this.method, this.url, true);
29653         
29654         var headers = {
29655             "Accept": "application/json",
29656             "Cache-Control": "no-cache",
29657             "X-Requested-With": "XMLHttpRequest"
29658         };
29659         
29660         for (var headerName in headers) {
29661             var headerValue = headers[headerName];
29662             if (headerValue) {
29663                 this.xhr.setRequestHeader(headerName, headerValue);
29664             }
29665         }
29666         
29667         var _this = this;
29668         
29669         this.xhr.onload = function()
29670         {
29671             _this.xhrOnLoad(_this.xhr);
29672         }
29673         
29674         this.xhr.onerror = function()
29675         {
29676             _this.xhrOnError(_this.xhr);
29677         }
29678         
29679         var formData = new FormData();
29680
29681         formData.append('returnHTML', 'NO');
29682         
29683         if(crop){
29684             formData.append('crop', crop);
29685         }
29686         
29687         formData.append(this.paramName, file, file.name);
29688         
29689         var options = {
29690             file : file, 
29691             manually : false
29692         };
29693         
29694         if(this.fireEvent('prepare', this, formData, options) != false){
29695             
29696             if(options.manually){
29697                 return;
29698             }
29699             
29700             this.xhr.send(formData);
29701             return;
29702         };
29703         
29704         this.uploadCancel();
29705     },
29706     
29707     uploadCancel : function()
29708     {
29709         if (this.xhr) {
29710             this.xhr.abort();
29711         }
29712         
29713         this.delegates = [];
29714         
29715         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29716             el.remove();
29717         }, this);
29718         
29719         this.arrange();
29720     },
29721     
29722     renderPreview : function(file)
29723     {
29724         if(typeof(file.target) != 'undefined' && file.target){
29725             return file;
29726         }
29727         
29728         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29729         
29730         var previewEl = this.managerEl.createChild({
29731             tag : 'div',
29732             cls : 'roo-document-manager-preview',
29733             cn : [
29734                 {
29735                     tag : 'div',
29736                     tooltip : file[this.toolTipName],
29737                     cls : 'roo-document-manager-thumb',
29738                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29739                 },
29740                 {
29741                     tag : 'button',
29742                     cls : 'close',
29743                     html : '<i class="fa fa-times-circle"></i>'
29744                 }
29745             ]
29746         });
29747
29748         var close = previewEl.select('button.close', true).first();
29749
29750         close.on('click', this.onRemove, this, file);
29751
29752         file.target = previewEl;
29753
29754         var image = previewEl.select('img', true).first();
29755         
29756         var _this = this;
29757         
29758         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29759         
29760         image.on('click', this.onClick, this, file);
29761         
29762         this.fireEvent('previewrendered', this, file);
29763         
29764         return file;
29765         
29766     },
29767     
29768     onPreviewLoad : function(file, image)
29769     {
29770         if(typeof(file.target) == 'undefined' || !file.target){
29771             return;
29772         }
29773         
29774         var width = image.dom.naturalWidth || image.dom.width;
29775         var height = image.dom.naturalHeight || image.dom.height;
29776         
29777         if(!this.previewResize) {
29778             return;
29779         }
29780         
29781         if(width > height){
29782             file.target.addClass('wide');
29783             return;
29784         }
29785         
29786         file.target.addClass('tall');
29787         return;
29788         
29789     },
29790     
29791     uploadFromSource : function(file, crop)
29792     {
29793         this.xhr = new XMLHttpRequest();
29794         
29795         this.managerEl.createChild({
29796             tag : 'div',
29797             cls : 'roo-document-manager-loading',
29798             cn : [
29799                 {
29800                     tag : 'div',
29801                     tooltip : file.name,
29802                     cls : 'roo-document-manager-thumb',
29803                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29804                 }
29805             ]
29806
29807         });
29808
29809         this.xhr.open(this.method, this.url, true);
29810         
29811         var headers = {
29812             "Accept": "application/json",
29813             "Cache-Control": "no-cache",
29814             "X-Requested-With": "XMLHttpRequest"
29815         };
29816         
29817         for (var headerName in headers) {
29818             var headerValue = headers[headerName];
29819             if (headerValue) {
29820                 this.xhr.setRequestHeader(headerName, headerValue);
29821             }
29822         }
29823         
29824         var _this = this;
29825         
29826         this.xhr.onload = function()
29827         {
29828             _this.xhrOnLoad(_this.xhr);
29829         }
29830         
29831         this.xhr.onerror = function()
29832         {
29833             _this.xhrOnError(_this.xhr);
29834         }
29835         
29836         var formData = new FormData();
29837
29838         formData.append('returnHTML', 'NO');
29839         
29840         formData.append('crop', crop);
29841         
29842         if(typeof(file.filename) != 'undefined'){
29843             formData.append('filename', file.filename);
29844         }
29845         
29846         if(typeof(file.mimetype) != 'undefined'){
29847             formData.append('mimetype', file.mimetype);
29848         }
29849         
29850         Roo.log(formData);
29851         
29852         if(this.fireEvent('prepare', this, formData) != false){
29853             this.xhr.send(formData);
29854         };
29855     }
29856 });
29857
29858 /*
29859 * Licence: LGPL
29860 */
29861
29862 /**
29863  * @class Roo.bootstrap.DocumentViewer
29864  * @extends Roo.bootstrap.Component
29865  * Bootstrap DocumentViewer class
29866  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29867  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29868  * 
29869  * @constructor
29870  * Create a new DocumentViewer
29871  * @param {Object} config The config object
29872  */
29873
29874 Roo.bootstrap.DocumentViewer = function(config){
29875     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29876     
29877     this.addEvents({
29878         /**
29879          * @event initial
29880          * Fire after initEvent
29881          * @param {Roo.bootstrap.DocumentViewer} this
29882          */
29883         "initial" : true,
29884         /**
29885          * @event click
29886          * Fire after click
29887          * @param {Roo.bootstrap.DocumentViewer} this
29888          */
29889         "click" : true,
29890         /**
29891          * @event download
29892          * Fire after download button
29893          * @param {Roo.bootstrap.DocumentViewer} this
29894          */
29895         "download" : true,
29896         /**
29897          * @event trash
29898          * Fire after trash button
29899          * @param {Roo.bootstrap.DocumentViewer} this
29900          */
29901         "trash" : true
29902         
29903     });
29904 };
29905
29906 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29907     
29908     showDownload : true,
29909     
29910     showTrash : true,
29911     
29912     getAutoCreate : function()
29913     {
29914         var cfg = {
29915             tag : 'div',
29916             cls : 'roo-document-viewer',
29917             cn : [
29918                 {
29919                     tag : 'div',
29920                     cls : 'roo-document-viewer-body',
29921                     cn : [
29922                         {
29923                             tag : 'div',
29924                             cls : 'roo-document-viewer-thumb',
29925                             cn : [
29926                                 {
29927                                     tag : 'img',
29928                                     cls : 'roo-document-viewer-image'
29929                                 }
29930                             ]
29931                         }
29932                     ]
29933                 },
29934                 {
29935                     tag : 'div',
29936                     cls : 'roo-document-viewer-footer',
29937                     cn : {
29938                         tag : 'div',
29939                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29940                         cn : [
29941                             {
29942                                 tag : 'div',
29943                                 cls : 'btn-group roo-document-viewer-download',
29944                                 cn : [
29945                                     {
29946                                         tag : 'button',
29947                                         cls : 'btn btn-default',
29948                                         html : '<i class="fa fa-download"></i>'
29949                                     }
29950                                 ]
29951                             },
29952                             {
29953                                 tag : 'div',
29954                                 cls : 'btn-group roo-document-viewer-trash',
29955                                 cn : [
29956                                     {
29957                                         tag : 'button',
29958                                         cls : 'btn btn-default',
29959                                         html : '<i class="fa fa-trash"></i>'
29960                                     }
29961                                 ]
29962                             }
29963                         ]
29964                     }
29965                 }
29966             ]
29967         };
29968         
29969         return cfg;
29970     },
29971     
29972     initEvents : function()
29973     {
29974         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29975         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29976         
29977         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29978         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29979         
29980         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29981         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29982         
29983         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29984         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29985         
29986         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29987         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29988         
29989         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29990         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29991         
29992         this.bodyEl.on('click', this.onClick, this);
29993         this.downloadBtn.on('click', this.onDownload, this);
29994         this.trashBtn.on('click', this.onTrash, this);
29995         
29996         this.downloadBtn.hide();
29997         this.trashBtn.hide();
29998         
29999         if(this.showDownload){
30000             this.downloadBtn.show();
30001         }
30002         
30003         if(this.showTrash){
30004             this.trashBtn.show();
30005         }
30006         
30007         if(!this.showDownload && !this.showTrash) {
30008             this.footerEl.hide();
30009         }
30010         
30011     },
30012     
30013     initial : function()
30014     {
30015         this.fireEvent('initial', this);
30016         
30017     },
30018     
30019     onClick : function(e)
30020     {
30021         e.preventDefault();
30022         
30023         this.fireEvent('click', this);
30024     },
30025     
30026     onDownload : function(e)
30027     {
30028         e.preventDefault();
30029         
30030         this.fireEvent('download', this);
30031     },
30032     
30033     onTrash : function(e)
30034     {
30035         e.preventDefault();
30036         
30037         this.fireEvent('trash', this);
30038     }
30039     
30040 });
30041 /*
30042  * - LGPL
30043  *
30044  * nav progress bar
30045  * 
30046  */
30047
30048 /**
30049  * @class Roo.bootstrap.NavProgressBar
30050  * @extends Roo.bootstrap.Component
30051  * Bootstrap NavProgressBar class
30052  * 
30053  * @constructor
30054  * Create a new nav progress bar
30055  * @param {Object} config The config object
30056  */
30057
30058 Roo.bootstrap.NavProgressBar = function(config){
30059     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30060
30061     this.bullets = this.bullets || [];
30062    
30063 //    Roo.bootstrap.NavProgressBar.register(this);
30064      this.addEvents({
30065         /**
30066              * @event changed
30067              * Fires when the active item changes
30068              * @param {Roo.bootstrap.NavProgressBar} this
30069              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30070              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30071          */
30072         'changed': true
30073      });
30074     
30075 };
30076
30077 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30078     
30079     bullets : [],
30080     barItems : [],
30081     
30082     getAutoCreate : function()
30083     {
30084         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30085         
30086         cfg = {
30087             tag : 'div',
30088             cls : 'roo-navigation-bar-group',
30089             cn : [
30090                 {
30091                     tag : 'div',
30092                     cls : 'roo-navigation-top-bar'
30093                 },
30094                 {
30095                     tag : 'div',
30096                     cls : 'roo-navigation-bullets-bar',
30097                     cn : [
30098                         {
30099                             tag : 'ul',
30100                             cls : 'roo-navigation-bar'
30101                         }
30102                     ]
30103                 },
30104                 
30105                 {
30106                     tag : 'div',
30107                     cls : 'roo-navigation-bottom-bar'
30108                 }
30109             ]
30110             
30111         };
30112         
30113         return cfg;
30114         
30115     },
30116     
30117     initEvents: function() 
30118     {
30119         
30120     },
30121     
30122     onRender : function(ct, position) 
30123     {
30124         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30125         
30126         if(this.bullets.length){
30127             Roo.each(this.bullets, function(b){
30128                this.addItem(b);
30129             }, this);
30130         }
30131         
30132         this.format();
30133         
30134     },
30135     
30136     addItem : function(cfg)
30137     {
30138         var item = new Roo.bootstrap.NavProgressItem(cfg);
30139         
30140         item.parentId = this.id;
30141         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30142         
30143         if(cfg.html){
30144             var top = new Roo.bootstrap.Element({
30145                 tag : 'div',
30146                 cls : 'roo-navigation-bar-text'
30147             });
30148             
30149             var bottom = new Roo.bootstrap.Element({
30150                 tag : 'div',
30151                 cls : 'roo-navigation-bar-text'
30152             });
30153             
30154             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30155             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30156             
30157             var topText = new Roo.bootstrap.Element({
30158                 tag : 'span',
30159                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30160             });
30161             
30162             var bottomText = new Roo.bootstrap.Element({
30163                 tag : 'span',
30164                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30165             });
30166             
30167             topText.onRender(top.el, null);
30168             bottomText.onRender(bottom.el, null);
30169             
30170             item.topEl = top;
30171             item.bottomEl = bottom;
30172         }
30173         
30174         this.barItems.push(item);
30175         
30176         return item;
30177     },
30178     
30179     getActive : function()
30180     {
30181         var active = false;
30182         
30183         Roo.each(this.barItems, function(v){
30184             
30185             if (!v.isActive()) {
30186                 return;
30187             }
30188             
30189             active = v;
30190             return false;
30191             
30192         });
30193         
30194         return active;
30195     },
30196     
30197     setActiveItem : function(item)
30198     {
30199         var prev = false;
30200         
30201         Roo.each(this.barItems, function(v){
30202             if (v.rid == item.rid) {
30203                 return ;
30204             }
30205             
30206             if (v.isActive()) {
30207                 v.setActive(false);
30208                 prev = v;
30209             }
30210         });
30211
30212         item.setActive(true);
30213         
30214         this.fireEvent('changed', this, item, prev);
30215     },
30216     
30217     getBarItem: function(rid)
30218     {
30219         var ret = false;
30220         
30221         Roo.each(this.barItems, function(e) {
30222             if (e.rid != rid) {
30223                 return;
30224             }
30225             
30226             ret =  e;
30227             return false;
30228         });
30229         
30230         return ret;
30231     },
30232     
30233     indexOfItem : function(item)
30234     {
30235         var index = false;
30236         
30237         Roo.each(this.barItems, function(v, i){
30238             
30239             if (v.rid != item.rid) {
30240                 return;
30241             }
30242             
30243             index = i;
30244             return false
30245         });
30246         
30247         return index;
30248     },
30249     
30250     setActiveNext : function()
30251     {
30252         var i = this.indexOfItem(this.getActive());
30253         
30254         if (i > this.barItems.length) {
30255             return;
30256         }
30257         
30258         this.setActiveItem(this.barItems[i+1]);
30259     },
30260     
30261     setActivePrev : function()
30262     {
30263         var i = this.indexOfItem(this.getActive());
30264         
30265         if (i  < 1) {
30266             return;
30267         }
30268         
30269         this.setActiveItem(this.barItems[i-1]);
30270     },
30271     
30272     format : function()
30273     {
30274         if(!this.barItems.length){
30275             return;
30276         }
30277      
30278         var width = 100 / this.barItems.length;
30279         
30280         Roo.each(this.barItems, function(i){
30281             i.el.setStyle('width', width + '%');
30282             i.topEl.el.setStyle('width', width + '%');
30283             i.bottomEl.el.setStyle('width', width + '%');
30284         }, this);
30285         
30286     }
30287     
30288 });
30289 /*
30290  * - LGPL
30291  *
30292  * Nav Progress Item
30293  * 
30294  */
30295
30296 /**
30297  * @class Roo.bootstrap.NavProgressItem
30298  * @extends Roo.bootstrap.Component
30299  * Bootstrap NavProgressItem class
30300  * @cfg {String} rid the reference id
30301  * @cfg {Boolean} active (true|false) Is item active default false
30302  * @cfg {Boolean} disabled (true|false) Is item active default false
30303  * @cfg {String} html
30304  * @cfg {String} position (top|bottom) text position default bottom
30305  * @cfg {String} icon show icon instead of number
30306  * 
30307  * @constructor
30308  * Create a new NavProgressItem
30309  * @param {Object} config The config object
30310  */
30311 Roo.bootstrap.NavProgressItem = function(config){
30312     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30313     this.addEvents({
30314         // raw events
30315         /**
30316          * @event click
30317          * The raw click event for the entire grid.
30318          * @param {Roo.bootstrap.NavProgressItem} this
30319          * @param {Roo.EventObject} e
30320          */
30321         "click" : true
30322     });
30323    
30324 };
30325
30326 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30327     
30328     rid : '',
30329     active : false,
30330     disabled : false,
30331     html : '',
30332     position : 'bottom',
30333     icon : false,
30334     
30335     getAutoCreate : function()
30336     {
30337         var iconCls = 'roo-navigation-bar-item-icon';
30338         
30339         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30340         
30341         var cfg = {
30342             tag: 'li',
30343             cls: 'roo-navigation-bar-item',
30344             cn : [
30345                 {
30346                     tag : 'i',
30347                     cls : iconCls
30348                 }
30349             ]
30350         };
30351         
30352         if(this.active){
30353             cfg.cls += ' active';
30354         }
30355         if(this.disabled){
30356             cfg.cls += ' disabled';
30357         }
30358         
30359         return cfg;
30360     },
30361     
30362     disable : function()
30363     {
30364         this.setDisabled(true);
30365     },
30366     
30367     enable : function()
30368     {
30369         this.setDisabled(false);
30370     },
30371     
30372     initEvents: function() 
30373     {
30374         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30375         
30376         this.iconEl.on('click', this.onClick, this);
30377     },
30378     
30379     onClick : function(e)
30380     {
30381         e.preventDefault();
30382         
30383         if(this.disabled){
30384             return;
30385         }
30386         
30387         if(this.fireEvent('click', this, e) === false){
30388             return;
30389         };
30390         
30391         this.parent().setActiveItem(this);
30392     },
30393     
30394     isActive: function () 
30395     {
30396         return this.active;
30397     },
30398     
30399     setActive : function(state)
30400     {
30401         if(this.active == state){
30402             return;
30403         }
30404         
30405         this.active = state;
30406         
30407         if (state) {
30408             this.el.addClass('active');
30409             return;
30410         }
30411         
30412         this.el.removeClass('active');
30413         
30414         return;
30415     },
30416     
30417     setDisabled : function(state)
30418     {
30419         if(this.disabled == state){
30420             return;
30421         }
30422         
30423         this.disabled = state;
30424         
30425         if (state) {
30426             this.el.addClass('disabled');
30427             return;
30428         }
30429         
30430         this.el.removeClass('disabled');
30431     },
30432     
30433     tooltipEl : function()
30434     {
30435         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30436     }
30437 });
30438  
30439
30440  /*
30441  * - LGPL
30442  *
30443  * FieldLabel
30444  * 
30445  */
30446
30447 /**
30448  * @class Roo.bootstrap.FieldLabel
30449  * @extends Roo.bootstrap.Component
30450  * Bootstrap FieldLabel class
30451  * @cfg {String} html contents of the element
30452  * @cfg {String} tag tag of the element default label
30453  * @cfg {String} cls class of the element
30454  * @cfg {String} target label target 
30455  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30456  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30457  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30458  * @cfg {String} iconTooltip default "This field is required"
30459  * @cfg {String} indicatorpos (left|right) default left
30460  * 
30461  * @constructor
30462  * Create a new FieldLabel
30463  * @param {Object} config The config object
30464  */
30465
30466 Roo.bootstrap.FieldLabel = function(config){
30467     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30468     
30469     this.addEvents({
30470             /**
30471              * @event invalid
30472              * Fires after the field has been marked as invalid.
30473              * @param {Roo.form.FieldLabel} this
30474              * @param {String} msg The validation message
30475              */
30476             invalid : true,
30477             /**
30478              * @event valid
30479              * Fires after the field has been validated with no errors.
30480              * @param {Roo.form.FieldLabel} this
30481              */
30482             valid : true
30483         });
30484 };
30485
30486 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30487     
30488     tag: 'label',
30489     cls: '',
30490     html: '',
30491     target: '',
30492     allowBlank : true,
30493     invalidClass : 'has-warning',
30494     validClass : 'has-success',
30495     iconTooltip : 'This field is required',
30496     indicatorpos : 'left',
30497     
30498     getAutoCreate : function(){
30499         
30500         var cls = "";
30501         if (!this.allowBlank) {
30502             cls  = "visible";
30503         }
30504         
30505         var cfg = {
30506             tag : this.tag,
30507             cls : 'roo-bootstrap-field-label ' + this.cls,
30508             for : this.target,
30509             cn : [
30510                 {
30511                     tag : 'i',
30512                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30513                     tooltip : this.iconTooltip
30514                 },
30515                 {
30516                     tag : 'span',
30517                     html : this.html
30518                 }
30519             ] 
30520         };
30521         
30522         if(this.indicatorpos == 'right'){
30523             var cfg = {
30524                 tag : this.tag,
30525                 cls : 'roo-bootstrap-field-label ' + this.cls,
30526                 for : this.target,
30527                 cn : [
30528                     {
30529                         tag : 'span',
30530                         html : this.html
30531                     },
30532                     {
30533                         tag : 'i',
30534                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30535                         tooltip : this.iconTooltip
30536                     }
30537                 ] 
30538             };
30539         }
30540         
30541         return cfg;
30542     },
30543     
30544     initEvents: function() 
30545     {
30546         Roo.bootstrap.Element.superclass.initEvents.call(this);
30547         
30548         this.indicator = this.indicatorEl();
30549         
30550         if(this.indicator){
30551             this.indicator.removeClass('visible');
30552             this.indicator.addClass('invisible');
30553         }
30554         
30555         Roo.bootstrap.FieldLabel.register(this);
30556     },
30557     
30558     indicatorEl : function()
30559     {
30560         var indicator = this.el.select('i.roo-required-indicator',true).first();
30561         
30562         if(!indicator){
30563             return false;
30564         }
30565         
30566         return indicator;
30567         
30568     },
30569     
30570     /**
30571      * Mark this field as valid
30572      */
30573     markValid : function()
30574     {
30575         if(this.indicator){
30576             this.indicator.removeClass('visible');
30577             this.indicator.addClass('invisible');
30578         }
30579         if (Roo.bootstrap.version == 3) {
30580             this.el.removeClass(this.invalidClass);
30581             this.el.addClass(this.validClass);
30582         } else {
30583             this.el.removeClass('is-invalid');
30584             this.el.addClass('is-valid');
30585         }
30586         
30587         
30588         this.fireEvent('valid', this);
30589     },
30590     
30591     /**
30592      * Mark this field as invalid
30593      * @param {String} msg The validation message
30594      */
30595     markInvalid : function(msg)
30596     {
30597         if(this.indicator){
30598             this.indicator.removeClass('invisible');
30599             this.indicator.addClass('visible');
30600         }
30601           if (Roo.bootstrap.version == 3) {
30602             this.el.removeClass(this.validClass);
30603             this.el.addClass(this.invalidClass);
30604         } else {
30605             this.el.removeClass('is-valid');
30606             this.el.addClass('is-invalid');
30607         }
30608         
30609         
30610         this.fireEvent('invalid', this, msg);
30611     }
30612     
30613    
30614 });
30615
30616 Roo.apply(Roo.bootstrap.FieldLabel, {
30617     
30618     groups: {},
30619     
30620      /**
30621     * register a FieldLabel Group
30622     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30623     */
30624     register : function(label)
30625     {
30626         if(this.groups.hasOwnProperty(label.target)){
30627             return;
30628         }
30629      
30630         this.groups[label.target] = label;
30631         
30632     },
30633     /**
30634     * fetch a FieldLabel Group based on the target
30635     * @param {string} target
30636     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30637     */
30638     get: function(target) {
30639         if (typeof(this.groups[target]) == 'undefined') {
30640             return false;
30641         }
30642         
30643         return this.groups[target] ;
30644     }
30645 });
30646
30647  
30648
30649  /*
30650  * - LGPL
30651  *
30652  * page DateSplitField.
30653  * 
30654  */
30655
30656
30657 /**
30658  * @class Roo.bootstrap.DateSplitField
30659  * @extends Roo.bootstrap.Component
30660  * Bootstrap DateSplitField class
30661  * @cfg {string} fieldLabel - the label associated
30662  * @cfg {Number} labelWidth set the width of label (0-12)
30663  * @cfg {String} labelAlign (top|left)
30664  * @cfg {Boolean} dayAllowBlank (true|false) default false
30665  * @cfg {Boolean} monthAllowBlank (true|false) default false
30666  * @cfg {Boolean} yearAllowBlank (true|false) default false
30667  * @cfg {string} dayPlaceholder 
30668  * @cfg {string} monthPlaceholder
30669  * @cfg {string} yearPlaceholder
30670  * @cfg {string} dayFormat default 'd'
30671  * @cfg {string} monthFormat default 'm'
30672  * @cfg {string} yearFormat default 'Y'
30673  * @cfg {Number} labellg set the width of label (1-12)
30674  * @cfg {Number} labelmd set the width of label (1-12)
30675  * @cfg {Number} labelsm set the width of label (1-12)
30676  * @cfg {Number} labelxs set the width of label (1-12)
30677
30678  *     
30679  * @constructor
30680  * Create a new DateSplitField
30681  * @param {Object} config The config object
30682  */
30683
30684 Roo.bootstrap.DateSplitField = function(config){
30685     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30686     
30687     this.addEvents({
30688         // raw events
30689          /**
30690          * @event years
30691          * getting the data of years
30692          * @param {Roo.bootstrap.DateSplitField} this
30693          * @param {Object} years
30694          */
30695         "years" : true,
30696         /**
30697          * @event days
30698          * getting the data of days
30699          * @param {Roo.bootstrap.DateSplitField} this
30700          * @param {Object} days
30701          */
30702         "days" : true,
30703         /**
30704          * @event invalid
30705          * Fires after the field has been marked as invalid.
30706          * @param {Roo.form.Field} this
30707          * @param {String} msg The validation message
30708          */
30709         invalid : true,
30710        /**
30711          * @event valid
30712          * Fires after the field has been validated with no errors.
30713          * @param {Roo.form.Field} this
30714          */
30715         valid : true
30716     });
30717 };
30718
30719 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30720     
30721     fieldLabel : '',
30722     labelAlign : 'top',
30723     labelWidth : 3,
30724     dayAllowBlank : false,
30725     monthAllowBlank : false,
30726     yearAllowBlank : false,
30727     dayPlaceholder : '',
30728     monthPlaceholder : '',
30729     yearPlaceholder : '',
30730     dayFormat : 'd',
30731     monthFormat : 'm',
30732     yearFormat : 'Y',
30733     isFormField : true,
30734     labellg : 0,
30735     labelmd : 0,
30736     labelsm : 0,
30737     labelxs : 0,
30738     
30739     getAutoCreate : function()
30740     {
30741         var cfg = {
30742             tag : 'div',
30743             cls : 'row roo-date-split-field-group',
30744             cn : [
30745                 {
30746                     tag : 'input',
30747                     type : 'hidden',
30748                     cls : 'form-hidden-field roo-date-split-field-group-value',
30749                     name : this.name
30750                 }
30751             ]
30752         };
30753         
30754         var labelCls = 'col-md-12';
30755         var contentCls = 'col-md-4';
30756         
30757         if(this.fieldLabel){
30758             
30759             var label = {
30760                 tag : 'div',
30761                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30762                 cn : [
30763                     {
30764                         tag : 'label',
30765                         html : this.fieldLabel
30766                     }
30767                 ]
30768             };
30769             
30770             if(this.labelAlign == 'left'){
30771             
30772                 if(this.labelWidth > 12){
30773                     label.style = "width: " + this.labelWidth + 'px';
30774                 }
30775
30776                 if(this.labelWidth < 13 && this.labelmd == 0){
30777                     this.labelmd = this.labelWidth;
30778                 }
30779
30780                 if(this.labellg > 0){
30781                     labelCls = ' col-lg-' + this.labellg;
30782                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30783                 }
30784
30785                 if(this.labelmd > 0){
30786                     labelCls = ' col-md-' + this.labelmd;
30787                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30788                 }
30789
30790                 if(this.labelsm > 0){
30791                     labelCls = ' col-sm-' + this.labelsm;
30792                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30793                 }
30794
30795                 if(this.labelxs > 0){
30796                     labelCls = ' col-xs-' + this.labelxs;
30797                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30798                 }
30799             }
30800             
30801             label.cls += ' ' + labelCls;
30802             
30803             cfg.cn.push(label);
30804         }
30805         
30806         Roo.each(['day', 'month', 'year'], function(t){
30807             cfg.cn.push({
30808                 tag : 'div',
30809                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30810             });
30811         }, this);
30812         
30813         return cfg;
30814     },
30815     
30816     inputEl: function ()
30817     {
30818         return this.el.select('.roo-date-split-field-group-value', true).first();
30819     },
30820     
30821     onRender : function(ct, position) 
30822     {
30823         var _this = this;
30824         
30825         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30826         
30827         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30828         
30829         this.dayField = new Roo.bootstrap.ComboBox({
30830             allowBlank : this.dayAllowBlank,
30831             alwaysQuery : true,
30832             displayField : 'value',
30833             editable : false,
30834             fieldLabel : '',
30835             forceSelection : true,
30836             mode : 'local',
30837             placeholder : this.dayPlaceholder,
30838             selectOnFocus : true,
30839             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30840             triggerAction : 'all',
30841             typeAhead : true,
30842             valueField : 'value',
30843             store : new Roo.data.SimpleStore({
30844                 data : (function() {    
30845                     var days = [];
30846                     _this.fireEvent('days', _this, days);
30847                     return days;
30848                 })(),
30849                 fields : [ 'value' ]
30850             }),
30851             listeners : {
30852                 select : function (_self, record, index)
30853                 {
30854                     _this.setValue(_this.getValue());
30855                 }
30856             }
30857         });
30858
30859         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30860         
30861         this.monthField = new Roo.bootstrap.MonthField({
30862             after : '<i class=\"fa fa-calendar\"></i>',
30863             allowBlank : this.monthAllowBlank,
30864             placeholder : this.monthPlaceholder,
30865             readOnly : true,
30866             listeners : {
30867                 render : function (_self)
30868                 {
30869                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30870                         e.preventDefault();
30871                         _self.focus();
30872                     });
30873                 },
30874                 select : function (_self, oldvalue, newvalue)
30875                 {
30876                     _this.setValue(_this.getValue());
30877                 }
30878             }
30879         });
30880         
30881         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30882         
30883         this.yearField = new Roo.bootstrap.ComboBox({
30884             allowBlank : this.yearAllowBlank,
30885             alwaysQuery : true,
30886             displayField : 'value',
30887             editable : false,
30888             fieldLabel : '',
30889             forceSelection : true,
30890             mode : 'local',
30891             placeholder : this.yearPlaceholder,
30892             selectOnFocus : true,
30893             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30894             triggerAction : 'all',
30895             typeAhead : true,
30896             valueField : 'value',
30897             store : new Roo.data.SimpleStore({
30898                 data : (function() {
30899                     var years = [];
30900                     _this.fireEvent('years', _this, years);
30901                     return years;
30902                 })(),
30903                 fields : [ 'value' ]
30904             }),
30905             listeners : {
30906                 select : function (_self, record, index)
30907                 {
30908                     _this.setValue(_this.getValue());
30909                 }
30910             }
30911         });
30912
30913         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30914     },
30915     
30916     setValue : function(v, format)
30917     {
30918         this.inputEl.dom.value = v;
30919         
30920         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30921         
30922         var d = Date.parseDate(v, f);
30923         
30924         if(!d){
30925             this.validate();
30926             return;
30927         }
30928         
30929         this.setDay(d.format(this.dayFormat));
30930         this.setMonth(d.format(this.monthFormat));
30931         this.setYear(d.format(this.yearFormat));
30932         
30933         this.validate();
30934         
30935         return;
30936     },
30937     
30938     setDay : function(v)
30939     {
30940         this.dayField.setValue(v);
30941         this.inputEl.dom.value = this.getValue();
30942         this.validate();
30943         return;
30944     },
30945     
30946     setMonth : function(v)
30947     {
30948         this.monthField.setValue(v, true);
30949         this.inputEl.dom.value = this.getValue();
30950         this.validate();
30951         return;
30952     },
30953     
30954     setYear : function(v)
30955     {
30956         this.yearField.setValue(v);
30957         this.inputEl.dom.value = this.getValue();
30958         this.validate();
30959         return;
30960     },
30961     
30962     getDay : function()
30963     {
30964         return this.dayField.getValue();
30965     },
30966     
30967     getMonth : function()
30968     {
30969         return this.monthField.getValue();
30970     },
30971     
30972     getYear : function()
30973     {
30974         return this.yearField.getValue();
30975     },
30976     
30977     getValue : function()
30978     {
30979         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30980         
30981         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30982         
30983         return date;
30984     },
30985     
30986     reset : function()
30987     {
30988         this.setDay('');
30989         this.setMonth('');
30990         this.setYear('');
30991         this.inputEl.dom.value = '';
30992         this.validate();
30993         return;
30994     },
30995     
30996     validate : function()
30997     {
30998         var d = this.dayField.validate();
30999         var m = this.monthField.validate();
31000         var y = this.yearField.validate();
31001         
31002         var valid = true;
31003         
31004         if(
31005                 (!this.dayAllowBlank && !d) ||
31006                 (!this.monthAllowBlank && !m) ||
31007                 (!this.yearAllowBlank && !y)
31008         ){
31009             valid = false;
31010         }
31011         
31012         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31013             return valid;
31014         }
31015         
31016         if(valid){
31017             this.markValid();
31018             return valid;
31019         }
31020         
31021         this.markInvalid();
31022         
31023         return valid;
31024     },
31025     
31026     markValid : function()
31027     {
31028         
31029         var label = this.el.select('label', true).first();
31030         var icon = this.el.select('i.fa-star', true).first();
31031
31032         if(label && icon){
31033             icon.remove();
31034         }
31035         
31036         this.fireEvent('valid', this);
31037     },
31038     
31039      /**
31040      * Mark this field as invalid
31041      * @param {String} msg The validation message
31042      */
31043     markInvalid : function(msg)
31044     {
31045         
31046         var label = this.el.select('label', true).first();
31047         var icon = this.el.select('i.fa-star', true).first();
31048
31049         if(label && !icon){
31050             this.el.select('.roo-date-split-field-label', true).createChild({
31051                 tag : 'i',
31052                 cls : 'text-danger fa fa-lg fa-star',
31053                 tooltip : 'This field is required',
31054                 style : 'margin-right:5px;'
31055             }, label, true);
31056         }
31057         
31058         this.fireEvent('invalid', this, msg);
31059     },
31060     
31061     clearInvalid : function()
31062     {
31063         var label = this.el.select('label', true).first();
31064         var icon = this.el.select('i.fa-star', true).first();
31065
31066         if(label && icon){
31067             icon.remove();
31068         }
31069         
31070         this.fireEvent('valid', this);
31071     },
31072     
31073     getName: function()
31074     {
31075         return this.name;
31076     }
31077     
31078 });
31079
31080  /**
31081  *
31082  * This is based on 
31083  * http://masonry.desandro.com
31084  *
31085  * The idea is to render all the bricks based on vertical width...
31086  *
31087  * The original code extends 'outlayer' - we might need to use that....
31088  * 
31089  */
31090
31091
31092 /**
31093  * @class Roo.bootstrap.LayoutMasonry
31094  * @extends Roo.bootstrap.Component
31095  * Bootstrap Layout Masonry class
31096  * 
31097  * @constructor
31098  * Create a new Element
31099  * @param {Object} config The config object
31100  */
31101
31102 Roo.bootstrap.LayoutMasonry = function(config){
31103     
31104     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31105     
31106     this.bricks = [];
31107     
31108     Roo.bootstrap.LayoutMasonry.register(this);
31109     
31110     this.addEvents({
31111         // raw events
31112         /**
31113          * @event layout
31114          * Fire after layout the items
31115          * @param {Roo.bootstrap.LayoutMasonry} this
31116          * @param {Roo.EventObject} e
31117          */
31118         "layout" : true
31119     });
31120     
31121 };
31122
31123 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31124     
31125     /**
31126      * @cfg {Boolean} isLayoutInstant = no animation?
31127      */   
31128     isLayoutInstant : false, // needed?
31129    
31130     /**
31131      * @cfg {Number} boxWidth  width of the columns
31132      */   
31133     boxWidth : 450,
31134     
31135       /**
31136      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31137      */   
31138     boxHeight : 0,
31139     
31140     /**
31141      * @cfg {Number} padWidth padding below box..
31142      */   
31143     padWidth : 10, 
31144     
31145     /**
31146      * @cfg {Number} gutter gutter width..
31147      */   
31148     gutter : 10,
31149     
31150      /**
31151      * @cfg {Number} maxCols maximum number of columns
31152      */   
31153     
31154     maxCols: 0,
31155     
31156     /**
31157      * @cfg {Boolean} isAutoInitial defalut true
31158      */   
31159     isAutoInitial : true, 
31160     
31161     containerWidth: 0,
31162     
31163     /**
31164      * @cfg {Boolean} isHorizontal defalut false
31165      */   
31166     isHorizontal : false, 
31167
31168     currentSize : null,
31169     
31170     tag: 'div',
31171     
31172     cls: '',
31173     
31174     bricks: null, //CompositeElement
31175     
31176     cols : 1,
31177     
31178     _isLayoutInited : false,
31179     
31180 //    isAlternative : false, // only use for vertical layout...
31181     
31182     /**
31183      * @cfg {Number} alternativePadWidth padding below box..
31184      */   
31185     alternativePadWidth : 50,
31186     
31187     selectedBrick : [],
31188     
31189     getAutoCreate : function(){
31190         
31191         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31192         
31193         var cfg = {
31194             tag: this.tag,
31195             cls: 'blog-masonary-wrapper ' + this.cls,
31196             cn : {
31197                 cls : 'mas-boxes masonary'
31198             }
31199         };
31200         
31201         return cfg;
31202     },
31203     
31204     getChildContainer: function( )
31205     {
31206         if (this.boxesEl) {
31207             return this.boxesEl;
31208         }
31209         
31210         this.boxesEl = this.el.select('.mas-boxes').first();
31211         
31212         return this.boxesEl;
31213     },
31214     
31215     
31216     initEvents : function()
31217     {
31218         var _this = this;
31219         
31220         if(this.isAutoInitial){
31221             Roo.log('hook children rendered');
31222             this.on('childrenrendered', function() {
31223                 Roo.log('children rendered');
31224                 _this.initial();
31225             } ,this);
31226         }
31227     },
31228     
31229     initial : function()
31230     {
31231         this.selectedBrick = [];
31232         
31233         this.currentSize = this.el.getBox(true);
31234         
31235         Roo.EventManager.onWindowResize(this.resize, this); 
31236
31237         if(!this.isAutoInitial){
31238             this.layout();
31239             return;
31240         }
31241         
31242         this.layout();
31243         
31244         return;
31245         //this.layout.defer(500,this);
31246         
31247     },
31248     
31249     resize : function()
31250     {
31251         var cs = this.el.getBox(true);
31252         
31253         if (
31254                 this.currentSize.width == cs.width && 
31255                 this.currentSize.x == cs.x && 
31256                 this.currentSize.height == cs.height && 
31257                 this.currentSize.y == cs.y 
31258         ) {
31259             Roo.log("no change in with or X or Y");
31260             return;
31261         }
31262         
31263         this.currentSize = cs;
31264         
31265         this.layout();
31266         
31267     },
31268     
31269     layout : function()
31270     {   
31271         this._resetLayout();
31272         
31273         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31274         
31275         this.layoutItems( isInstant );
31276       
31277         this._isLayoutInited = true;
31278         
31279         this.fireEvent('layout', this);
31280         
31281     },
31282     
31283     _resetLayout : function()
31284     {
31285         if(this.isHorizontal){
31286             this.horizontalMeasureColumns();
31287             return;
31288         }
31289         
31290         this.verticalMeasureColumns();
31291         
31292     },
31293     
31294     verticalMeasureColumns : function()
31295     {
31296         this.getContainerWidth();
31297         
31298 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31299 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31300 //            return;
31301 //        }
31302         
31303         var boxWidth = this.boxWidth + this.padWidth;
31304         
31305         if(this.containerWidth < this.boxWidth){
31306             boxWidth = this.containerWidth
31307         }
31308         
31309         var containerWidth = this.containerWidth;
31310         
31311         var cols = Math.floor(containerWidth / boxWidth);
31312         
31313         this.cols = Math.max( cols, 1 );
31314         
31315         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31316         
31317         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31318         
31319         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31320         
31321         this.colWidth = boxWidth + avail - this.padWidth;
31322         
31323         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31324         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31325     },
31326     
31327     horizontalMeasureColumns : function()
31328     {
31329         this.getContainerWidth();
31330         
31331         var boxWidth = this.boxWidth;
31332         
31333         if(this.containerWidth < boxWidth){
31334             boxWidth = this.containerWidth;
31335         }
31336         
31337         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31338         
31339         this.el.setHeight(boxWidth);
31340         
31341     },
31342     
31343     getContainerWidth : function()
31344     {
31345         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31346     },
31347     
31348     layoutItems : function( isInstant )
31349     {
31350         Roo.log(this.bricks);
31351         
31352         var items = Roo.apply([], this.bricks);
31353         
31354         if(this.isHorizontal){
31355             this._horizontalLayoutItems( items , isInstant );
31356             return;
31357         }
31358         
31359 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31360 //            this._verticalAlternativeLayoutItems( items , isInstant );
31361 //            return;
31362 //        }
31363         
31364         this._verticalLayoutItems( items , isInstant );
31365         
31366     },
31367     
31368     _verticalLayoutItems : function ( items , isInstant)
31369     {
31370         if ( !items || !items.length ) {
31371             return;
31372         }
31373         
31374         var standard = [
31375             ['xs', 'xs', 'xs', 'tall'],
31376             ['xs', 'xs', 'tall'],
31377             ['xs', 'xs', 'sm'],
31378             ['xs', 'xs', 'xs'],
31379             ['xs', 'tall'],
31380             ['xs', 'sm'],
31381             ['xs', 'xs'],
31382             ['xs'],
31383             
31384             ['sm', 'xs', 'xs'],
31385             ['sm', 'xs'],
31386             ['sm'],
31387             
31388             ['tall', 'xs', 'xs', 'xs'],
31389             ['tall', 'xs', 'xs'],
31390             ['tall', 'xs'],
31391             ['tall']
31392             
31393         ];
31394         
31395         var queue = [];
31396         
31397         var boxes = [];
31398         
31399         var box = [];
31400         
31401         Roo.each(items, function(item, k){
31402             
31403             switch (item.size) {
31404                 // these layouts take up a full box,
31405                 case 'md' :
31406                 case 'md-left' :
31407                 case 'md-right' :
31408                 case 'wide' :
31409                     
31410                     if(box.length){
31411                         boxes.push(box);
31412                         box = [];
31413                     }
31414                     
31415                     boxes.push([item]);
31416                     
31417                     break;
31418                     
31419                 case 'xs' :
31420                 case 'sm' :
31421                 case 'tall' :
31422                     
31423                     box.push(item);
31424                     
31425                     break;
31426                 default :
31427                     break;
31428                     
31429             }
31430             
31431         }, this);
31432         
31433         if(box.length){
31434             boxes.push(box);
31435             box = [];
31436         }
31437         
31438         var filterPattern = function(box, length)
31439         {
31440             if(!box.length){
31441                 return;
31442             }
31443             
31444             var match = false;
31445             
31446             var pattern = box.slice(0, length);
31447             
31448             var format = [];
31449             
31450             Roo.each(pattern, function(i){
31451                 format.push(i.size);
31452             }, this);
31453             
31454             Roo.each(standard, function(s){
31455                 
31456                 if(String(s) != String(format)){
31457                     return;
31458                 }
31459                 
31460                 match = true;
31461                 return false;
31462                 
31463             }, this);
31464             
31465             if(!match && length == 1){
31466                 return;
31467             }
31468             
31469             if(!match){
31470                 filterPattern(box, length - 1);
31471                 return;
31472             }
31473                 
31474             queue.push(pattern);
31475
31476             box = box.slice(length, box.length);
31477
31478             filterPattern(box, 4);
31479
31480             return;
31481             
31482         }
31483         
31484         Roo.each(boxes, function(box, k){
31485             
31486             if(!box.length){
31487                 return;
31488             }
31489             
31490             if(box.length == 1){
31491                 queue.push(box);
31492                 return;
31493             }
31494             
31495             filterPattern(box, 4);
31496             
31497         }, this);
31498         
31499         this._processVerticalLayoutQueue( queue, isInstant );
31500         
31501     },
31502     
31503 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31504 //    {
31505 //        if ( !items || !items.length ) {
31506 //            return;
31507 //        }
31508 //
31509 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31510 //        
31511 //    },
31512     
31513     _horizontalLayoutItems : function ( items , isInstant)
31514     {
31515         if ( !items || !items.length || items.length < 3) {
31516             return;
31517         }
31518         
31519         items.reverse();
31520         
31521         var eItems = items.slice(0, 3);
31522         
31523         items = items.slice(3, items.length);
31524         
31525         var standard = [
31526             ['xs', 'xs', 'xs', 'wide'],
31527             ['xs', 'xs', 'wide'],
31528             ['xs', 'xs', 'sm'],
31529             ['xs', 'xs', 'xs'],
31530             ['xs', 'wide'],
31531             ['xs', 'sm'],
31532             ['xs', 'xs'],
31533             ['xs'],
31534             
31535             ['sm', 'xs', 'xs'],
31536             ['sm', 'xs'],
31537             ['sm'],
31538             
31539             ['wide', 'xs', 'xs', 'xs'],
31540             ['wide', 'xs', 'xs'],
31541             ['wide', 'xs'],
31542             ['wide'],
31543             
31544             ['wide-thin']
31545         ];
31546         
31547         var queue = [];
31548         
31549         var boxes = [];
31550         
31551         var box = [];
31552         
31553         Roo.each(items, function(item, k){
31554             
31555             switch (item.size) {
31556                 case 'md' :
31557                 case 'md-left' :
31558                 case 'md-right' :
31559                 case 'tall' :
31560                     
31561                     if(box.length){
31562                         boxes.push(box);
31563                         box = [];
31564                     }
31565                     
31566                     boxes.push([item]);
31567                     
31568                     break;
31569                     
31570                 case 'xs' :
31571                 case 'sm' :
31572                 case 'wide' :
31573                 case 'wide-thin' :
31574                     
31575                     box.push(item);
31576                     
31577                     break;
31578                 default :
31579                     break;
31580                     
31581             }
31582             
31583         }, this);
31584         
31585         if(box.length){
31586             boxes.push(box);
31587             box = [];
31588         }
31589         
31590         var filterPattern = function(box, length)
31591         {
31592             if(!box.length){
31593                 return;
31594             }
31595             
31596             var match = false;
31597             
31598             var pattern = box.slice(0, length);
31599             
31600             var format = [];
31601             
31602             Roo.each(pattern, function(i){
31603                 format.push(i.size);
31604             }, this);
31605             
31606             Roo.each(standard, function(s){
31607                 
31608                 if(String(s) != String(format)){
31609                     return;
31610                 }
31611                 
31612                 match = true;
31613                 return false;
31614                 
31615             }, this);
31616             
31617             if(!match && length == 1){
31618                 return;
31619             }
31620             
31621             if(!match){
31622                 filterPattern(box, length - 1);
31623                 return;
31624             }
31625                 
31626             queue.push(pattern);
31627
31628             box = box.slice(length, box.length);
31629
31630             filterPattern(box, 4);
31631
31632             return;
31633             
31634         }
31635         
31636         Roo.each(boxes, function(box, k){
31637             
31638             if(!box.length){
31639                 return;
31640             }
31641             
31642             if(box.length == 1){
31643                 queue.push(box);
31644                 return;
31645             }
31646             
31647             filterPattern(box, 4);
31648             
31649         }, this);
31650         
31651         
31652         var prune = [];
31653         
31654         var pos = this.el.getBox(true);
31655         
31656         var minX = pos.x;
31657         
31658         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31659         
31660         var hit_end = false;
31661         
31662         Roo.each(queue, function(box){
31663             
31664             if(hit_end){
31665                 
31666                 Roo.each(box, function(b){
31667                 
31668                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31669                     b.el.hide();
31670
31671                 }, this);
31672
31673                 return;
31674             }
31675             
31676             var mx = 0;
31677             
31678             Roo.each(box, function(b){
31679                 
31680                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31681                 b.el.show();
31682
31683                 mx = Math.max(mx, b.x);
31684                 
31685             }, this);
31686             
31687             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31688             
31689             if(maxX < minX){
31690                 
31691                 Roo.each(box, function(b){
31692                 
31693                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31694                     b.el.hide();
31695                     
31696                 }, this);
31697                 
31698                 hit_end = true;
31699                 
31700                 return;
31701             }
31702             
31703             prune.push(box);
31704             
31705         }, this);
31706         
31707         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31708     },
31709     
31710     /** Sets position of item in DOM
31711     * @param {Element} item
31712     * @param {Number} x - horizontal position
31713     * @param {Number} y - vertical position
31714     * @param {Boolean} isInstant - disables transitions
31715     */
31716     _processVerticalLayoutQueue : function( queue, isInstant )
31717     {
31718         var pos = this.el.getBox(true);
31719         var x = pos.x;
31720         var y = pos.y;
31721         var maxY = [];
31722         
31723         for (var i = 0; i < this.cols; i++){
31724             maxY[i] = pos.y;
31725         }
31726         
31727         Roo.each(queue, function(box, k){
31728             
31729             var col = k % this.cols;
31730             
31731             Roo.each(box, function(b,kk){
31732                 
31733                 b.el.position('absolute');
31734                 
31735                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31736                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31737                 
31738                 if(b.size == 'md-left' || b.size == 'md-right'){
31739                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31740                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31741                 }
31742                 
31743                 b.el.setWidth(width);
31744                 b.el.setHeight(height);
31745                 // iframe?
31746                 b.el.select('iframe',true).setSize(width,height);
31747                 
31748             }, this);
31749             
31750             for (var i = 0; i < this.cols; i++){
31751                 
31752                 if(maxY[i] < maxY[col]){
31753                     col = i;
31754                     continue;
31755                 }
31756                 
31757                 col = Math.min(col, i);
31758                 
31759             }
31760             
31761             x = pos.x + col * (this.colWidth + this.padWidth);
31762             
31763             y = maxY[col];
31764             
31765             var positions = [];
31766             
31767             switch (box.length){
31768                 case 1 :
31769                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31770                     break;
31771                 case 2 :
31772                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31773                     break;
31774                 case 3 :
31775                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31776                     break;
31777                 case 4 :
31778                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31779                     break;
31780                 default :
31781                     break;
31782             }
31783             
31784             Roo.each(box, function(b,kk){
31785                 
31786                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31787                 
31788                 var sz = b.el.getSize();
31789                 
31790                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31791                 
31792             }, this);
31793             
31794         }, this);
31795         
31796         var mY = 0;
31797         
31798         for (var i = 0; i < this.cols; i++){
31799             mY = Math.max(mY, maxY[i]);
31800         }
31801         
31802         this.el.setHeight(mY - pos.y);
31803         
31804     },
31805     
31806 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31807 //    {
31808 //        var pos = this.el.getBox(true);
31809 //        var x = pos.x;
31810 //        var y = pos.y;
31811 //        var maxX = pos.right;
31812 //        
31813 //        var maxHeight = 0;
31814 //        
31815 //        Roo.each(items, function(item, k){
31816 //            
31817 //            var c = k % 2;
31818 //            
31819 //            item.el.position('absolute');
31820 //                
31821 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31822 //
31823 //            item.el.setWidth(width);
31824 //
31825 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31826 //
31827 //            item.el.setHeight(height);
31828 //            
31829 //            if(c == 0){
31830 //                item.el.setXY([x, y], isInstant ? false : true);
31831 //            } else {
31832 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31833 //            }
31834 //            
31835 //            y = y + height + this.alternativePadWidth;
31836 //            
31837 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31838 //            
31839 //        }, this);
31840 //        
31841 //        this.el.setHeight(maxHeight);
31842 //        
31843 //    },
31844     
31845     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31846     {
31847         var pos = this.el.getBox(true);
31848         
31849         var minX = pos.x;
31850         var minY = pos.y;
31851         
31852         var maxX = pos.right;
31853         
31854         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31855         
31856         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31857         
31858         Roo.each(queue, function(box, k){
31859             
31860             Roo.each(box, function(b, kk){
31861                 
31862                 b.el.position('absolute');
31863                 
31864                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31865                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31866                 
31867                 if(b.size == 'md-left' || b.size == 'md-right'){
31868                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31869                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31870                 }
31871                 
31872                 b.el.setWidth(width);
31873                 b.el.setHeight(height);
31874                 
31875             }, this);
31876             
31877             if(!box.length){
31878                 return;
31879             }
31880             
31881             var positions = [];
31882             
31883             switch (box.length){
31884                 case 1 :
31885                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31886                     break;
31887                 case 2 :
31888                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31889                     break;
31890                 case 3 :
31891                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31892                     break;
31893                 case 4 :
31894                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31895                     break;
31896                 default :
31897                     break;
31898             }
31899             
31900             Roo.each(box, function(b,kk){
31901                 
31902                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31903                 
31904                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31905                 
31906             }, this);
31907             
31908         }, this);
31909         
31910     },
31911     
31912     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31913     {
31914         Roo.each(eItems, function(b,k){
31915             
31916             b.size = (k == 0) ? 'sm' : 'xs';
31917             b.x = (k == 0) ? 2 : 1;
31918             b.y = (k == 0) ? 2 : 1;
31919             
31920             b.el.position('absolute');
31921             
31922             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31923                 
31924             b.el.setWidth(width);
31925             
31926             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31927             
31928             b.el.setHeight(height);
31929             
31930         }, this);
31931
31932         var positions = [];
31933         
31934         positions.push({
31935             x : maxX - this.unitWidth * 2 - this.gutter,
31936             y : minY
31937         });
31938         
31939         positions.push({
31940             x : maxX - this.unitWidth,
31941             y : minY + (this.unitWidth + this.gutter) * 2
31942         });
31943         
31944         positions.push({
31945             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31946             y : minY
31947         });
31948         
31949         Roo.each(eItems, function(b,k){
31950             
31951             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31952
31953         }, this);
31954         
31955     },
31956     
31957     getVerticalOneBoxColPositions : function(x, y, box)
31958     {
31959         var pos = [];
31960         
31961         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31962         
31963         if(box[0].size == 'md-left'){
31964             rand = 0;
31965         }
31966         
31967         if(box[0].size == 'md-right'){
31968             rand = 1;
31969         }
31970         
31971         pos.push({
31972             x : x + (this.unitWidth + this.gutter) * rand,
31973             y : y
31974         });
31975         
31976         return pos;
31977     },
31978     
31979     getVerticalTwoBoxColPositions : function(x, y, box)
31980     {
31981         var pos = [];
31982         
31983         if(box[0].size == 'xs'){
31984             
31985             pos.push({
31986                 x : x,
31987                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31988             });
31989
31990             pos.push({
31991                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31992                 y : y
31993             });
31994             
31995             return pos;
31996             
31997         }
31998         
31999         pos.push({
32000             x : x,
32001             y : y
32002         });
32003
32004         pos.push({
32005             x : x + (this.unitWidth + this.gutter) * 2,
32006             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32007         });
32008         
32009         return pos;
32010         
32011     },
32012     
32013     getVerticalThreeBoxColPositions : function(x, y, box)
32014     {
32015         var pos = [];
32016         
32017         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32018             
32019             pos.push({
32020                 x : x,
32021                 y : y
32022             });
32023
32024             pos.push({
32025                 x : x + (this.unitWidth + this.gutter) * 1,
32026                 y : y
32027             });
32028             
32029             pos.push({
32030                 x : x + (this.unitWidth + this.gutter) * 2,
32031                 y : y
32032             });
32033             
32034             return pos;
32035             
32036         }
32037         
32038         if(box[0].size == 'xs' && box[1].size == 'xs'){
32039             
32040             pos.push({
32041                 x : x,
32042                 y : y
32043             });
32044
32045             pos.push({
32046                 x : x,
32047                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32048             });
32049             
32050             pos.push({
32051                 x : x + (this.unitWidth + this.gutter) * 1,
32052                 y : y
32053             });
32054             
32055             return pos;
32056             
32057         }
32058         
32059         pos.push({
32060             x : x,
32061             y : y
32062         });
32063
32064         pos.push({
32065             x : x + (this.unitWidth + this.gutter) * 2,
32066             y : y
32067         });
32068
32069         pos.push({
32070             x : x + (this.unitWidth + this.gutter) * 2,
32071             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32072         });
32073             
32074         return pos;
32075         
32076     },
32077     
32078     getVerticalFourBoxColPositions : function(x, y, box)
32079     {
32080         var pos = [];
32081         
32082         if(box[0].size == 'xs'){
32083             
32084             pos.push({
32085                 x : x,
32086                 y : y
32087             });
32088
32089             pos.push({
32090                 x : x,
32091                 y : y + (this.unitHeight + this.gutter) * 1
32092             });
32093             
32094             pos.push({
32095                 x : x,
32096                 y : y + (this.unitHeight + this.gutter) * 2
32097             });
32098             
32099             pos.push({
32100                 x : x + (this.unitWidth + this.gutter) * 1,
32101                 y : y
32102             });
32103             
32104             return pos;
32105             
32106         }
32107         
32108         pos.push({
32109             x : x,
32110             y : y
32111         });
32112
32113         pos.push({
32114             x : x + (this.unitWidth + this.gutter) * 2,
32115             y : y
32116         });
32117
32118         pos.push({
32119             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32120             y : y + (this.unitHeight + this.gutter) * 1
32121         });
32122
32123         pos.push({
32124             x : x + (this.unitWidth + this.gutter) * 2,
32125             y : y + (this.unitWidth + this.gutter) * 2
32126         });
32127
32128         return pos;
32129         
32130     },
32131     
32132     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32133     {
32134         var pos = [];
32135         
32136         if(box[0].size == 'md-left'){
32137             pos.push({
32138                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32139                 y : minY
32140             });
32141             
32142             return pos;
32143         }
32144         
32145         if(box[0].size == 'md-right'){
32146             pos.push({
32147                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32148                 y : minY + (this.unitWidth + this.gutter) * 1
32149             });
32150             
32151             return pos;
32152         }
32153         
32154         var rand = Math.floor(Math.random() * (4 - box[0].y));
32155         
32156         pos.push({
32157             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32158             y : minY + (this.unitWidth + this.gutter) * rand
32159         });
32160         
32161         return pos;
32162         
32163     },
32164     
32165     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32166     {
32167         var pos = [];
32168         
32169         if(box[0].size == 'xs'){
32170             
32171             pos.push({
32172                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32173                 y : minY
32174             });
32175
32176             pos.push({
32177                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32178                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32179             });
32180             
32181             return pos;
32182             
32183         }
32184         
32185         pos.push({
32186             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32187             y : minY
32188         });
32189
32190         pos.push({
32191             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32192             y : minY + (this.unitWidth + this.gutter) * 2
32193         });
32194         
32195         return pos;
32196         
32197     },
32198     
32199     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32200     {
32201         var pos = [];
32202         
32203         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32204             
32205             pos.push({
32206                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32207                 y : minY
32208             });
32209
32210             pos.push({
32211                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32212                 y : minY + (this.unitWidth + this.gutter) * 1
32213             });
32214             
32215             pos.push({
32216                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32217                 y : minY + (this.unitWidth + this.gutter) * 2
32218             });
32219             
32220             return pos;
32221             
32222         }
32223         
32224         if(box[0].size == 'xs' && box[1].size == 'xs'){
32225             
32226             pos.push({
32227                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32228                 y : minY
32229             });
32230
32231             pos.push({
32232                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32233                 y : minY
32234             });
32235             
32236             pos.push({
32237                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32238                 y : minY + (this.unitWidth + this.gutter) * 1
32239             });
32240             
32241             return pos;
32242             
32243         }
32244         
32245         pos.push({
32246             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32247             y : minY
32248         });
32249
32250         pos.push({
32251             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32252             y : minY + (this.unitWidth + this.gutter) * 2
32253         });
32254
32255         pos.push({
32256             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32257             y : minY + (this.unitWidth + this.gutter) * 2
32258         });
32259             
32260         return pos;
32261         
32262     },
32263     
32264     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32265     {
32266         var pos = [];
32267         
32268         if(box[0].size == 'xs'){
32269             
32270             pos.push({
32271                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32272                 y : minY
32273             });
32274
32275             pos.push({
32276                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32277                 y : minY
32278             });
32279             
32280             pos.push({
32281                 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),
32282                 y : minY
32283             });
32284             
32285             pos.push({
32286                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32287                 y : minY + (this.unitWidth + this.gutter) * 1
32288             });
32289             
32290             return pos;
32291             
32292         }
32293         
32294         pos.push({
32295             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32296             y : minY
32297         });
32298         
32299         pos.push({
32300             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32301             y : minY + (this.unitWidth + this.gutter) * 2
32302         });
32303         
32304         pos.push({
32305             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32306             y : minY + (this.unitWidth + this.gutter) * 2
32307         });
32308         
32309         pos.push({
32310             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),
32311             y : minY + (this.unitWidth + this.gutter) * 2
32312         });
32313
32314         return pos;
32315         
32316     },
32317     
32318     /**
32319     * remove a Masonry Brick
32320     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32321     */
32322     removeBrick : function(brick_id)
32323     {
32324         if (!brick_id) {
32325             return;
32326         }
32327         
32328         for (var i = 0; i<this.bricks.length; i++) {
32329             if (this.bricks[i].id == brick_id) {
32330                 this.bricks.splice(i,1);
32331                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32332                 this.initial();
32333             }
32334         }
32335     },
32336     
32337     /**
32338     * adds a Masonry Brick
32339     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32340     */
32341     addBrick : function(cfg)
32342     {
32343         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32344         //this.register(cn);
32345         cn.parentId = this.id;
32346         cn.render(this.el);
32347         return cn;
32348     },
32349     
32350     /**
32351     * register a Masonry Brick
32352     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32353     */
32354     
32355     register : function(brick)
32356     {
32357         this.bricks.push(brick);
32358         brick.masonryId = this.id;
32359     },
32360     
32361     /**
32362     * clear all the Masonry Brick
32363     */
32364     clearAll : function()
32365     {
32366         this.bricks = [];
32367         //this.getChildContainer().dom.innerHTML = "";
32368         this.el.dom.innerHTML = '';
32369     },
32370     
32371     getSelected : function()
32372     {
32373         if (!this.selectedBrick) {
32374             return false;
32375         }
32376         
32377         return this.selectedBrick;
32378     }
32379 });
32380
32381 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32382     
32383     groups: {},
32384      /**
32385     * register a Masonry Layout
32386     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32387     */
32388     
32389     register : function(layout)
32390     {
32391         this.groups[layout.id] = layout;
32392     },
32393     /**
32394     * fetch a  Masonry Layout based on the masonry layout ID
32395     * @param {string} the masonry layout to add
32396     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32397     */
32398     
32399     get: function(layout_id) {
32400         if (typeof(this.groups[layout_id]) == 'undefined') {
32401             return false;
32402         }
32403         return this.groups[layout_id] ;
32404     }
32405     
32406     
32407     
32408 });
32409
32410  
32411
32412  /**
32413  *
32414  * This is based on 
32415  * http://masonry.desandro.com
32416  *
32417  * The idea is to render all the bricks based on vertical width...
32418  *
32419  * The original code extends 'outlayer' - we might need to use that....
32420  * 
32421  */
32422
32423
32424 /**
32425  * @class Roo.bootstrap.LayoutMasonryAuto
32426  * @extends Roo.bootstrap.Component
32427  * Bootstrap Layout Masonry class
32428  * 
32429  * @constructor
32430  * Create a new Element
32431  * @param {Object} config The config object
32432  */
32433
32434 Roo.bootstrap.LayoutMasonryAuto = function(config){
32435     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32436 };
32437
32438 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32439     
32440       /**
32441      * @cfg {Boolean} isFitWidth  - resize the width..
32442      */   
32443     isFitWidth : false,  // options..
32444     /**
32445      * @cfg {Boolean} isOriginLeft = left align?
32446      */   
32447     isOriginLeft : true,
32448     /**
32449      * @cfg {Boolean} isOriginTop = top align?
32450      */   
32451     isOriginTop : false,
32452     /**
32453      * @cfg {Boolean} isLayoutInstant = no animation?
32454      */   
32455     isLayoutInstant : false, // needed?
32456     /**
32457      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32458      */   
32459     isResizingContainer : true,
32460     /**
32461      * @cfg {Number} columnWidth  width of the columns 
32462      */   
32463     
32464     columnWidth : 0,
32465     
32466     /**
32467      * @cfg {Number} maxCols maximum number of columns
32468      */   
32469     
32470     maxCols: 0,
32471     /**
32472      * @cfg {Number} padHeight padding below box..
32473      */   
32474     
32475     padHeight : 10, 
32476     
32477     /**
32478      * @cfg {Boolean} isAutoInitial defalut true
32479      */   
32480     
32481     isAutoInitial : true, 
32482     
32483     // private?
32484     gutter : 0,
32485     
32486     containerWidth: 0,
32487     initialColumnWidth : 0,
32488     currentSize : null,
32489     
32490     colYs : null, // array.
32491     maxY : 0,
32492     padWidth: 10,
32493     
32494     
32495     tag: 'div',
32496     cls: '',
32497     bricks: null, //CompositeElement
32498     cols : 0, // array?
32499     // element : null, // wrapped now this.el
32500     _isLayoutInited : null, 
32501     
32502     
32503     getAutoCreate : function(){
32504         
32505         var cfg = {
32506             tag: this.tag,
32507             cls: 'blog-masonary-wrapper ' + this.cls,
32508             cn : {
32509                 cls : 'mas-boxes masonary'
32510             }
32511         };
32512         
32513         return cfg;
32514     },
32515     
32516     getChildContainer: function( )
32517     {
32518         if (this.boxesEl) {
32519             return this.boxesEl;
32520         }
32521         
32522         this.boxesEl = this.el.select('.mas-boxes').first();
32523         
32524         return this.boxesEl;
32525     },
32526     
32527     
32528     initEvents : function()
32529     {
32530         var _this = this;
32531         
32532         if(this.isAutoInitial){
32533             Roo.log('hook children rendered');
32534             this.on('childrenrendered', function() {
32535                 Roo.log('children rendered');
32536                 _this.initial();
32537             } ,this);
32538         }
32539         
32540     },
32541     
32542     initial : function()
32543     {
32544         this.reloadItems();
32545
32546         this.currentSize = this.el.getBox(true);
32547
32548         /// was window resize... - let's see if this works..
32549         Roo.EventManager.onWindowResize(this.resize, this); 
32550
32551         if(!this.isAutoInitial){
32552             this.layout();
32553             return;
32554         }
32555         
32556         this.layout.defer(500,this);
32557     },
32558     
32559     reloadItems: function()
32560     {
32561         this.bricks = this.el.select('.masonry-brick', true);
32562         
32563         this.bricks.each(function(b) {
32564             //Roo.log(b.getSize());
32565             if (!b.attr('originalwidth')) {
32566                 b.attr('originalwidth',  b.getSize().width);
32567             }
32568             
32569         });
32570         
32571         Roo.log(this.bricks.elements.length);
32572     },
32573     
32574     resize : function()
32575     {
32576         Roo.log('resize');
32577         var cs = this.el.getBox(true);
32578         
32579         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32580             Roo.log("no change in with or X");
32581             return;
32582         }
32583         this.currentSize = cs;
32584         this.layout();
32585     },
32586     
32587     layout : function()
32588     {
32589          Roo.log('layout');
32590         this._resetLayout();
32591         //this._manageStamps();
32592       
32593         // don't animate first layout
32594         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32595         this.layoutItems( isInstant );
32596       
32597         // flag for initalized
32598         this._isLayoutInited = true;
32599     },
32600     
32601     layoutItems : function( isInstant )
32602     {
32603         //var items = this._getItemsForLayout( this.items );
32604         // original code supports filtering layout items.. we just ignore it..
32605         
32606         this._layoutItems( this.bricks , isInstant );
32607       
32608         this._postLayout();
32609     },
32610     _layoutItems : function ( items , isInstant)
32611     {
32612        //this.fireEvent( 'layout', this, items );
32613     
32614
32615         if ( !items || !items.elements.length ) {
32616           // no items, emit event with empty array
32617             return;
32618         }
32619
32620         var queue = [];
32621         items.each(function(item) {
32622             Roo.log("layout item");
32623             Roo.log(item);
32624             // get x/y object from method
32625             var position = this._getItemLayoutPosition( item );
32626             // enqueue
32627             position.item = item;
32628             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32629             queue.push( position );
32630         }, this);
32631       
32632         this._processLayoutQueue( queue );
32633     },
32634     /** Sets position of item in DOM
32635     * @param {Element} item
32636     * @param {Number} x - horizontal position
32637     * @param {Number} y - vertical position
32638     * @param {Boolean} isInstant - disables transitions
32639     */
32640     _processLayoutQueue : function( queue )
32641     {
32642         for ( var i=0, len = queue.length; i < len; i++ ) {
32643             var obj = queue[i];
32644             obj.item.position('absolute');
32645             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32646         }
32647     },
32648       
32649     
32650     /**
32651     * Any logic you want to do after each layout,
32652     * i.e. size the container
32653     */
32654     _postLayout : function()
32655     {
32656         this.resizeContainer();
32657     },
32658     
32659     resizeContainer : function()
32660     {
32661         if ( !this.isResizingContainer ) {
32662             return;
32663         }
32664         var size = this._getContainerSize();
32665         if ( size ) {
32666             this.el.setSize(size.width,size.height);
32667             this.boxesEl.setSize(size.width,size.height);
32668         }
32669     },
32670     
32671     
32672     
32673     _resetLayout : function()
32674     {
32675         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32676         this.colWidth = this.el.getWidth();
32677         //this.gutter = this.el.getWidth(); 
32678         
32679         this.measureColumns();
32680
32681         // reset column Y
32682         var i = this.cols;
32683         this.colYs = [];
32684         while (i--) {
32685             this.colYs.push( 0 );
32686         }
32687     
32688         this.maxY = 0;
32689     },
32690
32691     measureColumns : function()
32692     {
32693         this.getContainerWidth();
32694       // if columnWidth is 0, default to outerWidth of first item
32695         if ( !this.columnWidth ) {
32696             var firstItem = this.bricks.first();
32697             Roo.log(firstItem);
32698             this.columnWidth  = this.containerWidth;
32699             if (firstItem && firstItem.attr('originalwidth') ) {
32700                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32701             }
32702             // columnWidth fall back to item of first element
32703             Roo.log("set column width?");
32704                         this.initialColumnWidth = this.columnWidth  ;
32705
32706             // if first elem has no width, default to size of container
32707             
32708         }
32709         
32710         
32711         if (this.initialColumnWidth) {
32712             this.columnWidth = this.initialColumnWidth;
32713         }
32714         
32715         
32716             
32717         // column width is fixed at the top - however if container width get's smaller we should
32718         // reduce it...
32719         
32720         // this bit calcs how man columns..
32721             
32722         var columnWidth = this.columnWidth += this.gutter;
32723       
32724         // calculate columns
32725         var containerWidth = this.containerWidth + this.gutter;
32726         
32727         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32728         // fix rounding errors, typically with gutters
32729         var excess = columnWidth - containerWidth % columnWidth;
32730         
32731         
32732         // if overshoot is less than a pixel, round up, otherwise floor it
32733         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32734         cols = Math[ mathMethod ]( cols );
32735         this.cols = Math.max( cols, 1 );
32736         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32737         
32738          // padding positioning..
32739         var totalColWidth = this.cols * this.columnWidth;
32740         var padavail = this.containerWidth - totalColWidth;
32741         // so for 2 columns - we need 3 'pads'
32742         
32743         var padNeeded = (1+this.cols) * this.padWidth;
32744         
32745         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32746         
32747         this.columnWidth += padExtra
32748         //this.padWidth = Math.floor(padavail /  ( this.cols));
32749         
32750         // adjust colum width so that padding is fixed??
32751         
32752         // we have 3 columns ... total = width * 3
32753         // we have X left over... that should be used by 
32754         
32755         //if (this.expandC) {
32756             
32757         //}
32758         
32759         
32760         
32761     },
32762     
32763     getContainerWidth : function()
32764     {
32765        /* // container is parent if fit width
32766         var container = this.isFitWidth ? this.element.parentNode : this.element;
32767         // check that this.size and size are there
32768         // IE8 triggers resize on body size change, so they might not be
32769         
32770         var size = getSize( container );  //FIXME
32771         this.containerWidth = size && size.innerWidth; //FIXME
32772         */
32773          
32774         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32775         
32776     },
32777     
32778     _getItemLayoutPosition : function( item )  // what is item?
32779     {
32780         // we resize the item to our columnWidth..
32781       
32782         item.setWidth(this.columnWidth);
32783         item.autoBoxAdjust  = false;
32784         
32785         var sz = item.getSize();
32786  
32787         // how many columns does this brick span
32788         var remainder = this.containerWidth % this.columnWidth;
32789         
32790         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32791         // round if off by 1 pixel, otherwise use ceil
32792         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32793         colSpan = Math.min( colSpan, this.cols );
32794         
32795         // normally this should be '1' as we dont' currently allow multi width columns..
32796         
32797         var colGroup = this._getColGroup( colSpan );
32798         // get the minimum Y value from the columns
32799         var minimumY = Math.min.apply( Math, colGroup );
32800         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32801         
32802         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32803          
32804         // position the brick
32805         var position = {
32806             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32807             y: this.currentSize.y + minimumY + this.padHeight
32808         };
32809         
32810         Roo.log(position);
32811         // apply setHeight to necessary columns
32812         var setHeight = minimumY + sz.height + this.padHeight;
32813         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32814         
32815         var setSpan = this.cols + 1 - colGroup.length;
32816         for ( var i = 0; i < setSpan; i++ ) {
32817           this.colYs[ shortColIndex + i ] = setHeight ;
32818         }
32819       
32820         return position;
32821     },
32822     
32823     /**
32824      * @param {Number} colSpan - number of columns the element spans
32825      * @returns {Array} colGroup
32826      */
32827     _getColGroup : function( colSpan )
32828     {
32829         if ( colSpan < 2 ) {
32830           // if brick spans only one column, use all the column Ys
32831           return this.colYs;
32832         }
32833       
32834         var colGroup = [];
32835         // how many different places could this brick fit horizontally
32836         var groupCount = this.cols + 1 - colSpan;
32837         // for each group potential horizontal position
32838         for ( var i = 0; i < groupCount; i++ ) {
32839           // make an array of colY values for that one group
32840           var groupColYs = this.colYs.slice( i, i + colSpan );
32841           // and get the max value of the array
32842           colGroup[i] = Math.max.apply( Math, groupColYs );
32843         }
32844         return colGroup;
32845     },
32846     /*
32847     _manageStamp : function( stamp )
32848     {
32849         var stampSize =  stamp.getSize();
32850         var offset = stamp.getBox();
32851         // get the columns that this stamp affects
32852         var firstX = this.isOriginLeft ? offset.x : offset.right;
32853         var lastX = firstX + stampSize.width;
32854         var firstCol = Math.floor( firstX / this.columnWidth );
32855         firstCol = Math.max( 0, firstCol );
32856         
32857         var lastCol = Math.floor( lastX / this.columnWidth );
32858         // lastCol should not go over if multiple of columnWidth #425
32859         lastCol -= lastX % this.columnWidth ? 0 : 1;
32860         lastCol = Math.min( this.cols - 1, lastCol );
32861         
32862         // set colYs to bottom of the stamp
32863         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32864             stampSize.height;
32865             
32866         for ( var i = firstCol; i <= lastCol; i++ ) {
32867           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32868         }
32869     },
32870     */
32871     
32872     _getContainerSize : function()
32873     {
32874         this.maxY = Math.max.apply( Math, this.colYs );
32875         var size = {
32876             height: this.maxY
32877         };
32878       
32879         if ( this.isFitWidth ) {
32880             size.width = this._getContainerFitWidth();
32881         }
32882       
32883         return size;
32884     },
32885     
32886     _getContainerFitWidth : function()
32887     {
32888         var unusedCols = 0;
32889         // count unused columns
32890         var i = this.cols;
32891         while ( --i ) {
32892           if ( this.colYs[i] !== 0 ) {
32893             break;
32894           }
32895           unusedCols++;
32896         }
32897         // fit container to columns that have been used
32898         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32899     },
32900     
32901     needsResizeLayout : function()
32902     {
32903         var previousWidth = this.containerWidth;
32904         this.getContainerWidth();
32905         return previousWidth !== this.containerWidth;
32906     }
32907  
32908 });
32909
32910  
32911
32912  /*
32913  * - LGPL
32914  *
32915  * element
32916  * 
32917  */
32918
32919 /**
32920  * @class Roo.bootstrap.MasonryBrick
32921  * @extends Roo.bootstrap.Component
32922  * Bootstrap MasonryBrick class
32923  * 
32924  * @constructor
32925  * Create a new MasonryBrick
32926  * @param {Object} config The config object
32927  */
32928
32929 Roo.bootstrap.MasonryBrick = function(config){
32930     
32931     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32932     
32933     Roo.bootstrap.MasonryBrick.register(this);
32934     
32935     this.addEvents({
32936         // raw events
32937         /**
32938          * @event click
32939          * When a MasonryBrick is clcik
32940          * @param {Roo.bootstrap.MasonryBrick} this
32941          * @param {Roo.EventObject} e
32942          */
32943         "click" : true
32944     });
32945 };
32946
32947 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32948     
32949     /**
32950      * @cfg {String} title
32951      */   
32952     title : '',
32953     /**
32954      * @cfg {String} html
32955      */   
32956     html : '',
32957     /**
32958      * @cfg {String} bgimage
32959      */   
32960     bgimage : '',
32961     /**
32962      * @cfg {String} videourl
32963      */   
32964     videourl : '',
32965     /**
32966      * @cfg {String} cls
32967      */   
32968     cls : '',
32969     /**
32970      * @cfg {String} href
32971      */   
32972     href : '',
32973     /**
32974      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32975      */   
32976     size : 'xs',
32977     
32978     /**
32979      * @cfg {String} placetitle (center|bottom)
32980      */   
32981     placetitle : '',
32982     
32983     /**
32984      * @cfg {Boolean} isFitContainer defalut true
32985      */   
32986     isFitContainer : true, 
32987     
32988     /**
32989      * @cfg {Boolean} preventDefault defalut false
32990      */   
32991     preventDefault : false, 
32992     
32993     /**
32994      * @cfg {Boolean} inverse defalut false
32995      */   
32996     maskInverse : false, 
32997     
32998     getAutoCreate : function()
32999     {
33000         if(!this.isFitContainer){
33001             return this.getSplitAutoCreate();
33002         }
33003         
33004         var cls = 'masonry-brick masonry-brick-full';
33005         
33006         if(this.href.length){
33007             cls += ' masonry-brick-link';
33008         }
33009         
33010         if(this.bgimage.length){
33011             cls += ' masonry-brick-image';
33012         }
33013         
33014         if(this.maskInverse){
33015             cls += ' mask-inverse';
33016         }
33017         
33018         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33019             cls += ' enable-mask';
33020         }
33021         
33022         if(this.size){
33023             cls += ' masonry-' + this.size + '-brick';
33024         }
33025         
33026         if(this.placetitle.length){
33027             
33028             switch (this.placetitle) {
33029                 case 'center' :
33030                     cls += ' masonry-center-title';
33031                     break;
33032                 case 'bottom' :
33033                     cls += ' masonry-bottom-title';
33034                     break;
33035                 default:
33036                     break;
33037             }
33038             
33039         } else {
33040             if(!this.html.length && !this.bgimage.length){
33041                 cls += ' masonry-center-title';
33042             }
33043
33044             if(!this.html.length && this.bgimage.length){
33045                 cls += ' masonry-bottom-title';
33046             }
33047         }
33048         
33049         if(this.cls){
33050             cls += ' ' + this.cls;
33051         }
33052         
33053         var cfg = {
33054             tag: (this.href.length) ? 'a' : 'div',
33055             cls: cls,
33056             cn: [
33057                 {
33058                     tag: 'div',
33059                     cls: 'masonry-brick-mask'
33060                 },
33061                 {
33062                     tag: 'div',
33063                     cls: 'masonry-brick-paragraph',
33064                     cn: []
33065                 }
33066             ]
33067         };
33068         
33069         if(this.href.length){
33070             cfg.href = this.href;
33071         }
33072         
33073         var cn = cfg.cn[1].cn;
33074         
33075         if(this.title.length){
33076             cn.push({
33077                 tag: 'h4',
33078                 cls: 'masonry-brick-title',
33079                 html: this.title
33080             });
33081         }
33082         
33083         if(this.html.length){
33084             cn.push({
33085                 tag: 'p',
33086                 cls: 'masonry-brick-text',
33087                 html: this.html
33088             });
33089         }
33090         
33091         if (!this.title.length && !this.html.length) {
33092             cfg.cn[1].cls += ' hide';
33093         }
33094         
33095         if(this.bgimage.length){
33096             cfg.cn.push({
33097                 tag: 'img',
33098                 cls: 'masonry-brick-image-view',
33099                 src: this.bgimage
33100             });
33101         }
33102         
33103         if(this.videourl.length){
33104             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33105             // youtube support only?
33106             cfg.cn.push({
33107                 tag: 'iframe',
33108                 cls: 'masonry-brick-image-view',
33109                 src: vurl,
33110                 frameborder : 0,
33111                 allowfullscreen : true
33112             });
33113         }
33114         
33115         return cfg;
33116         
33117     },
33118     
33119     getSplitAutoCreate : function()
33120     {
33121         var cls = 'masonry-brick masonry-brick-split';
33122         
33123         if(this.href.length){
33124             cls += ' masonry-brick-link';
33125         }
33126         
33127         if(this.bgimage.length){
33128             cls += ' masonry-brick-image';
33129         }
33130         
33131         if(this.size){
33132             cls += ' masonry-' + this.size + '-brick';
33133         }
33134         
33135         switch (this.placetitle) {
33136             case 'center' :
33137                 cls += ' masonry-center-title';
33138                 break;
33139             case 'bottom' :
33140                 cls += ' masonry-bottom-title';
33141                 break;
33142             default:
33143                 if(!this.bgimage.length){
33144                     cls += ' masonry-center-title';
33145                 }
33146
33147                 if(this.bgimage.length){
33148                     cls += ' masonry-bottom-title';
33149                 }
33150                 break;
33151         }
33152         
33153         if(this.cls){
33154             cls += ' ' + this.cls;
33155         }
33156         
33157         var cfg = {
33158             tag: (this.href.length) ? 'a' : 'div',
33159             cls: cls,
33160             cn: [
33161                 {
33162                     tag: 'div',
33163                     cls: 'masonry-brick-split-head',
33164                     cn: [
33165                         {
33166                             tag: 'div',
33167                             cls: 'masonry-brick-paragraph',
33168                             cn: []
33169                         }
33170                     ]
33171                 },
33172                 {
33173                     tag: 'div',
33174                     cls: 'masonry-brick-split-body',
33175                     cn: []
33176                 }
33177             ]
33178         };
33179         
33180         if(this.href.length){
33181             cfg.href = this.href;
33182         }
33183         
33184         if(this.title.length){
33185             cfg.cn[0].cn[0].cn.push({
33186                 tag: 'h4',
33187                 cls: 'masonry-brick-title',
33188                 html: this.title
33189             });
33190         }
33191         
33192         if(this.html.length){
33193             cfg.cn[1].cn.push({
33194                 tag: 'p',
33195                 cls: 'masonry-brick-text',
33196                 html: this.html
33197             });
33198         }
33199
33200         if(this.bgimage.length){
33201             cfg.cn[0].cn.push({
33202                 tag: 'img',
33203                 cls: 'masonry-brick-image-view',
33204                 src: this.bgimage
33205             });
33206         }
33207         
33208         if(this.videourl.length){
33209             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33210             // youtube support only?
33211             cfg.cn[0].cn.cn.push({
33212                 tag: 'iframe',
33213                 cls: 'masonry-brick-image-view',
33214                 src: vurl,
33215                 frameborder : 0,
33216                 allowfullscreen : true
33217             });
33218         }
33219         
33220         return cfg;
33221     },
33222     
33223     initEvents: function() 
33224     {
33225         switch (this.size) {
33226             case 'xs' :
33227                 this.x = 1;
33228                 this.y = 1;
33229                 break;
33230             case 'sm' :
33231                 this.x = 2;
33232                 this.y = 2;
33233                 break;
33234             case 'md' :
33235             case 'md-left' :
33236             case 'md-right' :
33237                 this.x = 3;
33238                 this.y = 3;
33239                 break;
33240             case 'tall' :
33241                 this.x = 2;
33242                 this.y = 3;
33243                 break;
33244             case 'wide' :
33245                 this.x = 3;
33246                 this.y = 2;
33247                 break;
33248             case 'wide-thin' :
33249                 this.x = 3;
33250                 this.y = 1;
33251                 break;
33252                         
33253             default :
33254                 break;
33255         }
33256         
33257         if(Roo.isTouch){
33258             this.el.on('touchstart', this.onTouchStart, this);
33259             this.el.on('touchmove', this.onTouchMove, this);
33260             this.el.on('touchend', this.onTouchEnd, this);
33261             this.el.on('contextmenu', this.onContextMenu, this);
33262         } else {
33263             this.el.on('mouseenter'  ,this.enter, this);
33264             this.el.on('mouseleave', this.leave, this);
33265             this.el.on('click', this.onClick, this);
33266         }
33267         
33268         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33269             this.parent().bricks.push(this);   
33270         }
33271         
33272     },
33273     
33274     onClick: function(e, el)
33275     {
33276         var time = this.endTimer - this.startTimer;
33277         // Roo.log(e.preventDefault());
33278         if(Roo.isTouch){
33279             if(time > 1000){
33280                 e.preventDefault();
33281                 return;
33282             }
33283         }
33284         
33285         if(!this.preventDefault){
33286             return;
33287         }
33288         
33289         e.preventDefault();
33290         
33291         if (this.activeClass != '') {
33292             this.selectBrick();
33293         }
33294         
33295         this.fireEvent('click', this, e);
33296     },
33297     
33298     enter: function(e, el)
33299     {
33300         e.preventDefault();
33301         
33302         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33303             return;
33304         }
33305         
33306         if(this.bgimage.length && this.html.length){
33307             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33308         }
33309     },
33310     
33311     leave: function(e, el)
33312     {
33313         e.preventDefault();
33314         
33315         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33316             return;
33317         }
33318         
33319         if(this.bgimage.length && this.html.length){
33320             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33321         }
33322     },
33323     
33324     onTouchStart: function(e, el)
33325     {
33326 //        e.preventDefault();
33327         
33328         this.touchmoved = false;
33329         
33330         if(!this.isFitContainer){
33331             return;
33332         }
33333         
33334         if(!this.bgimage.length || !this.html.length){
33335             return;
33336         }
33337         
33338         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33339         
33340         this.timer = new Date().getTime();
33341         
33342     },
33343     
33344     onTouchMove: function(e, el)
33345     {
33346         this.touchmoved = true;
33347     },
33348     
33349     onContextMenu : function(e,el)
33350     {
33351         e.preventDefault();
33352         e.stopPropagation();
33353         return false;
33354     },
33355     
33356     onTouchEnd: function(e, el)
33357     {
33358 //        e.preventDefault();
33359         
33360         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33361         
33362             this.leave(e,el);
33363             
33364             return;
33365         }
33366         
33367         if(!this.bgimage.length || !this.html.length){
33368             
33369             if(this.href.length){
33370                 window.location.href = this.href;
33371             }
33372             
33373             return;
33374         }
33375         
33376         if(!this.isFitContainer){
33377             return;
33378         }
33379         
33380         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33381         
33382         window.location.href = this.href;
33383     },
33384     
33385     //selection on single brick only
33386     selectBrick : function() {
33387         
33388         if (!this.parentId) {
33389             return;
33390         }
33391         
33392         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33393         var index = m.selectedBrick.indexOf(this.id);
33394         
33395         if ( index > -1) {
33396             m.selectedBrick.splice(index,1);
33397             this.el.removeClass(this.activeClass);
33398             return;
33399         }
33400         
33401         for(var i = 0; i < m.selectedBrick.length; i++) {
33402             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33403             b.el.removeClass(b.activeClass);
33404         }
33405         
33406         m.selectedBrick = [];
33407         
33408         m.selectedBrick.push(this.id);
33409         this.el.addClass(this.activeClass);
33410         return;
33411     },
33412     
33413     isSelected : function(){
33414         return this.el.hasClass(this.activeClass);
33415         
33416     }
33417 });
33418
33419 Roo.apply(Roo.bootstrap.MasonryBrick, {
33420     
33421     //groups: {},
33422     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33423      /**
33424     * register a Masonry Brick
33425     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33426     */
33427     
33428     register : function(brick)
33429     {
33430         //this.groups[brick.id] = brick;
33431         this.groups.add(brick.id, brick);
33432     },
33433     /**
33434     * fetch a  masonry brick based on the masonry brick ID
33435     * @param {string} the masonry brick to add
33436     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33437     */
33438     
33439     get: function(brick_id) 
33440     {
33441         // if (typeof(this.groups[brick_id]) == 'undefined') {
33442         //     return false;
33443         // }
33444         // return this.groups[brick_id] ;
33445         
33446         if(this.groups.key(brick_id)) {
33447             return this.groups.key(brick_id);
33448         }
33449         
33450         return false;
33451     }
33452     
33453     
33454     
33455 });
33456
33457  /*
33458  * - LGPL
33459  *
33460  * element
33461  * 
33462  */
33463
33464 /**
33465  * @class Roo.bootstrap.Brick
33466  * @extends Roo.bootstrap.Component
33467  * Bootstrap Brick class
33468  * 
33469  * @constructor
33470  * Create a new Brick
33471  * @param {Object} config The config object
33472  */
33473
33474 Roo.bootstrap.Brick = function(config){
33475     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33476     
33477     this.addEvents({
33478         // raw events
33479         /**
33480          * @event click
33481          * When a Brick is click
33482          * @param {Roo.bootstrap.Brick} this
33483          * @param {Roo.EventObject} e
33484          */
33485         "click" : true
33486     });
33487 };
33488
33489 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33490     
33491     /**
33492      * @cfg {String} title
33493      */   
33494     title : '',
33495     /**
33496      * @cfg {String} html
33497      */   
33498     html : '',
33499     /**
33500      * @cfg {String} bgimage
33501      */   
33502     bgimage : '',
33503     /**
33504      * @cfg {String} cls
33505      */   
33506     cls : '',
33507     /**
33508      * @cfg {String} href
33509      */   
33510     href : '',
33511     /**
33512      * @cfg {String} video
33513      */   
33514     video : '',
33515     /**
33516      * @cfg {Boolean} square
33517      */   
33518     square : true,
33519     
33520     getAutoCreate : function()
33521     {
33522         var cls = 'roo-brick';
33523         
33524         if(this.href.length){
33525             cls += ' roo-brick-link';
33526         }
33527         
33528         if(this.bgimage.length){
33529             cls += ' roo-brick-image';
33530         }
33531         
33532         if(!this.html.length && !this.bgimage.length){
33533             cls += ' roo-brick-center-title';
33534         }
33535         
33536         if(!this.html.length && this.bgimage.length){
33537             cls += ' roo-brick-bottom-title';
33538         }
33539         
33540         if(this.cls){
33541             cls += ' ' + this.cls;
33542         }
33543         
33544         var cfg = {
33545             tag: (this.href.length) ? 'a' : 'div',
33546             cls: cls,
33547             cn: [
33548                 {
33549                     tag: 'div',
33550                     cls: 'roo-brick-paragraph',
33551                     cn: []
33552                 }
33553             ]
33554         };
33555         
33556         if(this.href.length){
33557             cfg.href = this.href;
33558         }
33559         
33560         var cn = cfg.cn[0].cn;
33561         
33562         if(this.title.length){
33563             cn.push({
33564                 tag: 'h4',
33565                 cls: 'roo-brick-title',
33566                 html: this.title
33567             });
33568         }
33569         
33570         if(this.html.length){
33571             cn.push({
33572                 tag: 'p',
33573                 cls: 'roo-brick-text',
33574                 html: this.html
33575             });
33576         } else {
33577             cn.cls += ' hide';
33578         }
33579         
33580         if(this.bgimage.length){
33581             cfg.cn.push({
33582                 tag: 'img',
33583                 cls: 'roo-brick-image-view',
33584                 src: this.bgimage
33585             });
33586         }
33587         
33588         return cfg;
33589     },
33590     
33591     initEvents: function() 
33592     {
33593         if(this.title.length || this.html.length){
33594             this.el.on('mouseenter'  ,this.enter, this);
33595             this.el.on('mouseleave', this.leave, this);
33596         }
33597         
33598         Roo.EventManager.onWindowResize(this.resize, this); 
33599         
33600         if(this.bgimage.length){
33601             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33602             this.imageEl.on('load', this.onImageLoad, this);
33603             return;
33604         }
33605         
33606         this.resize();
33607     },
33608     
33609     onImageLoad : function()
33610     {
33611         this.resize();
33612     },
33613     
33614     resize : function()
33615     {
33616         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33617         
33618         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33619         
33620         if(this.bgimage.length){
33621             var image = this.el.select('.roo-brick-image-view', true).first();
33622             
33623             image.setWidth(paragraph.getWidth());
33624             
33625             if(this.square){
33626                 image.setHeight(paragraph.getWidth());
33627             }
33628             
33629             this.el.setHeight(image.getHeight());
33630             paragraph.setHeight(image.getHeight());
33631             
33632         }
33633         
33634     },
33635     
33636     enter: function(e, el)
33637     {
33638         e.preventDefault();
33639         
33640         if(this.bgimage.length){
33641             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33642             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33643         }
33644     },
33645     
33646     leave: function(e, el)
33647     {
33648         e.preventDefault();
33649         
33650         if(this.bgimage.length){
33651             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33652             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33653         }
33654     }
33655     
33656 });
33657
33658  
33659
33660  /*
33661  * - LGPL
33662  *
33663  * Number field 
33664  */
33665
33666 /**
33667  * @class Roo.bootstrap.NumberField
33668  * @extends Roo.bootstrap.Input
33669  * Bootstrap NumberField class
33670  * 
33671  * 
33672  * 
33673  * 
33674  * @constructor
33675  * Create a new NumberField
33676  * @param {Object} config The config object
33677  */
33678
33679 Roo.bootstrap.NumberField = function(config){
33680     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33681 };
33682
33683 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33684     
33685     /**
33686      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33687      */
33688     allowDecimals : true,
33689     /**
33690      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33691      */
33692     decimalSeparator : ".",
33693     /**
33694      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33695      */
33696     decimalPrecision : 2,
33697     /**
33698      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33699      */
33700     allowNegative : true,
33701     
33702     /**
33703      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33704      */
33705     allowZero: true,
33706     /**
33707      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33708      */
33709     minValue : Number.NEGATIVE_INFINITY,
33710     /**
33711      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33712      */
33713     maxValue : Number.MAX_VALUE,
33714     /**
33715      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33716      */
33717     minText : "The minimum value for this field is {0}",
33718     /**
33719      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33720      */
33721     maxText : "The maximum value for this field is {0}",
33722     /**
33723      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33724      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33725      */
33726     nanText : "{0} is not a valid number",
33727     /**
33728      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33729      */
33730     thousandsDelimiter : false,
33731     /**
33732      * @cfg {String} valueAlign alignment of value
33733      */
33734     valueAlign : "left",
33735
33736     getAutoCreate : function()
33737     {
33738         var hiddenInput = {
33739             tag: 'input',
33740             type: 'hidden',
33741             id: Roo.id(),
33742             cls: 'hidden-number-input'
33743         };
33744         
33745         if (this.name) {
33746             hiddenInput.name = this.name;
33747         }
33748         
33749         this.name = '';
33750         
33751         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33752         
33753         this.name = hiddenInput.name;
33754         
33755         if(cfg.cn.length > 0) {
33756             cfg.cn.push(hiddenInput);
33757         }
33758         
33759         return cfg;
33760     },
33761
33762     // private
33763     initEvents : function()
33764     {   
33765         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33766         
33767         var allowed = "0123456789";
33768         
33769         if(this.allowDecimals){
33770             allowed += this.decimalSeparator;
33771         }
33772         
33773         if(this.allowNegative){
33774             allowed += "-";
33775         }
33776         
33777         if(this.thousandsDelimiter) {
33778             allowed += ",";
33779         }
33780         
33781         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33782         
33783         var keyPress = function(e){
33784             
33785             var k = e.getKey();
33786             
33787             var c = e.getCharCode();
33788             
33789             if(
33790                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33791                     allowed.indexOf(String.fromCharCode(c)) === -1
33792             ){
33793                 e.stopEvent();
33794                 return;
33795             }
33796             
33797             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33798                 return;
33799             }
33800             
33801             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33802                 e.stopEvent();
33803             }
33804         };
33805         
33806         this.el.on("keypress", keyPress, this);
33807     },
33808     
33809     validateValue : function(value)
33810     {
33811         
33812         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33813             return false;
33814         }
33815         
33816         var num = this.parseValue(value);
33817         
33818         if(isNaN(num)){
33819             this.markInvalid(String.format(this.nanText, value));
33820             return false;
33821         }
33822         
33823         if(num < this.minValue){
33824             this.markInvalid(String.format(this.minText, this.minValue));
33825             return false;
33826         }
33827         
33828         if(num > this.maxValue){
33829             this.markInvalid(String.format(this.maxText, this.maxValue));
33830             return false;
33831         }
33832         
33833         return true;
33834     },
33835
33836     getValue : function()
33837     {
33838         var v = this.hiddenEl().getValue();
33839         
33840         return this.fixPrecision(this.parseValue(v));
33841     },
33842
33843     parseValue : function(value)
33844     {
33845         if(this.thousandsDelimiter) {
33846             value += "";
33847             r = new RegExp(",", "g");
33848             value = value.replace(r, "");
33849         }
33850         
33851         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33852         return isNaN(value) ? '' : value;
33853     },
33854
33855     fixPrecision : function(value)
33856     {
33857         if(this.thousandsDelimiter) {
33858             value += "";
33859             r = new RegExp(",", "g");
33860             value = value.replace(r, "");
33861         }
33862         
33863         var nan = isNaN(value);
33864         
33865         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33866             return nan ? '' : value;
33867         }
33868         return parseFloat(value).toFixed(this.decimalPrecision);
33869     },
33870
33871     setValue : function(v)
33872     {
33873         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33874         
33875         this.value = v;
33876         
33877         if(this.rendered){
33878             
33879             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33880             
33881             this.inputEl().dom.value = (v == '') ? '' :
33882                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33883             
33884             if(!this.allowZero && v === '0') {
33885                 this.hiddenEl().dom.value = '';
33886                 this.inputEl().dom.value = '';
33887             }
33888             
33889             this.validate();
33890         }
33891     },
33892
33893     decimalPrecisionFcn : function(v)
33894     {
33895         return Math.floor(v);
33896     },
33897
33898     beforeBlur : function()
33899     {
33900         var v = this.parseValue(this.getRawValue());
33901         
33902         if(v || v === 0 || v === ''){
33903             this.setValue(v);
33904         }
33905     },
33906     
33907     hiddenEl : function()
33908     {
33909         return this.el.select('input.hidden-number-input',true).first();
33910     }
33911     
33912 });
33913
33914  
33915
33916 /*
33917 * Licence: LGPL
33918 */
33919
33920 /**
33921  * @class Roo.bootstrap.DocumentSlider
33922  * @extends Roo.bootstrap.Component
33923  * Bootstrap DocumentSlider class
33924  * 
33925  * @constructor
33926  * Create a new DocumentViewer
33927  * @param {Object} config The config object
33928  */
33929
33930 Roo.bootstrap.DocumentSlider = function(config){
33931     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33932     
33933     this.files = [];
33934     
33935     this.addEvents({
33936         /**
33937          * @event initial
33938          * Fire after initEvent
33939          * @param {Roo.bootstrap.DocumentSlider} this
33940          */
33941         "initial" : true,
33942         /**
33943          * @event update
33944          * Fire after update
33945          * @param {Roo.bootstrap.DocumentSlider} this
33946          */
33947         "update" : true,
33948         /**
33949          * @event click
33950          * Fire after click
33951          * @param {Roo.bootstrap.DocumentSlider} this
33952          */
33953         "click" : true
33954     });
33955 };
33956
33957 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33958     
33959     files : false,
33960     
33961     indicator : 0,
33962     
33963     getAutoCreate : function()
33964     {
33965         var cfg = {
33966             tag : 'div',
33967             cls : 'roo-document-slider',
33968             cn : [
33969                 {
33970                     tag : 'div',
33971                     cls : 'roo-document-slider-header',
33972                     cn : [
33973                         {
33974                             tag : 'div',
33975                             cls : 'roo-document-slider-header-title'
33976                         }
33977                     ]
33978                 },
33979                 {
33980                     tag : 'div',
33981                     cls : 'roo-document-slider-body',
33982                     cn : [
33983                         {
33984                             tag : 'div',
33985                             cls : 'roo-document-slider-prev',
33986                             cn : [
33987                                 {
33988                                     tag : 'i',
33989                                     cls : 'fa fa-chevron-left'
33990                                 }
33991                             ]
33992                         },
33993                         {
33994                             tag : 'div',
33995                             cls : 'roo-document-slider-thumb',
33996                             cn : [
33997                                 {
33998                                     tag : 'img',
33999                                     cls : 'roo-document-slider-image'
34000                                 }
34001                             ]
34002                         },
34003                         {
34004                             tag : 'div',
34005                             cls : 'roo-document-slider-next',
34006                             cn : [
34007                                 {
34008                                     tag : 'i',
34009                                     cls : 'fa fa-chevron-right'
34010                                 }
34011                             ]
34012                         }
34013                     ]
34014                 }
34015             ]
34016         };
34017         
34018         return cfg;
34019     },
34020     
34021     initEvents : function()
34022     {
34023         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34024         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34025         
34026         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34027         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34028         
34029         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34030         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34031         
34032         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34033         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34034         
34035         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34036         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34037         
34038         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34039         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34040         
34041         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34042         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34043         
34044         this.thumbEl.on('click', this.onClick, this);
34045         
34046         this.prevIndicator.on('click', this.prev, this);
34047         
34048         this.nextIndicator.on('click', this.next, this);
34049         
34050     },
34051     
34052     initial : function()
34053     {
34054         if(this.files.length){
34055             this.indicator = 1;
34056             this.update()
34057         }
34058         
34059         this.fireEvent('initial', this);
34060     },
34061     
34062     update : function()
34063     {
34064         this.imageEl.attr('src', this.files[this.indicator - 1]);
34065         
34066         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34067         
34068         this.prevIndicator.show();
34069         
34070         if(this.indicator == 1){
34071             this.prevIndicator.hide();
34072         }
34073         
34074         this.nextIndicator.show();
34075         
34076         if(this.indicator == this.files.length){
34077             this.nextIndicator.hide();
34078         }
34079         
34080         this.thumbEl.scrollTo('top');
34081         
34082         this.fireEvent('update', this);
34083     },
34084     
34085     onClick : function(e)
34086     {
34087         e.preventDefault();
34088         
34089         this.fireEvent('click', this);
34090     },
34091     
34092     prev : function(e)
34093     {
34094         e.preventDefault();
34095         
34096         this.indicator = Math.max(1, this.indicator - 1);
34097         
34098         this.update();
34099     },
34100     
34101     next : function(e)
34102     {
34103         e.preventDefault();
34104         
34105         this.indicator = Math.min(this.files.length, this.indicator + 1);
34106         
34107         this.update();
34108     }
34109 });
34110 /*
34111  * - LGPL
34112  *
34113  * RadioSet
34114  *
34115  *
34116  */
34117
34118 /**
34119  * @class Roo.bootstrap.RadioSet
34120  * @extends Roo.bootstrap.Input
34121  * Bootstrap RadioSet class
34122  * @cfg {String} indicatorpos (left|right) default left
34123  * @cfg {Boolean} inline (true|false) inline the element (default true)
34124  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34125  * @constructor
34126  * Create a new RadioSet
34127  * @param {Object} config The config object
34128  */
34129
34130 Roo.bootstrap.RadioSet = function(config){
34131     
34132     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34133     
34134     this.radioes = [];
34135     
34136     Roo.bootstrap.RadioSet.register(this);
34137     
34138     this.addEvents({
34139         /**
34140         * @event check
34141         * Fires when the element is checked or unchecked.
34142         * @param {Roo.bootstrap.RadioSet} this This radio
34143         * @param {Roo.bootstrap.Radio} item The checked item
34144         */
34145        check : true,
34146        /**
34147         * @event click
34148         * Fires when the element is click.
34149         * @param {Roo.bootstrap.RadioSet} this This radio set
34150         * @param {Roo.bootstrap.Radio} item The checked item
34151         * @param {Roo.EventObject} e The event object
34152         */
34153        click : true
34154     });
34155     
34156 };
34157
34158 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34159
34160     radioes : false,
34161     
34162     inline : true,
34163     
34164     weight : '',
34165     
34166     indicatorpos : 'left',
34167     
34168     getAutoCreate : function()
34169     {
34170         var label = {
34171             tag : 'label',
34172             cls : 'roo-radio-set-label',
34173             cn : [
34174                 {
34175                     tag : 'span',
34176                     html : this.fieldLabel
34177                 }
34178             ]
34179         };
34180         if (Roo.bootstrap.version == 3) {
34181             
34182             
34183             if(this.indicatorpos == 'left'){
34184                 label.cn.unshift({
34185                     tag : 'i',
34186                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34187                     tooltip : 'This field is required'
34188                 });
34189             } else {
34190                 label.cn.push({
34191                     tag : 'i',
34192                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34193                     tooltip : 'This field is required'
34194                 });
34195             }
34196         }
34197         var items = {
34198             tag : 'div',
34199             cls : 'roo-radio-set-items'
34200         };
34201         
34202         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34203         
34204         if (align === 'left' && this.fieldLabel.length) {
34205             
34206             items = {
34207                 cls : "roo-radio-set-right", 
34208                 cn: [
34209                     items
34210                 ]
34211             };
34212             
34213             if(this.labelWidth > 12){
34214                 label.style = "width: " + this.labelWidth + 'px';
34215             }
34216             
34217             if(this.labelWidth < 13 && this.labelmd == 0){
34218                 this.labelmd = this.labelWidth;
34219             }
34220             
34221             if(this.labellg > 0){
34222                 label.cls += ' col-lg-' + this.labellg;
34223                 items.cls += ' col-lg-' + (12 - this.labellg);
34224             }
34225             
34226             if(this.labelmd > 0){
34227                 label.cls += ' col-md-' + this.labelmd;
34228                 items.cls += ' col-md-' + (12 - this.labelmd);
34229             }
34230             
34231             if(this.labelsm > 0){
34232                 label.cls += ' col-sm-' + this.labelsm;
34233                 items.cls += ' col-sm-' + (12 - this.labelsm);
34234             }
34235             
34236             if(this.labelxs > 0){
34237                 label.cls += ' col-xs-' + this.labelxs;
34238                 items.cls += ' col-xs-' + (12 - this.labelxs);
34239             }
34240         }
34241         
34242         var cfg = {
34243             tag : 'div',
34244             cls : 'roo-radio-set',
34245             cn : [
34246                 {
34247                     tag : 'input',
34248                     cls : 'roo-radio-set-input',
34249                     type : 'hidden',
34250                     name : this.name,
34251                     value : this.value ? this.value :  ''
34252                 },
34253                 label,
34254                 items
34255             ]
34256         };
34257         
34258         if(this.weight.length){
34259             cfg.cls += ' roo-radio-' + this.weight;
34260         }
34261         
34262         if(this.inline) {
34263             cfg.cls += ' roo-radio-set-inline';
34264         }
34265         
34266         var settings=this;
34267         ['xs','sm','md','lg'].map(function(size){
34268             if (settings[size]) {
34269                 cfg.cls += ' col-' + size + '-' + settings[size];
34270             }
34271         });
34272         
34273         return cfg;
34274         
34275     },
34276
34277     initEvents : function()
34278     {
34279         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34280         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34281         
34282         if(!this.fieldLabel.length){
34283             this.labelEl.hide();
34284         }
34285         
34286         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34287         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34288         
34289         this.indicator = this.indicatorEl();
34290         
34291         if(this.indicator){
34292             this.indicator.addClass('invisible');
34293         }
34294         
34295         this.originalValue = this.getValue();
34296         
34297     },
34298     
34299     inputEl: function ()
34300     {
34301         return this.el.select('.roo-radio-set-input', true).first();
34302     },
34303     
34304     getChildContainer : function()
34305     {
34306         return this.itemsEl;
34307     },
34308     
34309     register : function(item)
34310     {
34311         this.radioes.push(item);
34312         
34313     },
34314     
34315     validate : function()
34316     {   
34317         if(this.getVisibilityEl().hasClass('hidden')){
34318             return true;
34319         }
34320         
34321         var valid = false;
34322         
34323         Roo.each(this.radioes, function(i){
34324             if(!i.checked){
34325                 return;
34326             }
34327             
34328             valid = true;
34329             return false;
34330         });
34331         
34332         if(this.allowBlank) {
34333             return true;
34334         }
34335         
34336         if(this.disabled || valid){
34337             this.markValid();
34338             return true;
34339         }
34340         
34341         this.markInvalid();
34342         return false;
34343         
34344     },
34345     
34346     markValid : function()
34347     {
34348         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34349             this.indicatorEl().removeClass('visible');
34350             this.indicatorEl().addClass('invisible');
34351         }
34352         
34353         
34354         if (Roo.bootstrap.version == 3) {
34355             this.el.removeClass([this.invalidClass, this.validClass]);
34356             this.el.addClass(this.validClass);
34357         } else {
34358             this.el.removeClass(['is-invalid','is-valid']);
34359             this.el.addClass(['is-valid']);
34360         }
34361         this.fireEvent('valid', this);
34362     },
34363     
34364     markInvalid : function(msg)
34365     {
34366         if(this.allowBlank || this.disabled){
34367             return;
34368         }
34369         
34370         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34371             this.indicatorEl().removeClass('invisible');
34372             this.indicatorEl().addClass('visible');
34373         }
34374         if (Roo.bootstrap.version == 3) {
34375             this.el.removeClass([this.invalidClass, this.validClass]);
34376             this.el.addClass(this.invalidClass);
34377         } else {
34378             this.el.removeClass(['is-invalid','is-valid']);
34379             this.el.addClass(['is-invalid']);
34380         }
34381         
34382         this.fireEvent('invalid', this, msg);
34383         
34384     },
34385     
34386     setValue : function(v, suppressEvent)
34387     {   
34388         if(this.value === v){
34389             return;
34390         }
34391         
34392         this.value = v;
34393         
34394         if(this.rendered){
34395             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34396         }
34397         
34398         Roo.each(this.radioes, function(i){
34399             i.checked = false;
34400             i.el.removeClass('checked');
34401         });
34402         
34403         Roo.each(this.radioes, function(i){
34404             
34405             if(i.value === v || i.value.toString() === v.toString()){
34406                 i.checked = true;
34407                 i.el.addClass('checked');
34408                 
34409                 if(suppressEvent !== true){
34410                     this.fireEvent('check', this, i);
34411                 }
34412                 
34413                 return false;
34414             }
34415             
34416         }, this);
34417         
34418         this.validate();
34419     },
34420     
34421     clearInvalid : function(){
34422         
34423         if(!this.el || this.preventMark){
34424             return;
34425         }
34426         
34427         this.el.removeClass([this.invalidClass]);
34428         
34429         this.fireEvent('valid', this);
34430     }
34431     
34432 });
34433
34434 Roo.apply(Roo.bootstrap.RadioSet, {
34435     
34436     groups: {},
34437     
34438     register : function(set)
34439     {
34440         this.groups[set.name] = set;
34441     },
34442     
34443     get: function(name) 
34444     {
34445         if (typeof(this.groups[name]) == 'undefined') {
34446             return false;
34447         }
34448         
34449         return this.groups[name] ;
34450     }
34451     
34452 });
34453 /*
34454  * Based on:
34455  * Ext JS Library 1.1.1
34456  * Copyright(c) 2006-2007, Ext JS, LLC.
34457  *
34458  * Originally Released Under LGPL - original licence link has changed is not relivant.
34459  *
34460  * Fork - LGPL
34461  * <script type="text/javascript">
34462  */
34463
34464
34465 /**
34466  * @class Roo.bootstrap.SplitBar
34467  * @extends Roo.util.Observable
34468  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34469  * <br><br>
34470  * Usage:
34471  * <pre><code>
34472 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34473                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34474 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34475 split.minSize = 100;
34476 split.maxSize = 600;
34477 split.animate = true;
34478 split.on('moved', splitterMoved);
34479 </code></pre>
34480  * @constructor
34481  * Create a new SplitBar
34482  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34483  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34484  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34485  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34486                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34487                         position of the SplitBar).
34488  */
34489 Roo.bootstrap.SplitBar = function(cfg){
34490     
34491     /** @private */
34492     
34493     //{
34494     //  dragElement : elm
34495     //  resizingElement: el,
34496         // optional..
34497     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34498     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34499         // existingProxy ???
34500     //}
34501     
34502     this.el = Roo.get(cfg.dragElement, true);
34503     this.el.dom.unselectable = "on";
34504     /** @private */
34505     this.resizingEl = Roo.get(cfg.resizingElement, true);
34506
34507     /**
34508      * @private
34509      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34510      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34511      * @type Number
34512      */
34513     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34514     
34515     /**
34516      * The minimum size of the resizing element. (Defaults to 0)
34517      * @type Number
34518      */
34519     this.minSize = 0;
34520     
34521     /**
34522      * The maximum size of the resizing element. (Defaults to 2000)
34523      * @type Number
34524      */
34525     this.maxSize = 2000;
34526     
34527     /**
34528      * Whether to animate the transition to the new size
34529      * @type Boolean
34530      */
34531     this.animate = false;
34532     
34533     /**
34534      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34535      * @type Boolean
34536      */
34537     this.useShim = false;
34538     
34539     /** @private */
34540     this.shim = null;
34541     
34542     if(!cfg.existingProxy){
34543         /** @private */
34544         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34545     }else{
34546         this.proxy = Roo.get(cfg.existingProxy).dom;
34547     }
34548     /** @private */
34549     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34550     
34551     /** @private */
34552     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34553     
34554     /** @private */
34555     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34556     
34557     /** @private */
34558     this.dragSpecs = {};
34559     
34560     /**
34561      * @private The adapter to use to positon and resize elements
34562      */
34563     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34564     this.adapter.init(this);
34565     
34566     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34567         /** @private */
34568         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34569         this.el.addClass("roo-splitbar-h");
34570     }else{
34571         /** @private */
34572         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34573         this.el.addClass("roo-splitbar-v");
34574     }
34575     
34576     this.addEvents({
34577         /**
34578          * @event resize
34579          * Fires when the splitter is moved (alias for {@link #event-moved})
34580          * @param {Roo.bootstrap.SplitBar} this
34581          * @param {Number} newSize the new width or height
34582          */
34583         "resize" : true,
34584         /**
34585          * @event moved
34586          * Fires when the splitter is moved
34587          * @param {Roo.bootstrap.SplitBar} this
34588          * @param {Number} newSize the new width or height
34589          */
34590         "moved" : true,
34591         /**
34592          * @event beforeresize
34593          * Fires before the splitter is dragged
34594          * @param {Roo.bootstrap.SplitBar} this
34595          */
34596         "beforeresize" : true,
34597
34598         "beforeapply" : true
34599     });
34600
34601     Roo.util.Observable.call(this);
34602 };
34603
34604 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34605     onStartProxyDrag : function(x, y){
34606         this.fireEvent("beforeresize", this);
34607         if(!this.overlay){
34608             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34609             o.unselectable();
34610             o.enableDisplayMode("block");
34611             // all splitbars share the same overlay
34612             Roo.bootstrap.SplitBar.prototype.overlay = o;
34613         }
34614         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34615         this.overlay.show();
34616         Roo.get(this.proxy).setDisplayed("block");
34617         var size = this.adapter.getElementSize(this);
34618         this.activeMinSize = this.getMinimumSize();;
34619         this.activeMaxSize = this.getMaximumSize();;
34620         var c1 = size - this.activeMinSize;
34621         var c2 = Math.max(this.activeMaxSize - size, 0);
34622         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34623             this.dd.resetConstraints();
34624             this.dd.setXConstraint(
34625                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34626                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34627             );
34628             this.dd.setYConstraint(0, 0);
34629         }else{
34630             this.dd.resetConstraints();
34631             this.dd.setXConstraint(0, 0);
34632             this.dd.setYConstraint(
34633                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34634                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34635             );
34636          }
34637         this.dragSpecs.startSize = size;
34638         this.dragSpecs.startPoint = [x, y];
34639         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34640     },
34641     
34642     /** 
34643      * @private Called after the drag operation by the DDProxy
34644      */
34645     onEndProxyDrag : function(e){
34646         Roo.get(this.proxy).setDisplayed(false);
34647         var endPoint = Roo.lib.Event.getXY(e);
34648         if(this.overlay){
34649             this.overlay.hide();
34650         }
34651         var newSize;
34652         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34653             newSize = this.dragSpecs.startSize + 
34654                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34655                     endPoint[0] - this.dragSpecs.startPoint[0] :
34656                     this.dragSpecs.startPoint[0] - endPoint[0]
34657                 );
34658         }else{
34659             newSize = this.dragSpecs.startSize + 
34660                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34661                     endPoint[1] - this.dragSpecs.startPoint[1] :
34662                     this.dragSpecs.startPoint[1] - endPoint[1]
34663                 );
34664         }
34665         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34666         if(newSize != this.dragSpecs.startSize){
34667             if(this.fireEvent('beforeapply', this, newSize) !== false){
34668                 this.adapter.setElementSize(this, newSize);
34669                 this.fireEvent("moved", this, newSize);
34670                 this.fireEvent("resize", this, newSize);
34671             }
34672         }
34673     },
34674     
34675     /**
34676      * Get the adapter this SplitBar uses
34677      * @return The adapter object
34678      */
34679     getAdapter : function(){
34680         return this.adapter;
34681     },
34682     
34683     /**
34684      * Set the adapter this SplitBar uses
34685      * @param {Object} adapter A SplitBar adapter object
34686      */
34687     setAdapter : function(adapter){
34688         this.adapter = adapter;
34689         this.adapter.init(this);
34690     },
34691     
34692     /**
34693      * Gets the minimum size for the resizing element
34694      * @return {Number} The minimum size
34695      */
34696     getMinimumSize : function(){
34697         return this.minSize;
34698     },
34699     
34700     /**
34701      * Sets the minimum size for the resizing element
34702      * @param {Number} minSize The minimum size
34703      */
34704     setMinimumSize : function(minSize){
34705         this.minSize = minSize;
34706     },
34707     
34708     /**
34709      * Gets the maximum size for the resizing element
34710      * @return {Number} The maximum size
34711      */
34712     getMaximumSize : function(){
34713         return this.maxSize;
34714     },
34715     
34716     /**
34717      * Sets the maximum size for the resizing element
34718      * @param {Number} maxSize The maximum size
34719      */
34720     setMaximumSize : function(maxSize){
34721         this.maxSize = maxSize;
34722     },
34723     
34724     /**
34725      * Sets the initialize size for the resizing element
34726      * @param {Number} size The initial size
34727      */
34728     setCurrentSize : function(size){
34729         var oldAnimate = this.animate;
34730         this.animate = false;
34731         this.adapter.setElementSize(this, size);
34732         this.animate = oldAnimate;
34733     },
34734     
34735     /**
34736      * Destroy this splitbar. 
34737      * @param {Boolean} removeEl True to remove the element
34738      */
34739     destroy : function(removeEl){
34740         if(this.shim){
34741             this.shim.remove();
34742         }
34743         this.dd.unreg();
34744         this.proxy.parentNode.removeChild(this.proxy);
34745         if(removeEl){
34746             this.el.remove();
34747         }
34748     }
34749 });
34750
34751 /**
34752  * @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.
34753  */
34754 Roo.bootstrap.SplitBar.createProxy = function(dir){
34755     var proxy = new Roo.Element(document.createElement("div"));
34756     proxy.unselectable();
34757     var cls = 'roo-splitbar-proxy';
34758     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34759     document.body.appendChild(proxy.dom);
34760     return proxy.dom;
34761 };
34762
34763 /** 
34764  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34765  * Default Adapter. It assumes the splitter and resizing element are not positioned
34766  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34767  */
34768 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34769 };
34770
34771 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34772     // do nothing for now
34773     init : function(s){
34774     
34775     },
34776     /**
34777      * Called before drag operations to get the current size of the resizing element. 
34778      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34779      */
34780      getElementSize : function(s){
34781         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34782             return s.resizingEl.getWidth();
34783         }else{
34784             return s.resizingEl.getHeight();
34785         }
34786     },
34787     
34788     /**
34789      * Called after drag operations to set the size of the resizing element.
34790      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34791      * @param {Number} newSize The new size to set
34792      * @param {Function} onComplete A function to be invoked when resizing is complete
34793      */
34794     setElementSize : function(s, newSize, onComplete){
34795         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34796             if(!s.animate){
34797                 s.resizingEl.setWidth(newSize);
34798                 if(onComplete){
34799                     onComplete(s, newSize);
34800                 }
34801             }else{
34802                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34803             }
34804         }else{
34805             
34806             if(!s.animate){
34807                 s.resizingEl.setHeight(newSize);
34808                 if(onComplete){
34809                     onComplete(s, newSize);
34810                 }
34811             }else{
34812                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34813             }
34814         }
34815     }
34816 };
34817
34818 /** 
34819  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34820  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34821  * Adapter that  moves the splitter element to align with the resized sizing element. 
34822  * Used with an absolute positioned SplitBar.
34823  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34824  * document.body, make sure you assign an id to the body element.
34825  */
34826 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34827     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34828     this.container = Roo.get(container);
34829 };
34830
34831 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34832     init : function(s){
34833         this.basic.init(s);
34834     },
34835     
34836     getElementSize : function(s){
34837         return this.basic.getElementSize(s);
34838     },
34839     
34840     setElementSize : function(s, newSize, onComplete){
34841         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34842     },
34843     
34844     moveSplitter : function(s){
34845         var yes = Roo.bootstrap.SplitBar;
34846         switch(s.placement){
34847             case yes.LEFT:
34848                 s.el.setX(s.resizingEl.getRight());
34849                 break;
34850             case yes.RIGHT:
34851                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34852                 break;
34853             case yes.TOP:
34854                 s.el.setY(s.resizingEl.getBottom());
34855                 break;
34856             case yes.BOTTOM:
34857                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34858                 break;
34859         }
34860     }
34861 };
34862
34863 /**
34864  * Orientation constant - Create a vertical SplitBar
34865  * @static
34866  * @type Number
34867  */
34868 Roo.bootstrap.SplitBar.VERTICAL = 1;
34869
34870 /**
34871  * Orientation constant - Create a horizontal SplitBar
34872  * @static
34873  * @type Number
34874  */
34875 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34876
34877 /**
34878  * Placement constant - The resizing element is to the left of the splitter element
34879  * @static
34880  * @type Number
34881  */
34882 Roo.bootstrap.SplitBar.LEFT = 1;
34883
34884 /**
34885  * Placement constant - The resizing element is to the right of the splitter element
34886  * @static
34887  * @type Number
34888  */
34889 Roo.bootstrap.SplitBar.RIGHT = 2;
34890
34891 /**
34892  * Placement constant - The resizing element is positioned above the splitter element
34893  * @static
34894  * @type Number
34895  */
34896 Roo.bootstrap.SplitBar.TOP = 3;
34897
34898 /**
34899  * Placement constant - The resizing element is positioned under splitter element
34900  * @static
34901  * @type Number
34902  */
34903 Roo.bootstrap.SplitBar.BOTTOM = 4;
34904 Roo.namespace("Roo.bootstrap.layout");/*
34905  * Based on:
34906  * Ext JS Library 1.1.1
34907  * Copyright(c) 2006-2007, Ext JS, LLC.
34908  *
34909  * Originally Released Under LGPL - original licence link has changed is not relivant.
34910  *
34911  * Fork - LGPL
34912  * <script type="text/javascript">
34913  */
34914
34915 /**
34916  * @class Roo.bootstrap.layout.Manager
34917  * @extends Roo.bootstrap.Component
34918  * Base class for layout managers.
34919  */
34920 Roo.bootstrap.layout.Manager = function(config)
34921 {
34922     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34923
34924
34925
34926
34927
34928     /** false to disable window resize monitoring @type Boolean */
34929     this.monitorWindowResize = true;
34930     this.regions = {};
34931     this.addEvents({
34932         /**
34933          * @event layout
34934          * Fires when a layout is performed.
34935          * @param {Roo.LayoutManager} this
34936          */
34937         "layout" : true,
34938         /**
34939          * @event regionresized
34940          * Fires when the user resizes a region.
34941          * @param {Roo.LayoutRegion} region The resized region
34942          * @param {Number} newSize The new size (width for east/west, height for north/south)
34943          */
34944         "regionresized" : true,
34945         /**
34946          * @event regioncollapsed
34947          * Fires when a region is collapsed.
34948          * @param {Roo.LayoutRegion} region The collapsed region
34949          */
34950         "regioncollapsed" : true,
34951         /**
34952          * @event regionexpanded
34953          * Fires when a region is expanded.
34954          * @param {Roo.LayoutRegion} region The expanded region
34955          */
34956         "regionexpanded" : true
34957     });
34958     this.updating = false;
34959
34960     if (config.el) {
34961         this.el = Roo.get(config.el);
34962         this.initEvents();
34963     }
34964
34965 };
34966
34967 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34968
34969
34970     regions : null,
34971
34972     monitorWindowResize : true,
34973
34974
34975     updating : false,
34976
34977
34978     onRender : function(ct, position)
34979     {
34980         if(!this.el){
34981             this.el = Roo.get(ct);
34982             this.initEvents();
34983         }
34984         //this.fireEvent('render',this);
34985     },
34986
34987
34988     initEvents: function()
34989     {
34990
34991
34992         // ie scrollbar fix
34993         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34994             document.body.scroll = "no";
34995         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34996             this.el.position('relative');
34997         }
34998         this.id = this.el.id;
34999         this.el.addClass("roo-layout-container");
35000         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35001         if(this.el.dom != document.body ) {
35002             this.el.on('resize', this.layout,this);
35003             this.el.on('show', this.layout,this);
35004         }
35005
35006     },
35007
35008     /**
35009      * Returns true if this layout is currently being updated
35010      * @return {Boolean}
35011      */
35012     isUpdating : function(){
35013         return this.updating;
35014     },
35015
35016     /**
35017      * Suspend the LayoutManager from doing auto-layouts while
35018      * making multiple add or remove calls
35019      */
35020     beginUpdate : function(){
35021         this.updating = true;
35022     },
35023
35024     /**
35025      * Restore auto-layouts and optionally disable the manager from performing a layout
35026      * @param {Boolean} noLayout true to disable a layout update
35027      */
35028     endUpdate : function(noLayout){
35029         this.updating = false;
35030         if(!noLayout){
35031             this.layout();
35032         }
35033     },
35034
35035     layout: function(){
35036         // abstract...
35037     },
35038
35039     onRegionResized : function(region, newSize){
35040         this.fireEvent("regionresized", region, newSize);
35041         this.layout();
35042     },
35043
35044     onRegionCollapsed : function(region){
35045         this.fireEvent("regioncollapsed", region);
35046     },
35047
35048     onRegionExpanded : function(region){
35049         this.fireEvent("regionexpanded", region);
35050     },
35051
35052     /**
35053      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35054      * performs box-model adjustments.
35055      * @return {Object} The size as an object {width: (the width), height: (the height)}
35056      */
35057     getViewSize : function()
35058     {
35059         var size;
35060         if(this.el.dom != document.body){
35061             size = this.el.getSize();
35062         }else{
35063             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35064         }
35065         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35066         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35067         return size;
35068     },
35069
35070     /**
35071      * Returns the Element this layout is bound to.
35072      * @return {Roo.Element}
35073      */
35074     getEl : function(){
35075         return this.el;
35076     },
35077
35078     /**
35079      * Returns the specified region.
35080      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35081      * @return {Roo.LayoutRegion}
35082      */
35083     getRegion : function(target){
35084         return this.regions[target.toLowerCase()];
35085     },
35086
35087     onWindowResize : function(){
35088         if(this.monitorWindowResize){
35089             this.layout();
35090         }
35091     }
35092 });
35093 /*
35094  * Based on:
35095  * Ext JS Library 1.1.1
35096  * Copyright(c) 2006-2007, Ext JS, LLC.
35097  *
35098  * Originally Released Under LGPL - original licence link has changed is not relivant.
35099  *
35100  * Fork - LGPL
35101  * <script type="text/javascript">
35102  */
35103 /**
35104  * @class Roo.bootstrap.layout.Border
35105  * @extends Roo.bootstrap.layout.Manager
35106  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35107  * please see: examples/bootstrap/nested.html<br><br>
35108  
35109 <b>The container the layout is rendered into can be either the body element or any other element.
35110 If it is not the body element, the container needs to either be an absolute positioned element,
35111 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35112 the container size if it is not the body element.</b>
35113
35114 * @constructor
35115 * Create a new Border
35116 * @param {Object} config Configuration options
35117  */
35118 Roo.bootstrap.layout.Border = function(config){
35119     config = config || {};
35120     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35121     
35122     
35123     
35124     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35125         if(config[region]){
35126             config[region].region = region;
35127             this.addRegion(config[region]);
35128         }
35129     },this);
35130     
35131 };
35132
35133 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35134
35135 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35136     
35137     parent : false, // this might point to a 'nest' or a ???
35138     
35139     /**
35140      * Creates and adds a new region if it doesn't already exist.
35141      * @param {String} target The target region key (north, south, east, west or center).
35142      * @param {Object} config The regions config object
35143      * @return {BorderLayoutRegion} The new region
35144      */
35145     addRegion : function(config)
35146     {
35147         if(!this.regions[config.region]){
35148             var r = this.factory(config);
35149             this.bindRegion(r);
35150         }
35151         return this.regions[config.region];
35152     },
35153
35154     // private (kinda)
35155     bindRegion : function(r){
35156         this.regions[r.config.region] = r;
35157         
35158         r.on("visibilitychange",    this.layout, this);
35159         r.on("paneladded",          this.layout, this);
35160         r.on("panelremoved",        this.layout, this);
35161         r.on("invalidated",         this.layout, this);
35162         r.on("resized",             this.onRegionResized, this);
35163         r.on("collapsed",           this.onRegionCollapsed, this);
35164         r.on("expanded",            this.onRegionExpanded, this);
35165     },
35166
35167     /**
35168      * Performs a layout update.
35169      */
35170     layout : function()
35171     {
35172         if(this.updating) {
35173             return;
35174         }
35175         
35176         // render all the rebions if they have not been done alreayd?
35177         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35178             if(this.regions[region] && !this.regions[region].bodyEl){
35179                 this.regions[region].onRender(this.el)
35180             }
35181         },this);
35182         
35183         var size = this.getViewSize();
35184         var w = size.width;
35185         var h = size.height;
35186         var centerW = w;
35187         var centerH = h;
35188         var centerY = 0;
35189         var centerX = 0;
35190         //var x = 0, y = 0;
35191
35192         var rs = this.regions;
35193         var north = rs["north"];
35194         var south = rs["south"]; 
35195         var west = rs["west"];
35196         var east = rs["east"];
35197         var center = rs["center"];
35198         //if(this.hideOnLayout){ // not supported anymore
35199             //c.el.setStyle("display", "none");
35200         //}
35201         if(north && north.isVisible()){
35202             var b = north.getBox();
35203             var m = north.getMargins();
35204             b.width = w - (m.left+m.right);
35205             b.x = m.left;
35206             b.y = m.top;
35207             centerY = b.height + b.y + m.bottom;
35208             centerH -= centerY;
35209             north.updateBox(this.safeBox(b));
35210         }
35211         if(south && south.isVisible()){
35212             var b = south.getBox();
35213             var m = south.getMargins();
35214             b.width = w - (m.left+m.right);
35215             b.x = m.left;
35216             var totalHeight = (b.height + m.top + m.bottom);
35217             b.y = h - totalHeight + m.top;
35218             centerH -= totalHeight;
35219             south.updateBox(this.safeBox(b));
35220         }
35221         if(west && west.isVisible()){
35222             var b = west.getBox();
35223             var m = west.getMargins();
35224             b.height = centerH - (m.top+m.bottom);
35225             b.x = m.left;
35226             b.y = centerY + m.top;
35227             var totalWidth = (b.width + m.left + m.right);
35228             centerX += totalWidth;
35229             centerW -= totalWidth;
35230             west.updateBox(this.safeBox(b));
35231         }
35232         if(east && east.isVisible()){
35233             var b = east.getBox();
35234             var m = east.getMargins();
35235             b.height = centerH - (m.top+m.bottom);
35236             var totalWidth = (b.width + m.left + m.right);
35237             b.x = w - totalWidth + m.left;
35238             b.y = centerY + m.top;
35239             centerW -= totalWidth;
35240             east.updateBox(this.safeBox(b));
35241         }
35242         if(center){
35243             var m = center.getMargins();
35244             var centerBox = {
35245                 x: centerX + m.left,
35246                 y: centerY + m.top,
35247                 width: centerW - (m.left+m.right),
35248                 height: centerH - (m.top+m.bottom)
35249             };
35250             //if(this.hideOnLayout){
35251                 //center.el.setStyle("display", "block");
35252             //}
35253             center.updateBox(this.safeBox(centerBox));
35254         }
35255         this.el.repaint();
35256         this.fireEvent("layout", this);
35257     },
35258
35259     // private
35260     safeBox : function(box){
35261         box.width = Math.max(0, box.width);
35262         box.height = Math.max(0, box.height);
35263         return box;
35264     },
35265
35266     /**
35267      * Adds a ContentPanel (or subclass) to this layout.
35268      * @param {String} target The target region key (north, south, east, west or center).
35269      * @param {Roo.ContentPanel} panel The panel to add
35270      * @return {Roo.ContentPanel} The added panel
35271      */
35272     add : function(target, panel){
35273          
35274         target = target.toLowerCase();
35275         return this.regions[target].add(panel);
35276     },
35277
35278     /**
35279      * Remove a ContentPanel (or subclass) to this layout.
35280      * @param {String} target The target region key (north, south, east, west or center).
35281      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35282      * @return {Roo.ContentPanel} The removed panel
35283      */
35284     remove : function(target, panel){
35285         target = target.toLowerCase();
35286         return this.regions[target].remove(panel);
35287     },
35288
35289     /**
35290      * Searches all regions for a panel with the specified id
35291      * @param {String} panelId
35292      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35293      */
35294     findPanel : function(panelId){
35295         var rs = this.regions;
35296         for(var target in rs){
35297             if(typeof rs[target] != "function"){
35298                 var p = rs[target].getPanel(panelId);
35299                 if(p){
35300                     return p;
35301                 }
35302             }
35303         }
35304         return null;
35305     },
35306
35307     /**
35308      * Searches all regions for a panel with the specified id and activates (shows) it.
35309      * @param {String/ContentPanel} panelId The panels id or the panel itself
35310      * @return {Roo.ContentPanel} The shown panel or null
35311      */
35312     showPanel : function(panelId) {
35313       var rs = this.regions;
35314       for(var target in rs){
35315          var r = rs[target];
35316          if(typeof r != "function"){
35317             if(r.hasPanel(panelId)){
35318                return r.showPanel(panelId);
35319             }
35320          }
35321       }
35322       return null;
35323    },
35324
35325    /**
35326      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35327      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35328      */
35329    /*
35330     restoreState : function(provider){
35331         if(!provider){
35332             provider = Roo.state.Manager;
35333         }
35334         var sm = new Roo.LayoutStateManager();
35335         sm.init(this, provider);
35336     },
35337 */
35338  
35339  
35340     /**
35341      * Adds a xtype elements to the layout.
35342      * <pre><code>
35343
35344 layout.addxtype({
35345        xtype : 'ContentPanel',
35346        region: 'west',
35347        items: [ .... ]
35348    }
35349 );
35350
35351 layout.addxtype({
35352         xtype : 'NestedLayoutPanel',
35353         region: 'west',
35354         layout: {
35355            center: { },
35356            west: { }   
35357         },
35358         items : [ ... list of content panels or nested layout panels.. ]
35359    }
35360 );
35361 </code></pre>
35362      * @param {Object} cfg Xtype definition of item to add.
35363      */
35364     addxtype : function(cfg)
35365     {
35366         // basically accepts a pannel...
35367         // can accept a layout region..!?!?
35368         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35369         
35370         
35371         // theory?  children can only be panels??
35372         
35373         //if (!cfg.xtype.match(/Panel$/)) {
35374         //    return false;
35375         //}
35376         var ret = false;
35377         
35378         if (typeof(cfg.region) == 'undefined') {
35379             Roo.log("Failed to add Panel, region was not set");
35380             Roo.log(cfg);
35381             return false;
35382         }
35383         var region = cfg.region;
35384         delete cfg.region;
35385         
35386           
35387         var xitems = [];
35388         if (cfg.items) {
35389             xitems = cfg.items;
35390             delete cfg.items;
35391         }
35392         var nb = false;
35393         
35394         if ( region == 'center') {
35395             Roo.log("Center: " + cfg.title);
35396         }
35397         
35398         
35399         switch(cfg.xtype) 
35400         {
35401             case 'Content':  // ContentPanel (el, cfg)
35402             case 'Scroll':  // ContentPanel (el, cfg)
35403             case 'View': 
35404                 cfg.autoCreate = cfg.autoCreate || true;
35405                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35406                 //} else {
35407                 //    var el = this.el.createChild();
35408                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35409                 //}
35410                 
35411                 this.add(region, ret);
35412                 break;
35413             
35414             /*
35415             case 'TreePanel': // our new panel!
35416                 cfg.el = this.el.createChild();
35417                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35418                 this.add(region, ret);
35419                 break;
35420             */
35421             
35422             case 'Nest': 
35423                 // create a new Layout (which is  a Border Layout...
35424                 
35425                 var clayout = cfg.layout;
35426                 clayout.el  = this.el.createChild();
35427                 clayout.items   = clayout.items  || [];
35428                 
35429                 delete cfg.layout;
35430                 
35431                 // replace this exitems with the clayout ones..
35432                 xitems = clayout.items;
35433                  
35434                 // force background off if it's in center...
35435                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35436                     cfg.background = false;
35437                 }
35438                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35439                 
35440                 
35441                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35442                 //console.log('adding nested layout panel '  + cfg.toSource());
35443                 this.add(region, ret);
35444                 nb = {}; /// find first...
35445                 break;
35446             
35447             case 'Grid':
35448                 
35449                 // needs grid and region
35450                 
35451                 //var el = this.getRegion(region).el.createChild();
35452                 /*
35453                  *var el = this.el.createChild();
35454                 // create the grid first...
35455                 cfg.grid.container = el;
35456                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35457                 */
35458                 
35459                 if (region == 'center' && this.active ) {
35460                     cfg.background = false;
35461                 }
35462                 
35463                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35464                 
35465                 this.add(region, ret);
35466                 /*
35467                 if (cfg.background) {
35468                     // render grid on panel activation (if panel background)
35469                     ret.on('activate', function(gp) {
35470                         if (!gp.grid.rendered) {
35471                     //        gp.grid.render(el);
35472                         }
35473                     });
35474                 } else {
35475                   //  cfg.grid.render(el);
35476                 }
35477                 */
35478                 break;
35479            
35480            
35481             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35482                 // it was the old xcomponent building that caused this before.
35483                 // espeically if border is the top element in the tree.
35484                 ret = this;
35485                 break; 
35486                 
35487                     
35488                 
35489                 
35490                 
35491             default:
35492                 /*
35493                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35494                     
35495                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35496                     this.add(region, ret);
35497                 } else {
35498                 */
35499                     Roo.log(cfg);
35500                     throw "Can not add '" + cfg.xtype + "' to Border";
35501                     return null;
35502              
35503                                 
35504              
35505         }
35506         this.beginUpdate();
35507         // add children..
35508         var region = '';
35509         var abn = {};
35510         Roo.each(xitems, function(i)  {
35511             region = nb && i.region ? i.region : false;
35512             
35513             var add = ret.addxtype(i);
35514            
35515             if (region) {
35516                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35517                 if (!i.background) {
35518                     abn[region] = nb[region] ;
35519                 }
35520             }
35521             
35522         });
35523         this.endUpdate();
35524
35525         // make the last non-background panel active..
35526         //if (nb) { Roo.log(abn); }
35527         if (nb) {
35528             
35529             for(var r in abn) {
35530                 region = this.getRegion(r);
35531                 if (region) {
35532                     // tried using nb[r], but it does not work..
35533                      
35534                     region.showPanel(abn[r]);
35535                    
35536                 }
35537             }
35538         }
35539         return ret;
35540         
35541     },
35542     
35543     
35544 // private
35545     factory : function(cfg)
35546     {
35547         
35548         var validRegions = Roo.bootstrap.layout.Border.regions;
35549
35550         var target = cfg.region;
35551         cfg.mgr = this;
35552         
35553         var r = Roo.bootstrap.layout;
35554         Roo.log(target);
35555         switch(target){
35556             case "north":
35557                 return new r.North(cfg);
35558             case "south":
35559                 return new r.South(cfg);
35560             case "east":
35561                 return new r.East(cfg);
35562             case "west":
35563                 return new r.West(cfg);
35564             case "center":
35565                 return new r.Center(cfg);
35566         }
35567         throw 'Layout region "'+target+'" not supported.';
35568     }
35569     
35570     
35571 });
35572  /*
35573  * Based on:
35574  * Ext JS Library 1.1.1
35575  * Copyright(c) 2006-2007, Ext JS, LLC.
35576  *
35577  * Originally Released Under LGPL - original licence link has changed is not relivant.
35578  *
35579  * Fork - LGPL
35580  * <script type="text/javascript">
35581  */
35582  
35583 /**
35584  * @class Roo.bootstrap.layout.Basic
35585  * @extends Roo.util.Observable
35586  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35587  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35588  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35589  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35590  * @cfg {string}   region  the region that it inhabits..
35591  * @cfg {bool}   skipConfig skip config?
35592  * 
35593
35594  */
35595 Roo.bootstrap.layout.Basic = function(config){
35596     
35597     this.mgr = config.mgr;
35598     
35599     this.position = config.region;
35600     
35601     var skipConfig = config.skipConfig;
35602     
35603     this.events = {
35604         /**
35605          * @scope Roo.BasicLayoutRegion
35606          */
35607         
35608         /**
35609          * @event beforeremove
35610          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35611          * @param {Roo.LayoutRegion} this
35612          * @param {Roo.ContentPanel} panel The panel
35613          * @param {Object} e The cancel event object
35614          */
35615         "beforeremove" : true,
35616         /**
35617          * @event invalidated
35618          * Fires when the layout for this region is changed.
35619          * @param {Roo.LayoutRegion} this
35620          */
35621         "invalidated" : true,
35622         /**
35623          * @event visibilitychange
35624          * Fires when this region is shown or hidden 
35625          * @param {Roo.LayoutRegion} this
35626          * @param {Boolean} visibility true or false
35627          */
35628         "visibilitychange" : true,
35629         /**
35630          * @event paneladded
35631          * Fires when a panel is added. 
35632          * @param {Roo.LayoutRegion} this
35633          * @param {Roo.ContentPanel} panel The panel
35634          */
35635         "paneladded" : true,
35636         /**
35637          * @event panelremoved
35638          * Fires when a panel is removed. 
35639          * @param {Roo.LayoutRegion} this
35640          * @param {Roo.ContentPanel} panel The panel
35641          */
35642         "panelremoved" : true,
35643         /**
35644          * @event beforecollapse
35645          * Fires when this region before collapse.
35646          * @param {Roo.LayoutRegion} this
35647          */
35648         "beforecollapse" : true,
35649         /**
35650          * @event collapsed
35651          * Fires when this region is collapsed.
35652          * @param {Roo.LayoutRegion} this
35653          */
35654         "collapsed" : true,
35655         /**
35656          * @event expanded
35657          * Fires when this region is expanded.
35658          * @param {Roo.LayoutRegion} this
35659          */
35660         "expanded" : true,
35661         /**
35662          * @event slideshow
35663          * Fires when this region is slid into view.
35664          * @param {Roo.LayoutRegion} this
35665          */
35666         "slideshow" : true,
35667         /**
35668          * @event slidehide
35669          * Fires when this region slides out of view. 
35670          * @param {Roo.LayoutRegion} this
35671          */
35672         "slidehide" : true,
35673         /**
35674          * @event panelactivated
35675          * Fires when a panel is activated. 
35676          * @param {Roo.LayoutRegion} this
35677          * @param {Roo.ContentPanel} panel The activated panel
35678          */
35679         "panelactivated" : true,
35680         /**
35681          * @event resized
35682          * Fires when the user resizes this region. 
35683          * @param {Roo.LayoutRegion} this
35684          * @param {Number} newSize The new size (width for east/west, height for north/south)
35685          */
35686         "resized" : true
35687     };
35688     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35689     this.panels = new Roo.util.MixedCollection();
35690     this.panels.getKey = this.getPanelId.createDelegate(this);
35691     this.box = null;
35692     this.activePanel = null;
35693     // ensure listeners are added...
35694     
35695     if (config.listeners || config.events) {
35696         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35697             listeners : config.listeners || {},
35698             events : config.events || {}
35699         });
35700     }
35701     
35702     if(skipConfig !== true){
35703         this.applyConfig(config);
35704     }
35705 };
35706
35707 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35708 {
35709     getPanelId : function(p){
35710         return p.getId();
35711     },
35712     
35713     applyConfig : function(config){
35714         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35715         this.config = config;
35716         
35717     },
35718     
35719     /**
35720      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35721      * the width, for horizontal (north, south) the height.
35722      * @param {Number} newSize The new width or height
35723      */
35724     resizeTo : function(newSize){
35725         var el = this.el ? this.el :
35726                  (this.activePanel ? this.activePanel.getEl() : null);
35727         if(el){
35728             switch(this.position){
35729                 case "east":
35730                 case "west":
35731                     el.setWidth(newSize);
35732                     this.fireEvent("resized", this, newSize);
35733                 break;
35734                 case "north":
35735                 case "south":
35736                     el.setHeight(newSize);
35737                     this.fireEvent("resized", this, newSize);
35738                 break;                
35739             }
35740         }
35741     },
35742     
35743     getBox : function(){
35744         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35745     },
35746     
35747     getMargins : function(){
35748         return this.margins;
35749     },
35750     
35751     updateBox : function(box){
35752         this.box = box;
35753         var el = this.activePanel.getEl();
35754         el.dom.style.left = box.x + "px";
35755         el.dom.style.top = box.y + "px";
35756         this.activePanel.setSize(box.width, box.height);
35757     },
35758     
35759     /**
35760      * Returns the container element for this region.
35761      * @return {Roo.Element}
35762      */
35763     getEl : function(){
35764         return this.activePanel;
35765     },
35766     
35767     /**
35768      * Returns true if this region is currently visible.
35769      * @return {Boolean}
35770      */
35771     isVisible : function(){
35772         return this.activePanel ? true : false;
35773     },
35774     
35775     setActivePanel : function(panel){
35776         panel = this.getPanel(panel);
35777         if(this.activePanel && this.activePanel != panel){
35778             this.activePanel.setActiveState(false);
35779             this.activePanel.getEl().setLeftTop(-10000,-10000);
35780         }
35781         this.activePanel = panel;
35782         panel.setActiveState(true);
35783         if(this.box){
35784             panel.setSize(this.box.width, this.box.height);
35785         }
35786         this.fireEvent("panelactivated", this, panel);
35787         this.fireEvent("invalidated");
35788     },
35789     
35790     /**
35791      * Show the specified panel.
35792      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35793      * @return {Roo.ContentPanel} The shown panel or null
35794      */
35795     showPanel : function(panel){
35796         panel = this.getPanel(panel);
35797         if(panel){
35798             this.setActivePanel(panel);
35799         }
35800         return panel;
35801     },
35802     
35803     /**
35804      * Get the active panel for this region.
35805      * @return {Roo.ContentPanel} The active panel or null
35806      */
35807     getActivePanel : function(){
35808         return this.activePanel;
35809     },
35810     
35811     /**
35812      * Add the passed ContentPanel(s)
35813      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35814      * @return {Roo.ContentPanel} The panel added (if only one was added)
35815      */
35816     add : function(panel){
35817         if(arguments.length > 1){
35818             for(var i = 0, len = arguments.length; i < len; i++) {
35819                 this.add(arguments[i]);
35820             }
35821             return null;
35822         }
35823         if(this.hasPanel(panel)){
35824             this.showPanel(panel);
35825             return panel;
35826         }
35827         var el = panel.getEl();
35828         if(el.dom.parentNode != this.mgr.el.dom){
35829             this.mgr.el.dom.appendChild(el.dom);
35830         }
35831         if(panel.setRegion){
35832             panel.setRegion(this);
35833         }
35834         this.panels.add(panel);
35835         el.setStyle("position", "absolute");
35836         if(!panel.background){
35837             this.setActivePanel(panel);
35838             if(this.config.initialSize && this.panels.getCount()==1){
35839                 this.resizeTo(this.config.initialSize);
35840             }
35841         }
35842         this.fireEvent("paneladded", this, panel);
35843         return panel;
35844     },
35845     
35846     /**
35847      * Returns true if the panel is in this region.
35848      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35849      * @return {Boolean}
35850      */
35851     hasPanel : function(panel){
35852         if(typeof panel == "object"){ // must be panel obj
35853             panel = panel.getId();
35854         }
35855         return this.getPanel(panel) ? true : false;
35856     },
35857     
35858     /**
35859      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35860      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35861      * @param {Boolean} preservePanel Overrides the config preservePanel option
35862      * @return {Roo.ContentPanel} The panel that was removed
35863      */
35864     remove : function(panel, preservePanel){
35865         panel = this.getPanel(panel);
35866         if(!panel){
35867             return null;
35868         }
35869         var e = {};
35870         this.fireEvent("beforeremove", this, panel, e);
35871         if(e.cancel === true){
35872             return null;
35873         }
35874         var panelId = panel.getId();
35875         this.panels.removeKey(panelId);
35876         return panel;
35877     },
35878     
35879     /**
35880      * Returns the panel specified or null if it's not in this region.
35881      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35882      * @return {Roo.ContentPanel}
35883      */
35884     getPanel : function(id){
35885         if(typeof id == "object"){ // must be panel obj
35886             return id;
35887         }
35888         return this.panels.get(id);
35889     },
35890     
35891     /**
35892      * Returns this regions position (north/south/east/west/center).
35893      * @return {String} 
35894      */
35895     getPosition: function(){
35896         return this.position;    
35897     }
35898 });/*
35899  * Based on:
35900  * Ext JS Library 1.1.1
35901  * Copyright(c) 2006-2007, Ext JS, LLC.
35902  *
35903  * Originally Released Under LGPL - original licence link has changed is not relivant.
35904  *
35905  * Fork - LGPL
35906  * <script type="text/javascript">
35907  */
35908  
35909 /**
35910  * @class Roo.bootstrap.layout.Region
35911  * @extends Roo.bootstrap.layout.Basic
35912  * This class represents a region in a layout manager.
35913  
35914  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35915  * @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})
35916  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35917  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35918  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35919  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35920  * @cfg {String}    title           The title for the region (overrides panel titles)
35921  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35922  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35923  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35924  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35925  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35926  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35927  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35928  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35929  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35930  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35931
35932  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35933  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35934  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35935  * @cfg {Number}    width           For East/West panels
35936  * @cfg {Number}    height          For North/South panels
35937  * @cfg {Boolean}   split           To show the splitter
35938  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35939  * 
35940  * @cfg {string}   cls             Extra CSS classes to add to region
35941  * 
35942  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35943  * @cfg {string}   region  the region that it inhabits..
35944  *
35945
35946  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35947  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35948
35949  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35950  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35951  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35952  */
35953 Roo.bootstrap.layout.Region = function(config)
35954 {
35955     this.applyConfig(config);
35956
35957     var mgr = config.mgr;
35958     var pos = config.region;
35959     config.skipConfig = true;
35960     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35961     
35962     if (mgr.el) {
35963         this.onRender(mgr.el);   
35964     }
35965      
35966     this.visible = true;
35967     this.collapsed = false;
35968     this.unrendered_panels = [];
35969 };
35970
35971 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35972
35973     position: '', // set by wrapper (eg. north/south etc..)
35974     unrendered_panels : null,  // unrendered panels.
35975     
35976     tabPosition : false,
35977     
35978     mgr: false, // points to 'Border'
35979     
35980     
35981     createBody : function(){
35982         /** This region's body element 
35983         * @type Roo.Element */
35984         this.bodyEl = this.el.createChild({
35985                 tag: "div",
35986                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35987         });
35988     },
35989
35990     onRender: function(ctr, pos)
35991     {
35992         var dh = Roo.DomHelper;
35993         /** This region's container element 
35994         * @type Roo.Element */
35995         this.el = dh.append(ctr.dom, {
35996                 tag: "div",
35997                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35998             }, true);
35999         /** This region's title element 
36000         * @type Roo.Element */
36001     
36002         this.titleEl = dh.append(this.el.dom,  {
36003                 tag: "div",
36004                 unselectable: "on",
36005                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36006                 children:[
36007                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36008                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36009                 ]
36010             }, true);
36011         
36012         this.titleEl.enableDisplayMode();
36013         /** This region's title text element 
36014         * @type HTMLElement */
36015         this.titleTextEl = this.titleEl.dom.firstChild;
36016         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36017         /*
36018         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36019         this.closeBtn.enableDisplayMode();
36020         this.closeBtn.on("click", this.closeClicked, this);
36021         this.closeBtn.hide();
36022     */
36023         this.createBody(this.config);
36024         if(this.config.hideWhenEmpty){
36025             this.hide();
36026             this.on("paneladded", this.validateVisibility, this);
36027             this.on("panelremoved", this.validateVisibility, this);
36028         }
36029         if(this.autoScroll){
36030             this.bodyEl.setStyle("overflow", "auto");
36031         }else{
36032             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36033         }
36034         //if(c.titlebar !== false){
36035             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36036                 this.titleEl.hide();
36037             }else{
36038                 this.titleEl.show();
36039                 if(this.config.title){
36040                     this.titleTextEl.innerHTML = this.config.title;
36041                 }
36042             }
36043         //}
36044         if(this.config.collapsed){
36045             this.collapse(true);
36046         }
36047         if(this.config.hidden){
36048             this.hide();
36049         }
36050         
36051         if (this.unrendered_panels && this.unrendered_panels.length) {
36052             for (var i =0;i< this.unrendered_panels.length; i++) {
36053                 this.add(this.unrendered_panels[i]);
36054             }
36055             this.unrendered_panels = null;
36056             
36057         }
36058         
36059     },
36060     
36061     applyConfig : function(c)
36062     {
36063         /*
36064          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36065             var dh = Roo.DomHelper;
36066             if(c.titlebar !== false){
36067                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36068                 this.collapseBtn.on("click", this.collapse, this);
36069                 this.collapseBtn.enableDisplayMode();
36070                 /*
36071                 if(c.showPin === true || this.showPin){
36072                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36073                     this.stickBtn.enableDisplayMode();
36074                     this.stickBtn.on("click", this.expand, this);
36075                     this.stickBtn.hide();
36076                 }
36077                 
36078             }
36079             */
36080             /** This region's collapsed element
36081             * @type Roo.Element */
36082             /*
36083              *
36084             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36085                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36086             ]}, true);
36087             
36088             if(c.floatable !== false){
36089                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36090                this.collapsedEl.on("click", this.collapseClick, this);
36091             }
36092
36093             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36094                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36095                    id: "message", unselectable: "on", style:{"float":"left"}});
36096                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36097              }
36098             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36099             this.expandBtn.on("click", this.expand, this);
36100             
36101         }
36102         
36103         if(this.collapseBtn){
36104             this.collapseBtn.setVisible(c.collapsible == true);
36105         }
36106         
36107         this.cmargins = c.cmargins || this.cmargins ||
36108                          (this.position == "west" || this.position == "east" ?
36109                              {top: 0, left: 2, right:2, bottom: 0} :
36110                              {top: 2, left: 0, right:0, bottom: 2});
36111         */
36112         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36113         
36114         
36115         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36116         
36117         this.autoScroll = c.autoScroll || false;
36118         
36119         
36120        
36121         
36122         this.duration = c.duration || .30;
36123         this.slideDuration = c.slideDuration || .45;
36124         this.config = c;
36125        
36126     },
36127     /**
36128      * Returns true if this region is currently visible.
36129      * @return {Boolean}
36130      */
36131     isVisible : function(){
36132         return this.visible;
36133     },
36134
36135     /**
36136      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36137      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36138      */
36139     //setCollapsedTitle : function(title){
36140     //    title = title || "&#160;";
36141      //   if(this.collapsedTitleTextEl){
36142       //      this.collapsedTitleTextEl.innerHTML = title;
36143        // }
36144     //},
36145
36146     getBox : function(){
36147         var b;
36148       //  if(!this.collapsed){
36149             b = this.el.getBox(false, true);
36150        // }else{
36151           //  b = this.collapsedEl.getBox(false, true);
36152         //}
36153         return b;
36154     },
36155
36156     getMargins : function(){
36157         return this.margins;
36158         //return this.collapsed ? this.cmargins : this.margins;
36159     },
36160 /*
36161     highlight : function(){
36162         this.el.addClass("x-layout-panel-dragover");
36163     },
36164
36165     unhighlight : function(){
36166         this.el.removeClass("x-layout-panel-dragover");
36167     },
36168 */
36169     updateBox : function(box)
36170     {
36171         if (!this.bodyEl) {
36172             return; // not rendered yet..
36173         }
36174         
36175         this.box = box;
36176         if(!this.collapsed){
36177             this.el.dom.style.left = box.x + "px";
36178             this.el.dom.style.top = box.y + "px";
36179             this.updateBody(box.width, box.height);
36180         }else{
36181             this.collapsedEl.dom.style.left = box.x + "px";
36182             this.collapsedEl.dom.style.top = box.y + "px";
36183             this.collapsedEl.setSize(box.width, box.height);
36184         }
36185         if(this.tabs){
36186             this.tabs.autoSizeTabs();
36187         }
36188     },
36189
36190     updateBody : function(w, h)
36191     {
36192         if(w !== null){
36193             this.el.setWidth(w);
36194             w -= this.el.getBorderWidth("rl");
36195             if(this.config.adjustments){
36196                 w += this.config.adjustments[0];
36197             }
36198         }
36199         if(h !== null && h > 0){
36200             this.el.setHeight(h);
36201             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36202             h -= this.el.getBorderWidth("tb");
36203             if(this.config.adjustments){
36204                 h += this.config.adjustments[1];
36205             }
36206             this.bodyEl.setHeight(h);
36207             if(this.tabs){
36208                 h = this.tabs.syncHeight(h);
36209             }
36210         }
36211         if(this.panelSize){
36212             w = w !== null ? w : this.panelSize.width;
36213             h = h !== null ? h : this.panelSize.height;
36214         }
36215         if(this.activePanel){
36216             var el = this.activePanel.getEl();
36217             w = w !== null ? w : el.getWidth();
36218             h = h !== null ? h : el.getHeight();
36219             this.panelSize = {width: w, height: h};
36220             this.activePanel.setSize(w, h);
36221         }
36222         if(Roo.isIE && this.tabs){
36223             this.tabs.el.repaint();
36224         }
36225     },
36226
36227     /**
36228      * Returns the container element for this region.
36229      * @return {Roo.Element}
36230      */
36231     getEl : function(){
36232         return this.el;
36233     },
36234
36235     /**
36236      * Hides this region.
36237      */
36238     hide : function(){
36239         //if(!this.collapsed){
36240             this.el.dom.style.left = "-2000px";
36241             this.el.hide();
36242         //}else{
36243          //   this.collapsedEl.dom.style.left = "-2000px";
36244          //   this.collapsedEl.hide();
36245        // }
36246         this.visible = false;
36247         this.fireEvent("visibilitychange", this, false);
36248     },
36249
36250     /**
36251      * Shows this region if it was previously hidden.
36252      */
36253     show : function(){
36254         //if(!this.collapsed){
36255             this.el.show();
36256         //}else{
36257         //    this.collapsedEl.show();
36258        // }
36259         this.visible = true;
36260         this.fireEvent("visibilitychange", this, true);
36261     },
36262 /*
36263     closeClicked : function(){
36264         if(this.activePanel){
36265             this.remove(this.activePanel);
36266         }
36267     },
36268
36269     collapseClick : function(e){
36270         if(this.isSlid){
36271            e.stopPropagation();
36272            this.slideIn();
36273         }else{
36274            e.stopPropagation();
36275            this.slideOut();
36276         }
36277     },
36278 */
36279     /**
36280      * Collapses this region.
36281      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36282      */
36283     /*
36284     collapse : function(skipAnim, skipCheck = false){
36285         if(this.collapsed) {
36286             return;
36287         }
36288         
36289         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36290             
36291             this.collapsed = true;
36292             if(this.split){
36293                 this.split.el.hide();
36294             }
36295             if(this.config.animate && skipAnim !== true){
36296                 this.fireEvent("invalidated", this);
36297                 this.animateCollapse();
36298             }else{
36299                 this.el.setLocation(-20000,-20000);
36300                 this.el.hide();
36301                 this.collapsedEl.show();
36302                 this.fireEvent("collapsed", this);
36303                 this.fireEvent("invalidated", this);
36304             }
36305         }
36306         
36307     },
36308 */
36309     animateCollapse : function(){
36310         // overridden
36311     },
36312
36313     /**
36314      * Expands this region if it was previously collapsed.
36315      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36316      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36317      */
36318     /*
36319     expand : function(e, skipAnim){
36320         if(e) {
36321             e.stopPropagation();
36322         }
36323         if(!this.collapsed || this.el.hasActiveFx()) {
36324             return;
36325         }
36326         if(this.isSlid){
36327             this.afterSlideIn();
36328             skipAnim = true;
36329         }
36330         this.collapsed = false;
36331         if(this.config.animate && skipAnim !== true){
36332             this.animateExpand();
36333         }else{
36334             this.el.show();
36335             if(this.split){
36336                 this.split.el.show();
36337             }
36338             this.collapsedEl.setLocation(-2000,-2000);
36339             this.collapsedEl.hide();
36340             this.fireEvent("invalidated", this);
36341             this.fireEvent("expanded", this);
36342         }
36343     },
36344 */
36345     animateExpand : function(){
36346         // overridden
36347     },
36348
36349     initTabs : function()
36350     {
36351         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36352         
36353         var ts = new Roo.bootstrap.panel.Tabs({
36354             el: this.bodyEl.dom,
36355             region : this,
36356             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36357             disableTooltips: this.config.disableTabTips,
36358             toolbar : this.config.toolbar
36359         });
36360         
36361         if(this.config.hideTabs){
36362             ts.stripWrap.setDisplayed(false);
36363         }
36364         this.tabs = ts;
36365         ts.resizeTabs = this.config.resizeTabs === true;
36366         ts.minTabWidth = this.config.minTabWidth || 40;
36367         ts.maxTabWidth = this.config.maxTabWidth || 250;
36368         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36369         ts.monitorResize = false;
36370         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36371         ts.bodyEl.addClass('roo-layout-tabs-body');
36372         this.panels.each(this.initPanelAsTab, this);
36373     },
36374
36375     initPanelAsTab : function(panel){
36376         var ti = this.tabs.addTab(
36377             panel.getEl().id,
36378             panel.getTitle(),
36379             null,
36380             this.config.closeOnTab && panel.isClosable(),
36381             panel.tpl
36382         );
36383         if(panel.tabTip !== undefined){
36384             ti.setTooltip(panel.tabTip);
36385         }
36386         ti.on("activate", function(){
36387               this.setActivePanel(panel);
36388         }, this);
36389         
36390         if(this.config.closeOnTab){
36391             ti.on("beforeclose", function(t, e){
36392                 e.cancel = true;
36393                 this.remove(panel);
36394             }, this);
36395         }
36396         
36397         panel.tabItem = ti;
36398         
36399         return ti;
36400     },
36401
36402     updatePanelTitle : function(panel, title)
36403     {
36404         if(this.activePanel == panel){
36405             this.updateTitle(title);
36406         }
36407         if(this.tabs){
36408             var ti = this.tabs.getTab(panel.getEl().id);
36409             ti.setText(title);
36410             if(panel.tabTip !== undefined){
36411                 ti.setTooltip(panel.tabTip);
36412             }
36413         }
36414     },
36415
36416     updateTitle : function(title){
36417         if(this.titleTextEl && !this.config.title){
36418             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36419         }
36420     },
36421
36422     setActivePanel : function(panel)
36423     {
36424         panel = this.getPanel(panel);
36425         if(this.activePanel && this.activePanel != panel){
36426             if(this.activePanel.setActiveState(false) === false){
36427                 return;
36428             }
36429         }
36430         this.activePanel = panel;
36431         panel.setActiveState(true);
36432         if(this.panelSize){
36433             panel.setSize(this.panelSize.width, this.panelSize.height);
36434         }
36435         if(this.closeBtn){
36436             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36437         }
36438         this.updateTitle(panel.getTitle());
36439         if(this.tabs){
36440             this.fireEvent("invalidated", this);
36441         }
36442         this.fireEvent("panelactivated", this, panel);
36443     },
36444
36445     /**
36446      * Shows the specified panel.
36447      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36448      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36449      */
36450     showPanel : function(panel)
36451     {
36452         panel = this.getPanel(panel);
36453         if(panel){
36454             if(this.tabs){
36455                 var tab = this.tabs.getTab(panel.getEl().id);
36456                 if(tab.isHidden()){
36457                     this.tabs.unhideTab(tab.id);
36458                 }
36459                 tab.activate();
36460             }else{
36461                 this.setActivePanel(panel);
36462             }
36463         }
36464         return panel;
36465     },
36466
36467     /**
36468      * Get the active panel for this region.
36469      * @return {Roo.ContentPanel} The active panel or null
36470      */
36471     getActivePanel : function(){
36472         return this.activePanel;
36473     },
36474
36475     validateVisibility : function(){
36476         if(this.panels.getCount() < 1){
36477             this.updateTitle("&#160;");
36478             this.closeBtn.hide();
36479             this.hide();
36480         }else{
36481             if(!this.isVisible()){
36482                 this.show();
36483             }
36484         }
36485     },
36486
36487     /**
36488      * Adds the passed ContentPanel(s) to this region.
36489      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36490      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36491      */
36492     add : function(panel)
36493     {
36494         if(arguments.length > 1){
36495             for(var i = 0, len = arguments.length; i < len; i++) {
36496                 this.add(arguments[i]);
36497             }
36498             return null;
36499         }
36500         
36501         // if we have not been rendered yet, then we can not really do much of this..
36502         if (!this.bodyEl) {
36503             this.unrendered_panels.push(panel);
36504             return panel;
36505         }
36506         
36507         
36508         
36509         
36510         if(this.hasPanel(panel)){
36511             this.showPanel(panel);
36512             return panel;
36513         }
36514         panel.setRegion(this);
36515         this.panels.add(panel);
36516        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36517             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36518             // and hide them... ???
36519             this.bodyEl.dom.appendChild(panel.getEl().dom);
36520             if(panel.background !== true){
36521                 this.setActivePanel(panel);
36522             }
36523             this.fireEvent("paneladded", this, panel);
36524             return panel;
36525         }
36526         */
36527         if(!this.tabs){
36528             this.initTabs();
36529         }else{
36530             this.initPanelAsTab(panel);
36531         }
36532         
36533         
36534         if(panel.background !== true){
36535             this.tabs.activate(panel.getEl().id);
36536         }
36537         this.fireEvent("paneladded", this, panel);
36538         return panel;
36539     },
36540
36541     /**
36542      * Hides the tab for the specified panel.
36543      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36544      */
36545     hidePanel : function(panel){
36546         if(this.tabs && (panel = this.getPanel(panel))){
36547             this.tabs.hideTab(panel.getEl().id);
36548         }
36549     },
36550
36551     /**
36552      * Unhides the tab for a previously hidden panel.
36553      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36554      */
36555     unhidePanel : function(panel){
36556         if(this.tabs && (panel = this.getPanel(panel))){
36557             this.tabs.unhideTab(panel.getEl().id);
36558         }
36559     },
36560
36561     clearPanels : function(){
36562         while(this.panels.getCount() > 0){
36563              this.remove(this.panels.first());
36564         }
36565     },
36566
36567     /**
36568      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36569      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36570      * @param {Boolean} preservePanel Overrides the config preservePanel option
36571      * @return {Roo.ContentPanel} The panel that was removed
36572      */
36573     remove : function(panel, preservePanel)
36574     {
36575         panel = this.getPanel(panel);
36576         if(!panel){
36577             return null;
36578         }
36579         var e = {};
36580         this.fireEvent("beforeremove", this, panel, e);
36581         if(e.cancel === true){
36582             return null;
36583         }
36584         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36585         var panelId = panel.getId();
36586         this.panels.removeKey(panelId);
36587         if(preservePanel){
36588             document.body.appendChild(panel.getEl().dom);
36589         }
36590         if(this.tabs){
36591             this.tabs.removeTab(panel.getEl().id);
36592         }else if (!preservePanel){
36593             this.bodyEl.dom.removeChild(panel.getEl().dom);
36594         }
36595         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36596             var p = this.panels.first();
36597             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36598             tempEl.appendChild(p.getEl().dom);
36599             this.bodyEl.update("");
36600             this.bodyEl.dom.appendChild(p.getEl().dom);
36601             tempEl = null;
36602             this.updateTitle(p.getTitle());
36603             this.tabs = null;
36604             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36605             this.setActivePanel(p);
36606         }
36607         panel.setRegion(null);
36608         if(this.activePanel == panel){
36609             this.activePanel = null;
36610         }
36611         if(this.config.autoDestroy !== false && preservePanel !== true){
36612             try{panel.destroy();}catch(e){}
36613         }
36614         this.fireEvent("panelremoved", this, panel);
36615         return panel;
36616     },
36617
36618     /**
36619      * Returns the TabPanel component used by this region
36620      * @return {Roo.TabPanel}
36621      */
36622     getTabs : function(){
36623         return this.tabs;
36624     },
36625
36626     createTool : function(parentEl, className){
36627         var btn = Roo.DomHelper.append(parentEl, {
36628             tag: "div",
36629             cls: "x-layout-tools-button",
36630             children: [ {
36631                 tag: "div",
36632                 cls: "roo-layout-tools-button-inner " + className,
36633                 html: "&#160;"
36634             }]
36635         }, true);
36636         btn.addClassOnOver("roo-layout-tools-button-over");
36637         return btn;
36638     }
36639 });/*
36640  * Based on:
36641  * Ext JS Library 1.1.1
36642  * Copyright(c) 2006-2007, Ext JS, LLC.
36643  *
36644  * Originally Released Under LGPL - original licence link has changed is not relivant.
36645  *
36646  * Fork - LGPL
36647  * <script type="text/javascript">
36648  */
36649  
36650
36651
36652 /**
36653  * @class Roo.SplitLayoutRegion
36654  * @extends Roo.LayoutRegion
36655  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36656  */
36657 Roo.bootstrap.layout.Split = function(config){
36658     this.cursor = config.cursor;
36659     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36660 };
36661
36662 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36663 {
36664     splitTip : "Drag to resize.",
36665     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36666     useSplitTips : false,
36667
36668     applyConfig : function(config){
36669         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36670     },
36671     
36672     onRender : function(ctr,pos) {
36673         
36674         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36675         if(!this.config.split){
36676             return;
36677         }
36678         if(!this.split){
36679             
36680             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36681                             tag: "div",
36682                             id: this.el.id + "-split",
36683                             cls: "roo-layout-split roo-layout-split-"+this.position,
36684                             html: "&#160;"
36685             });
36686             /** The SplitBar for this region 
36687             * @type Roo.SplitBar */
36688             // does not exist yet...
36689             Roo.log([this.position, this.orientation]);
36690             
36691             this.split = new Roo.bootstrap.SplitBar({
36692                 dragElement : splitEl,
36693                 resizingElement: this.el,
36694                 orientation : this.orientation
36695             });
36696             
36697             this.split.on("moved", this.onSplitMove, this);
36698             this.split.useShim = this.config.useShim === true;
36699             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36700             if(this.useSplitTips){
36701                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36702             }
36703             //if(config.collapsible){
36704             //    this.split.el.on("dblclick", this.collapse,  this);
36705             //}
36706         }
36707         if(typeof this.config.minSize != "undefined"){
36708             this.split.minSize = this.config.minSize;
36709         }
36710         if(typeof this.config.maxSize != "undefined"){
36711             this.split.maxSize = this.config.maxSize;
36712         }
36713         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36714             this.hideSplitter();
36715         }
36716         
36717     },
36718
36719     getHMaxSize : function(){
36720          var cmax = this.config.maxSize || 10000;
36721          var center = this.mgr.getRegion("center");
36722          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36723     },
36724
36725     getVMaxSize : function(){
36726          var cmax = this.config.maxSize || 10000;
36727          var center = this.mgr.getRegion("center");
36728          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36729     },
36730
36731     onSplitMove : function(split, newSize){
36732         this.fireEvent("resized", this, newSize);
36733     },
36734     
36735     /** 
36736      * Returns the {@link Roo.SplitBar} for this region.
36737      * @return {Roo.SplitBar}
36738      */
36739     getSplitBar : function(){
36740         return this.split;
36741     },
36742     
36743     hide : function(){
36744         this.hideSplitter();
36745         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36746     },
36747
36748     hideSplitter : function(){
36749         if(this.split){
36750             this.split.el.setLocation(-2000,-2000);
36751             this.split.el.hide();
36752         }
36753     },
36754
36755     show : function(){
36756         if(this.split){
36757             this.split.el.show();
36758         }
36759         Roo.bootstrap.layout.Split.superclass.show.call(this);
36760     },
36761     
36762     beforeSlide: function(){
36763         if(Roo.isGecko){// firefox overflow auto bug workaround
36764             this.bodyEl.clip();
36765             if(this.tabs) {
36766                 this.tabs.bodyEl.clip();
36767             }
36768             if(this.activePanel){
36769                 this.activePanel.getEl().clip();
36770                 
36771                 if(this.activePanel.beforeSlide){
36772                     this.activePanel.beforeSlide();
36773                 }
36774             }
36775         }
36776     },
36777     
36778     afterSlide : function(){
36779         if(Roo.isGecko){// firefox overflow auto bug workaround
36780             this.bodyEl.unclip();
36781             if(this.tabs) {
36782                 this.tabs.bodyEl.unclip();
36783             }
36784             if(this.activePanel){
36785                 this.activePanel.getEl().unclip();
36786                 if(this.activePanel.afterSlide){
36787                     this.activePanel.afterSlide();
36788                 }
36789             }
36790         }
36791     },
36792
36793     initAutoHide : function(){
36794         if(this.autoHide !== false){
36795             if(!this.autoHideHd){
36796                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36797                 this.autoHideHd = {
36798                     "mouseout": function(e){
36799                         if(!e.within(this.el, true)){
36800                             st.delay(500);
36801                         }
36802                     },
36803                     "mouseover" : function(e){
36804                         st.cancel();
36805                     },
36806                     scope : this
36807                 };
36808             }
36809             this.el.on(this.autoHideHd);
36810         }
36811     },
36812
36813     clearAutoHide : function(){
36814         if(this.autoHide !== false){
36815             this.el.un("mouseout", this.autoHideHd.mouseout);
36816             this.el.un("mouseover", this.autoHideHd.mouseover);
36817         }
36818     },
36819
36820     clearMonitor : function(){
36821         Roo.get(document).un("click", this.slideInIf, this);
36822     },
36823
36824     // these names are backwards but not changed for compat
36825     slideOut : function(){
36826         if(this.isSlid || this.el.hasActiveFx()){
36827             return;
36828         }
36829         this.isSlid = true;
36830         if(this.collapseBtn){
36831             this.collapseBtn.hide();
36832         }
36833         this.closeBtnState = this.closeBtn.getStyle('display');
36834         this.closeBtn.hide();
36835         if(this.stickBtn){
36836             this.stickBtn.show();
36837         }
36838         this.el.show();
36839         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36840         this.beforeSlide();
36841         this.el.setStyle("z-index", 10001);
36842         this.el.slideIn(this.getSlideAnchor(), {
36843             callback: function(){
36844                 this.afterSlide();
36845                 this.initAutoHide();
36846                 Roo.get(document).on("click", this.slideInIf, this);
36847                 this.fireEvent("slideshow", this);
36848             },
36849             scope: this,
36850             block: true
36851         });
36852     },
36853
36854     afterSlideIn : function(){
36855         this.clearAutoHide();
36856         this.isSlid = false;
36857         this.clearMonitor();
36858         this.el.setStyle("z-index", "");
36859         if(this.collapseBtn){
36860             this.collapseBtn.show();
36861         }
36862         this.closeBtn.setStyle('display', this.closeBtnState);
36863         if(this.stickBtn){
36864             this.stickBtn.hide();
36865         }
36866         this.fireEvent("slidehide", this);
36867     },
36868
36869     slideIn : function(cb){
36870         if(!this.isSlid || this.el.hasActiveFx()){
36871             Roo.callback(cb);
36872             return;
36873         }
36874         this.isSlid = false;
36875         this.beforeSlide();
36876         this.el.slideOut(this.getSlideAnchor(), {
36877             callback: function(){
36878                 this.el.setLeftTop(-10000, -10000);
36879                 this.afterSlide();
36880                 this.afterSlideIn();
36881                 Roo.callback(cb);
36882             },
36883             scope: this,
36884             block: true
36885         });
36886     },
36887     
36888     slideInIf : function(e){
36889         if(!e.within(this.el)){
36890             this.slideIn();
36891         }
36892     },
36893
36894     animateCollapse : function(){
36895         this.beforeSlide();
36896         this.el.setStyle("z-index", 20000);
36897         var anchor = this.getSlideAnchor();
36898         this.el.slideOut(anchor, {
36899             callback : function(){
36900                 this.el.setStyle("z-index", "");
36901                 this.collapsedEl.slideIn(anchor, {duration:.3});
36902                 this.afterSlide();
36903                 this.el.setLocation(-10000,-10000);
36904                 this.el.hide();
36905                 this.fireEvent("collapsed", this);
36906             },
36907             scope: this,
36908             block: true
36909         });
36910     },
36911
36912     animateExpand : function(){
36913         this.beforeSlide();
36914         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36915         this.el.setStyle("z-index", 20000);
36916         this.collapsedEl.hide({
36917             duration:.1
36918         });
36919         this.el.slideIn(this.getSlideAnchor(), {
36920             callback : function(){
36921                 this.el.setStyle("z-index", "");
36922                 this.afterSlide();
36923                 if(this.split){
36924                     this.split.el.show();
36925                 }
36926                 this.fireEvent("invalidated", this);
36927                 this.fireEvent("expanded", this);
36928             },
36929             scope: this,
36930             block: true
36931         });
36932     },
36933
36934     anchors : {
36935         "west" : "left",
36936         "east" : "right",
36937         "north" : "top",
36938         "south" : "bottom"
36939     },
36940
36941     sanchors : {
36942         "west" : "l",
36943         "east" : "r",
36944         "north" : "t",
36945         "south" : "b"
36946     },
36947
36948     canchors : {
36949         "west" : "tl-tr",
36950         "east" : "tr-tl",
36951         "north" : "tl-bl",
36952         "south" : "bl-tl"
36953     },
36954
36955     getAnchor : function(){
36956         return this.anchors[this.position];
36957     },
36958
36959     getCollapseAnchor : function(){
36960         return this.canchors[this.position];
36961     },
36962
36963     getSlideAnchor : function(){
36964         return this.sanchors[this.position];
36965     },
36966
36967     getAlignAdj : function(){
36968         var cm = this.cmargins;
36969         switch(this.position){
36970             case "west":
36971                 return [0, 0];
36972             break;
36973             case "east":
36974                 return [0, 0];
36975             break;
36976             case "north":
36977                 return [0, 0];
36978             break;
36979             case "south":
36980                 return [0, 0];
36981             break;
36982         }
36983     },
36984
36985     getExpandAdj : function(){
36986         var c = this.collapsedEl, cm = this.cmargins;
36987         switch(this.position){
36988             case "west":
36989                 return [-(cm.right+c.getWidth()+cm.left), 0];
36990             break;
36991             case "east":
36992                 return [cm.right+c.getWidth()+cm.left, 0];
36993             break;
36994             case "north":
36995                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36996             break;
36997             case "south":
36998                 return [0, cm.top+cm.bottom+c.getHeight()];
36999             break;
37000         }
37001     }
37002 });/*
37003  * Based on:
37004  * Ext JS Library 1.1.1
37005  * Copyright(c) 2006-2007, Ext JS, LLC.
37006  *
37007  * Originally Released Under LGPL - original licence link has changed is not relivant.
37008  *
37009  * Fork - LGPL
37010  * <script type="text/javascript">
37011  */
37012 /*
37013  * These classes are private internal classes
37014  */
37015 Roo.bootstrap.layout.Center = function(config){
37016     config.region = "center";
37017     Roo.bootstrap.layout.Region.call(this, config);
37018     this.visible = true;
37019     this.minWidth = config.minWidth || 20;
37020     this.minHeight = config.minHeight || 20;
37021 };
37022
37023 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37024     hide : function(){
37025         // center panel can't be hidden
37026     },
37027     
37028     show : function(){
37029         // center panel can't be hidden
37030     },
37031     
37032     getMinWidth: function(){
37033         return this.minWidth;
37034     },
37035     
37036     getMinHeight: function(){
37037         return this.minHeight;
37038     }
37039 });
37040
37041
37042
37043
37044  
37045
37046
37047
37048
37049
37050
37051 Roo.bootstrap.layout.North = function(config)
37052 {
37053     config.region = 'north';
37054     config.cursor = 'n-resize';
37055     
37056     Roo.bootstrap.layout.Split.call(this, config);
37057     
37058     
37059     if(this.split){
37060         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37061         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37062         this.split.el.addClass("roo-layout-split-v");
37063     }
37064     var size = config.initialSize || config.height;
37065     if(typeof size != "undefined"){
37066         this.el.setHeight(size);
37067     }
37068 };
37069 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37070 {
37071     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37072     
37073     
37074     
37075     getBox : function(){
37076         if(this.collapsed){
37077             return this.collapsedEl.getBox();
37078         }
37079         var box = this.el.getBox();
37080         if(this.split){
37081             box.height += this.split.el.getHeight();
37082         }
37083         return box;
37084     },
37085     
37086     updateBox : function(box){
37087         if(this.split && !this.collapsed){
37088             box.height -= this.split.el.getHeight();
37089             this.split.el.setLeft(box.x);
37090             this.split.el.setTop(box.y+box.height);
37091             this.split.el.setWidth(box.width);
37092         }
37093         if(this.collapsed){
37094             this.updateBody(box.width, null);
37095         }
37096         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37097     }
37098 });
37099
37100
37101
37102
37103
37104 Roo.bootstrap.layout.South = function(config){
37105     config.region = 'south';
37106     config.cursor = 's-resize';
37107     Roo.bootstrap.layout.Split.call(this, config);
37108     if(this.split){
37109         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37110         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37111         this.split.el.addClass("roo-layout-split-v");
37112     }
37113     var size = config.initialSize || config.height;
37114     if(typeof size != "undefined"){
37115         this.el.setHeight(size);
37116     }
37117 };
37118
37119 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37120     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37121     getBox : function(){
37122         if(this.collapsed){
37123             return this.collapsedEl.getBox();
37124         }
37125         var box = this.el.getBox();
37126         if(this.split){
37127             var sh = this.split.el.getHeight();
37128             box.height += sh;
37129             box.y -= sh;
37130         }
37131         return box;
37132     },
37133     
37134     updateBox : function(box){
37135         if(this.split && !this.collapsed){
37136             var sh = this.split.el.getHeight();
37137             box.height -= sh;
37138             box.y += sh;
37139             this.split.el.setLeft(box.x);
37140             this.split.el.setTop(box.y-sh);
37141             this.split.el.setWidth(box.width);
37142         }
37143         if(this.collapsed){
37144             this.updateBody(box.width, null);
37145         }
37146         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37147     }
37148 });
37149
37150 Roo.bootstrap.layout.East = function(config){
37151     config.region = "east";
37152     config.cursor = "e-resize";
37153     Roo.bootstrap.layout.Split.call(this, config);
37154     if(this.split){
37155         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37156         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37157         this.split.el.addClass("roo-layout-split-h");
37158     }
37159     var size = config.initialSize || config.width;
37160     if(typeof size != "undefined"){
37161         this.el.setWidth(size);
37162     }
37163 };
37164 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37165     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37166     getBox : function(){
37167         if(this.collapsed){
37168             return this.collapsedEl.getBox();
37169         }
37170         var box = this.el.getBox();
37171         if(this.split){
37172             var sw = this.split.el.getWidth();
37173             box.width += sw;
37174             box.x -= sw;
37175         }
37176         return box;
37177     },
37178
37179     updateBox : function(box){
37180         if(this.split && !this.collapsed){
37181             var sw = this.split.el.getWidth();
37182             box.width -= sw;
37183             this.split.el.setLeft(box.x);
37184             this.split.el.setTop(box.y);
37185             this.split.el.setHeight(box.height);
37186             box.x += sw;
37187         }
37188         if(this.collapsed){
37189             this.updateBody(null, box.height);
37190         }
37191         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37192     }
37193 });
37194
37195 Roo.bootstrap.layout.West = function(config){
37196     config.region = "west";
37197     config.cursor = "w-resize";
37198     
37199     Roo.bootstrap.layout.Split.call(this, config);
37200     if(this.split){
37201         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37202         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37203         this.split.el.addClass("roo-layout-split-h");
37204     }
37205     
37206 };
37207 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37208     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37209     
37210     onRender: function(ctr, pos)
37211     {
37212         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37213         var size = this.config.initialSize || this.config.width;
37214         if(typeof size != "undefined"){
37215             this.el.setWidth(size);
37216         }
37217     },
37218     
37219     getBox : function(){
37220         if(this.collapsed){
37221             return this.collapsedEl.getBox();
37222         }
37223         var box = this.el.getBox();
37224         if(this.split){
37225             box.width += this.split.el.getWidth();
37226         }
37227         return box;
37228     },
37229     
37230     updateBox : function(box){
37231         if(this.split && !this.collapsed){
37232             var sw = this.split.el.getWidth();
37233             box.width -= sw;
37234             this.split.el.setLeft(box.x+box.width);
37235             this.split.el.setTop(box.y);
37236             this.split.el.setHeight(box.height);
37237         }
37238         if(this.collapsed){
37239             this.updateBody(null, box.height);
37240         }
37241         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37242     }
37243 });Roo.namespace("Roo.bootstrap.panel");/*
37244  * Based on:
37245  * Ext JS Library 1.1.1
37246  * Copyright(c) 2006-2007, Ext JS, LLC.
37247  *
37248  * Originally Released Under LGPL - original licence link has changed is not relivant.
37249  *
37250  * Fork - LGPL
37251  * <script type="text/javascript">
37252  */
37253 /**
37254  * @class Roo.ContentPanel
37255  * @extends Roo.util.Observable
37256  * A basic ContentPanel element.
37257  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37258  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37259  * @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
37260  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37261  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37262  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37263  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37264  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37265  * @cfg {String} title          The title for this panel
37266  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37267  * @cfg {String} url            Calls {@link #setUrl} with this value
37268  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37269  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37270  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37271  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37272  * @cfg {Boolean} badges render the badges
37273
37274  * @constructor
37275  * Create a new ContentPanel.
37276  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37277  * @param {String/Object} config A string to set only the title or a config object
37278  * @param {String} content (optional) Set the HTML content for this panel
37279  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37280  */
37281 Roo.bootstrap.panel.Content = function( config){
37282     
37283     this.tpl = config.tpl || false;
37284     
37285     var el = config.el;
37286     var content = config.content;
37287
37288     if(config.autoCreate){ // xtype is available if this is called from factory
37289         el = Roo.id();
37290     }
37291     this.el = Roo.get(el);
37292     if(!this.el && config && config.autoCreate){
37293         if(typeof config.autoCreate == "object"){
37294             if(!config.autoCreate.id){
37295                 config.autoCreate.id = config.id||el;
37296             }
37297             this.el = Roo.DomHelper.append(document.body,
37298                         config.autoCreate, true);
37299         }else{
37300             var elcfg =  {   tag: "div",
37301                             cls: "roo-layout-inactive-content",
37302                             id: config.id||el
37303                             };
37304             if (config.html) {
37305                 elcfg.html = config.html;
37306                 
37307             }
37308                         
37309             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37310         }
37311     } 
37312     this.closable = false;
37313     this.loaded = false;
37314     this.active = false;
37315    
37316       
37317     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37318         
37319         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37320         
37321         this.wrapEl = this.el; //this.el.wrap();
37322         var ti = [];
37323         if (config.toolbar.items) {
37324             ti = config.toolbar.items ;
37325             delete config.toolbar.items ;
37326         }
37327         
37328         var nitems = [];
37329         this.toolbar.render(this.wrapEl, 'before');
37330         for(var i =0;i < ti.length;i++) {
37331           //  Roo.log(['add child', items[i]]);
37332             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37333         }
37334         this.toolbar.items = nitems;
37335         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37336         delete config.toolbar;
37337         
37338     }
37339     /*
37340     // xtype created footer. - not sure if will work as we normally have to render first..
37341     if (this.footer && !this.footer.el && this.footer.xtype) {
37342         if (!this.wrapEl) {
37343             this.wrapEl = this.el.wrap();
37344         }
37345     
37346         this.footer.container = this.wrapEl.createChild();
37347          
37348         this.footer = Roo.factory(this.footer, Roo);
37349         
37350     }
37351     */
37352     
37353      if(typeof config == "string"){
37354         this.title = config;
37355     }else{
37356         Roo.apply(this, config);
37357     }
37358     
37359     if(this.resizeEl){
37360         this.resizeEl = Roo.get(this.resizeEl, true);
37361     }else{
37362         this.resizeEl = this.el;
37363     }
37364     // handle view.xtype
37365     
37366  
37367     
37368     
37369     this.addEvents({
37370         /**
37371          * @event activate
37372          * Fires when this panel is activated. 
37373          * @param {Roo.ContentPanel} this
37374          */
37375         "activate" : true,
37376         /**
37377          * @event deactivate
37378          * Fires when this panel is activated. 
37379          * @param {Roo.ContentPanel} this
37380          */
37381         "deactivate" : true,
37382
37383         /**
37384          * @event resize
37385          * Fires when this panel is resized if fitToFrame is true.
37386          * @param {Roo.ContentPanel} this
37387          * @param {Number} width The width after any component adjustments
37388          * @param {Number} height The height after any component adjustments
37389          */
37390         "resize" : true,
37391         
37392          /**
37393          * @event render
37394          * Fires when this tab is created
37395          * @param {Roo.ContentPanel} this
37396          */
37397         "render" : true
37398         
37399         
37400         
37401     });
37402     
37403
37404     
37405     
37406     if(this.autoScroll){
37407         this.resizeEl.setStyle("overflow", "auto");
37408     } else {
37409         // fix randome scrolling
37410         //this.el.on('scroll', function() {
37411         //    Roo.log('fix random scolling');
37412         //    this.scrollTo('top',0); 
37413         //});
37414     }
37415     content = content || this.content;
37416     if(content){
37417         this.setContent(content);
37418     }
37419     if(config && config.url){
37420         this.setUrl(this.url, this.params, this.loadOnce);
37421     }
37422     
37423     
37424     
37425     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37426     
37427     if (this.view && typeof(this.view.xtype) != 'undefined') {
37428         this.view.el = this.el.appendChild(document.createElement("div"));
37429         this.view = Roo.factory(this.view); 
37430         this.view.render  &&  this.view.render(false, '');  
37431     }
37432     
37433     
37434     this.fireEvent('render', this);
37435 };
37436
37437 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37438     
37439     tabTip : '',
37440     
37441     setRegion : function(region){
37442         this.region = region;
37443         this.setActiveClass(region && !this.background);
37444     },
37445     
37446     
37447     setActiveClass: function(state)
37448     {
37449         if(state){
37450            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37451            this.el.setStyle('position','relative');
37452         }else{
37453            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37454            this.el.setStyle('position', 'absolute');
37455         } 
37456     },
37457     
37458     /**
37459      * Returns the toolbar for this Panel if one was configured. 
37460      * @return {Roo.Toolbar} 
37461      */
37462     getToolbar : function(){
37463         return this.toolbar;
37464     },
37465     
37466     setActiveState : function(active)
37467     {
37468         this.active = active;
37469         this.setActiveClass(active);
37470         if(!active){
37471             if(this.fireEvent("deactivate", this) === false){
37472                 return false;
37473             }
37474             return true;
37475         }
37476         this.fireEvent("activate", this);
37477         return true;
37478     },
37479     /**
37480      * Updates this panel's element
37481      * @param {String} content The new content
37482      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37483     */
37484     setContent : function(content, loadScripts){
37485         this.el.update(content, loadScripts);
37486     },
37487
37488     ignoreResize : function(w, h){
37489         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37490             return true;
37491         }else{
37492             this.lastSize = {width: w, height: h};
37493             return false;
37494         }
37495     },
37496     /**
37497      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37498      * @return {Roo.UpdateManager} The UpdateManager
37499      */
37500     getUpdateManager : function(){
37501         return this.el.getUpdateManager();
37502     },
37503      /**
37504      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37505      * @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:
37506 <pre><code>
37507 panel.load({
37508     url: "your-url.php",
37509     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37510     callback: yourFunction,
37511     scope: yourObject, //(optional scope)
37512     discardUrl: false,
37513     nocache: false,
37514     text: "Loading...",
37515     timeout: 30,
37516     scripts: false
37517 });
37518 </code></pre>
37519      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37520      * 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.
37521      * @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}
37522      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37523      * @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.
37524      * @return {Roo.ContentPanel} this
37525      */
37526     load : function(){
37527         var um = this.el.getUpdateManager();
37528         um.update.apply(um, arguments);
37529         return this;
37530     },
37531
37532
37533     /**
37534      * 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.
37535      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37536      * @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)
37537      * @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)
37538      * @return {Roo.UpdateManager} The UpdateManager
37539      */
37540     setUrl : function(url, params, loadOnce){
37541         if(this.refreshDelegate){
37542             this.removeListener("activate", this.refreshDelegate);
37543         }
37544         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37545         this.on("activate", this.refreshDelegate);
37546         return this.el.getUpdateManager();
37547     },
37548     
37549     _handleRefresh : function(url, params, loadOnce){
37550         if(!loadOnce || !this.loaded){
37551             var updater = this.el.getUpdateManager();
37552             updater.update(url, params, this._setLoaded.createDelegate(this));
37553         }
37554     },
37555     
37556     _setLoaded : function(){
37557         this.loaded = true;
37558     }, 
37559     
37560     /**
37561      * Returns this panel's id
37562      * @return {String} 
37563      */
37564     getId : function(){
37565         return this.el.id;
37566     },
37567     
37568     /** 
37569      * Returns this panel's element - used by regiosn to add.
37570      * @return {Roo.Element} 
37571      */
37572     getEl : function(){
37573         return this.wrapEl || this.el;
37574     },
37575     
37576    
37577     
37578     adjustForComponents : function(width, height)
37579     {
37580         //Roo.log('adjustForComponents ');
37581         if(this.resizeEl != this.el){
37582             width -= this.el.getFrameWidth('lr');
37583             height -= this.el.getFrameWidth('tb');
37584         }
37585         if(this.toolbar){
37586             var te = this.toolbar.getEl();
37587             te.setWidth(width);
37588             height -= te.getHeight();
37589         }
37590         if(this.footer){
37591             var te = this.footer.getEl();
37592             te.setWidth(width);
37593             height -= te.getHeight();
37594         }
37595         
37596         
37597         if(this.adjustments){
37598             width += this.adjustments[0];
37599             height += this.adjustments[1];
37600         }
37601         return {"width": width, "height": height};
37602     },
37603     
37604     setSize : function(width, height){
37605         if(this.fitToFrame && !this.ignoreResize(width, height)){
37606             if(this.fitContainer && this.resizeEl != this.el){
37607                 this.el.setSize(width, height);
37608             }
37609             var size = this.adjustForComponents(width, height);
37610             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37611             this.fireEvent('resize', this, size.width, size.height);
37612         }
37613     },
37614     
37615     /**
37616      * Returns this panel's title
37617      * @return {String} 
37618      */
37619     getTitle : function(){
37620         
37621         if (typeof(this.title) != 'object') {
37622             return this.title;
37623         }
37624         
37625         var t = '';
37626         for (var k in this.title) {
37627             if (!this.title.hasOwnProperty(k)) {
37628                 continue;
37629             }
37630             
37631             if (k.indexOf('-') >= 0) {
37632                 var s = k.split('-');
37633                 for (var i = 0; i<s.length; i++) {
37634                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37635                 }
37636             } else {
37637                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37638             }
37639         }
37640         return t;
37641     },
37642     
37643     /**
37644      * Set this panel's title
37645      * @param {String} title
37646      */
37647     setTitle : function(title){
37648         this.title = title;
37649         if(this.region){
37650             this.region.updatePanelTitle(this, title);
37651         }
37652     },
37653     
37654     /**
37655      * Returns true is this panel was configured to be closable
37656      * @return {Boolean} 
37657      */
37658     isClosable : function(){
37659         return this.closable;
37660     },
37661     
37662     beforeSlide : function(){
37663         this.el.clip();
37664         this.resizeEl.clip();
37665     },
37666     
37667     afterSlide : function(){
37668         this.el.unclip();
37669         this.resizeEl.unclip();
37670     },
37671     
37672     /**
37673      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37674      *   Will fail silently if the {@link #setUrl} method has not been called.
37675      *   This does not activate the panel, just updates its content.
37676      */
37677     refresh : function(){
37678         if(this.refreshDelegate){
37679            this.loaded = false;
37680            this.refreshDelegate();
37681         }
37682     },
37683     
37684     /**
37685      * Destroys this panel
37686      */
37687     destroy : function(){
37688         this.el.removeAllListeners();
37689         var tempEl = document.createElement("span");
37690         tempEl.appendChild(this.el.dom);
37691         tempEl.innerHTML = "";
37692         this.el.remove();
37693         this.el = null;
37694     },
37695     
37696     /**
37697      * form - if the content panel contains a form - this is a reference to it.
37698      * @type {Roo.form.Form}
37699      */
37700     form : false,
37701     /**
37702      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37703      *    This contains a reference to it.
37704      * @type {Roo.View}
37705      */
37706     view : false,
37707     
37708       /**
37709      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37710      * <pre><code>
37711
37712 layout.addxtype({
37713        xtype : 'Form',
37714        items: [ .... ]
37715    }
37716 );
37717
37718 </code></pre>
37719      * @param {Object} cfg Xtype definition of item to add.
37720      */
37721     
37722     
37723     getChildContainer: function () {
37724         return this.getEl();
37725     }
37726     
37727     
37728     /*
37729         var  ret = new Roo.factory(cfg);
37730         return ret;
37731         
37732         
37733         // add form..
37734         if (cfg.xtype.match(/^Form$/)) {
37735             
37736             var el;
37737             //if (this.footer) {
37738             //    el = this.footer.container.insertSibling(false, 'before');
37739             //} else {
37740                 el = this.el.createChild();
37741             //}
37742
37743             this.form = new  Roo.form.Form(cfg);
37744             
37745             
37746             if ( this.form.allItems.length) {
37747                 this.form.render(el.dom);
37748             }
37749             return this.form;
37750         }
37751         // should only have one of theses..
37752         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37753             // views.. should not be just added - used named prop 'view''
37754             
37755             cfg.el = this.el.appendChild(document.createElement("div"));
37756             // factory?
37757             
37758             var ret = new Roo.factory(cfg);
37759              
37760              ret.render && ret.render(false, ''); // render blank..
37761             this.view = ret;
37762             return ret;
37763         }
37764         return false;
37765     }
37766     \*/
37767 });
37768  
37769 /**
37770  * @class Roo.bootstrap.panel.Grid
37771  * @extends Roo.bootstrap.panel.Content
37772  * @constructor
37773  * Create a new GridPanel.
37774  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37775  * @param {Object} config A the config object
37776   
37777  */
37778
37779
37780
37781 Roo.bootstrap.panel.Grid = function(config)
37782 {
37783     
37784       
37785     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37786         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37787
37788     config.el = this.wrapper;
37789     //this.el = this.wrapper;
37790     
37791       if (config.container) {
37792         // ctor'ed from a Border/panel.grid
37793         
37794         
37795         this.wrapper.setStyle("overflow", "hidden");
37796         this.wrapper.addClass('roo-grid-container');
37797
37798     }
37799     
37800     
37801     if(config.toolbar){
37802         var tool_el = this.wrapper.createChild();    
37803         this.toolbar = Roo.factory(config.toolbar);
37804         var ti = [];
37805         if (config.toolbar.items) {
37806             ti = config.toolbar.items ;
37807             delete config.toolbar.items ;
37808         }
37809         
37810         var nitems = [];
37811         this.toolbar.render(tool_el);
37812         for(var i =0;i < ti.length;i++) {
37813           //  Roo.log(['add child', items[i]]);
37814             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37815         }
37816         this.toolbar.items = nitems;
37817         
37818         delete config.toolbar;
37819     }
37820     
37821     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37822     config.grid.scrollBody = true;;
37823     config.grid.monitorWindowResize = false; // turn off autosizing
37824     config.grid.autoHeight = false;
37825     config.grid.autoWidth = false;
37826     
37827     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37828     
37829     if (config.background) {
37830         // render grid on panel activation (if panel background)
37831         this.on('activate', function(gp) {
37832             if (!gp.grid.rendered) {
37833                 gp.grid.render(this.wrapper);
37834                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37835             }
37836         });
37837             
37838     } else {
37839         this.grid.render(this.wrapper);
37840         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37841
37842     }
37843     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37844     // ??? needed ??? config.el = this.wrapper;
37845     
37846     
37847     
37848   
37849     // xtype created footer. - not sure if will work as we normally have to render first..
37850     if (this.footer && !this.footer.el && this.footer.xtype) {
37851         
37852         var ctr = this.grid.getView().getFooterPanel(true);
37853         this.footer.dataSource = this.grid.dataSource;
37854         this.footer = Roo.factory(this.footer, Roo);
37855         this.footer.render(ctr);
37856         
37857     }
37858     
37859     
37860     
37861     
37862      
37863 };
37864
37865 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37866     getId : function(){
37867         return this.grid.id;
37868     },
37869     
37870     /**
37871      * Returns the grid for this panel
37872      * @return {Roo.bootstrap.Table} 
37873      */
37874     getGrid : function(){
37875         return this.grid;    
37876     },
37877     
37878     setSize : function(width, height){
37879         if(!this.ignoreResize(width, height)){
37880             var grid = this.grid;
37881             var size = this.adjustForComponents(width, height);
37882             var gridel = grid.getGridEl();
37883             gridel.setSize(size.width, size.height);
37884             /*
37885             var thd = grid.getGridEl().select('thead',true).first();
37886             var tbd = grid.getGridEl().select('tbody', true).first();
37887             if (tbd) {
37888                 tbd.setSize(width, height - thd.getHeight());
37889             }
37890             */
37891             grid.autoSize();
37892         }
37893     },
37894      
37895     
37896     
37897     beforeSlide : function(){
37898         this.grid.getView().scroller.clip();
37899     },
37900     
37901     afterSlide : function(){
37902         this.grid.getView().scroller.unclip();
37903     },
37904     
37905     destroy : function(){
37906         this.grid.destroy();
37907         delete this.grid;
37908         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37909     }
37910 });
37911
37912 /**
37913  * @class Roo.bootstrap.panel.Nest
37914  * @extends Roo.bootstrap.panel.Content
37915  * @constructor
37916  * Create a new Panel, that can contain a layout.Border.
37917  * 
37918  * 
37919  * @param {Roo.BorderLayout} layout The layout for this panel
37920  * @param {String/Object} config A string to set only the title or a config object
37921  */
37922 Roo.bootstrap.panel.Nest = function(config)
37923 {
37924     // construct with only one argument..
37925     /* FIXME - implement nicer consturctors
37926     if (layout.layout) {
37927         config = layout;
37928         layout = config.layout;
37929         delete config.layout;
37930     }
37931     if (layout.xtype && !layout.getEl) {
37932         // then layout needs constructing..
37933         layout = Roo.factory(layout, Roo);
37934     }
37935     */
37936     
37937     config.el =  config.layout.getEl();
37938     
37939     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37940     
37941     config.layout.monitorWindowResize = false; // turn off autosizing
37942     this.layout = config.layout;
37943     this.layout.getEl().addClass("roo-layout-nested-layout");
37944     this.layout.parent = this;
37945     
37946     
37947     
37948     
37949 };
37950
37951 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37952
37953     setSize : function(width, height){
37954         if(!this.ignoreResize(width, height)){
37955             var size = this.adjustForComponents(width, height);
37956             var el = this.layout.getEl();
37957             if (size.height < 1) {
37958                 el.setWidth(size.width);   
37959             } else {
37960                 el.setSize(size.width, size.height);
37961             }
37962             var touch = el.dom.offsetWidth;
37963             this.layout.layout();
37964             // ie requires a double layout on the first pass
37965             if(Roo.isIE && !this.initialized){
37966                 this.initialized = true;
37967                 this.layout.layout();
37968             }
37969         }
37970     },
37971     
37972     // activate all subpanels if not currently active..
37973     
37974     setActiveState : function(active){
37975         this.active = active;
37976         this.setActiveClass(active);
37977         
37978         if(!active){
37979             this.fireEvent("deactivate", this);
37980             return;
37981         }
37982         
37983         this.fireEvent("activate", this);
37984         // not sure if this should happen before or after..
37985         if (!this.layout) {
37986             return; // should not happen..
37987         }
37988         var reg = false;
37989         for (var r in this.layout.regions) {
37990             reg = this.layout.getRegion(r);
37991             if (reg.getActivePanel()) {
37992                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37993                 reg.setActivePanel(reg.getActivePanel());
37994                 continue;
37995             }
37996             if (!reg.panels.length) {
37997                 continue;
37998             }
37999             reg.showPanel(reg.getPanel(0));
38000         }
38001         
38002         
38003         
38004         
38005     },
38006     
38007     /**
38008      * Returns the nested BorderLayout for this panel
38009      * @return {Roo.BorderLayout} 
38010      */
38011     getLayout : function(){
38012         return this.layout;
38013     },
38014     
38015      /**
38016      * Adds a xtype elements to the layout of the nested panel
38017      * <pre><code>
38018
38019 panel.addxtype({
38020        xtype : 'ContentPanel',
38021        region: 'west',
38022        items: [ .... ]
38023    }
38024 );
38025
38026 panel.addxtype({
38027         xtype : 'NestedLayoutPanel',
38028         region: 'west',
38029         layout: {
38030            center: { },
38031            west: { }   
38032         },
38033         items : [ ... list of content panels or nested layout panels.. ]
38034    }
38035 );
38036 </code></pre>
38037      * @param {Object} cfg Xtype definition of item to add.
38038      */
38039     addxtype : function(cfg) {
38040         return this.layout.addxtype(cfg);
38041     
38042     }
38043 });/*
38044  * Based on:
38045  * Ext JS Library 1.1.1
38046  * Copyright(c) 2006-2007, Ext JS, LLC.
38047  *
38048  * Originally Released Under LGPL - original licence link has changed is not relivant.
38049  *
38050  * Fork - LGPL
38051  * <script type="text/javascript">
38052  */
38053 /**
38054  * @class Roo.TabPanel
38055  * @extends Roo.util.Observable
38056  * A lightweight tab container.
38057  * <br><br>
38058  * Usage:
38059  * <pre><code>
38060 // basic tabs 1, built from existing content
38061 var tabs = new Roo.TabPanel("tabs1");
38062 tabs.addTab("script", "View Script");
38063 tabs.addTab("markup", "View Markup");
38064 tabs.activate("script");
38065
38066 // more advanced tabs, built from javascript
38067 var jtabs = new Roo.TabPanel("jtabs");
38068 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38069
38070 // set up the UpdateManager
38071 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38072 var updater = tab2.getUpdateManager();
38073 updater.setDefaultUrl("ajax1.htm");
38074 tab2.on('activate', updater.refresh, updater, true);
38075
38076 // Use setUrl for Ajax loading
38077 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38078 tab3.setUrl("ajax2.htm", null, true);
38079
38080 // Disabled tab
38081 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38082 tab4.disable();
38083
38084 jtabs.activate("jtabs-1");
38085  * </code></pre>
38086  * @constructor
38087  * Create a new TabPanel.
38088  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38089  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38090  */
38091 Roo.bootstrap.panel.Tabs = function(config){
38092     /**
38093     * The container element for this TabPanel.
38094     * @type Roo.Element
38095     */
38096     this.el = Roo.get(config.el);
38097     delete config.el;
38098     if(config){
38099         if(typeof config == "boolean"){
38100             this.tabPosition = config ? "bottom" : "top";
38101         }else{
38102             Roo.apply(this, config);
38103         }
38104     }
38105     
38106     if(this.tabPosition == "bottom"){
38107         // if tabs are at the bottom = create the body first.
38108         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38109         this.el.addClass("roo-tabs-bottom");
38110     }
38111     // next create the tabs holders
38112     
38113     if (this.tabPosition == "west"){
38114         
38115         var reg = this.region; // fake it..
38116         while (reg) {
38117             if (!reg.mgr.parent) {
38118                 break;
38119             }
38120             reg = reg.mgr.parent.region;
38121         }
38122         Roo.log("got nest?");
38123         Roo.log(reg);
38124         if (reg.mgr.getRegion('west')) {
38125             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38126             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38127             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38128             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38129             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38130         
38131             
38132         }
38133         
38134         
38135     } else {
38136      
38137         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38138         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38139         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38140         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38141     }
38142     
38143     
38144     if(Roo.isIE){
38145         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38146     }
38147     
38148     // finally - if tabs are at the top, then create the body last..
38149     if(this.tabPosition != "bottom"){
38150         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38151          * @type Roo.Element
38152          */
38153         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38154         this.el.addClass("roo-tabs-top");
38155     }
38156     this.items = [];
38157
38158     this.bodyEl.setStyle("position", "relative");
38159
38160     this.active = null;
38161     this.activateDelegate = this.activate.createDelegate(this);
38162
38163     this.addEvents({
38164         /**
38165          * @event tabchange
38166          * Fires when the active tab changes
38167          * @param {Roo.TabPanel} this
38168          * @param {Roo.TabPanelItem} activePanel The new active tab
38169          */
38170         "tabchange": true,
38171         /**
38172          * @event beforetabchange
38173          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38174          * @param {Roo.TabPanel} this
38175          * @param {Object} e Set cancel to true on this object to cancel the tab change
38176          * @param {Roo.TabPanelItem} tab The tab being changed to
38177          */
38178         "beforetabchange" : true
38179     });
38180
38181     Roo.EventManager.onWindowResize(this.onResize, this);
38182     this.cpad = this.el.getPadding("lr");
38183     this.hiddenCount = 0;
38184
38185
38186     // toolbar on the tabbar support...
38187     if (this.toolbar) {
38188         alert("no toolbar support yet");
38189         this.toolbar  = false;
38190         /*
38191         var tcfg = this.toolbar;
38192         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38193         this.toolbar = new Roo.Toolbar(tcfg);
38194         if (Roo.isSafari) {
38195             var tbl = tcfg.container.child('table', true);
38196             tbl.setAttribute('width', '100%');
38197         }
38198         */
38199         
38200     }
38201    
38202
38203
38204     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38205 };
38206
38207 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38208     /*
38209      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38210      */
38211     tabPosition : "top",
38212     /*
38213      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38214      */
38215     currentTabWidth : 0,
38216     /*
38217      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38218      */
38219     minTabWidth : 40,
38220     /*
38221      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38222      */
38223     maxTabWidth : 250,
38224     /*
38225      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38226      */
38227     preferredTabWidth : 175,
38228     /*
38229      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38230      */
38231     resizeTabs : false,
38232     /*
38233      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38234      */
38235     monitorResize : true,
38236     /*
38237      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38238      */
38239     toolbar : false,  // set by caller..
38240     
38241     region : false, /// set by caller
38242     
38243     disableTooltips : true, // not used yet...
38244
38245     /**
38246      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38247      * @param {String} id The id of the div to use <b>or create</b>
38248      * @param {String} text The text for the tab
38249      * @param {String} content (optional) Content to put in the TabPanelItem body
38250      * @param {Boolean} closable (optional) True to create a close icon on the tab
38251      * @return {Roo.TabPanelItem} The created TabPanelItem
38252      */
38253     addTab : function(id, text, content, closable, tpl)
38254     {
38255         var item = new Roo.bootstrap.panel.TabItem({
38256             panel: this,
38257             id : id,
38258             text : text,
38259             closable : closable,
38260             tpl : tpl
38261         });
38262         this.addTabItem(item);
38263         if(content){
38264             item.setContent(content);
38265         }
38266         return item;
38267     },
38268
38269     /**
38270      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38271      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38272      * @return {Roo.TabPanelItem}
38273      */
38274     getTab : function(id){
38275         return this.items[id];
38276     },
38277
38278     /**
38279      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38280      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38281      */
38282     hideTab : function(id){
38283         var t = this.items[id];
38284         if(!t.isHidden()){
38285            t.setHidden(true);
38286            this.hiddenCount++;
38287            this.autoSizeTabs();
38288         }
38289     },
38290
38291     /**
38292      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38293      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38294      */
38295     unhideTab : function(id){
38296         var t = this.items[id];
38297         if(t.isHidden()){
38298            t.setHidden(false);
38299            this.hiddenCount--;
38300            this.autoSizeTabs();
38301         }
38302     },
38303
38304     /**
38305      * Adds an existing {@link Roo.TabPanelItem}.
38306      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38307      */
38308     addTabItem : function(item)
38309     {
38310         this.items[item.id] = item;
38311         this.items.push(item);
38312         this.autoSizeTabs();
38313       //  if(this.resizeTabs){
38314     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38315   //         this.autoSizeTabs();
38316 //        }else{
38317 //            item.autoSize();
38318        // }
38319     },
38320
38321     /**
38322      * Removes a {@link Roo.TabPanelItem}.
38323      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38324      */
38325     removeTab : function(id){
38326         var items = this.items;
38327         var tab = items[id];
38328         if(!tab) { return; }
38329         var index = items.indexOf(tab);
38330         if(this.active == tab && items.length > 1){
38331             var newTab = this.getNextAvailable(index);
38332             if(newTab) {
38333                 newTab.activate();
38334             }
38335         }
38336         this.stripEl.dom.removeChild(tab.pnode.dom);
38337         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38338             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38339         }
38340         items.splice(index, 1);
38341         delete this.items[tab.id];
38342         tab.fireEvent("close", tab);
38343         tab.purgeListeners();
38344         this.autoSizeTabs();
38345     },
38346
38347     getNextAvailable : function(start){
38348         var items = this.items;
38349         var index = start;
38350         // look for a next tab that will slide over to
38351         // replace the one being removed
38352         while(index < items.length){
38353             var item = items[++index];
38354             if(item && !item.isHidden()){
38355                 return item;
38356             }
38357         }
38358         // if one isn't found select the previous tab (on the left)
38359         index = start;
38360         while(index >= 0){
38361             var item = items[--index];
38362             if(item && !item.isHidden()){
38363                 return item;
38364             }
38365         }
38366         return null;
38367     },
38368
38369     /**
38370      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38371      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38372      */
38373     disableTab : function(id){
38374         var tab = this.items[id];
38375         if(tab && this.active != tab){
38376             tab.disable();
38377         }
38378     },
38379
38380     /**
38381      * Enables a {@link Roo.TabPanelItem} that is disabled.
38382      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38383      */
38384     enableTab : function(id){
38385         var tab = this.items[id];
38386         tab.enable();
38387     },
38388
38389     /**
38390      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38391      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38392      * @return {Roo.TabPanelItem} The TabPanelItem.
38393      */
38394     activate : function(id)
38395     {
38396         //Roo.log('activite:'  + id);
38397         
38398         var tab = this.items[id];
38399         if(!tab){
38400             return null;
38401         }
38402         if(tab == this.active || tab.disabled){
38403             return tab;
38404         }
38405         var e = {};
38406         this.fireEvent("beforetabchange", this, e, tab);
38407         if(e.cancel !== true && !tab.disabled){
38408             if(this.active){
38409                 this.active.hide();
38410             }
38411             this.active = this.items[id];
38412             this.active.show();
38413             this.fireEvent("tabchange", this, this.active);
38414         }
38415         return tab;
38416     },
38417
38418     /**
38419      * Gets the active {@link Roo.TabPanelItem}.
38420      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38421      */
38422     getActiveTab : function(){
38423         return this.active;
38424     },
38425
38426     /**
38427      * Updates the tab body element to fit the height of the container element
38428      * for overflow scrolling
38429      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38430      */
38431     syncHeight : function(targetHeight){
38432         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38433         var bm = this.bodyEl.getMargins();
38434         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38435         this.bodyEl.setHeight(newHeight);
38436         return newHeight;
38437     },
38438
38439     onResize : function(){
38440         if(this.monitorResize){
38441             this.autoSizeTabs();
38442         }
38443     },
38444
38445     /**
38446      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38447      */
38448     beginUpdate : function(){
38449         this.updating = true;
38450     },
38451
38452     /**
38453      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38454      */
38455     endUpdate : function(){
38456         this.updating = false;
38457         this.autoSizeTabs();
38458     },
38459
38460     /**
38461      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38462      */
38463     autoSizeTabs : function()
38464     {
38465         var count = this.items.length;
38466         var vcount = count - this.hiddenCount;
38467         
38468         if (vcount < 2) {
38469             this.stripEl.hide();
38470         } else {
38471             this.stripEl.show();
38472         }
38473         
38474         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38475             return;
38476         }
38477         
38478         
38479         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38480         var availWidth = Math.floor(w / vcount);
38481         var b = this.stripBody;
38482         if(b.getWidth() > w){
38483             var tabs = this.items;
38484             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38485             if(availWidth < this.minTabWidth){
38486                 /*if(!this.sleft){    // incomplete scrolling code
38487                     this.createScrollButtons();
38488                 }
38489                 this.showScroll();
38490                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38491             }
38492         }else{
38493             if(this.currentTabWidth < this.preferredTabWidth){
38494                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38495             }
38496         }
38497     },
38498
38499     /**
38500      * Returns the number of tabs in this TabPanel.
38501      * @return {Number}
38502      */
38503      getCount : function(){
38504          return this.items.length;
38505      },
38506
38507     /**
38508      * Resizes all the tabs to the passed width
38509      * @param {Number} The new width
38510      */
38511     setTabWidth : function(width){
38512         this.currentTabWidth = width;
38513         for(var i = 0, len = this.items.length; i < len; i++) {
38514                 if(!this.items[i].isHidden()) {
38515                 this.items[i].setWidth(width);
38516             }
38517         }
38518     },
38519
38520     /**
38521      * Destroys this TabPanel
38522      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38523      */
38524     destroy : function(removeEl){
38525         Roo.EventManager.removeResizeListener(this.onResize, this);
38526         for(var i = 0, len = this.items.length; i < len; i++){
38527             this.items[i].purgeListeners();
38528         }
38529         if(removeEl === true){
38530             this.el.update("");
38531             this.el.remove();
38532         }
38533     },
38534     
38535     createStrip : function(container)
38536     {
38537         var strip = document.createElement("nav");
38538         strip.className = Roo.bootstrap.version == 4 ?
38539             "navbar-light bg-light" : 
38540             "navbar navbar-default"; //"x-tabs-wrap";
38541         container.appendChild(strip);
38542         return strip;
38543     },
38544     
38545     createStripList : function(strip)
38546     {
38547         // div wrapper for retard IE
38548         // returns the "tr" element.
38549         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38550         //'<div class="x-tabs-strip-wrap">'+
38551           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38552           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38553         return strip.firstChild; //.firstChild.firstChild.firstChild;
38554     },
38555     createBody : function(container)
38556     {
38557         var body = document.createElement("div");
38558         Roo.id(body, "tab-body");
38559         //Roo.fly(body).addClass("x-tabs-body");
38560         Roo.fly(body).addClass("tab-content");
38561         container.appendChild(body);
38562         return body;
38563     },
38564     createItemBody :function(bodyEl, id){
38565         var body = Roo.getDom(id);
38566         if(!body){
38567             body = document.createElement("div");
38568             body.id = id;
38569         }
38570         //Roo.fly(body).addClass("x-tabs-item-body");
38571         Roo.fly(body).addClass("tab-pane");
38572          bodyEl.insertBefore(body, bodyEl.firstChild);
38573         return body;
38574     },
38575     /** @private */
38576     createStripElements :  function(stripEl, text, closable, tpl)
38577     {
38578         var td = document.createElement("li"); // was td..
38579         td.className = 'nav-item';
38580         
38581         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38582         
38583         
38584         stripEl.appendChild(td);
38585         /*if(closable){
38586             td.className = "x-tabs-closable";
38587             if(!this.closeTpl){
38588                 this.closeTpl = new Roo.Template(
38589                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38590                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38591                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38592                 );
38593             }
38594             var el = this.closeTpl.overwrite(td, {"text": text});
38595             var close = el.getElementsByTagName("div")[0];
38596             var inner = el.getElementsByTagName("em")[0];
38597             return {"el": el, "close": close, "inner": inner};
38598         } else {
38599         */
38600         // not sure what this is..
38601 //            if(!this.tabTpl){
38602                 //this.tabTpl = new Roo.Template(
38603                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38604                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38605                 //);
38606 //                this.tabTpl = new Roo.Template(
38607 //                   '<a href="#">' +
38608 //                   '<span unselectable="on"' +
38609 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38610 //                            ' >{text}</span></a>'
38611 //                );
38612 //                
38613 //            }
38614
38615
38616             var template = tpl || this.tabTpl || false;
38617             
38618             if(!template){
38619                 template =  new Roo.Template(
38620                         Roo.bootstrap.version == 4 ? 
38621                             (
38622                                 '<a class="nav-link" href="#" unselectable="on"' +
38623                                      (this.disableTooltips ? '' : ' title="{text}"') +
38624                                      ' >{text}</a>'
38625                             ) : (
38626                                 '<a class="nav-link" href="#">' +
38627                                 '<span unselectable="on"' +
38628                                          (this.disableTooltips ? '' : ' title="{text}"') +
38629                                     ' >{text}</span></a>'
38630                             )
38631                 );
38632             }
38633             
38634             switch (typeof(template)) {
38635                 case 'object' :
38636                     break;
38637                 case 'string' :
38638                     template = new Roo.Template(template);
38639                     break;
38640                 default :
38641                     break;
38642             }
38643             
38644             var el = template.overwrite(td, {"text": text});
38645             
38646             var inner = el.getElementsByTagName("span")[0];
38647             
38648             return {"el": el, "inner": inner};
38649             
38650     }
38651         
38652     
38653 });
38654
38655 /**
38656  * @class Roo.TabPanelItem
38657  * @extends Roo.util.Observable
38658  * Represents an individual item (tab plus body) in a TabPanel.
38659  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38660  * @param {String} id The id of this TabPanelItem
38661  * @param {String} text The text for the tab of this TabPanelItem
38662  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38663  */
38664 Roo.bootstrap.panel.TabItem = function(config){
38665     /**
38666      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38667      * @type Roo.TabPanel
38668      */
38669     this.tabPanel = config.panel;
38670     /**
38671      * The id for this TabPanelItem
38672      * @type String
38673      */
38674     this.id = config.id;
38675     /** @private */
38676     this.disabled = false;
38677     /** @private */
38678     this.text = config.text;
38679     /** @private */
38680     this.loaded = false;
38681     this.closable = config.closable;
38682
38683     /**
38684      * The body element for this TabPanelItem.
38685      * @type Roo.Element
38686      */
38687     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38688     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38689     this.bodyEl.setStyle("display", "block");
38690     this.bodyEl.setStyle("zoom", "1");
38691     //this.hideAction();
38692
38693     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38694     /** @private */
38695     this.el = Roo.get(els.el);
38696     this.inner = Roo.get(els.inner, true);
38697      this.textEl = Roo.bootstrap.version == 4 ?
38698         this.el : Roo.get(this.el.dom.firstChild, true);
38699
38700     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38701     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38702
38703     
38704 //    this.el.on("mousedown", this.onTabMouseDown, this);
38705     this.el.on("click", this.onTabClick, this);
38706     /** @private */
38707     if(config.closable){
38708         var c = Roo.get(els.close, true);
38709         c.dom.title = this.closeText;
38710         c.addClassOnOver("close-over");
38711         c.on("click", this.closeClick, this);
38712      }
38713
38714     this.addEvents({
38715          /**
38716          * @event activate
38717          * Fires when this tab becomes the active tab.
38718          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38719          * @param {Roo.TabPanelItem} this
38720          */
38721         "activate": true,
38722         /**
38723          * @event beforeclose
38724          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38725          * @param {Roo.TabPanelItem} this
38726          * @param {Object} e Set cancel to true on this object to cancel the close.
38727          */
38728         "beforeclose": true,
38729         /**
38730          * @event close
38731          * Fires when this tab is closed.
38732          * @param {Roo.TabPanelItem} this
38733          */
38734          "close": true,
38735         /**
38736          * @event deactivate
38737          * Fires when this tab is no longer the active tab.
38738          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38739          * @param {Roo.TabPanelItem} this
38740          */
38741          "deactivate" : true
38742     });
38743     this.hidden = false;
38744
38745     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38746 };
38747
38748 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38749            {
38750     purgeListeners : function(){
38751        Roo.util.Observable.prototype.purgeListeners.call(this);
38752        this.el.removeAllListeners();
38753     },
38754     /**
38755      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38756      */
38757     show : function(){
38758         this.status_node.addClass("active");
38759         this.showAction();
38760         if(Roo.isOpera){
38761             this.tabPanel.stripWrap.repaint();
38762         }
38763         this.fireEvent("activate", this.tabPanel, this);
38764     },
38765
38766     /**
38767      * Returns true if this tab is the active tab.
38768      * @return {Boolean}
38769      */
38770     isActive : function(){
38771         return this.tabPanel.getActiveTab() == this;
38772     },
38773
38774     /**
38775      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38776      */
38777     hide : function(){
38778         this.status_node.removeClass("active");
38779         this.hideAction();
38780         this.fireEvent("deactivate", this.tabPanel, this);
38781     },
38782
38783     hideAction : function(){
38784         this.bodyEl.hide();
38785         this.bodyEl.setStyle("position", "absolute");
38786         this.bodyEl.setLeft("-20000px");
38787         this.bodyEl.setTop("-20000px");
38788     },
38789
38790     showAction : function(){
38791         this.bodyEl.setStyle("position", "relative");
38792         this.bodyEl.setTop("");
38793         this.bodyEl.setLeft("");
38794         this.bodyEl.show();
38795     },
38796
38797     /**
38798      * Set the tooltip for the tab.
38799      * @param {String} tooltip The tab's tooltip
38800      */
38801     setTooltip : function(text){
38802         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38803             this.textEl.dom.qtip = text;
38804             this.textEl.dom.removeAttribute('title');
38805         }else{
38806             this.textEl.dom.title = text;
38807         }
38808     },
38809
38810     onTabClick : function(e){
38811         e.preventDefault();
38812         this.tabPanel.activate(this.id);
38813     },
38814
38815     onTabMouseDown : function(e){
38816         e.preventDefault();
38817         this.tabPanel.activate(this.id);
38818     },
38819 /*
38820     getWidth : function(){
38821         return this.inner.getWidth();
38822     },
38823
38824     setWidth : function(width){
38825         var iwidth = width - this.linode.getPadding("lr");
38826         this.inner.setWidth(iwidth);
38827         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38828         this.linode.setWidth(width);
38829     },
38830 */
38831     /**
38832      * Show or hide the tab
38833      * @param {Boolean} hidden True to hide or false to show.
38834      */
38835     setHidden : function(hidden){
38836         this.hidden = hidden;
38837         this.linode.setStyle("display", hidden ? "none" : "");
38838     },
38839
38840     /**
38841      * Returns true if this tab is "hidden"
38842      * @return {Boolean}
38843      */
38844     isHidden : function(){
38845         return this.hidden;
38846     },
38847
38848     /**
38849      * Returns the text for this tab
38850      * @return {String}
38851      */
38852     getText : function(){
38853         return this.text;
38854     },
38855     /*
38856     autoSize : function(){
38857         //this.el.beginMeasure();
38858         this.textEl.setWidth(1);
38859         /*
38860          *  #2804 [new] Tabs in Roojs
38861          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38862          */
38863         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38864         //this.el.endMeasure();
38865     //},
38866
38867     /**
38868      * Sets the text for the tab (Note: this also sets the tooltip text)
38869      * @param {String} text The tab's text and tooltip
38870      */
38871     setText : function(text){
38872         this.text = text;
38873         this.textEl.update(text);
38874         this.setTooltip(text);
38875         //if(!this.tabPanel.resizeTabs){
38876         //    this.autoSize();
38877         //}
38878     },
38879     /**
38880      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38881      */
38882     activate : function(){
38883         this.tabPanel.activate(this.id);
38884     },
38885
38886     /**
38887      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38888      */
38889     disable : function(){
38890         if(this.tabPanel.active != this){
38891             this.disabled = true;
38892             this.status_node.addClass("disabled");
38893         }
38894     },
38895
38896     /**
38897      * Enables this TabPanelItem if it was previously disabled.
38898      */
38899     enable : function(){
38900         this.disabled = false;
38901         this.status_node.removeClass("disabled");
38902     },
38903
38904     /**
38905      * Sets the content for this TabPanelItem.
38906      * @param {String} content The content
38907      * @param {Boolean} loadScripts true to look for and load scripts
38908      */
38909     setContent : function(content, loadScripts){
38910         this.bodyEl.update(content, loadScripts);
38911     },
38912
38913     /**
38914      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38915      * @return {Roo.UpdateManager} The UpdateManager
38916      */
38917     getUpdateManager : function(){
38918         return this.bodyEl.getUpdateManager();
38919     },
38920
38921     /**
38922      * Set a URL to be used to load the content for this TabPanelItem.
38923      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38924      * @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)
38925      * @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)
38926      * @return {Roo.UpdateManager} The UpdateManager
38927      */
38928     setUrl : function(url, params, loadOnce){
38929         if(this.refreshDelegate){
38930             this.un('activate', this.refreshDelegate);
38931         }
38932         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38933         this.on("activate", this.refreshDelegate);
38934         return this.bodyEl.getUpdateManager();
38935     },
38936
38937     /** @private */
38938     _handleRefresh : function(url, params, loadOnce){
38939         if(!loadOnce || !this.loaded){
38940             var updater = this.bodyEl.getUpdateManager();
38941             updater.update(url, params, this._setLoaded.createDelegate(this));
38942         }
38943     },
38944
38945     /**
38946      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38947      *   Will fail silently if the setUrl method has not been called.
38948      *   This does not activate the panel, just updates its content.
38949      */
38950     refresh : function(){
38951         if(this.refreshDelegate){
38952            this.loaded = false;
38953            this.refreshDelegate();
38954         }
38955     },
38956
38957     /** @private */
38958     _setLoaded : function(){
38959         this.loaded = true;
38960     },
38961
38962     /** @private */
38963     closeClick : function(e){
38964         var o = {};
38965         e.stopEvent();
38966         this.fireEvent("beforeclose", this, o);
38967         if(o.cancel !== true){
38968             this.tabPanel.removeTab(this.id);
38969         }
38970     },
38971     /**
38972      * The text displayed in the tooltip for the close icon.
38973      * @type String
38974      */
38975     closeText : "Close this tab"
38976 });
38977 /**
38978 *    This script refer to:
38979 *    Title: International Telephone Input
38980 *    Author: Jack O'Connor
38981 *    Code version:  v12.1.12
38982 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38983 **/
38984
38985 Roo.bootstrap.PhoneInputData = function() {
38986     var d = [
38987       [
38988         "Afghanistan (‫افغانستان‬‎)",
38989         "af",
38990         "93"
38991       ],
38992       [
38993         "Albania (Shqipëri)",
38994         "al",
38995         "355"
38996       ],
38997       [
38998         "Algeria (‫الجزائر‬‎)",
38999         "dz",
39000         "213"
39001       ],
39002       [
39003         "American Samoa",
39004         "as",
39005         "1684"
39006       ],
39007       [
39008         "Andorra",
39009         "ad",
39010         "376"
39011       ],
39012       [
39013         "Angola",
39014         "ao",
39015         "244"
39016       ],
39017       [
39018         "Anguilla",
39019         "ai",
39020         "1264"
39021       ],
39022       [
39023         "Antigua and Barbuda",
39024         "ag",
39025         "1268"
39026       ],
39027       [
39028         "Argentina",
39029         "ar",
39030         "54"
39031       ],
39032       [
39033         "Armenia (Հայաստան)",
39034         "am",
39035         "374"
39036       ],
39037       [
39038         "Aruba",
39039         "aw",
39040         "297"
39041       ],
39042       [
39043         "Australia",
39044         "au",
39045         "61",
39046         0
39047       ],
39048       [
39049         "Austria (Österreich)",
39050         "at",
39051         "43"
39052       ],
39053       [
39054         "Azerbaijan (Azərbaycan)",
39055         "az",
39056         "994"
39057       ],
39058       [
39059         "Bahamas",
39060         "bs",
39061         "1242"
39062       ],
39063       [
39064         "Bahrain (‫البحرين‬‎)",
39065         "bh",
39066         "973"
39067       ],
39068       [
39069         "Bangladesh (বাংলাদেশ)",
39070         "bd",
39071         "880"
39072       ],
39073       [
39074         "Barbados",
39075         "bb",
39076         "1246"
39077       ],
39078       [
39079         "Belarus (Беларусь)",
39080         "by",
39081         "375"
39082       ],
39083       [
39084         "Belgium (België)",
39085         "be",
39086         "32"
39087       ],
39088       [
39089         "Belize",
39090         "bz",
39091         "501"
39092       ],
39093       [
39094         "Benin (Bénin)",
39095         "bj",
39096         "229"
39097       ],
39098       [
39099         "Bermuda",
39100         "bm",
39101         "1441"
39102       ],
39103       [
39104         "Bhutan (འབྲུག)",
39105         "bt",
39106         "975"
39107       ],
39108       [
39109         "Bolivia",
39110         "bo",
39111         "591"
39112       ],
39113       [
39114         "Bosnia and Herzegovina (Босна и Херцеговина)",
39115         "ba",
39116         "387"
39117       ],
39118       [
39119         "Botswana",
39120         "bw",
39121         "267"
39122       ],
39123       [
39124         "Brazil (Brasil)",
39125         "br",
39126         "55"
39127       ],
39128       [
39129         "British Indian Ocean Territory",
39130         "io",
39131         "246"
39132       ],
39133       [
39134         "British Virgin Islands",
39135         "vg",
39136         "1284"
39137       ],
39138       [
39139         "Brunei",
39140         "bn",
39141         "673"
39142       ],
39143       [
39144         "Bulgaria (България)",
39145         "bg",
39146         "359"
39147       ],
39148       [
39149         "Burkina Faso",
39150         "bf",
39151         "226"
39152       ],
39153       [
39154         "Burundi (Uburundi)",
39155         "bi",
39156         "257"
39157       ],
39158       [
39159         "Cambodia (កម្ពុជា)",
39160         "kh",
39161         "855"
39162       ],
39163       [
39164         "Cameroon (Cameroun)",
39165         "cm",
39166         "237"
39167       ],
39168       [
39169         "Canada",
39170         "ca",
39171         "1",
39172         1,
39173         ["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"]
39174       ],
39175       [
39176         "Cape Verde (Kabu Verdi)",
39177         "cv",
39178         "238"
39179       ],
39180       [
39181         "Caribbean Netherlands",
39182         "bq",
39183         "599",
39184         1
39185       ],
39186       [
39187         "Cayman Islands",
39188         "ky",
39189         "1345"
39190       ],
39191       [
39192         "Central African Republic (République centrafricaine)",
39193         "cf",
39194         "236"
39195       ],
39196       [
39197         "Chad (Tchad)",
39198         "td",
39199         "235"
39200       ],
39201       [
39202         "Chile",
39203         "cl",
39204         "56"
39205       ],
39206       [
39207         "China (中国)",
39208         "cn",
39209         "86"
39210       ],
39211       [
39212         "Christmas Island",
39213         "cx",
39214         "61",
39215         2
39216       ],
39217       [
39218         "Cocos (Keeling) Islands",
39219         "cc",
39220         "61",
39221         1
39222       ],
39223       [
39224         "Colombia",
39225         "co",
39226         "57"
39227       ],
39228       [
39229         "Comoros (‫جزر القمر‬‎)",
39230         "km",
39231         "269"
39232       ],
39233       [
39234         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39235         "cd",
39236         "243"
39237       ],
39238       [
39239         "Congo (Republic) (Congo-Brazzaville)",
39240         "cg",
39241         "242"
39242       ],
39243       [
39244         "Cook Islands",
39245         "ck",
39246         "682"
39247       ],
39248       [
39249         "Costa Rica",
39250         "cr",
39251         "506"
39252       ],
39253       [
39254         "Côte d’Ivoire",
39255         "ci",
39256         "225"
39257       ],
39258       [
39259         "Croatia (Hrvatska)",
39260         "hr",
39261         "385"
39262       ],
39263       [
39264         "Cuba",
39265         "cu",
39266         "53"
39267       ],
39268       [
39269         "Curaçao",
39270         "cw",
39271         "599",
39272         0
39273       ],
39274       [
39275         "Cyprus (Κύπρος)",
39276         "cy",
39277         "357"
39278       ],
39279       [
39280         "Czech Republic (Česká republika)",
39281         "cz",
39282         "420"
39283       ],
39284       [
39285         "Denmark (Danmark)",
39286         "dk",
39287         "45"
39288       ],
39289       [
39290         "Djibouti",
39291         "dj",
39292         "253"
39293       ],
39294       [
39295         "Dominica",
39296         "dm",
39297         "1767"
39298       ],
39299       [
39300         "Dominican Republic (República Dominicana)",
39301         "do",
39302         "1",
39303         2,
39304         ["809", "829", "849"]
39305       ],
39306       [
39307         "Ecuador",
39308         "ec",
39309         "593"
39310       ],
39311       [
39312         "Egypt (‫مصر‬‎)",
39313         "eg",
39314         "20"
39315       ],
39316       [
39317         "El Salvador",
39318         "sv",
39319         "503"
39320       ],
39321       [
39322         "Equatorial Guinea (Guinea Ecuatorial)",
39323         "gq",
39324         "240"
39325       ],
39326       [
39327         "Eritrea",
39328         "er",
39329         "291"
39330       ],
39331       [
39332         "Estonia (Eesti)",
39333         "ee",
39334         "372"
39335       ],
39336       [
39337         "Ethiopia",
39338         "et",
39339         "251"
39340       ],
39341       [
39342         "Falkland Islands (Islas Malvinas)",
39343         "fk",
39344         "500"
39345       ],
39346       [
39347         "Faroe Islands (Føroyar)",
39348         "fo",
39349         "298"
39350       ],
39351       [
39352         "Fiji",
39353         "fj",
39354         "679"
39355       ],
39356       [
39357         "Finland (Suomi)",
39358         "fi",
39359         "358",
39360         0
39361       ],
39362       [
39363         "France",
39364         "fr",
39365         "33"
39366       ],
39367       [
39368         "French Guiana (Guyane française)",
39369         "gf",
39370         "594"
39371       ],
39372       [
39373         "French Polynesia (Polynésie française)",
39374         "pf",
39375         "689"
39376       ],
39377       [
39378         "Gabon",
39379         "ga",
39380         "241"
39381       ],
39382       [
39383         "Gambia",
39384         "gm",
39385         "220"
39386       ],
39387       [
39388         "Georgia (საქართველო)",
39389         "ge",
39390         "995"
39391       ],
39392       [
39393         "Germany (Deutschland)",
39394         "de",
39395         "49"
39396       ],
39397       [
39398         "Ghana (Gaana)",
39399         "gh",
39400         "233"
39401       ],
39402       [
39403         "Gibraltar",
39404         "gi",
39405         "350"
39406       ],
39407       [
39408         "Greece (Ελλάδα)",
39409         "gr",
39410         "30"
39411       ],
39412       [
39413         "Greenland (Kalaallit Nunaat)",
39414         "gl",
39415         "299"
39416       ],
39417       [
39418         "Grenada",
39419         "gd",
39420         "1473"
39421       ],
39422       [
39423         "Guadeloupe",
39424         "gp",
39425         "590",
39426         0
39427       ],
39428       [
39429         "Guam",
39430         "gu",
39431         "1671"
39432       ],
39433       [
39434         "Guatemala",
39435         "gt",
39436         "502"
39437       ],
39438       [
39439         "Guernsey",
39440         "gg",
39441         "44",
39442         1
39443       ],
39444       [
39445         "Guinea (Guinée)",
39446         "gn",
39447         "224"
39448       ],
39449       [
39450         "Guinea-Bissau (Guiné Bissau)",
39451         "gw",
39452         "245"
39453       ],
39454       [
39455         "Guyana",
39456         "gy",
39457         "592"
39458       ],
39459       [
39460         "Haiti",
39461         "ht",
39462         "509"
39463       ],
39464       [
39465         "Honduras",
39466         "hn",
39467         "504"
39468       ],
39469       [
39470         "Hong Kong (香港)",
39471         "hk",
39472         "852"
39473       ],
39474       [
39475         "Hungary (Magyarország)",
39476         "hu",
39477         "36"
39478       ],
39479       [
39480         "Iceland (Ísland)",
39481         "is",
39482         "354"
39483       ],
39484       [
39485         "India (भारत)",
39486         "in",
39487         "91"
39488       ],
39489       [
39490         "Indonesia",
39491         "id",
39492         "62"
39493       ],
39494       [
39495         "Iran (‫ایران‬‎)",
39496         "ir",
39497         "98"
39498       ],
39499       [
39500         "Iraq (‫العراق‬‎)",
39501         "iq",
39502         "964"
39503       ],
39504       [
39505         "Ireland",
39506         "ie",
39507         "353"
39508       ],
39509       [
39510         "Isle of Man",
39511         "im",
39512         "44",
39513         2
39514       ],
39515       [
39516         "Israel (‫ישראל‬‎)",
39517         "il",
39518         "972"
39519       ],
39520       [
39521         "Italy (Italia)",
39522         "it",
39523         "39",
39524         0
39525       ],
39526       [
39527         "Jamaica",
39528         "jm",
39529         "1876"
39530       ],
39531       [
39532         "Japan (日本)",
39533         "jp",
39534         "81"
39535       ],
39536       [
39537         "Jersey",
39538         "je",
39539         "44",
39540         3
39541       ],
39542       [
39543         "Jordan (‫الأردن‬‎)",
39544         "jo",
39545         "962"
39546       ],
39547       [
39548         "Kazakhstan (Казахстан)",
39549         "kz",
39550         "7",
39551         1
39552       ],
39553       [
39554         "Kenya",
39555         "ke",
39556         "254"
39557       ],
39558       [
39559         "Kiribati",
39560         "ki",
39561         "686"
39562       ],
39563       [
39564         "Kosovo",
39565         "xk",
39566         "383"
39567       ],
39568       [
39569         "Kuwait (‫الكويت‬‎)",
39570         "kw",
39571         "965"
39572       ],
39573       [
39574         "Kyrgyzstan (Кыргызстан)",
39575         "kg",
39576         "996"
39577       ],
39578       [
39579         "Laos (ລາວ)",
39580         "la",
39581         "856"
39582       ],
39583       [
39584         "Latvia (Latvija)",
39585         "lv",
39586         "371"
39587       ],
39588       [
39589         "Lebanon (‫لبنان‬‎)",
39590         "lb",
39591         "961"
39592       ],
39593       [
39594         "Lesotho",
39595         "ls",
39596         "266"
39597       ],
39598       [
39599         "Liberia",
39600         "lr",
39601         "231"
39602       ],
39603       [
39604         "Libya (‫ليبيا‬‎)",
39605         "ly",
39606         "218"
39607       ],
39608       [
39609         "Liechtenstein",
39610         "li",
39611         "423"
39612       ],
39613       [
39614         "Lithuania (Lietuva)",
39615         "lt",
39616         "370"
39617       ],
39618       [
39619         "Luxembourg",
39620         "lu",
39621         "352"
39622       ],
39623       [
39624         "Macau (澳門)",
39625         "mo",
39626         "853"
39627       ],
39628       [
39629         "Macedonia (FYROM) (Македонија)",
39630         "mk",
39631         "389"
39632       ],
39633       [
39634         "Madagascar (Madagasikara)",
39635         "mg",
39636         "261"
39637       ],
39638       [
39639         "Malawi",
39640         "mw",
39641         "265"
39642       ],
39643       [
39644         "Malaysia",
39645         "my",
39646         "60"
39647       ],
39648       [
39649         "Maldives",
39650         "mv",
39651         "960"
39652       ],
39653       [
39654         "Mali",
39655         "ml",
39656         "223"
39657       ],
39658       [
39659         "Malta",
39660         "mt",
39661         "356"
39662       ],
39663       [
39664         "Marshall Islands",
39665         "mh",
39666         "692"
39667       ],
39668       [
39669         "Martinique",
39670         "mq",
39671         "596"
39672       ],
39673       [
39674         "Mauritania (‫موريتانيا‬‎)",
39675         "mr",
39676         "222"
39677       ],
39678       [
39679         "Mauritius (Moris)",
39680         "mu",
39681         "230"
39682       ],
39683       [
39684         "Mayotte",
39685         "yt",
39686         "262",
39687         1
39688       ],
39689       [
39690         "Mexico (México)",
39691         "mx",
39692         "52"
39693       ],
39694       [
39695         "Micronesia",
39696         "fm",
39697         "691"
39698       ],
39699       [
39700         "Moldova (Republica Moldova)",
39701         "md",
39702         "373"
39703       ],
39704       [
39705         "Monaco",
39706         "mc",
39707         "377"
39708       ],
39709       [
39710         "Mongolia (Монгол)",
39711         "mn",
39712         "976"
39713       ],
39714       [
39715         "Montenegro (Crna Gora)",
39716         "me",
39717         "382"
39718       ],
39719       [
39720         "Montserrat",
39721         "ms",
39722         "1664"
39723       ],
39724       [
39725         "Morocco (‫المغرب‬‎)",
39726         "ma",
39727         "212",
39728         0
39729       ],
39730       [
39731         "Mozambique (Moçambique)",
39732         "mz",
39733         "258"
39734       ],
39735       [
39736         "Myanmar (Burma) (မြန်မာ)",
39737         "mm",
39738         "95"
39739       ],
39740       [
39741         "Namibia (Namibië)",
39742         "na",
39743         "264"
39744       ],
39745       [
39746         "Nauru",
39747         "nr",
39748         "674"
39749       ],
39750       [
39751         "Nepal (नेपाल)",
39752         "np",
39753         "977"
39754       ],
39755       [
39756         "Netherlands (Nederland)",
39757         "nl",
39758         "31"
39759       ],
39760       [
39761         "New Caledonia (Nouvelle-Calédonie)",
39762         "nc",
39763         "687"
39764       ],
39765       [
39766         "New Zealand",
39767         "nz",
39768         "64"
39769       ],
39770       [
39771         "Nicaragua",
39772         "ni",
39773         "505"
39774       ],
39775       [
39776         "Niger (Nijar)",
39777         "ne",
39778         "227"
39779       ],
39780       [
39781         "Nigeria",
39782         "ng",
39783         "234"
39784       ],
39785       [
39786         "Niue",
39787         "nu",
39788         "683"
39789       ],
39790       [
39791         "Norfolk Island",
39792         "nf",
39793         "672"
39794       ],
39795       [
39796         "North Korea (조선 민주주의 인민 공화국)",
39797         "kp",
39798         "850"
39799       ],
39800       [
39801         "Northern Mariana Islands",
39802         "mp",
39803         "1670"
39804       ],
39805       [
39806         "Norway (Norge)",
39807         "no",
39808         "47",
39809         0
39810       ],
39811       [
39812         "Oman (‫عُمان‬‎)",
39813         "om",
39814         "968"
39815       ],
39816       [
39817         "Pakistan (‫پاکستان‬‎)",
39818         "pk",
39819         "92"
39820       ],
39821       [
39822         "Palau",
39823         "pw",
39824         "680"
39825       ],
39826       [
39827         "Palestine (‫فلسطين‬‎)",
39828         "ps",
39829         "970"
39830       ],
39831       [
39832         "Panama (Panamá)",
39833         "pa",
39834         "507"
39835       ],
39836       [
39837         "Papua New Guinea",
39838         "pg",
39839         "675"
39840       ],
39841       [
39842         "Paraguay",
39843         "py",
39844         "595"
39845       ],
39846       [
39847         "Peru (Perú)",
39848         "pe",
39849         "51"
39850       ],
39851       [
39852         "Philippines",
39853         "ph",
39854         "63"
39855       ],
39856       [
39857         "Poland (Polska)",
39858         "pl",
39859         "48"
39860       ],
39861       [
39862         "Portugal",
39863         "pt",
39864         "351"
39865       ],
39866       [
39867         "Puerto Rico",
39868         "pr",
39869         "1",
39870         3,
39871         ["787", "939"]
39872       ],
39873       [
39874         "Qatar (‫قطر‬‎)",
39875         "qa",
39876         "974"
39877       ],
39878       [
39879         "Réunion (La Réunion)",
39880         "re",
39881         "262",
39882         0
39883       ],
39884       [
39885         "Romania (România)",
39886         "ro",
39887         "40"
39888       ],
39889       [
39890         "Russia (Россия)",
39891         "ru",
39892         "7",
39893         0
39894       ],
39895       [
39896         "Rwanda",
39897         "rw",
39898         "250"
39899       ],
39900       [
39901         "Saint Barthélemy",
39902         "bl",
39903         "590",
39904         1
39905       ],
39906       [
39907         "Saint Helena",
39908         "sh",
39909         "290"
39910       ],
39911       [
39912         "Saint Kitts and Nevis",
39913         "kn",
39914         "1869"
39915       ],
39916       [
39917         "Saint Lucia",
39918         "lc",
39919         "1758"
39920       ],
39921       [
39922         "Saint Martin (Saint-Martin (partie française))",
39923         "mf",
39924         "590",
39925         2
39926       ],
39927       [
39928         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39929         "pm",
39930         "508"
39931       ],
39932       [
39933         "Saint Vincent and the Grenadines",
39934         "vc",
39935         "1784"
39936       ],
39937       [
39938         "Samoa",
39939         "ws",
39940         "685"
39941       ],
39942       [
39943         "San Marino",
39944         "sm",
39945         "378"
39946       ],
39947       [
39948         "São Tomé and Príncipe (São Tomé e Príncipe)",
39949         "st",
39950         "239"
39951       ],
39952       [
39953         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39954         "sa",
39955         "966"
39956       ],
39957       [
39958         "Senegal (Sénégal)",
39959         "sn",
39960         "221"
39961       ],
39962       [
39963         "Serbia (Србија)",
39964         "rs",
39965         "381"
39966       ],
39967       [
39968         "Seychelles",
39969         "sc",
39970         "248"
39971       ],
39972       [
39973         "Sierra Leone",
39974         "sl",
39975         "232"
39976       ],
39977       [
39978         "Singapore",
39979         "sg",
39980         "65"
39981       ],
39982       [
39983         "Sint Maarten",
39984         "sx",
39985         "1721"
39986       ],
39987       [
39988         "Slovakia (Slovensko)",
39989         "sk",
39990         "421"
39991       ],
39992       [
39993         "Slovenia (Slovenija)",
39994         "si",
39995         "386"
39996       ],
39997       [
39998         "Solomon Islands",
39999         "sb",
40000         "677"
40001       ],
40002       [
40003         "Somalia (Soomaaliya)",
40004         "so",
40005         "252"
40006       ],
40007       [
40008         "South Africa",
40009         "za",
40010         "27"
40011       ],
40012       [
40013         "South Korea (대한민국)",
40014         "kr",
40015         "82"
40016       ],
40017       [
40018         "South Sudan (‫جنوب السودان‬‎)",
40019         "ss",
40020         "211"
40021       ],
40022       [
40023         "Spain (España)",
40024         "es",
40025         "34"
40026       ],
40027       [
40028         "Sri Lanka (ශ්‍රී ලංකාව)",
40029         "lk",
40030         "94"
40031       ],
40032       [
40033         "Sudan (‫السودان‬‎)",
40034         "sd",
40035         "249"
40036       ],
40037       [
40038         "Suriname",
40039         "sr",
40040         "597"
40041       ],
40042       [
40043         "Svalbard and Jan Mayen",
40044         "sj",
40045         "47",
40046         1
40047       ],
40048       [
40049         "Swaziland",
40050         "sz",
40051         "268"
40052       ],
40053       [
40054         "Sweden (Sverige)",
40055         "se",
40056         "46"
40057       ],
40058       [
40059         "Switzerland (Schweiz)",
40060         "ch",
40061         "41"
40062       ],
40063       [
40064         "Syria (‫سوريا‬‎)",
40065         "sy",
40066         "963"
40067       ],
40068       [
40069         "Taiwan (台灣)",
40070         "tw",
40071         "886"
40072       ],
40073       [
40074         "Tajikistan",
40075         "tj",
40076         "992"
40077       ],
40078       [
40079         "Tanzania",
40080         "tz",
40081         "255"
40082       ],
40083       [
40084         "Thailand (ไทย)",
40085         "th",
40086         "66"
40087       ],
40088       [
40089         "Timor-Leste",
40090         "tl",
40091         "670"
40092       ],
40093       [
40094         "Togo",
40095         "tg",
40096         "228"
40097       ],
40098       [
40099         "Tokelau",
40100         "tk",
40101         "690"
40102       ],
40103       [
40104         "Tonga",
40105         "to",
40106         "676"
40107       ],
40108       [
40109         "Trinidad and Tobago",
40110         "tt",
40111         "1868"
40112       ],
40113       [
40114         "Tunisia (‫تونس‬‎)",
40115         "tn",
40116         "216"
40117       ],
40118       [
40119         "Turkey (Türkiye)",
40120         "tr",
40121         "90"
40122       ],
40123       [
40124         "Turkmenistan",
40125         "tm",
40126         "993"
40127       ],
40128       [
40129         "Turks and Caicos Islands",
40130         "tc",
40131         "1649"
40132       ],
40133       [
40134         "Tuvalu",
40135         "tv",
40136         "688"
40137       ],
40138       [
40139         "U.S. Virgin Islands",
40140         "vi",
40141         "1340"
40142       ],
40143       [
40144         "Uganda",
40145         "ug",
40146         "256"
40147       ],
40148       [
40149         "Ukraine (Україна)",
40150         "ua",
40151         "380"
40152       ],
40153       [
40154         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40155         "ae",
40156         "971"
40157       ],
40158       [
40159         "United Kingdom",
40160         "gb",
40161         "44",
40162         0
40163       ],
40164       [
40165         "United States",
40166         "us",
40167         "1",
40168         0
40169       ],
40170       [
40171         "Uruguay",
40172         "uy",
40173         "598"
40174       ],
40175       [
40176         "Uzbekistan (Oʻzbekiston)",
40177         "uz",
40178         "998"
40179       ],
40180       [
40181         "Vanuatu",
40182         "vu",
40183         "678"
40184       ],
40185       [
40186         "Vatican City (Città del Vaticano)",
40187         "va",
40188         "39",
40189         1
40190       ],
40191       [
40192         "Venezuela",
40193         "ve",
40194         "58"
40195       ],
40196       [
40197         "Vietnam (Việt Nam)",
40198         "vn",
40199         "84"
40200       ],
40201       [
40202         "Wallis and Futuna (Wallis-et-Futuna)",
40203         "wf",
40204         "681"
40205       ],
40206       [
40207         "Western Sahara (‫الصحراء الغربية‬‎)",
40208         "eh",
40209         "212",
40210         1
40211       ],
40212       [
40213         "Yemen (‫اليمن‬‎)",
40214         "ye",
40215         "967"
40216       ],
40217       [
40218         "Zambia",
40219         "zm",
40220         "260"
40221       ],
40222       [
40223         "Zimbabwe",
40224         "zw",
40225         "263"
40226       ],
40227       [
40228         "Åland Islands",
40229         "ax",
40230         "358",
40231         1
40232       ]
40233   ];
40234   
40235   return d;
40236 }/**
40237 *    This script refer to:
40238 *    Title: International Telephone Input
40239 *    Author: Jack O'Connor
40240 *    Code version:  v12.1.12
40241 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40242 **/
40243
40244 /**
40245  * @class Roo.bootstrap.PhoneInput
40246  * @extends Roo.bootstrap.TriggerField
40247  * An input with International dial-code selection
40248  
40249  * @cfg {String} defaultDialCode default '+852'
40250  * @cfg {Array} preferedCountries default []
40251   
40252  * @constructor
40253  * Create a new PhoneInput.
40254  * @param {Object} config Configuration options
40255  */
40256
40257 Roo.bootstrap.PhoneInput = function(config) {
40258     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40259 };
40260
40261 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40262         
40263         listWidth: undefined,
40264         
40265         selectedClass: 'active',
40266         
40267         invalidClass : "has-warning",
40268         
40269         validClass: 'has-success',
40270         
40271         allowed: '0123456789',
40272         
40273         max_length: 15,
40274         
40275         /**
40276          * @cfg {String} defaultDialCode The default dial code when initializing the input
40277          */
40278         defaultDialCode: '+852',
40279         
40280         /**
40281          * @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
40282          */
40283         preferedCountries: false,
40284         
40285         getAutoCreate : function()
40286         {
40287             var data = Roo.bootstrap.PhoneInputData();
40288             var align = this.labelAlign || this.parentLabelAlign();
40289             var id = Roo.id();
40290             
40291             this.allCountries = [];
40292             this.dialCodeMapping = [];
40293             
40294             for (var i = 0; i < data.length; i++) {
40295               var c = data[i];
40296               this.allCountries[i] = {
40297                 name: c[0],
40298                 iso2: c[1],
40299                 dialCode: c[2],
40300                 priority: c[3] || 0,
40301                 areaCodes: c[4] || null
40302               };
40303               this.dialCodeMapping[c[2]] = {
40304                   name: c[0],
40305                   iso2: c[1],
40306                   priority: c[3] || 0,
40307                   areaCodes: c[4] || null
40308               };
40309             }
40310             
40311             var cfg = {
40312                 cls: 'form-group',
40313                 cn: []
40314             };
40315             
40316             var input =  {
40317                 tag: 'input',
40318                 id : id,
40319                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40320                 maxlength: this.max_length,
40321                 cls : 'form-control tel-input',
40322                 autocomplete: 'new-password'
40323             };
40324             
40325             var hiddenInput = {
40326                 tag: 'input',
40327                 type: 'hidden',
40328                 cls: 'hidden-tel-input'
40329             };
40330             
40331             if (this.name) {
40332                 hiddenInput.name = this.name;
40333             }
40334             
40335             if (this.disabled) {
40336                 input.disabled = true;
40337             }
40338             
40339             var flag_container = {
40340                 tag: 'div',
40341                 cls: 'flag-box',
40342                 cn: [
40343                     {
40344                         tag: 'div',
40345                         cls: 'flag'
40346                     },
40347                     {
40348                         tag: 'div',
40349                         cls: 'caret'
40350                     }
40351                 ]
40352             };
40353             
40354             var box = {
40355                 tag: 'div',
40356                 cls: this.hasFeedback ? 'has-feedback' : '',
40357                 cn: [
40358                     hiddenInput,
40359                     input,
40360                     {
40361                         tag: 'input',
40362                         cls: 'dial-code-holder',
40363                         disabled: true
40364                     }
40365                 ]
40366             };
40367             
40368             var container = {
40369                 cls: 'roo-select2-container input-group',
40370                 cn: [
40371                     flag_container,
40372                     box
40373                 ]
40374             };
40375             
40376             if (this.fieldLabel.length) {
40377                 var indicator = {
40378                     tag: 'i',
40379                     tooltip: 'This field is required'
40380                 };
40381                 
40382                 var label = {
40383                     tag: 'label',
40384                     'for':  id,
40385                     cls: 'control-label',
40386                     cn: []
40387                 };
40388                 
40389                 var label_text = {
40390                     tag: 'span',
40391                     html: this.fieldLabel
40392                 };
40393                 
40394                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40395                 label.cn = [
40396                     indicator,
40397                     label_text
40398                 ];
40399                 
40400                 if(this.indicatorpos == 'right') {
40401                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40402                     label.cn = [
40403                         label_text,
40404                         indicator
40405                     ];
40406                 }
40407                 
40408                 if(align == 'left') {
40409                     container = {
40410                         tag: 'div',
40411                         cn: [
40412                             container
40413                         ]
40414                     };
40415                     
40416                     if(this.labelWidth > 12){
40417                         label.style = "width: " + this.labelWidth + 'px';
40418                     }
40419                     if(this.labelWidth < 13 && this.labelmd == 0){
40420                         this.labelmd = this.labelWidth;
40421                     }
40422                     if(this.labellg > 0){
40423                         label.cls += ' col-lg-' + this.labellg;
40424                         input.cls += ' col-lg-' + (12 - this.labellg);
40425                     }
40426                     if(this.labelmd > 0){
40427                         label.cls += ' col-md-' + this.labelmd;
40428                         container.cls += ' col-md-' + (12 - this.labelmd);
40429                     }
40430                     if(this.labelsm > 0){
40431                         label.cls += ' col-sm-' + this.labelsm;
40432                         container.cls += ' col-sm-' + (12 - this.labelsm);
40433                     }
40434                     if(this.labelxs > 0){
40435                         label.cls += ' col-xs-' + this.labelxs;
40436                         container.cls += ' col-xs-' + (12 - this.labelxs);
40437                     }
40438                 }
40439             }
40440             
40441             cfg.cn = [
40442                 label,
40443                 container
40444             ];
40445             
40446             var settings = this;
40447             
40448             ['xs','sm','md','lg'].map(function(size){
40449                 if (settings[size]) {
40450                     cfg.cls += ' col-' + size + '-' + settings[size];
40451                 }
40452             });
40453             
40454             this.store = new Roo.data.Store({
40455                 proxy : new Roo.data.MemoryProxy({}),
40456                 reader : new Roo.data.JsonReader({
40457                     fields : [
40458                         {
40459                             'name' : 'name',
40460                             'type' : 'string'
40461                         },
40462                         {
40463                             'name' : 'iso2',
40464                             'type' : 'string'
40465                         },
40466                         {
40467                             'name' : 'dialCode',
40468                             'type' : 'string'
40469                         },
40470                         {
40471                             'name' : 'priority',
40472                             'type' : 'string'
40473                         },
40474                         {
40475                             'name' : 'areaCodes',
40476                             'type' : 'string'
40477                         }
40478                     ]
40479                 })
40480             });
40481             
40482             if(!this.preferedCountries) {
40483                 this.preferedCountries = [
40484                     'hk',
40485                     'gb',
40486                     'us'
40487                 ];
40488             }
40489             
40490             var p = this.preferedCountries.reverse();
40491             
40492             if(p) {
40493                 for (var i = 0; i < p.length; i++) {
40494                     for (var j = 0; j < this.allCountries.length; j++) {
40495                         if(this.allCountries[j].iso2 == p[i]) {
40496                             var t = this.allCountries[j];
40497                             this.allCountries.splice(j,1);
40498                             this.allCountries.unshift(t);
40499                         }
40500                     } 
40501                 }
40502             }
40503             
40504             this.store.proxy.data = {
40505                 success: true,
40506                 data: this.allCountries
40507             };
40508             
40509             return cfg;
40510         },
40511         
40512         initEvents : function()
40513         {
40514             this.createList();
40515             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40516             
40517             this.indicator = this.indicatorEl();
40518             this.flag = this.flagEl();
40519             this.dialCodeHolder = this.dialCodeHolderEl();
40520             
40521             this.trigger = this.el.select('div.flag-box',true).first();
40522             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40523             
40524             var _this = this;
40525             
40526             (function(){
40527                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40528                 _this.list.setWidth(lw);
40529             }).defer(100);
40530             
40531             this.list.on('mouseover', this.onViewOver, this);
40532             this.list.on('mousemove', this.onViewMove, this);
40533             this.inputEl().on("keyup", this.onKeyUp, this);
40534             this.inputEl().on("keypress", this.onKeyPress, this);
40535             
40536             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40537
40538             this.view = new Roo.View(this.list, this.tpl, {
40539                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40540             });
40541             
40542             this.view.on('click', this.onViewClick, this);
40543             this.setValue(this.defaultDialCode);
40544         },
40545         
40546         onTriggerClick : function(e)
40547         {
40548             Roo.log('trigger click');
40549             if(this.disabled){
40550                 return;
40551             }
40552             
40553             if(this.isExpanded()){
40554                 this.collapse();
40555                 this.hasFocus = false;
40556             }else {
40557                 this.store.load({});
40558                 this.hasFocus = true;
40559                 this.expand();
40560             }
40561         },
40562         
40563         isExpanded : function()
40564         {
40565             return this.list.isVisible();
40566         },
40567         
40568         collapse : function()
40569         {
40570             if(!this.isExpanded()){
40571                 return;
40572             }
40573             this.list.hide();
40574             Roo.get(document).un('mousedown', this.collapseIf, this);
40575             Roo.get(document).un('mousewheel', this.collapseIf, this);
40576             this.fireEvent('collapse', this);
40577             this.validate();
40578         },
40579         
40580         expand : function()
40581         {
40582             Roo.log('expand');
40583
40584             if(this.isExpanded() || !this.hasFocus){
40585                 return;
40586             }
40587             
40588             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40589             this.list.setWidth(lw);
40590             
40591             this.list.show();
40592             this.restrictHeight();
40593             
40594             Roo.get(document).on('mousedown', this.collapseIf, this);
40595             Roo.get(document).on('mousewheel', this.collapseIf, this);
40596             
40597             this.fireEvent('expand', this);
40598         },
40599         
40600         restrictHeight : function()
40601         {
40602             this.list.alignTo(this.inputEl(), this.listAlign);
40603             this.list.alignTo(this.inputEl(), this.listAlign);
40604         },
40605         
40606         onViewOver : function(e, t)
40607         {
40608             if(this.inKeyMode){
40609                 return;
40610             }
40611             var item = this.view.findItemFromChild(t);
40612             
40613             if(item){
40614                 var index = this.view.indexOf(item);
40615                 this.select(index, false);
40616             }
40617         },
40618
40619         // private
40620         onViewClick : function(view, doFocus, el, e)
40621         {
40622             var index = this.view.getSelectedIndexes()[0];
40623             
40624             var r = this.store.getAt(index);
40625             
40626             if(r){
40627                 this.onSelect(r, index);
40628             }
40629             if(doFocus !== false && !this.blockFocus){
40630                 this.inputEl().focus();
40631             }
40632         },
40633         
40634         onViewMove : function(e, t)
40635         {
40636             this.inKeyMode = false;
40637         },
40638         
40639         select : function(index, scrollIntoView)
40640         {
40641             this.selectedIndex = index;
40642             this.view.select(index);
40643             if(scrollIntoView !== false){
40644                 var el = this.view.getNode(index);
40645                 if(el){
40646                     this.list.scrollChildIntoView(el, false);
40647                 }
40648             }
40649         },
40650         
40651         createList : function()
40652         {
40653             this.list = Roo.get(document.body).createChild({
40654                 tag: 'ul',
40655                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40656                 style: 'display:none'
40657             });
40658             
40659             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40660         },
40661         
40662         collapseIf : function(e)
40663         {
40664             var in_combo  = e.within(this.el);
40665             var in_list =  e.within(this.list);
40666             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40667             
40668             if (in_combo || in_list || is_list) {
40669                 return;
40670             }
40671             this.collapse();
40672         },
40673         
40674         onSelect : function(record, index)
40675         {
40676             if(this.fireEvent('beforeselect', this, record, index) !== false){
40677                 
40678                 this.setFlagClass(record.data.iso2);
40679                 this.setDialCode(record.data.dialCode);
40680                 this.hasFocus = false;
40681                 this.collapse();
40682                 this.fireEvent('select', this, record, index);
40683             }
40684         },
40685         
40686         flagEl : function()
40687         {
40688             var flag = this.el.select('div.flag',true).first();
40689             if(!flag){
40690                 return false;
40691             }
40692             return flag;
40693         },
40694         
40695         dialCodeHolderEl : function()
40696         {
40697             var d = this.el.select('input.dial-code-holder',true).first();
40698             if(!d){
40699                 return false;
40700             }
40701             return d;
40702         },
40703         
40704         setDialCode : function(v)
40705         {
40706             this.dialCodeHolder.dom.value = '+'+v;
40707         },
40708         
40709         setFlagClass : function(n)
40710         {
40711             this.flag.dom.className = 'flag '+n;
40712         },
40713         
40714         getValue : function()
40715         {
40716             var v = this.inputEl().getValue();
40717             if(this.dialCodeHolder) {
40718                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40719             }
40720             return v;
40721         },
40722         
40723         setValue : function(v)
40724         {
40725             var d = this.getDialCode(v);
40726             
40727             //invalid dial code
40728             if(v.length == 0 || !d || d.length == 0) {
40729                 if(this.rendered){
40730                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40731                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40732                 }
40733                 return;
40734             }
40735             
40736             //valid dial code
40737             this.setFlagClass(this.dialCodeMapping[d].iso2);
40738             this.setDialCode(d);
40739             this.inputEl().dom.value = v.replace('+'+d,'');
40740             this.hiddenEl().dom.value = this.getValue();
40741             
40742             this.validate();
40743         },
40744         
40745         getDialCode : function(v)
40746         {
40747             v = v ||  '';
40748             
40749             if (v.length == 0) {
40750                 return this.dialCodeHolder.dom.value;
40751             }
40752             
40753             var dialCode = "";
40754             if (v.charAt(0) != "+") {
40755                 return false;
40756             }
40757             var numericChars = "";
40758             for (var i = 1; i < v.length; i++) {
40759               var c = v.charAt(i);
40760               if (!isNaN(c)) {
40761                 numericChars += c;
40762                 if (this.dialCodeMapping[numericChars]) {
40763                   dialCode = v.substr(1, i);
40764                 }
40765                 if (numericChars.length == 4) {
40766                   break;
40767                 }
40768               }
40769             }
40770             return dialCode;
40771         },
40772         
40773         reset : function()
40774         {
40775             this.setValue(this.defaultDialCode);
40776             this.validate();
40777         },
40778         
40779         hiddenEl : function()
40780         {
40781             return this.el.select('input.hidden-tel-input',true).first();
40782         },
40783         
40784         // after setting val
40785         onKeyUp : function(e){
40786             this.setValue(this.getValue());
40787         },
40788         
40789         onKeyPress : function(e){
40790             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40791                 e.stopEvent();
40792             }
40793         }
40794         
40795 });
40796 /**
40797  * @class Roo.bootstrap.MoneyField
40798  * @extends Roo.bootstrap.ComboBox
40799  * Bootstrap MoneyField class
40800  * 
40801  * @constructor
40802  * Create a new MoneyField.
40803  * @param {Object} config Configuration options
40804  */
40805
40806 Roo.bootstrap.MoneyField = function(config) {
40807     
40808     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40809     
40810 };
40811
40812 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40813     
40814     /**
40815      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40816      */
40817     allowDecimals : true,
40818     /**
40819      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40820      */
40821     decimalSeparator : ".",
40822     /**
40823      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40824      */
40825     decimalPrecision : 0,
40826     /**
40827      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40828      */
40829     allowNegative : true,
40830     /**
40831      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40832      */
40833     allowZero: true,
40834     /**
40835      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40836      */
40837     minValue : Number.NEGATIVE_INFINITY,
40838     /**
40839      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40840      */
40841     maxValue : Number.MAX_VALUE,
40842     /**
40843      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40844      */
40845     minText : "The minimum value for this field is {0}",
40846     /**
40847      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40848      */
40849     maxText : "The maximum value for this field is {0}",
40850     /**
40851      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40852      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40853      */
40854     nanText : "{0} is not a valid number",
40855     /**
40856      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40857      */
40858     castInt : true,
40859     /**
40860      * @cfg {String} defaults currency of the MoneyField
40861      * value should be in lkey
40862      */
40863     defaultCurrency : false,
40864     /**
40865      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40866      */
40867     thousandsDelimiter : false,
40868     /**
40869      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40870      */
40871     max_length: false,
40872     
40873     inputlg : 9,
40874     inputmd : 9,
40875     inputsm : 9,
40876     inputxs : 6,
40877     
40878     store : false,
40879     
40880     getAutoCreate : function()
40881     {
40882         var align = this.labelAlign || this.parentLabelAlign();
40883         
40884         var id = Roo.id();
40885
40886         var cfg = {
40887             cls: 'form-group',
40888             cn: []
40889         };
40890
40891         var input =  {
40892             tag: 'input',
40893             id : id,
40894             cls : 'form-control roo-money-amount-input',
40895             autocomplete: 'new-password'
40896         };
40897         
40898         var hiddenInput = {
40899             tag: 'input',
40900             type: 'hidden',
40901             id: Roo.id(),
40902             cls: 'hidden-number-input'
40903         };
40904         
40905         if(this.max_length) {
40906             input.maxlength = this.max_length; 
40907         }
40908         
40909         if (this.name) {
40910             hiddenInput.name = this.name;
40911         }
40912
40913         if (this.disabled) {
40914             input.disabled = true;
40915         }
40916
40917         var clg = 12 - this.inputlg;
40918         var cmd = 12 - this.inputmd;
40919         var csm = 12 - this.inputsm;
40920         var cxs = 12 - this.inputxs;
40921         
40922         var container = {
40923             tag : 'div',
40924             cls : 'row roo-money-field',
40925             cn : [
40926                 {
40927                     tag : 'div',
40928                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40929                     cn : [
40930                         {
40931                             tag : 'div',
40932                             cls: 'roo-select2-container input-group',
40933                             cn: [
40934                                 {
40935                                     tag : 'input',
40936                                     cls : 'form-control roo-money-currency-input',
40937                                     autocomplete: 'new-password',
40938                                     readOnly : 1,
40939                                     name : this.currencyName
40940                                 },
40941                                 {
40942                                     tag :'span',
40943                                     cls : 'input-group-addon',
40944                                     cn : [
40945                                         {
40946                                             tag: 'span',
40947                                             cls: 'caret'
40948                                         }
40949                                     ]
40950                                 }
40951                             ]
40952                         }
40953                     ]
40954                 },
40955                 {
40956                     tag : 'div',
40957                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40958                     cn : [
40959                         {
40960                             tag: 'div',
40961                             cls: this.hasFeedback ? 'has-feedback' : '',
40962                             cn: [
40963                                 input
40964                             ]
40965                         }
40966                     ]
40967                 }
40968             ]
40969             
40970         };
40971         
40972         if (this.fieldLabel.length) {
40973             var indicator = {
40974                 tag: 'i',
40975                 tooltip: 'This field is required'
40976             };
40977
40978             var label = {
40979                 tag: 'label',
40980                 'for':  id,
40981                 cls: 'control-label',
40982                 cn: []
40983             };
40984
40985             var label_text = {
40986                 tag: 'span',
40987                 html: this.fieldLabel
40988             };
40989
40990             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40991             label.cn = [
40992                 indicator,
40993                 label_text
40994             ];
40995
40996             if(this.indicatorpos == 'right') {
40997                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40998                 label.cn = [
40999                     label_text,
41000                     indicator
41001                 ];
41002             }
41003
41004             if(align == 'left') {
41005                 container = {
41006                     tag: 'div',
41007                     cn: [
41008                         container
41009                     ]
41010                 };
41011
41012                 if(this.labelWidth > 12){
41013                     label.style = "width: " + this.labelWidth + 'px';
41014                 }
41015                 if(this.labelWidth < 13 && this.labelmd == 0){
41016                     this.labelmd = this.labelWidth;
41017                 }
41018                 if(this.labellg > 0){
41019                     label.cls += ' col-lg-' + this.labellg;
41020                     input.cls += ' col-lg-' + (12 - this.labellg);
41021                 }
41022                 if(this.labelmd > 0){
41023                     label.cls += ' col-md-' + this.labelmd;
41024                     container.cls += ' col-md-' + (12 - this.labelmd);
41025                 }
41026                 if(this.labelsm > 0){
41027                     label.cls += ' col-sm-' + this.labelsm;
41028                     container.cls += ' col-sm-' + (12 - this.labelsm);
41029                 }
41030                 if(this.labelxs > 0){
41031                     label.cls += ' col-xs-' + this.labelxs;
41032                     container.cls += ' col-xs-' + (12 - this.labelxs);
41033                 }
41034             }
41035         }
41036
41037         cfg.cn = [
41038             label,
41039             container,
41040             hiddenInput
41041         ];
41042         
41043         var settings = this;
41044
41045         ['xs','sm','md','lg'].map(function(size){
41046             if (settings[size]) {
41047                 cfg.cls += ' col-' + size + '-' + settings[size];
41048             }
41049         });
41050         
41051         return cfg;
41052     },
41053     
41054     initEvents : function()
41055     {
41056         this.indicator = this.indicatorEl();
41057         
41058         this.initCurrencyEvent();
41059         
41060         this.initNumberEvent();
41061     },
41062     
41063     initCurrencyEvent : function()
41064     {
41065         if (!this.store) {
41066             throw "can not find store for combo";
41067         }
41068         
41069         this.store = Roo.factory(this.store, Roo.data);
41070         this.store.parent = this;
41071         
41072         this.createList();
41073         
41074         this.triggerEl = this.el.select('.input-group-addon', true).first();
41075         
41076         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41077         
41078         var _this = this;
41079         
41080         (function(){
41081             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41082             _this.list.setWidth(lw);
41083         }).defer(100);
41084         
41085         this.list.on('mouseover', this.onViewOver, this);
41086         this.list.on('mousemove', this.onViewMove, this);
41087         this.list.on('scroll', this.onViewScroll, this);
41088         
41089         if(!this.tpl){
41090             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41091         }
41092         
41093         this.view = new Roo.View(this.list, this.tpl, {
41094             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41095         });
41096         
41097         this.view.on('click', this.onViewClick, this);
41098         
41099         this.store.on('beforeload', this.onBeforeLoad, this);
41100         this.store.on('load', this.onLoad, this);
41101         this.store.on('loadexception', this.onLoadException, this);
41102         
41103         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41104             "up" : function(e){
41105                 this.inKeyMode = true;
41106                 this.selectPrev();
41107             },
41108
41109             "down" : function(e){
41110                 if(!this.isExpanded()){
41111                     this.onTriggerClick();
41112                 }else{
41113                     this.inKeyMode = true;
41114                     this.selectNext();
41115                 }
41116             },
41117
41118             "enter" : function(e){
41119                 this.collapse();
41120                 
41121                 if(this.fireEvent("specialkey", this, e)){
41122                     this.onViewClick(false);
41123                 }
41124                 
41125                 return true;
41126             },
41127
41128             "esc" : function(e){
41129                 this.collapse();
41130             },
41131
41132             "tab" : function(e){
41133                 this.collapse();
41134                 
41135                 if(this.fireEvent("specialkey", this, e)){
41136                     this.onViewClick(false);
41137                 }
41138                 
41139                 return true;
41140             },
41141
41142             scope : this,
41143
41144             doRelay : function(foo, bar, hname){
41145                 if(hname == 'down' || this.scope.isExpanded()){
41146                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41147                 }
41148                 return true;
41149             },
41150
41151             forceKeyDown: true
41152         });
41153         
41154         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41155         
41156     },
41157     
41158     initNumberEvent : function(e)
41159     {
41160         this.inputEl().on("keydown" , this.fireKey,  this);
41161         this.inputEl().on("focus", this.onFocus,  this);
41162         this.inputEl().on("blur", this.onBlur,  this);
41163         
41164         this.inputEl().relayEvent('keyup', this);
41165         
41166         if(this.indicator){
41167             this.indicator.addClass('invisible');
41168         }
41169  
41170         this.originalValue = this.getValue();
41171         
41172         if(this.validationEvent == 'keyup'){
41173             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41174             this.inputEl().on('keyup', this.filterValidation, this);
41175         }
41176         else if(this.validationEvent !== false){
41177             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41178         }
41179         
41180         if(this.selectOnFocus){
41181             this.on("focus", this.preFocus, this);
41182             
41183         }
41184         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41185             this.inputEl().on("keypress", this.filterKeys, this);
41186         } else {
41187             this.inputEl().relayEvent('keypress', this);
41188         }
41189         
41190         var allowed = "0123456789";
41191         
41192         if(this.allowDecimals){
41193             allowed += this.decimalSeparator;
41194         }
41195         
41196         if(this.allowNegative){
41197             allowed += "-";
41198         }
41199         
41200         if(this.thousandsDelimiter) {
41201             allowed += ",";
41202         }
41203         
41204         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41205         
41206         var keyPress = function(e){
41207             
41208             var k = e.getKey();
41209             
41210             var c = e.getCharCode();
41211             
41212             if(
41213                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41214                     allowed.indexOf(String.fromCharCode(c)) === -1
41215             ){
41216                 e.stopEvent();
41217                 return;
41218             }
41219             
41220             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41221                 return;
41222             }
41223             
41224             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41225                 e.stopEvent();
41226             }
41227         };
41228         
41229         this.inputEl().on("keypress", keyPress, this);
41230         
41231     },
41232     
41233     onTriggerClick : function(e)
41234     {   
41235         if(this.disabled){
41236             return;
41237         }
41238         
41239         this.page = 0;
41240         this.loadNext = false;
41241         
41242         if(this.isExpanded()){
41243             this.collapse();
41244             return;
41245         }
41246         
41247         this.hasFocus = true;
41248         
41249         if(this.triggerAction == 'all') {
41250             this.doQuery(this.allQuery, true);
41251             return;
41252         }
41253         
41254         this.doQuery(this.getRawValue());
41255     },
41256     
41257     getCurrency : function()
41258     {   
41259         var v = this.currencyEl().getValue();
41260         
41261         return v;
41262     },
41263     
41264     restrictHeight : function()
41265     {
41266         this.list.alignTo(this.currencyEl(), this.listAlign);
41267         this.list.alignTo(this.currencyEl(), this.listAlign);
41268     },
41269     
41270     onViewClick : function(view, doFocus, el, e)
41271     {
41272         var index = this.view.getSelectedIndexes()[0];
41273         
41274         var r = this.store.getAt(index);
41275         
41276         if(r){
41277             this.onSelect(r, index);
41278         }
41279     },
41280     
41281     onSelect : function(record, index){
41282         
41283         if(this.fireEvent('beforeselect', this, record, index) !== false){
41284         
41285             this.setFromCurrencyData(index > -1 ? record.data : false);
41286             
41287             this.collapse();
41288             
41289             this.fireEvent('select', this, record, index);
41290         }
41291     },
41292     
41293     setFromCurrencyData : function(o)
41294     {
41295         var currency = '';
41296         
41297         this.lastCurrency = o;
41298         
41299         if (this.currencyField) {
41300             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41301         } else {
41302             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41303         }
41304         
41305         this.lastSelectionText = currency;
41306         
41307         //setting default currency
41308         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41309             this.setCurrency(this.defaultCurrency);
41310             return;
41311         }
41312         
41313         this.setCurrency(currency);
41314     },
41315     
41316     setFromData : function(o)
41317     {
41318         var c = {};
41319         
41320         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41321         
41322         this.setFromCurrencyData(c);
41323         
41324         var value = '';
41325         
41326         if (this.name) {
41327             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41328         } else {
41329             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41330         }
41331         
41332         this.setValue(value);
41333         
41334     },
41335     
41336     setCurrency : function(v)
41337     {   
41338         this.currencyValue = v;
41339         
41340         if(this.rendered){
41341             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41342             this.validate();
41343         }
41344     },
41345     
41346     setValue : function(v)
41347     {
41348         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41349         
41350         this.value = v;
41351         
41352         if(this.rendered){
41353             
41354             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41355             
41356             this.inputEl().dom.value = (v == '') ? '' :
41357                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41358             
41359             if(!this.allowZero && v === '0') {
41360                 this.hiddenEl().dom.value = '';
41361                 this.inputEl().dom.value = '';
41362             }
41363             
41364             this.validate();
41365         }
41366     },
41367     
41368     getRawValue : function()
41369     {
41370         var v = this.inputEl().getValue();
41371         
41372         return v;
41373     },
41374     
41375     getValue : function()
41376     {
41377         return this.fixPrecision(this.parseValue(this.getRawValue()));
41378     },
41379     
41380     parseValue : function(value)
41381     {
41382         if(this.thousandsDelimiter) {
41383             value += "";
41384             r = new RegExp(",", "g");
41385             value = value.replace(r, "");
41386         }
41387         
41388         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41389         return isNaN(value) ? '' : value;
41390         
41391     },
41392     
41393     fixPrecision : function(value)
41394     {
41395         if(this.thousandsDelimiter) {
41396             value += "";
41397             r = new RegExp(",", "g");
41398             value = value.replace(r, "");
41399         }
41400         
41401         var nan = isNaN(value);
41402         
41403         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41404             return nan ? '' : value;
41405         }
41406         return parseFloat(value).toFixed(this.decimalPrecision);
41407     },
41408     
41409     decimalPrecisionFcn : function(v)
41410     {
41411         return Math.floor(v);
41412     },
41413     
41414     validateValue : function(value)
41415     {
41416         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41417             return false;
41418         }
41419         
41420         var num = this.parseValue(value);
41421         
41422         if(isNaN(num)){
41423             this.markInvalid(String.format(this.nanText, value));
41424             return false;
41425         }
41426         
41427         if(num < this.minValue){
41428             this.markInvalid(String.format(this.minText, this.minValue));
41429             return false;
41430         }
41431         
41432         if(num > this.maxValue){
41433             this.markInvalid(String.format(this.maxText, this.maxValue));
41434             return false;
41435         }
41436         
41437         return true;
41438     },
41439     
41440     validate : function()
41441     {
41442         if(this.disabled || this.allowBlank){
41443             this.markValid();
41444             return true;
41445         }
41446         
41447         var currency = this.getCurrency();
41448         
41449         if(this.validateValue(this.getRawValue()) && currency.length){
41450             this.markValid();
41451             return true;
41452         }
41453         
41454         this.markInvalid();
41455         return false;
41456     },
41457     
41458     getName: function()
41459     {
41460         return this.name;
41461     },
41462     
41463     beforeBlur : function()
41464     {
41465         if(!this.castInt){
41466             return;
41467         }
41468         
41469         var v = this.parseValue(this.getRawValue());
41470         
41471         if(v || v == 0){
41472             this.setValue(v);
41473         }
41474     },
41475     
41476     onBlur : function()
41477     {
41478         this.beforeBlur();
41479         
41480         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41481             //this.el.removeClass(this.focusClass);
41482         }
41483         
41484         this.hasFocus = false;
41485         
41486         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41487             this.validate();
41488         }
41489         
41490         var v = this.getValue();
41491         
41492         if(String(v) !== String(this.startValue)){
41493             this.fireEvent('change', this, v, this.startValue);
41494         }
41495         
41496         this.fireEvent("blur", this);
41497     },
41498     
41499     inputEl : function()
41500     {
41501         return this.el.select('.roo-money-amount-input', true).first();
41502     },
41503     
41504     currencyEl : function()
41505     {
41506         return this.el.select('.roo-money-currency-input', true).first();
41507     },
41508     
41509     hiddenEl : function()
41510     {
41511         return this.el.select('input.hidden-number-input',true).first();
41512     }
41513     
41514 });