FIX: compiled
[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.hide, this);
2913         }
2914         Roo.EventManager.onWindowResize(this.resize, this, true);
2915
2916
2917     },
2918   
2919
2920     resize : function()
2921     {
2922         this.maskEl.setSize(
2923             Roo.lib.Dom.getViewWidth(true),
2924             Roo.lib.Dom.getViewHeight(true)
2925         );
2926         
2927         if (this.fitwindow) {
2928             
2929            
2930             this.setSize(
2931                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2932                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2933             );
2934             return;
2935         }
2936         
2937         if(this.max_width !== 0) {
2938             
2939             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2940             
2941             if(this.height) {
2942                 this.setSize(w, this.height);
2943                 return;
2944             }
2945             
2946             if(this.max_height) {
2947                 this.setSize(w,Math.min(
2948                     this.max_height,
2949                     Roo.lib.Dom.getViewportHeight(true) - 60
2950                 ));
2951                 
2952                 return;
2953             }
2954             
2955             if(!this.fit_content) {
2956                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2957                 return;
2958             }
2959             
2960             this.setSize(w, Math.min(
2961                 60 +
2962                 this.headerEl.getHeight() + 
2963                 this.footerEl.getHeight() + 
2964                 this.getChildHeight(this.bodyEl.dom.childNodes),
2965                 Roo.lib.Dom.getViewportHeight(true) - 60)
2966             );
2967         }
2968         
2969     },
2970
2971     setSize : function(w,h)
2972     {
2973         if (!w && !h) {
2974             return;
2975         }
2976         
2977         this.resizeTo(w,h);
2978     },
2979
2980     show : function() {
2981
2982         if (!this.rendered) {
2983             this.render();
2984         }
2985
2986         //this.el.setStyle('display', 'block');
2987         this.el.removeClass('hideing');
2988         this.el.dom.style.display='block';
2989         
2990         Roo.get(document.body).addClass('modal-open');
2991  
2992         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2993             
2994             (function(){
2995                 this.el.addClass('show');
2996                 this.el.addClass('in');
2997             }).defer(50, this);
2998         }else{
2999             this.el.addClass('show');
3000             this.el.addClass('in');
3001         }
3002
3003         // not sure how we can show data in here..
3004         //if (this.tmpl) {
3005         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3006         //}
3007
3008         Roo.get(document.body).addClass("x-body-masked");
3009         
3010         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3011         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3012         this.maskEl.dom.style.display = 'block';
3013         this.maskEl.addClass('show');
3014         
3015         
3016         this.resize();
3017         
3018         this.fireEvent('show', this);
3019
3020         // set zindex here - otherwise it appears to be ignored...
3021         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3022
3023         (function () {
3024             this.items.forEach( function(e) {
3025                 e.layout ? e.layout() : false;
3026
3027             });
3028         }).defer(100,this);
3029
3030     },
3031     hide : function()
3032     {
3033         if(this.fireEvent("beforehide", this) !== false){
3034             
3035             this.maskEl.removeClass('show');
3036             
3037             this.maskEl.dom.style.display = '';
3038             Roo.get(document.body).removeClass("x-body-masked");
3039             this.el.removeClass('in');
3040             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3041
3042             if(this.animate){ // why
3043                 this.el.addClass('hideing');
3044                 this.el.removeClass('show');
3045                 (function(){
3046                     if (!this.el.hasClass('hideing')) {
3047                         return; // it's been shown again...
3048                     }
3049                     
3050                     this.el.dom.style.display='';
3051
3052                     Roo.get(document.body).removeClass('modal-open');
3053                     this.el.removeClass('hideing');
3054                 }).defer(150,this);
3055                 
3056             }else{
3057                 this.el.removeClass('show');
3058                 this.el.dom.style.display='';
3059                 Roo.get(document.body).removeClass('modal-open');
3060
3061             }
3062             this.fireEvent('hide', this);
3063         }
3064     },
3065     isVisible : function()
3066     {
3067         
3068         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3069         
3070     },
3071
3072     addButton : function(str, cb)
3073     {
3074
3075
3076         var b = Roo.apply({}, { html : str } );
3077         b.xns = b.xns || Roo.bootstrap;
3078         b.xtype = b.xtype || 'Button';
3079         if (typeof(b.listeners) == 'undefined') {
3080             b.listeners = { click : cb.createDelegate(this)  };
3081         }
3082
3083         var btn = Roo.factory(b);
3084
3085         btn.render(this.getButtonContainer());
3086
3087         return btn;
3088
3089     },
3090
3091     setDefaultButton : function(btn)
3092     {
3093         //this.el.select('.modal-footer').()
3094     },
3095
3096     resizeTo: function(w,h)
3097     {
3098         this.dialogEl.setWidth(w);
3099         
3100         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3101
3102         this.bodyEl.setHeight(h - diff);
3103         
3104         this.fireEvent('resize', this);
3105     },
3106     
3107     setContentSize  : function(w, h)
3108     {
3109
3110     },
3111     onButtonClick: function(btn,e)
3112     {
3113         //Roo.log([a,b,c]);
3114         this.fireEvent('btnclick', btn.name, e);
3115     },
3116      /**
3117      * Set the title of the Dialog
3118      * @param {String} str new Title
3119      */
3120     setTitle: function(str) {
3121         this.titleEl.dom.innerHTML = str;
3122     },
3123     /**
3124      * Set the body of the Dialog
3125      * @param {String} str new Title
3126      */
3127     setBody: function(str) {
3128         this.bodyEl.dom.innerHTML = str;
3129     },
3130     /**
3131      * Set the body of the Dialog using the template
3132      * @param {Obj} data - apply this data to the template and replace the body contents.
3133      */
3134     applyBody: function(obj)
3135     {
3136         if (!this.tmpl) {
3137             Roo.log("Error - using apply Body without a template");
3138             //code
3139         }
3140         this.tmpl.overwrite(this.bodyEl, obj);
3141     },
3142     
3143     getChildHeight : function(child_nodes)
3144     {
3145         if(
3146             !child_nodes ||
3147             child_nodes.length == 0
3148         ) {
3149             return;
3150         }
3151         
3152         var child_height = 0;
3153         
3154         for(var i = 0; i < child_nodes.length; i++) {
3155             
3156             /*
3157             * for modal with tabs...
3158             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3159                 
3160                 var layout_childs = child_nodes[i].childNodes;
3161                 
3162                 for(var j = 0; j < layout_childs.length; j++) {
3163                     
3164                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3165                         
3166                         var layout_body_childs = layout_childs[j].childNodes;
3167                         
3168                         for(var k = 0; k < layout_body_childs.length; k++) {
3169                             
3170                             if(layout_body_childs[k].classList.contains('navbar')) {
3171                                 child_height += layout_body_childs[k].offsetHeight;
3172                                 continue;
3173                             }
3174                             
3175                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3176                                 
3177                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3178                                 
3179                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3180                                     
3181                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3182                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3183                                         continue;
3184                                     }
3185                                     
3186                                 }
3187                                 
3188                             }
3189                             
3190                         }
3191                     }
3192                 }
3193                 continue;
3194             }
3195             */
3196             
3197             child_height += child_nodes[i].offsetHeight;
3198             // Roo.log(child_nodes[i].offsetHeight);
3199         }
3200         
3201         return child_height;
3202     }
3203
3204 });
3205
3206
3207 Roo.apply(Roo.bootstrap.Modal,  {
3208     /**
3209          * Button config that displays a single OK button
3210          * @type Object
3211          */
3212         OK :  [{
3213             name : 'ok',
3214             weight : 'primary',
3215             html : 'OK'
3216         }],
3217         /**
3218          * Button config that displays Yes and No buttons
3219          * @type Object
3220          */
3221         YESNO : [
3222             {
3223                 name  : 'no',
3224                 html : 'No'
3225             },
3226             {
3227                 name  :'yes',
3228                 weight : 'primary',
3229                 html : 'Yes'
3230             }
3231         ],
3232
3233         /**
3234          * Button config that displays OK and Cancel buttons
3235          * @type Object
3236          */
3237         OKCANCEL : [
3238             {
3239                name : 'cancel',
3240                 html : 'Cancel'
3241             },
3242             {
3243                 name : 'ok',
3244                 weight : 'primary',
3245                 html : 'OK'
3246             }
3247         ],
3248         /**
3249          * Button config that displays Yes, No and Cancel buttons
3250          * @type Object
3251          */
3252         YESNOCANCEL : [
3253             {
3254                 name : 'yes',
3255                 weight : 'primary',
3256                 html : 'Yes'
3257             },
3258             {
3259                 name : 'no',
3260                 html : 'No'
3261             },
3262             {
3263                 name : 'cancel',
3264                 html : 'Cancel'
3265             }
3266         ],
3267         
3268         zIndex : 10001
3269 });
3270 /*
3271  * - LGPL
3272  *
3273  * messagebox - can be used as a replace
3274  * 
3275  */
3276 /**
3277  * @class Roo.MessageBox
3278  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3279  * Example usage:
3280  *<pre><code>
3281 // Basic alert:
3282 Roo.Msg.alert('Status', 'Changes saved successfully.');
3283
3284 // Prompt for user data:
3285 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3286     if (btn == 'ok'){
3287         // process text value...
3288     }
3289 });
3290
3291 // Show a dialog using config options:
3292 Roo.Msg.show({
3293    title:'Save Changes?',
3294    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3295    buttons: Roo.Msg.YESNOCANCEL,
3296    fn: processResult,
3297    animEl: 'elId'
3298 });
3299 </code></pre>
3300  * @singleton
3301  */
3302 Roo.bootstrap.MessageBox = function(){
3303     var dlg, opt, mask, waitTimer;
3304     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3305     var buttons, activeTextEl, bwidth;
3306
3307     
3308     // private
3309     var handleButton = function(button){
3310         dlg.hide();
3311         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3312     };
3313
3314     // private
3315     var handleHide = function(){
3316         if(opt && opt.cls){
3317             dlg.el.removeClass(opt.cls);
3318         }
3319         //if(waitTimer){
3320         //    Roo.TaskMgr.stop(waitTimer);
3321         //    waitTimer = null;
3322         //}
3323     };
3324
3325     // private
3326     var updateButtons = function(b){
3327         var width = 0;
3328         if(!b){
3329             buttons["ok"].hide();
3330             buttons["cancel"].hide();
3331             buttons["yes"].hide();
3332             buttons["no"].hide();
3333             dlg.footerEl.hide();
3334             
3335             return width;
3336         }
3337         dlg.footerEl.show();
3338         for(var k in buttons){
3339             if(typeof buttons[k] != "function"){
3340                 if(b[k]){
3341                     buttons[k].show();
3342                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3343                     width += buttons[k].el.getWidth()+15;
3344                 }else{
3345                     buttons[k].hide();
3346                 }
3347             }
3348         }
3349         return width;
3350     };
3351
3352     // private
3353     var handleEsc = function(d, k, e){
3354         if(opt && opt.closable !== false){
3355             dlg.hide();
3356         }
3357         if(e){
3358             e.stopEvent();
3359         }
3360     };
3361
3362     return {
3363         /**
3364          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3365          * @return {Roo.BasicDialog} The BasicDialog element
3366          */
3367         getDialog : function(){
3368            if(!dlg){
3369                 dlg = new Roo.bootstrap.Modal( {
3370                     //draggable: true,
3371                     //resizable:false,
3372                     //constraintoviewport:false,
3373                     //fixedcenter:true,
3374                     //collapsible : false,
3375                     //shim:true,
3376                     //modal: true,
3377                 //    width: 'auto',
3378                   //  height:100,
3379                     //buttonAlign:"center",
3380                     closeClick : function(){
3381                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3382                             handleButton("no");
3383                         }else{
3384                             handleButton("cancel");
3385                         }
3386                     }
3387                 });
3388                 dlg.render();
3389                 dlg.on("hide", handleHide);
3390                 mask = dlg.mask;
3391                 //dlg.addKeyListener(27, handleEsc);
3392                 buttons = {};
3393                 this.buttons = buttons;
3394                 var bt = this.buttonText;
3395                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3396                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3397                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3398                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3399                 //Roo.log(buttons);
3400                 bodyEl = dlg.bodyEl.createChild({
3401
3402                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3403                         '<textarea class="roo-mb-textarea"></textarea>' +
3404                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3405                 });
3406                 msgEl = bodyEl.dom.firstChild;
3407                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3408                 textboxEl.enableDisplayMode();
3409                 textboxEl.addKeyListener([10,13], function(){
3410                     if(dlg.isVisible() && opt && opt.buttons){
3411                         if(opt.buttons.ok){
3412                             handleButton("ok");
3413                         }else if(opt.buttons.yes){
3414                             handleButton("yes");
3415                         }
3416                     }
3417                 });
3418                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3419                 textareaEl.enableDisplayMode();
3420                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3421                 progressEl.enableDisplayMode();
3422                 
3423                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3424                 var pf = progressEl.dom.firstChild;
3425                 if (pf) {
3426                     pp = Roo.get(pf.firstChild);
3427                     pp.setHeight(pf.offsetHeight);
3428                 }
3429                 
3430             }
3431             return dlg;
3432         },
3433
3434         /**
3435          * Updates the message box body text
3436          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3437          * the XHTML-compliant non-breaking space character '&amp;#160;')
3438          * @return {Roo.MessageBox} This message box
3439          */
3440         updateText : function(text)
3441         {
3442             if(!dlg.isVisible() && !opt.width){
3443                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3444                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3445             }
3446             msgEl.innerHTML = text || '&#160;';
3447       
3448             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3449             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3450             var w = Math.max(
3451                     Math.min(opt.width || cw , this.maxWidth), 
3452                     Math.max(opt.minWidth || this.minWidth, bwidth)
3453             );
3454             if(opt.prompt){
3455                 activeTextEl.setWidth(w);
3456             }
3457             if(dlg.isVisible()){
3458                 dlg.fixedcenter = false;
3459             }
3460             // to big, make it scroll. = But as usual stupid IE does not support
3461             // !important..
3462             
3463             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3464                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3465                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3466             } else {
3467                 bodyEl.dom.style.height = '';
3468                 bodyEl.dom.style.overflowY = '';
3469             }
3470             if (cw > w) {
3471                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3472             } else {
3473                 bodyEl.dom.style.overflowX = '';
3474             }
3475             
3476             dlg.setContentSize(w, bodyEl.getHeight());
3477             if(dlg.isVisible()){
3478                 dlg.fixedcenter = true;
3479             }
3480             return this;
3481         },
3482
3483         /**
3484          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3485          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3486          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3487          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3488          * @return {Roo.MessageBox} This message box
3489          */
3490         updateProgress : function(value, text){
3491             if(text){
3492                 this.updateText(text);
3493             }
3494             
3495             if (pp) { // weird bug on my firefox - for some reason this is not defined
3496                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3497                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3498             }
3499             return this;
3500         },        
3501
3502         /**
3503          * Returns true if the message box is currently displayed
3504          * @return {Boolean} True if the message box is visible, else false
3505          */
3506         isVisible : function(){
3507             return dlg && dlg.isVisible();  
3508         },
3509
3510         /**
3511          * Hides the message box if it is displayed
3512          */
3513         hide : function(){
3514             if(this.isVisible()){
3515                 dlg.hide();
3516             }  
3517         },
3518
3519         /**
3520          * Displays a new message box, or reinitializes an existing message box, based on the config options
3521          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3522          * The following config object properties are supported:
3523          * <pre>
3524 Property    Type             Description
3525 ----------  ---------------  ------------------------------------------------------------------------------------
3526 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3527                                    closes (defaults to undefined)
3528 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3529                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3530 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3531                                    progress and wait dialogs will ignore this property and always hide the
3532                                    close button as they can only be closed programmatically.
3533 cls               String           A custom CSS class to apply to the message box element
3534 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3535                                    displayed (defaults to 75)
3536 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3537                                    function will be btn (the name of the button that was clicked, if applicable,
3538                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3539                                    Progress and wait dialogs will ignore this option since they do not respond to
3540                                    user actions and can only be closed programmatically, so any required function
3541                                    should be called by the same code after it closes the dialog.
3542 icon              String           A CSS class that provides a background image to be used as an icon for
3543                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3544 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3545 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3546 modal             Boolean          False to allow user interaction with the page while the message box is
3547                                    displayed (defaults to true)
3548 msg               String           A string that will replace the existing message box body text (defaults
3549                                    to the XHTML-compliant non-breaking space character '&#160;')
3550 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3551 progress          Boolean          True to display a progress bar (defaults to false)
3552 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3553 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3554 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3555 title             String           The title text
3556 value             String           The string value to set into the active textbox element if displayed
3557 wait              Boolean          True to display a progress bar (defaults to false)
3558 width             Number           The width of the dialog in pixels
3559 </pre>
3560          *
3561          * Example usage:
3562          * <pre><code>
3563 Roo.Msg.show({
3564    title: 'Address',
3565    msg: 'Please enter your address:',
3566    width: 300,
3567    buttons: Roo.MessageBox.OKCANCEL,
3568    multiline: true,
3569    fn: saveAddress,
3570    animEl: 'addAddressBtn'
3571 });
3572 </code></pre>
3573          * @param {Object} config Configuration options
3574          * @return {Roo.MessageBox} This message box
3575          */
3576         show : function(options)
3577         {
3578             
3579             // this causes nightmares if you show one dialog after another
3580             // especially on callbacks..
3581              
3582             if(this.isVisible()){
3583                 
3584                 this.hide();
3585                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3586                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3587                 Roo.log("New Dialog Message:" +  options.msg )
3588                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3589                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3590                 
3591             }
3592             var d = this.getDialog();
3593             opt = options;
3594             d.setTitle(opt.title || "&#160;");
3595             d.closeEl.setDisplayed(opt.closable !== false);
3596             activeTextEl = textboxEl;
3597             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3598             if(opt.prompt){
3599                 if(opt.multiline){
3600                     textboxEl.hide();
3601                     textareaEl.show();
3602                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3603                         opt.multiline : this.defaultTextHeight);
3604                     activeTextEl = textareaEl;
3605                 }else{
3606                     textboxEl.show();
3607                     textareaEl.hide();
3608                 }
3609             }else{
3610                 textboxEl.hide();
3611                 textareaEl.hide();
3612             }
3613             progressEl.setDisplayed(opt.progress === true);
3614             if (opt.progress) {
3615                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3616             }
3617             this.updateProgress(0);
3618             activeTextEl.dom.value = opt.value || "";
3619             if(opt.prompt){
3620                 dlg.setDefaultButton(activeTextEl);
3621             }else{
3622                 var bs = opt.buttons;
3623                 var db = null;
3624                 if(bs && bs.ok){
3625                     db = buttons["ok"];
3626                 }else if(bs && bs.yes){
3627                     db = buttons["yes"];
3628                 }
3629                 dlg.setDefaultButton(db);
3630             }
3631             bwidth = updateButtons(opt.buttons);
3632             this.updateText(opt.msg);
3633             if(opt.cls){
3634                 d.el.addClass(opt.cls);
3635             }
3636             d.proxyDrag = opt.proxyDrag === true;
3637             d.modal = opt.modal !== false;
3638             d.mask = opt.modal !== false ? mask : false;
3639             if(!d.isVisible()){
3640                 // force it to the end of the z-index stack so it gets a cursor in FF
3641                 document.body.appendChild(dlg.el.dom);
3642                 d.animateTarget = null;
3643                 d.show(options.animEl);
3644             }
3645             return this;
3646         },
3647
3648         /**
3649          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3650          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3651          * and closing the message box when the process is complete.
3652          * @param {String} title The title bar text
3653          * @param {String} msg The message box body text
3654          * @return {Roo.MessageBox} This message box
3655          */
3656         progress : function(title, msg){
3657             this.show({
3658                 title : title,
3659                 msg : msg,
3660                 buttons: false,
3661                 progress:true,
3662                 closable:false,
3663                 minWidth: this.minProgressWidth,
3664                 modal : true
3665             });
3666             return this;
3667         },
3668
3669         /**
3670          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3671          * If a callback function is passed it will be called after the user clicks the button, and the
3672          * id of the button that was clicked will be passed as the only parameter to the callback
3673          * (could also be the top-right close button).
3674          * @param {String} title The title bar text
3675          * @param {String} msg The message box body text
3676          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3677          * @param {Object} scope (optional) The scope of the callback function
3678          * @return {Roo.MessageBox} This message box
3679          */
3680         alert : function(title, msg, fn, scope)
3681         {
3682             this.show({
3683                 title : title,
3684                 msg : msg,
3685                 buttons: this.OK,
3686                 fn: fn,
3687                 closable : false,
3688                 scope : scope,
3689                 modal : true
3690             });
3691             return this;
3692         },
3693
3694         /**
3695          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3696          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3697          * You are responsible for closing the message box when the process is complete.
3698          * @param {String} msg The message box body text
3699          * @param {String} title (optional) The title bar text
3700          * @return {Roo.MessageBox} This message box
3701          */
3702         wait : function(msg, title){
3703             this.show({
3704                 title : title,
3705                 msg : msg,
3706                 buttons: false,
3707                 closable:false,
3708                 progress:true,
3709                 modal:true,
3710                 width:300,
3711                 wait:true
3712             });
3713             waitTimer = Roo.TaskMgr.start({
3714                 run: function(i){
3715                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3716                 },
3717                 interval: 1000
3718             });
3719             return this;
3720         },
3721
3722         /**
3723          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3724          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3725          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3726          * @param {String} title The title bar text
3727          * @param {String} msg The message box body text
3728          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3729          * @param {Object} scope (optional) The scope of the callback function
3730          * @return {Roo.MessageBox} This message box
3731          */
3732         confirm : function(title, msg, fn, scope){
3733             this.show({
3734                 title : title,
3735                 msg : msg,
3736                 buttons: this.YESNO,
3737                 fn: fn,
3738                 scope : scope,
3739                 modal : true
3740             });
3741             return this;
3742         },
3743
3744         /**
3745          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3746          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3747          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3748          * (could also be the top-right close button) and the text that was entered will be passed as the two
3749          * parameters to the callback.
3750          * @param {String} title The title bar text
3751          * @param {String} msg The message box body text
3752          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3753          * @param {Object} scope (optional) The scope of the callback function
3754          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3755          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3756          * @return {Roo.MessageBox} This message box
3757          */
3758         prompt : function(title, msg, fn, scope, multiline){
3759             this.show({
3760                 title : title,
3761                 msg : msg,
3762                 buttons: this.OKCANCEL,
3763                 fn: fn,
3764                 minWidth:250,
3765                 scope : scope,
3766                 prompt:true,
3767                 multiline: multiline,
3768                 modal : true
3769             });
3770             return this;
3771         },
3772
3773         /**
3774          * Button config that displays a single OK button
3775          * @type Object
3776          */
3777         OK : {ok:true},
3778         /**
3779          * Button config that displays Yes and No buttons
3780          * @type Object
3781          */
3782         YESNO : {yes:true, no:true},
3783         /**
3784          * Button config that displays OK and Cancel buttons
3785          * @type Object
3786          */
3787         OKCANCEL : {ok:true, cancel:true},
3788         /**
3789          * Button config that displays Yes, No and Cancel buttons
3790          * @type Object
3791          */
3792         YESNOCANCEL : {yes:true, no:true, cancel:true},
3793
3794         /**
3795          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3796          * @type Number
3797          */
3798         defaultTextHeight : 75,
3799         /**
3800          * The maximum width in pixels of the message box (defaults to 600)
3801          * @type Number
3802          */
3803         maxWidth : 600,
3804         /**
3805          * The minimum width in pixels of the message box (defaults to 100)
3806          * @type Number
3807          */
3808         minWidth : 100,
3809         /**
3810          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3811          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3812          * @type Number
3813          */
3814         minProgressWidth : 250,
3815         /**
3816          * An object containing the default button text strings that can be overriden for localized language support.
3817          * Supported properties are: ok, cancel, yes and no.
3818          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3819          * @type Object
3820          */
3821         buttonText : {
3822             ok : "OK",
3823             cancel : "Cancel",
3824             yes : "Yes",
3825             no : "No"
3826         }
3827     };
3828 }();
3829
3830 /**
3831  * Shorthand for {@link Roo.MessageBox}
3832  */
3833 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3834 Roo.Msg = Roo.Msg || Roo.MessageBox;
3835 /*
3836  * - LGPL
3837  *
3838  * navbar
3839  * 
3840  */
3841
3842 /**
3843  * @class Roo.bootstrap.Navbar
3844  * @extends Roo.bootstrap.Component
3845  * Bootstrap Navbar class
3846
3847  * @constructor
3848  * Create a new Navbar
3849  * @param {Object} config The config object
3850  */
3851
3852
3853 Roo.bootstrap.Navbar = function(config){
3854     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3855     this.addEvents({
3856         // raw events
3857         /**
3858          * @event beforetoggle
3859          * Fire before toggle the menu
3860          * @param {Roo.EventObject} e
3861          */
3862         "beforetoggle" : true
3863     });
3864 };
3865
3866 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3867     
3868     
3869    
3870     // private
3871     navItems : false,
3872     loadMask : false,
3873     
3874     
3875     getAutoCreate : function(){
3876         
3877         
3878         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3879         
3880     },
3881     
3882     initEvents :function ()
3883     {
3884         //Roo.log(this.el.select('.navbar-toggle',true));
3885         this.el.select('.navbar-toggle',true).on('click', function() {
3886             if(this.fireEvent('beforetoggle', this) !== false){
3887                 var ce = this.el.select('.navbar-collapse',true).first();
3888                 ce.toggleClass('in'); // old...
3889                 if (ce.hasClass('collapse')) {
3890                     // show it...
3891                     ce.removeClass('collapse');
3892                     ce.addClass('show');
3893                     var h = ce.getHeight();
3894                     Roo.log(h);
3895                     ce.removeClass('show');
3896                     // at this point we should be able to see it..
3897                     ce.addClass('collapsing');
3898                     
3899                     ce.setHeight(0); // resize it ...
3900                     ce.on('transitionend', function() {
3901                         Roo.log('done transition');
3902                         ce.removeClass('collapsing');
3903                         ce.addClass('show');
3904                         ce.removeClass('collapse');
3905
3906                         ce.dom.style.height = '';
3907                     }, this, { single: true} );
3908                     ce.setHeight(h);
3909                     
3910                 } else {
3911                     ce.setHeight(ce.getHeight());
3912                     ce.removeClass('show');
3913                     ce.addClass('collapsing');
3914                     
3915                     ce.on('transitionend', function() {
3916                         ce.dom.style.height = '';
3917                         ce.removeClass('collapsing');
3918                         ce.addClass('collapse');
3919                     }, this, { single: true} );
3920                     ce.setHeight(0);
3921                 }
3922             }
3923             
3924         }, this);
3925         
3926         var mark = {
3927             tag: "div",
3928             cls:"x-dlg-mask"
3929         };
3930         
3931         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3932         
3933         var size = this.el.getSize();
3934         this.maskEl.setSize(size.width, size.height);
3935         this.maskEl.enableDisplayMode("block");
3936         this.maskEl.hide();
3937         
3938         if(this.loadMask){
3939             this.maskEl.show();
3940         }
3941     },
3942     
3943     
3944     getChildContainer : function()
3945     {
3946         if (this.el.select('.collapse').getCount()) {
3947             return this.el.select('.collapse',true).first();
3948         }
3949         
3950         return this.el;
3951     },
3952     
3953     mask : function()
3954     {
3955         this.maskEl.show();
3956     },
3957     
3958     unmask : function()
3959     {
3960         this.maskEl.hide();
3961     } 
3962     
3963     
3964     
3965     
3966 });
3967
3968
3969
3970  
3971
3972  /*
3973  * - LGPL
3974  *
3975  * navbar
3976  * 
3977  */
3978
3979 /**
3980  * @class Roo.bootstrap.NavSimplebar
3981  * @extends Roo.bootstrap.Navbar
3982  * Bootstrap Sidebar class
3983  *
3984  * @cfg {Boolean} inverse is inverted color
3985  * 
3986  * @cfg {String} type (nav | pills | tabs)
3987  * @cfg {Boolean} arrangement stacked | justified
3988  * @cfg {String} align (left | right) alignment
3989  * 
3990  * @cfg {Boolean} main (true|false) main nav bar? default false
3991  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3992  * 
3993  * @cfg {String} tag (header|footer|nav|div) default is nav 
3994
3995  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3996  * 
3997  * 
3998  * @constructor
3999  * Create a new Sidebar
4000  * @param {Object} config The config object
4001  */
4002
4003
4004 Roo.bootstrap.NavSimplebar = function(config){
4005     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4006 };
4007
4008 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4009     
4010     inverse: false,
4011     
4012     type: false,
4013     arrangement: '',
4014     align : false,
4015     
4016     weight : 'light',
4017     
4018     main : false,
4019     
4020     
4021     tag : false,
4022     
4023     
4024     getAutoCreate : function(){
4025         
4026         
4027         var cfg = {
4028             tag : this.tag || 'div',
4029             cls : 'navbar navbar-expand-lg roo-navbar-simple'
4030         };
4031         if (['light','white'].indexOf(this.weight) > -1) {
4032             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4033         }
4034         cfg.cls += ' bg-' + this.weight;
4035         
4036         if (this.inverse) {
4037             cfg.cls += ' navbar-inverse';
4038             
4039         }
4040         
4041         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4042         
4043         //if (Roo.bootstrap.version == 4) {
4044         //    return cfg;
4045         //}
4046         
4047         cfg.cn = [
4048             {
4049                 cls: 'nav',
4050                 tag : 'ul'
4051             }
4052         ];
4053         
4054          
4055         this.type = this.type || 'nav';
4056         if (['tabs','pills'].indexOf(this.type) != -1) {
4057             cfg.cn[0].cls += ' nav-' + this.type
4058         
4059         
4060         } else {
4061             if (this.type!=='nav') {
4062                 Roo.log('nav type must be nav/tabs/pills')
4063             }
4064             cfg.cn[0].cls += ' navbar-nav'
4065         }
4066         
4067         
4068         
4069         
4070         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4071             cfg.cn[0].cls += ' nav-' + this.arrangement;
4072         }
4073         
4074         
4075         if (this.align === 'right') {
4076             cfg.cn[0].cls += ' navbar-right';
4077         }
4078         
4079         
4080         
4081         
4082         return cfg;
4083     
4084         
4085     }
4086     
4087     
4088     
4089 });
4090
4091
4092
4093  
4094
4095  
4096        /*
4097  * - LGPL
4098  *
4099  * navbar
4100  * navbar-fixed-top
4101  * navbar-expand-md  fixed-top 
4102  */
4103
4104 /**
4105  * @class Roo.bootstrap.NavHeaderbar
4106  * @extends Roo.bootstrap.NavSimplebar
4107  * Bootstrap Sidebar class
4108  *
4109  * @cfg {String} brand what is brand
4110  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4111  * @cfg {String} brand_href href of the brand
4112  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4113  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4114  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4115  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4116  * 
4117  * @constructor
4118  * Create a new Sidebar
4119  * @param {Object} config The config object
4120  */
4121
4122
4123 Roo.bootstrap.NavHeaderbar = function(config){
4124     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4125       
4126 };
4127
4128 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4129     
4130     position: '',
4131     brand: '',
4132     brand_href: false,
4133     srButton : true,
4134     autohide : false,
4135     desktopCenter : false,
4136    
4137     
4138     getAutoCreate : function(){
4139         
4140         var   cfg = {
4141             tag: this.nav || 'nav',
4142             cls: 'navbar navbar-expand-md',
4143             role: 'navigation',
4144             cn: []
4145         };
4146         
4147         var cn = cfg.cn;
4148         if (this.desktopCenter) {
4149             cn.push({cls : 'container', cn : []});
4150             cn = cn[0].cn;
4151         }
4152         
4153         if(this.srButton){
4154             var btn = {
4155                 tag: 'button',
4156                 type: 'button',
4157                 cls: 'navbar-toggle navbar-toggler',
4158                 'data-toggle': 'collapse',
4159                 cn: [
4160                     {
4161                         tag: 'span',
4162                         cls: 'sr-only',
4163                         html: 'Toggle navigation'
4164                     },
4165                     {
4166                         tag: 'span',
4167                         cls: 'icon-bar navbar-toggler-icon'
4168                     },
4169                     {
4170                         tag: 'span',
4171                         cls: 'icon-bar'
4172                     },
4173                     {
4174                         tag: 'span',
4175                         cls: 'icon-bar'
4176                     }
4177                 ]
4178             };
4179             
4180             cn.push( Roo.bootstrap.version == 4 ? btn : {
4181                 tag: 'div',
4182                 cls: 'navbar-header',
4183                 cn: [
4184                     btn
4185                 ]
4186             });
4187         }
4188         
4189         cn.push({
4190             tag: 'div',
4191             cls: 'collapse navbar-collapse',
4192             cn : []
4193         });
4194         
4195         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4196         
4197         if (['light','white'].indexOf(this.weight) > -1) {
4198             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4199         }
4200         cfg.cls += ' bg-' + this.weight;
4201         
4202         
4203         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4204             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4205             
4206             // tag can override this..
4207             
4208             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4209         }
4210         
4211         if (this.brand !== '') {
4212             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4213             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4214                 tag: 'a',
4215                 href: this.brand_href ? this.brand_href : '#',
4216                 cls: 'navbar-brand',
4217                 cn: [
4218                 this.brand
4219                 ]
4220             });
4221         }
4222         
4223         if(this.main){
4224             cfg.cls += ' main-nav';
4225         }
4226         
4227         
4228         return cfg;
4229
4230         
4231     },
4232     getHeaderChildContainer : function()
4233     {
4234         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4235             return this.el.select('.navbar-header',true).first();
4236         }
4237         
4238         return this.getChildContainer();
4239     },
4240     
4241     
4242     initEvents : function()
4243     {
4244         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4245         
4246         if (this.autohide) {
4247             
4248             var prevScroll = 0;
4249             var ft = this.el;
4250             
4251             Roo.get(document).on('scroll',function(e) {
4252                 var ns = Roo.get(document).getScroll().top;
4253                 var os = prevScroll;
4254                 prevScroll = ns;
4255                 
4256                 if(ns > os){
4257                     ft.removeClass('slideDown');
4258                     ft.addClass('slideUp');
4259                     return;
4260                 }
4261                 ft.removeClass('slideUp');
4262                 ft.addClass('slideDown');
4263                  
4264               
4265           },this);
4266         }
4267     }    
4268     
4269 });
4270
4271
4272
4273  
4274
4275  /*
4276  * - LGPL
4277  *
4278  * navbar
4279  * 
4280  */
4281
4282 /**
4283  * @class Roo.bootstrap.NavSidebar
4284  * @extends Roo.bootstrap.Navbar
4285  * Bootstrap Sidebar class
4286  * 
4287  * @constructor
4288  * Create a new Sidebar
4289  * @param {Object} config The config object
4290  */
4291
4292
4293 Roo.bootstrap.NavSidebar = function(config){
4294     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4295 };
4296
4297 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4298     
4299     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4300     
4301     getAutoCreate : function(){
4302         
4303         
4304         return  {
4305             tag: 'div',
4306             cls: 'sidebar sidebar-nav'
4307         };
4308     
4309         
4310     }
4311     
4312     
4313     
4314 });
4315
4316
4317
4318  
4319
4320  /*
4321  * - LGPL
4322  *
4323  * nav group
4324  * 
4325  */
4326
4327 /**
4328  * @class Roo.bootstrap.NavGroup
4329  * @extends Roo.bootstrap.Component
4330  * Bootstrap NavGroup class
4331  * @cfg {String} align (left|right)
4332  * @cfg {Boolean} inverse
4333  * @cfg {String} type (nav|pills|tab) default nav
4334  * @cfg {String} navId - reference Id for navbar.
4335
4336  * 
4337  * @constructor
4338  * Create a new nav group
4339  * @param {Object} config The config object
4340  */
4341
4342 Roo.bootstrap.NavGroup = function(config){
4343     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4344     this.navItems = [];
4345    
4346     Roo.bootstrap.NavGroup.register(this);
4347      this.addEvents({
4348         /**
4349              * @event changed
4350              * Fires when the active item changes
4351              * @param {Roo.bootstrap.NavGroup} this
4352              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4353              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4354          */
4355         'changed': true
4356      });
4357     
4358 };
4359
4360 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4361     
4362     align: '',
4363     inverse: false,
4364     form: false,
4365     type: 'nav',
4366     navId : '',
4367     // private
4368     
4369     navItems : false, 
4370     
4371     getAutoCreate : function()
4372     {
4373         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4374         
4375         cfg = {
4376             tag : 'ul',
4377             cls: 'nav' 
4378         };
4379         if (Roo.bootstrap.version == 4) {
4380             if (['tabs','pills'].indexOf(this.type) != -1) {
4381                 cfg.cls += ' nav-' + this.type; 
4382             } else {
4383                 cfg.cls += ' navbar-nav';
4384             }
4385         } else {
4386             if (['tabs','pills'].indexOf(this.type) != -1) {
4387                 cfg.cls += ' nav-' + this.type
4388             } else {
4389                 if (this.type !== 'nav') {
4390                     Roo.log('nav type must be nav/tabs/pills')
4391                 }
4392                 cfg.cls += ' navbar-nav'
4393             }
4394         }
4395         
4396         if (this.parent() && this.parent().sidebar) {
4397             cfg = {
4398                 tag: 'ul',
4399                 cls: 'dashboard-menu sidebar-menu'
4400             };
4401             
4402             return cfg;
4403         }
4404         
4405         if (this.form === true) {
4406             cfg = {
4407                 tag: 'form',
4408                 cls: 'navbar-form form-inline'
4409             };
4410             
4411             if (this.align === 'right') {
4412                 cfg.cls += ' navbar-right ml-md-auto';
4413             } else {
4414                 cfg.cls += ' navbar-left';
4415             }
4416         }
4417         
4418         if (this.align === 'right') {
4419             cfg.cls += ' navbar-right ml-md-auto';
4420         } else {
4421             cfg.cls += ' mr-auto';
4422         }
4423         
4424         if (this.inverse) {
4425             cfg.cls += ' navbar-inverse';
4426             
4427         }
4428         
4429         
4430         return cfg;
4431     },
4432     /**
4433     * sets the active Navigation item
4434     * @param {Roo.bootstrap.NavItem} the new current navitem
4435     */
4436     setActiveItem : function(item)
4437     {
4438         var prev = false;
4439         Roo.each(this.navItems, function(v){
4440             if (v == item) {
4441                 return ;
4442             }
4443             if (v.isActive()) {
4444                 v.setActive(false, true);
4445                 prev = v;
4446                 
4447             }
4448             
4449         });
4450
4451         item.setActive(true, true);
4452         this.fireEvent('changed', this, item, prev);
4453         
4454         
4455     },
4456     /**
4457     * gets the active Navigation item
4458     * @return {Roo.bootstrap.NavItem} the current navitem
4459     */
4460     getActive : function()
4461     {
4462         
4463         var prev = false;
4464         Roo.each(this.navItems, function(v){
4465             
4466             if (v.isActive()) {
4467                 prev = v;
4468                 
4469             }
4470             
4471         });
4472         return prev;
4473     },
4474     
4475     indexOfNav : function()
4476     {
4477         
4478         var prev = false;
4479         Roo.each(this.navItems, function(v,i){
4480             
4481             if (v.isActive()) {
4482                 prev = i;
4483                 
4484             }
4485             
4486         });
4487         return prev;
4488     },
4489     /**
4490     * adds a Navigation item
4491     * @param {Roo.bootstrap.NavItem} the navitem to add
4492     */
4493     addItem : function(cfg)
4494     {
4495         if (this.form && Roo.bootstrap.version == 4) {
4496             cfg.tag = 'div';
4497         }
4498         var cn = new Roo.bootstrap.NavItem(cfg);
4499         this.register(cn);
4500         cn.parentId = this.id;
4501         cn.onRender(this.el, null);
4502         return cn;
4503     },
4504     /**
4505     * register a Navigation item
4506     * @param {Roo.bootstrap.NavItem} the navitem to add
4507     */
4508     register : function(item)
4509     {
4510         this.navItems.push( item);
4511         item.navId = this.navId;
4512     
4513     },
4514     
4515     /**
4516     * clear all the Navigation item
4517     */
4518    
4519     clearAll : function()
4520     {
4521         this.navItems = [];
4522         this.el.dom.innerHTML = '';
4523     },
4524     
4525     getNavItem: function(tabId)
4526     {
4527         var ret = false;
4528         Roo.each(this.navItems, function(e) {
4529             if (e.tabId == tabId) {
4530                ret =  e;
4531                return false;
4532             }
4533             return true;
4534             
4535         });
4536         return ret;
4537     },
4538     
4539     setActiveNext : function()
4540     {
4541         var i = this.indexOfNav(this.getActive());
4542         if (i > this.navItems.length) {
4543             return;
4544         }
4545         this.setActiveItem(this.navItems[i+1]);
4546     },
4547     setActivePrev : function()
4548     {
4549         var i = this.indexOfNav(this.getActive());
4550         if (i  < 1) {
4551             return;
4552         }
4553         this.setActiveItem(this.navItems[i-1]);
4554     },
4555     clearWasActive : function(except) {
4556         Roo.each(this.navItems, function(e) {
4557             if (e.tabId != except.tabId && e.was_active) {
4558                e.was_active = false;
4559                return false;
4560             }
4561             return true;
4562             
4563         });
4564     },
4565     getWasActive : function ()
4566     {
4567         var r = false;
4568         Roo.each(this.navItems, function(e) {
4569             if (e.was_active) {
4570                r = e;
4571                return false;
4572             }
4573             return true;
4574             
4575         });
4576         return r;
4577     }
4578     
4579     
4580 });
4581
4582  
4583 Roo.apply(Roo.bootstrap.NavGroup, {
4584     
4585     groups: {},
4586      /**
4587     * register a Navigation Group
4588     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4589     */
4590     register : function(navgrp)
4591     {
4592         this.groups[navgrp.navId] = navgrp;
4593         
4594     },
4595     /**
4596     * fetch a Navigation Group based on the navigation ID
4597     * @param {string} the navgroup to add
4598     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4599     */
4600     get: function(navId) {
4601         if (typeof(this.groups[navId]) == 'undefined') {
4602             return false;
4603             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4604         }
4605         return this.groups[navId] ;
4606     }
4607     
4608     
4609     
4610 });
4611
4612  /*
4613  * - LGPL
4614  *
4615  * row
4616  * 
4617  */
4618
4619 /**
4620  * @class Roo.bootstrap.NavItem
4621  * @extends Roo.bootstrap.Component
4622  * Bootstrap Navbar.NavItem class
4623  * @cfg {String} href  link to
4624  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4625
4626  * @cfg {String} html content of button
4627  * @cfg {String} badge text inside badge
4628  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4629  * @cfg {String} glyphicon DEPRICATED - use fa
4630  * @cfg {String} icon DEPRICATED - use fa
4631  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4632  * @cfg {Boolean} active Is item active
4633  * @cfg {Boolean} disabled Is item disabled
4634  
4635  * @cfg {Boolean} preventDefault (true | false) default false
4636  * @cfg {String} tabId the tab that this item activates.
4637  * @cfg {String} tagtype (a|span) render as a href or span?
4638  * @cfg {Boolean} animateRef (true|false) link to element default false  
4639   
4640  * @constructor
4641  * Create a new Navbar Item
4642  * @param {Object} config The config object
4643  */
4644 Roo.bootstrap.NavItem = function(config){
4645     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4646     this.addEvents({
4647         // raw events
4648         /**
4649          * @event click
4650          * The raw click event for the entire grid.
4651          * @param {Roo.EventObject} e
4652          */
4653         "click" : true,
4654          /**
4655             * @event changed
4656             * Fires when the active item active state changes
4657             * @param {Roo.bootstrap.NavItem} this
4658             * @param {boolean} state the new state
4659              
4660          */
4661         'changed': true,
4662         /**
4663             * @event scrollto
4664             * Fires when scroll to element
4665             * @param {Roo.bootstrap.NavItem} this
4666             * @param {Object} options
4667             * @param {Roo.EventObject} e
4668              
4669          */
4670         'scrollto': true
4671     });
4672    
4673 };
4674
4675 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4676     
4677     href: false,
4678     html: '',
4679     badge: '',
4680     icon: false,
4681     fa : false,
4682     glyphicon: false,
4683     active: false,
4684     preventDefault : false,
4685     tabId : false,
4686     tagtype : 'a',
4687     tag: 'li',
4688     disabled : false,
4689     animateRef : false,
4690     was_active : false,
4691     button_weight : '',
4692     button_outline : false,
4693     
4694     navLink: false,
4695     
4696     getAutoCreate : function(){
4697          
4698         var cfg = {
4699             tag: this.tag,
4700             cls: 'nav-item'
4701         };
4702         
4703         if (this.active) {
4704             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4705         }
4706         if (this.disabled) {
4707             cfg.cls += ' disabled';
4708         }
4709         
4710         // BS4 only?
4711         if (this.button_weight.length) {
4712             cfg.tag = this.href ? 'a' : 'button';
4713             cfg.html = this.html || '';
4714             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4715             if (this.href) {
4716                 cfg.href = this.href;
4717             }
4718             if (this.fa) {
4719                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4720             }
4721             
4722             // menu .. should add dropdown-menu class - so no need for carat..
4723             
4724             if (this.badge !== '') {
4725                  
4726                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4727             }
4728             return cfg;
4729         }
4730         
4731         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4732             cfg.cn = [
4733                 {
4734                     tag: this.tagtype,
4735                     href : this.href || "#",
4736                     html: this.html || ''
4737                 }
4738             ];
4739             if (this.tagtype == 'a') {
4740                 cfg.cn[0].cls = 'nav-link';
4741             }
4742             if (this.icon) {
4743                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4744             }
4745             if (this.fa) {
4746                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4747             }
4748             if(this.glyphicon) {
4749                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4750             }
4751             
4752             if (this.menu) {
4753                 
4754                 cfg.cn[0].html += " <span class='caret'></span>";
4755              
4756             }
4757             
4758             if (this.badge !== '') {
4759                  
4760                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4761             }
4762         }
4763         
4764         
4765         
4766         return cfg;
4767     },
4768     onRender : function(ct, position)
4769     {
4770        // Roo.log("Call onRender: " + this.xtype);
4771         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4772             this.tag = 'div';
4773         }
4774         
4775         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4776         this.navLink = this.el.select('.nav-link',true).first();
4777         return ret;
4778     },
4779       
4780     
4781     initEvents: function() 
4782     {
4783         if (typeof (this.menu) != 'undefined') {
4784             this.menu.parentType = this.xtype;
4785             this.menu.triggerEl = this.el;
4786             this.menu = this.addxtype(Roo.apply({}, this.menu));
4787         }
4788         
4789         this.el.select('a',true).on('click', this.onClick, this);
4790         
4791         if(this.tagtype == 'span'){
4792             this.el.select('span',true).on('click', this.onClick, this);
4793         }
4794        
4795         // at this point parent should be available..
4796         this.parent().register(this);
4797     },
4798     
4799     onClick : function(e)
4800     {
4801         if (e.getTarget('.dropdown-menu-item')) {
4802             // did you click on a menu itemm.... - then don't trigger onclick..
4803             return;
4804         }
4805         
4806         if(
4807                 this.preventDefault || 
4808                 this.href == '#' 
4809         ){
4810             Roo.log("NavItem - prevent Default?");
4811             e.preventDefault();
4812         }
4813         
4814         if (this.disabled) {
4815             return;
4816         }
4817         
4818         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4819         if (tg && tg.transition) {
4820             Roo.log("waiting for the transitionend");
4821             return;
4822         }
4823         
4824         
4825         
4826         //Roo.log("fire event clicked");
4827         if(this.fireEvent('click', this, e) === false){
4828             return;
4829         };
4830         
4831         if(this.tagtype == 'span'){
4832             return;
4833         }
4834         
4835         //Roo.log(this.href);
4836         var ael = this.el.select('a',true).first();
4837         //Roo.log(ael);
4838         
4839         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4840             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4841             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4842                 return; // ignore... - it's a 'hash' to another page.
4843             }
4844             Roo.log("NavItem - prevent Default?");
4845             e.preventDefault();
4846             this.scrollToElement(e);
4847         }
4848         
4849         
4850         var p =  this.parent();
4851    
4852         if (['tabs','pills'].indexOf(p.type)!==-1) {
4853             if (typeof(p.setActiveItem) !== 'undefined') {
4854                 p.setActiveItem(this);
4855             }
4856         }
4857         
4858         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4859         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4860             // remove the collapsed menu expand...
4861             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4862         }
4863     },
4864     
4865     isActive: function () {
4866         return this.active
4867     },
4868     setActive : function(state, fire, is_was_active)
4869     {
4870         if (this.active && !state && this.navId) {
4871             this.was_active = true;
4872             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4873             if (nv) {
4874                 nv.clearWasActive(this);
4875             }
4876             
4877         }
4878         this.active = state;
4879         
4880         if (!state ) {
4881             this.el.removeClass('active');
4882             this.navLink ? this.navLink.removeClass('active') : false;
4883         } else if (!this.el.hasClass('active')) {
4884             
4885             this.el.addClass('active');
4886             if (Roo.bootstrap.version == 4 && this.navLink ) {
4887                 this.navLink.addClass('active');
4888             }
4889             
4890         }
4891         if (fire) {
4892             this.fireEvent('changed', this, state);
4893         }
4894         
4895         // show a panel if it's registered and related..
4896         
4897         if (!this.navId || !this.tabId || !state || is_was_active) {
4898             return;
4899         }
4900         
4901         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4902         if (!tg) {
4903             return;
4904         }
4905         var pan = tg.getPanelByName(this.tabId);
4906         if (!pan) {
4907             return;
4908         }
4909         // if we can not flip to new panel - go back to old nav highlight..
4910         if (false == tg.showPanel(pan)) {
4911             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4912             if (nv) {
4913                 var onav = nv.getWasActive();
4914                 if (onav) {
4915                     onav.setActive(true, false, true);
4916                 }
4917             }
4918             
4919         }
4920         
4921         
4922         
4923     },
4924      // this should not be here...
4925     setDisabled : function(state)
4926     {
4927         this.disabled = state;
4928         if (!state ) {
4929             this.el.removeClass('disabled');
4930         } else if (!this.el.hasClass('disabled')) {
4931             this.el.addClass('disabled');
4932         }
4933         
4934     },
4935     
4936     /**
4937      * Fetch the element to display the tooltip on.
4938      * @return {Roo.Element} defaults to this.el
4939      */
4940     tooltipEl : function()
4941     {
4942         return this.el.select('' + this.tagtype + '', true).first();
4943     },
4944     
4945     scrollToElement : function(e)
4946     {
4947         var c = document.body;
4948         
4949         /*
4950          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4951          */
4952         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4953             c = document.documentElement;
4954         }
4955         
4956         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4957         
4958         if(!target){
4959             return;
4960         }
4961
4962         var o = target.calcOffsetsTo(c);
4963         
4964         var options = {
4965             target : target,
4966             value : o[1]
4967         };
4968         
4969         this.fireEvent('scrollto', this, options, e);
4970         
4971         Roo.get(c).scrollTo('top', options.value, true);
4972         
4973         return;
4974     }
4975 });
4976  
4977
4978  /*
4979  * - LGPL
4980  *
4981  * sidebar item
4982  *
4983  *  li
4984  *    <span> icon </span>
4985  *    <span> text </span>
4986  *    <span>badge </span>
4987  */
4988
4989 /**
4990  * @class Roo.bootstrap.NavSidebarItem
4991  * @extends Roo.bootstrap.NavItem
4992  * Bootstrap Navbar.NavSidebarItem class
4993  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4994  * {Boolean} open is the menu open
4995  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4996  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4997  * {String} buttonSize (sm|md|lg)the extra classes for the button
4998  * {Boolean} showArrow show arrow next to the text (default true)
4999  * @constructor
5000  * Create a new Navbar Button
5001  * @param {Object} config The config object
5002  */
5003 Roo.bootstrap.NavSidebarItem = function(config){
5004     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5005     this.addEvents({
5006         // raw events
5007         /**
5008          * @event click
5009          * The raw click event for the entire grid.
5010          * @param {Roo.EventObject} e
5011          */
5012         "click" : true,
5013          /**
5014             * @event changed
5015             * Fires when the active item active state changes
5016             * @param {Roo.bootstrap.NavSidebarItem} this
5017             * @param {boolean} state the new state
5018              
5019          */
5020         'changed': true
5021     });
5022    
5023 };
5024
5025 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5026     
5027     badgeWeight : 'default',
5028     
5029     open: false,
5030     
5031     buttonView : false,
5032     
5033     buttonWeight : 'default',
5034     
5035     buttonSize : 'md',
5036     
5037     showArrow : true,
5038     
5039     getAutoCreate : function(){
5040         
5041         
5042         var a = {
5043                 tag: 'a',
5044                 href : this.href || '#',
5045                 cls: '',
5046                 html : '',
5047                 cn : []
5048         };
5049         
5050         if(this.buttonView){
5051             a = {
5052                 tag: 'button',
5053                 href : this.href || '#',
5054                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5055                 html : this.html,
5056                 cn : []
5057             };
5058         }
5059         
5060         var cfg = {
5061             tag: 'li',
5062             cls: '',
5063             cn: [ a ]
5064         };
5065         
5066         if (this.active) {
5067             cfg.cls += ' active';
5068         }
5069         
5070         if (this.disabled) {
5071             cfg.cls += ' disabled';
5072         }
5073         if (this.open) {
5074             cfg.cls += ' open x-open';
5075         }
5076         // left icon..
5077         if (this.glyphicon || this.icon) {
5078             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5079             a.cn.push({ tag : 'i', cls : c }) ;
5080         }
5081         
5082         if(!this.buttonView){
5083             var span = {
5084                 tag: 'span',
5085                 html : this.html || ''
5086             };
5087
5088             a.cn.push(span);
5089             
5090         }
5091         
5092         if (this.badge !== '') {
5093             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5094         }
5095         
5096         if (this.menu) {
5097             
5098             if(this.showArrow){
5099                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5100             }
5101             
5102             a.cls += ' dropdown-toggle treeview' ;
5103         }
5104         
5105         return cfg;
5106     },
5107     
5108     initEvents : function()
5109     { 
5110         if (typeof (this.menu) != 'undefined') {
5111             this.menu.parentType = this.xtype;
5112             this.menu.triggerEl = this.el;
5113             this.menu = this.addxtype(Roo.apply({}, this.menu));
5114         }
5115         
5116         this.el.on('click', this.onClick, this);
5117         
5118         if(this.badge !== ''){
5119             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5120         }
5121         
5122     },
5123     
5124     onClick : function(e)
5125     {
5126         if(this.disabled){
5127             e.preventDefault();
5128             return;
5129         }
5130         
5131         if(this.preventDefault){
5132             e.preventDefault();
5133         }
5134         
5135         this.fireEvent('click', this);
5136     },
5137     
5138     disable : function()
5139     {
5140         this.setDisabled(true);
5141     },
5142     
5143     enable : function()
5144     {
5145         this.setDisabled(false);
5146     },
5147     
5148     setDisabled : function(state)
5149     {
5150         if(this.disabled == state){
5151             return;
5152         }
5153         
5154         this.disabled = state;
5155         
5156         if (state) {
5157             this.el.addClass('disabled');
5158             return;
5159         }
5160         
5161         this.el.removeClass('disabled');
5162         
5163         return;
5164     },
5165     
5166     setActive : function(state)
5167     {
5168         if(this.active == state){
5169             return;
5170         }
5171         
5172         this.active = state;
5173         
5174         if (state) {
5175             this.el.addClass('active');
5176             return;
5177         }
5178         
5179         this.el.removeClass('active');
5180         
5181         return;
5182     },
5183     
5184     isActive: function () 
5185     {
5186         return this.active;
5187     },
5188     
5189     setBadge : function(str)
5190     {
5191         if(!this.badgeEl){
5192             return;
5193         }
5194         
5195         this.badgeEl.dom.innerHTML = str;
5196     }
5197     
5198    
5199      
5200  
5201 });
5202  
5203
5204  /*
5205  * - LGPL
5206  *
5207  * row
5208  * 
5209  */
5210
5211 /**
5212  * @class Roo.bootstrap.Row
5213  * @extends Roo.bootstrap.Component
5214  * Bootstrap Row class (contains columns...)
5215  * 
5216  * @constructor
5217  * Create a new Row
5218  * @param {Object} config The config object
5219  */
5220
5221 Roo.bootstrap.Row = function(config){
5222     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5223 };
5224
5225 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5226     
5227     getAutoCreate : function(){
5228        return {
5229             cls: 'row clearfix'
5230        };
5231     }
5232     
5233     
5234 });
5235
5236  
5237
5238  /*
5239  * - LGPL
5240  *
5241  * element
5242  * 
5243  */
5244
5245 /**
5246  * @class Roo.bootstrap.Element
5247  * @extends Roo.bootstrap.Component
5248  * Bootstrap Element class
5249  * @cfg {String} html contents of the element
5250  * @cfg {String} tag tag of the element
5251  * @cfg {String} cls class of the element
5252  * @cfg {Boolean} preventDefault (true|false) default false
5253  * @cfg {Boolean} clickable (true|false) default false
5254  * 
5255  * @constructor
5256  * Create a new Element
5257  * @param {Object} config The config object
5258  */
5259
5260 Roo.bootstrap.Element = function(config){
5261     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5262     
5263     this.addEvents({
5264         // raw events
5265         /**
5266          * @event click
5267          * When a element is chick
5268          * @param {Roo.bootstrap.Element} this
5269          * @param {Roo.EventObject} e
5270          */
5271         "click" : true
5272     });
5273 };
5274
5275 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5276     
5277     tag: 'div',
5278     cls: '',
5279     html: '',
5280     preventDefault: false, 
5281     clickable: false,
5282     
5283     getAutoCreate : function(){
5284         
5285         var cfg = {
5286             tag: this.tag,
5287             // cls: this.cls, double assign in parent class Component.js :: onRender
5288             html: this.html
5289         };
5290         
5291         return cfg;
5292     },
5293     
5294     initEvents: function() 
5295     {
5296         Roo.bootstrap.Element.superclass.initEvents.call(this);
5297         
5298         if(this.clickable){
5299             this.el.on('click', this.onClick, this);
5300         }
5301         
5302     },
5303     
5304     onClick : function(e)
5305     {
5306         if(this.preventDefault){
5307             e.preventDefault();
5308         }
5309         
5310         this.fireEvent('click', this, e);
5311     },
5312     
5313     getValue : function()
5314     {
5315         return this.el.dom.innerHTML;
5316     },
5317     
5318     setValue : function(value)
5319     {
5320         this.el.dom.innerHTML = value;
5321     }
5322    
5323 });
5324
5325  
5326
5327  /*
5328  * - LGPL
5329  *
5330  * pagination
5331  * 
5332  */
5333
5334 /**
5335  * @class Roo.bootstrap.Pagination
5336  * @extends Roo.bootstrap.Component
5337  * Bootstrap Pagination class
5338  * @cfg {String} size xs | sm | md | lg
5339  * @cfg {Boolean} inverse false | true
5340  * 
5341  * @constructor
5342  * Create a new Pagination
5343  * @param {Object} config The config object
5344  */
5345
5346 Roo.bootstrap.Pagination = function(config){
5347     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5348 };
5349
5350 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5351     
5352     cls: false,
5353     size: false,
5354     inverse: false,
5355     
5356     getAutoCreate : function(){
5357         var cfg = {
5358             tag: 'ul',
5359                 cls: 'pagination'
5360         };
5361         if (this.inverse) {
5362             cfg.cls += ' inverse';
5363         }
5364         if (this.html) {
5365             cfg.html=this.html;
5366         }
5367         if (this.cls) {
5368             cfg.cls += " " + this.cls;
5369         }
5370         return cfg;
5371     }
5372    
5373 });
5374
5375  
5376
5377  /*
5378  * - LGPL
5379  *
5380  * Pagination item
5381  * 
5382  */
5383
5384
5385 /**
5386  * @class Roo.bootstrap.PaginationItem
5387  * @extends Roo.bootstrap.Component
5388  * Bootstrap PaginationItem class
5389  * @cfg {String} html text
5390  * @cfg {String} href the link
5391  * @cfg {Boolean} preventDefault (true | false) default true
5392  * @cfg {Boolean} active (true | false) default false
5393  * @cfg {Boolean} disabled default false
5394  * 
5395  * 
5396  * @constructor
5397  * Create a new PaginationItem
5398  * @param {Object} config The config object
5399  */
5400
5401
5402 Roo.bootstrap.PaginationItem = function(config){
5403     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5404     this.addEvents({
5405         // raw events
5406         /**
5407          * @event click
5408          * The raw click event for the entire grid.
5409          * @param {Roo.EventObject} e
5410          */
5411         "click" : true
5412     });
5413 };
5414
5415 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5416     
5417     href : false,
5418     html : false,
5419     preventDefault: true,
5420     active : false,
5421     cls : false,
5422     disabled: false,
5423     
5424     getAutoCreate : function(){
5425         var cfg= {
5426             tag: 'li',
5427             cn: [
5428                 {
5429                     tag : 'a',
5430                     href : this.href ? this.href : '#',
5431                     html : this.html ? this.html : ''
5432                 }
5433             ]
5434         };
5435         
5436         if(this.cls){
5437             cfg.cls = this.cls;
5438         }
5439         
5440         if(this.disabled){
5441             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5442         }
5443         
5444         if(this.active){
5445             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5446         }
5447         
5448         return cfg;
5449     },
5450     
5451     initEvents: function() {
5452         
5453         this.el.on('click', this.onClick, this);
5454         
5455     },
5456     onClick : function(e)
5457     {
5458         Roo.log('PaginationItem on click ');
5459         if(this.preventDefault){
5460             e.preventDefault();
5461         }
5462         
5463         if(this.disabled){
5464             return;
5465         }
5466         
5467         this.fireEvent('click', this, e);
5468     }
5469    
5470 });
5471
5472  
5473
5474  /*
5475  * - LGPL
5476  *
5477  * slider
5478  * 
5479  */
5480
5481
5482 /**
5483  * @class Roo.bootstrap.Slider
5484  * @extends Roo.bootstrap.Component
5485  * Bootstrap Slider class
5486  *    
5487  * @constructor
5488  * Create a new Slider
5489  * @param {Object} config The config object
5490  */
5491
5492 Roo.bootstrap.Slider = function(config){
5493     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5494 };
5495
5496 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5497     
5498     getAutoCreate : function(){
5499         
5500         var cfg = {
5501             tag: 'div',
5502             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5503             cn: [
5504                 {
5505                     tag: 'a',
5506                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5507                 }
5508             ]
5509         };
5510         
5511         return cfg;
5512     }
5513    
5514 });
5515
5516  /*
5517  * Based on:
5518  * Ext JS Library 1.1.1
5519  * Copyright(c) 2006-2007, Ext JS, LLC.
5520  *
5521  * Originally Released Under LGPL - original licence link has changed is not relivant.
5522  *
5523  * Fork - LGPL
5524  * <script type="text/javascript">
5525  */
5526  
5527
5528 /**
5529  * @class Roo.grid.ColumnModel
5530  * @extends Roo.util.Observable
5531  * This is the default implementation of a ColumnModel used by the Grid. It defines
5532  * the columns in the grid.
5533  * <br>Usage:<br>
5534  <pre><code>
5535  var colModel = new Roo.grid.ColumnModel([
5536         {header: "Ticker", width: 60, sortable: true, locked: true},
5537         {header: "Company Name", width: 150, sortable: true},
5538         {header: "Market Cap.", width: 100, sortable: true},
5539         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5540         {header: "Employees", width: 100, sortable: true, resizable: false}
5541  ]);
5542  </code></pre>
5543  * <p>
5544  
5545  * The config options listed for this class are options which may appear in each
5546  * individual column definition.
5547  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5548  * @constructor
5549  * @param {Object} config An Array of column config objects. See this class's
5550  * config objects for details.
5551 */
5552 Roo.grid.ColumnModel = function(config){
5553         /**
5554      * The config passed into the constructor
5555      */
5556     this.config = config;
5557     this.lookup = {};
5558
5559     // if no id, create one
5560     // if the column does not have a dataIndex mapping,
5561     // map it to the order it is in the config
5562     for(var i = 0, len = config.length; i < len; i++){
5563         var c = config[i];
5564         if(typeof c.dataIndex == "undefined"){
5565             c.dataIndex = i;
5566         }
5567         if(typeof c.renderer == "string"){
5568             c.renderer = Roo.util.Format[c.renderer];
5569         }
5570         if(typeof c.id == "undefined"){
5571             c.id = Roo.id();
5572         }
5573         if(c.editor && c.editor.xtype){
5574             c.editor  = Roo.factory(c.editor, Roo.grid);
5575         }
5576         if(c.editor && c.editor.isFormField){
5577             c.editor = new Roo.grid.GridEditor(c.editor);
5578         }
5579         this.lookup[c.id] = c;
5580     }
5581
5582     /**
5583      * The width of columns which have no width specified (defaults to 100)
5584      * @type Number
5585      */
5586     this.defaultWidth = 100;
5587
5588     /**
5589      * Default sortable of columns which have no sortable specified (defaults to false)
5590      * @type Boolean
5591      */
5592     this.defaultSortable = false;
5593
5594     this.addEvents({
5595         /**
5596              * @event widthchange
5597              * Fires when the width of a column changes.
5598              * @param {ColumnModel} this
5599              * @param {Number} columnIndex The column index
5600              * @param {Number} newWidth The new width
5601              */
5602             "widthchange": true,
5603         /**
5604              * @event headerchange
5605              * Fires when the text of a header changes.
5606              * @param {ColumnModel} this
5607              * @param {Number} columnIndex The column index
5608              * @param {Number} newText The new header text
5609              */
5610             "headerchange": true,
5611         /**
5612              * @event hiddenchange
5613              * Fires when a column is hidden or "unhidden".
5614              * @param {ColumnModel} this
5615              * @param {Number} columnIndex The column index
5616              * @param {Boolean} hidden true if hidden, false otherwise
5617              */
5618             "hiddenchange": true,
5619             /**
5620          * @event columnmoved
5621          * Fires when a column is moved.
5622          * @param {ColumnModel} this
5623          * @param {Number} oldIndex
5624          * @param {Number} newIndex
5625          */
5626         "columnmoved" : true,
5627         /**
5628          * @event columlockchange
5629          * Fires when a column's locked state is changed
5630          * @param {ColumnModel} this
5631          * @param {Number} colIndex
5632          * @param {Boolean} locked true if locked
5633          */
5634         "columnlockchange" : true
5635     });
5636     Roo.grid.ColumnModel.superclass.constructor.call(this);
5637 };
5638 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5639     /**
5640      * @cfg {String} header The header text to display in the Grid view.
5641      */
5642     /**
5643      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5644      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5645      * specified, the column's index is used as an index into the Record's data Array.
5646      */
5647     /**
5648      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5649      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5650      */
5651     /**
5652      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5653      * Defaults to the value of the {@link #defaultSortable} property.
5654      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5655      */
5656     /**
5657      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5658      */
5659     /**
5660      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5661      */
5662     /**
5663      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5664      */
5665     /**
5666      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5667      */
5668     /**
5669      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5670      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5671      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5672      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5673      */
5674        /**
5675      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5676      */
5677     /**
5678      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5679      */
5680     /**
5681      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5682      */
5683     /**
5684      * @cfg {String} cursor (Optional)
5685      */
5686     /**
5687      * @cfg {String} tooltip (Optional)
5688      */
5689     /**
5690      * @cfg {Number} xs (Optional)
5691      */
5692     /**
5693      * @cfg {Number} sm (Optional)
5694      */
5695     /**
5696      * @cfg {Number} md (Optional)
5697      */
5698     /**
5699      * @cfg {Number} lg (Optional)
5700      */
5701     /**
5702      * Returns the id of the column at the specified index.
5703      * @param {Number} index The column index
5704      * @return {String} the id
5705      */
5706     getColumnId : function(index){
5707         return this.config[index].id;
5708     },
5709
5710     /**
5711      * Returns the column for a specified id.
5712      * @param {String} id The column id
5713      * @return {Object} the column
5714      */
5715     getColumnById : function(id){
5716         return this.lookup[id];
5717     },
5718
5719     
5720     /**
5721      * Returns the column for a specified dataIndex.
5722      * @param {String} dataIndex The column dataIndex
5723      * @return {Object|Boolean} the column or false if not found
5724      */
5725     getColumnByDataIndex: function(dataIndex){
5726         var index = this.findColumnIndex(dataIndex);
5727         return index > -1 ? this.config[index] : false;
5728     },
5729     
5730     /**
5731      * Returns the index for a specified column id.
5732      * @param {String} id The column id
5733      * @return {Number} the index, or -1 if not found
5734      */
5735     getIndexById : function(id){
5736         for(var i = 0, len = this.config.length; i < len; i++){
5737             if(this.config[i].id == id){
5738                 return i;
5739             }
5740         }
5741         return -1;
5742     },
5743     
5744     /**
5745      * Returns the index for a specified column dataIndex.
5746      * @param {String} dataIndex The column dataIndex
5747      * @return {Number} the index, or -1 if not found
5748      */
5749     
5750     findColumnIndex : function(dataIndex){
5751         for(var i = 0, len = this.config.length; i < len; i++){
5752             if(this.config[i].dataIndex == dataIndex){
5753                 return i;
5754             }
5755         }
5756         return -1;
5757     },
5758     
5759     
5760     moveColumn : function(oldIndex, newIndex){
5761         var c = this.config[oldIndex];
5762         this.config.splice(oldIndex, 1);
5763         this.config.splice(newIndex, 0, c);
5764         this.dataMap = null;
5765         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5766     },
5767
5768     isLocked : function(colIndex){
5769         return this.config[colIndex].locked === true;
5770     },
5771
5772     setLocked : function(colIndex, value, suppressEvent){
5773         if(this.isLocked(colIndex) == value){
5774             return;
5775         }
5776         this.config[colIndex].locked = value;
5777         if(!suppressEvent){
5778             this.fireEvent("columnlockchange", this, colIndex, value);
5779         }
5780     },
5781
5782     getTotalLockedWidth : function(){
5783         var totalWidth = 0;
5784         for(var i = 0; i < this.config.length; i++){
5785             if(this.isLocked(i) && !this.isHidden(i)){
5786                 this.totalWidth += this.getColumnWidth(i);
5787             }
5788         }
5789         return totalWidth;
5790     },
5791
5792     getLockedCount : function(){
5793         for(var i = 0, len = this.config.length; i < len; i++){
5794             if(!this.isLocked(i)){
5795                 return i;
5796             }
5797         }
5798         
5799         return this.config.length;
5800     },
5801
5802     /**
5803      * Returns the number of columns.
5804      * @return {Number}
5805      */
5806     getColumnCount : function(visibleOnly){
5807         if(visibleOnly === true){
5808             var c = 0;
5809             for(var i = 0, len = this.config.length; i < len; i++){
5810                 if(!this.isHidden(i)){
5811                     c++;
5812                 }
5813             }
5814             return c;
5815         }
5816         return this.config.length;
5817     },
5818
5819     /**
5820      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5821      * @param {Function} fn
5822      * @param {Object} scope (optional)
5823      * @return {Array} result
5824      */
5825     getColumnsBy : function(fn, scope){
5826         var r = [];
5827         for(var i = 0, len = this.config.length; i < len; i++){
5828             var c = this.config[i];
5829             if(fn.call(scope||this, c, i) === true){
5830                 r[r.length] = c;
5831             }
5832         }
5833         return r;
5834     },
5835
5836     /**
5837      * Returns true if the specified column is sortable.
5838      * @param {Number} col The column index
5839      * @return {Boolean}
5840      */
5841     isSortable : function(col){
5842         if(typeof this.config[col].sortable == "undefined"){
5843             return this.defaultSortable;
5844         }
5845         return this.config[col].sortable;
5846     },
5847
5848     /**
5849      * Returns the rendering (formatting) function defined for the column.
5850      * @param {Number} col The column index.
5851      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5852      */
5853     getRenderer : function(col){
5854         if(!this.config[col].renderer){
5855             return Roo.grid.ColumnModel.defaultRenderer;
5856         }
5857         return this.config[col].renderer;
5858     },
5859
5860     /**
5861      * Sets the rendering (formatting) function for a column.
5862      * @param {Number} col The column index
5863      * @param {Function} fn The function to use to process the cell's raw data
5864      * to return HTML markup for the grid view. The render function is called with
5865      * the following parameters:<ul>
5866      * <li>Data value.</li>
5867      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5868      * <li>css A CSS style string to apply to the table cell.</li>
5869      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5870      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5871      * <li>Row index</li>
5872      * <li>Column index</li>
5873      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5874      */
5875     setRenderer : function(col, fn){
5876         this.config[col].renderer = fn;
5877     },
5878
5879     /**
5880      * Returns the width for the specified column.
5881      * @param {Number} col The column index
5882      * @return {Number}
5883      */
5884     getColumnWidth : function(col){
5885         return this.config[col].width * 1 || this.defaultWidth;
5886     },
5887
5888     /**
5889      * Sets the width for a column.
5890      * @param {Number} col The column index
5891      * @param {Number} width The new width
5892      */
5893     setColumnWidth : function(col, width, suppressEvent){
5894         this.config[col].width = width;
5895         this.totalWidth = null;
5896         if(!suppressEvent){
5897              this.fireEvent("widthchange", this, col, width);
5898         }
5899     },
5900
5901     /**
5902      * Returns the total width of all columns.
5903      * @param {Boolean} includeHidden True to include hidden column widths
5904      * @return {Number}
5905      */
5906     getTotalWidth : function(includeHidden){
5907         if(!this.totalWidth){
5908             this.totalWidth = 0;
5909             for(var i = 0, len = this.config.length; i < len; i++){
5910                 if(includeHidden || !this.isHidden(i)){
5911                     this.totalWidth += this.getColumnWidth(i);
5912                 }
5913             }
5914         }
5915         return this.totalWidth;
5916     },
5917
5918     /**
5919      * Returns the header for the specified column.
5920      * @param {Number} col The column index
5921      * @return {String}
5922      */
5923     getColumnHeader : function(col){
5924         return this.config[col].header;
5925     },
5926
5927     /**
5928      * Sets the header for a column.
5929      * @param {Number} col The column index
5930      * @param {String} header The new header
5931      */
5932     setColumnHeader : function(col, header){
5933         this.config[col].header = header;
5934         this.fireEvent("headerchange", this, col, header);
5935     },
5936
5937     /**
5938      * Returns the tooltip for the specified column.
5939      * @param {Number} col The column index
5940      * @return {String}
5941      */
5942     getColumnTooltip : function(col){
5943             return this.config[col].tooltip;
5944     },
5945     /**
5946      * Sets the tooltip for a column.
5947      * @param {Number} col The column index
5948      * @param {String} tooltip The new tooltip
5949      */
5950     setColumnTooltip : function(col, tooltip){
5951             this.config[col].tooltip = tooltip;
5952     },
5953
5954     /**
5955      * Returns the dataIndex for the specified column.
5956      * @param {Number} col The column index
5957      * @return {Number}
5958      */
5959     getDataIndex : function(col){
5960         return this.config[col].dataIndex;
5961     },
5962
5963     /**
5964      * Sets the dataIndex for a column.
5965      * @param {Number} col The column index
5966      * @param {Number} dataIndex The new dataIndex
5967      */
5968     setDataIndex : function(col, dataIndex){
5969         this.config[col].dataIndex = dataIndex;
5970     },
5971
5972     
5973     
5974     /**
5975      * Returns true if the cell is editable.
5976      * @param {Number} colIndex The column index
5977      * @param {Number} rowIndex The row index - this is nto actually used..?
5978      * @return {Boolean}
5979      */
5980     isCellEditable : function(colIndex, rowIndex){
5981         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5982     },
5983
5984     /**
5985      * Returns the editor defined for the cell/column.
5986      * return false or null to disable editing.
5987      * @param {Number} colIndex The column index
5988      * @param {Number} rowIndex The row index
5989      * @return {Object}
5990      */
5991     getCellEditor : function(colIndex, rowIndex){
5992         return this.config[colIndex].editor;
5993     },
5994
5995     /**
5996      * Sets if a column is editable.
5997      * @param {Number} col The column index
5998      * @param {Boolean} editable True if the column is editable
5999      */
6000     setEditable : function(col, editable){
6001         this.config[col].editable = editable;
6002     },
6003
6004
6005     /**
6006      * Returns true if the column is hidden.
6007      * @param {Number} colIndex The column index
6008      * @return {Boolean}
6009      */
6010     isHidden : function(colIndex){
6011         return this.config[colIndex].hidden;
6012     },
6013
6014
6015     /**
6016      * Returns true if the column width cannot be changed
6017      */
6018     isFixed : function(colIndex){
6019         return this.config[colIndex].fixed;
6020     },
6021
6022     /**
6023      * Returns true if the column can be resized
6024      * @return {Boolean}
6025      */
6026     isResizable : function(colIndex){
6027         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6028     },
6029     /**
6030      * Sets if a column is hidden.
6031      * @param {Number} colIndex The column index
6032      * @param {Boolean} hidden True if the column is hidden
6033      */
6034     setHidden : function(colIndex, hidden){
6035         this.config[colIndex].hidden = hidden;
6036         this.totalWidth = null;
6037         this.fireEvent("hiddenchange", this, colIndex, hidden);
6038     },
6039
6040     /**
6041      * Sets the editor for a column.
6042      * @param {Number} col The column index
6043      * @param {Object} editor The editor object
6044      */
6045     setEditor : function(col, editor){
6046         this.config[col].editor = editor;
6047     }
6048 });
6049
6050 Roo.grid.ColumnModel.defaultRenderer = function(value)
6051 {
6052     if(typeof value == "object") {
6053         return value;
6054     }
6055         if(typeof value == "string" && value.length < 1){
6056             return "&#160;";
6057         }
6058     
6059         return String.format("{0}", value);
6060 };
6061
6062 // Alias for backwards compatibility
6063 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6064 /*
6065  * Based on:
6066  * Ext JS Library 1.1.1
6067  * Copyright(c) 2006-2007, Ext JS, LLC.
6068  *
6069  * Originally Released Under LGPL - original licence link has changed is not relivant.
6070  *
6071  * Fork - LGPL
6072  * <script type="text/javascript">
6073  */
6074  
6075 /**
6076  * @class Roo.LoadMask
6077  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6078  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6079  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6080  * element's UpdateManager load indicator and will be destroyed after the initial load.
6081  * @constructor
6082  * Create a new LoadMask
6083  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6084  * @param {Object} config The config object
6085  */
6086 Roo.LoadMask = function(el, config){
6087     this.el = Roo.get(el);
6088     Roo.apply(this, config);
6089     if(this.store){
6090         this.store.on('beforeload', this.onBeforeLoad, this);
6091         this.store.on('load', this.onLoad, this);
6092         this.store.on('loadexception', this.onLoadException, this);
6093         this.removeMask = false;
6094     }else{
6095         var um = this.el.getUpdateManager();
6096         um.showLoadIndicator = false; // disable the default indicator
6097         um.on('beforeupdate', this.onBeforeLoad, this);
6098         um.on('update', this.onLoad, this);
6099         um.on('failure', this.onLoad, this);
6100         this.removeMask = true;
6101     }
6102 };
6103
6104 Roo.LoadMask.prototype = {
6105     /**
6106      * @cfg {Boolean} removeMask
6107      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6108      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6109      */
6110     /**
6111      * @cfg {String} msg
6112      * The text to display in a centered loading message box (defaults to 'Loading...')
6113      */
6114     msg : 'Loading...',
6115     /**
6116      * @cfg {String} msgCls
6117      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6118      */
6119     msgCls : 'x-mask-loading',
6120
6121     /**
6122      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6123      * @type Boolean
6124      */
6125     disabled: false,
6126
6127     /**
6128      * Disables the mask to prevent it from being displayed
6129      */
6130     disable : function(){
6131        this.disabled = true;
6132     },
6133
6134     /**
6135      * Enables the mask so that it can be displayed
6136      */
6137     enable : function(){
6138         this.disabled = false;
6139     },
6140     
6141     onLoadException : function()
6142     {
6143         Roo.log(arguments);
6144         
6145         if (typeof(arguments[3]) != 'undefined') {
6146             Roo.MessageBox.alert("Error loading",arguments[3]);
6147         } 
6148         /*
6149         try {
6150             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6151                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6152             }   
6153         } catch(e) {
6154             
6155         }
6156         */
6157     
6158         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6159     },
6160     // private
6161     onLoad : function()
6162     {
6163         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6164     },
6165
6166     // private
6167     onBeforeLoad : function(){
6168         if(!this.disabled){
6169             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6170         }
6171     },
6172
6173     // private
6174     destroy : function(){
6175         if(this.store){
6176             this.store.un('beforeload', this.onBeforeLoad, this);
6177             this.store.un('load', this.onLoad, this);
6178             this.store.un('loadexception', this.onLoadException, this);
6179         }else{
6180             var um = this.el.getUpdateManager();
6181             um.un('beforeupdate', this.onBeforeLoad, this);
6182             um.un('update', this.onLoad, this);
6183             um.un('failure', this.onLoad, this);
6184         }
6185     }
6186 };/*
6187  * - LGPL
6188  *
6189  * table
6190  * 
6191  */
6192
6193 /**
6194  * @class Roo.bootstrap.Table
6195  * @extends Roo.bootstrap.Component
6196  * Bootstrap Table class
6197  * @cfg {String} cls table class
6198  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6199  * @cfg {String} bgcolor Specifies the background color for a table
6200  * @cfg {Number} border Specifies whether the table cells should have borders or not
6201  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6202  * @cfg {Number} cellspacing Specifies the space between cells
6203  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6204  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6205  * @cfg {String} sortable Specifies that the table should be sortable
6206  * @cfg {String} summary Specifies a summary of the content of a table
6207  * @cfg {Number} width Specifies the width of a table
6208  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6209  * 
6210  * @cfg {boolean} striped Should the rows be alternative striped
6211  * @cfg {boolean} bordered Add borders to the table
6212  * @cfg {boolean} hover Add hover highlighting
6213  * @cfg {boolean} condensed Format condensed
6214  * @cfg {boolean} responsive Format condensed
6215  * @cfg {Boolean} loadMask (true|false) default false
6216  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6217  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6218  * @cfg {Boolean} rowSelection (true|false) default false
6219  * @cfg {Boolean} cellSelection (true|false) default false
6220  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6221  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6222  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6223  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6224  
6225  * 
6226  * @constructor
6227  * Create a new Table
6228  * @param {Object} config The config object
6229  */
6230
6231 Roo.bootstrap.Table = function(config){
6232     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6233     
6234   
6235     
6236     // BC...
6237     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6238     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6239     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6240     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6241     
6242     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6243     if (this.sm) {
6244         this.sm.grid = this;
6245         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6246         this.sm = this.selModel;
6247         this.sm.xmodule = this.xmodule || false;
6248     }
6249     
6250     if (this.cm && typeof(this.cm.config) == 'undefined') {
6251         this.colModel = new Roo.grid.ColumnModel(this.cm);
6252         this.cm = this.colModel;
6253         this.cm.xmodule = this.xmodule || false;
6254     }
6255     if (this.store) {
6256         this.store= Roo.factory(this.store, Roo.data);
6257         this.ds = this.store;
6258         this.ds.xmodule = this.xmodule || false;
6259          
6260     }
6261     if (this.footer && this.store) {
6262         this.footer.dataSource = this.ds;
6263         this.footer = Roo.factory(this.footer);
6264     }
6265     
6266     /** @private */
6267     this.addEvents({
6268         /**
6269          * @event cellclick
6270          * Fires when a cell is clicked
6271          * @param {Roo.bootstrap.Table} this
6272          * @param {Roo.Element} el
6273          * @param {Number} rowIndex
6274          * @param {Number} columnIndex
6275          * @param {Roo.EventObject} e
6276          */
6277         "cellclick" : true,
6278         /**
6279          * @event celldblclick
6280          * Fires when a cell is double clicked
6281          * @param {Roo.bootstrap.Table} this
6282          * @param {Roo.Element} el
6283          * @param {Number} rowIndex
6284          * @param {Number} columnIndex
6285          * @param {Roo.EventObject} e
6286          */
6287         "celldblclick" : true,
6288         /**
6289          * @event rowclick
6290          * Fires when a row is clicked
6291          * @param {Roo.bootstrap.Table} this
6292          * @param {Roo.Element} el
6293          * @param {Number} rowIndex
6294          * @param {Roo.EventObject} e
6295          */
6296         "rowclick" : true,
6297         /**
6298          * @event rowdblclick
6299          * Fires when a row is double clicked
6300          * @param {Roo.bootstrap.Table} this
6301          * @param {Roo.Element} el
6302          * @param {Number} rowIndex
6303          * @param {Roo.EventObject} e
6304          */
6305         "rowdblclick" : true,
6306         /**
6307          * @event mouseover
6308          * Fires when a mouseover occur
6309          * @param {Roo.bootstrap.Table} this
6310          * @param {Roo.Element} el
6311          * @param {Number} rowIndex
6312          * @param {Number} columnIndex
6313          * @param {Roo.EventObject} e
6314          */
6315         "mouseover" : true,
6316         /**
6317          * @event mouseout
6318          * Fires when a mouseout occur
6319          * @param {Roo.bootstrap.Table} this
6320          * @param {Roo.Element} el
6321          * @param {Number} rowIndex
6322          * @param {Number} columnIndex
6323          * @param {Roo.EventObject} e
6324          */
6325         "mouseout" : true,
6326         /**
6327          * @event rowclass
6328          * Fires when a row is rendered, so you can change add a style to it.
6329          * @param {Roo.bootstrap.Table} this
6330          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6331          */
6332         'rowclass' : true,
6333           /**
6334          * @event rowsrendered
6335          * Fires when all the  rows have been rendered
6336          * @param {Roo.bootstrap.Table} this
6337          */
6338         'rowsrendered' : true,
6339         /**
6340          * @event contextmenu
6341          * The raw contextmenu event for the entire grid.
6342          * @param {Roo.EventObject} e
6343          */
6344         "contextmenu" : true,
6345         /**
6346          * @event rowcontextmenu
6347          * Fires when a row is right clicked
6348          * @param {Roo.bootstrap.Table} this
6349          * @param {Number} rowIndex
6350          * @param {Roo.EventObject} e
6351          */
6352         "rowcontextmenu" : true,
6353         /**
6354          * @event cellcontextmenu
6355          * Fires when a cell is right clicked
6356          * @param {Roo.bootstrap.Table} this
6357          * @param {Number} rowIndex
6358          * @param {Number} cellIndex
6359          * @param {Roo.EventObject} e
6360          */
6361          "cellcontextmenu" : true,
6362          /**
6363          * @event headercontextmenu
6364          * Fires when a header is right clicked
6365          * @param {Roo.bootstrap.Table} this
6366          * @param {Number} columnIndex
6367          * @param {Roo.EventObject} e
6368          */
6369         "headercontextmenu" : true
6370     });
6371 };
6372
6373 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6374     
6375     cls: false,
6376     align: false,
6377     bgcolor: false,
6378     border: false,
6379     cellpadding: false,
6380     cellspacing: false,
6381     frame: false,
6382     rules: false,
6383     sortable: false,
6384     summary: false,
6385     width: false,
6386     striped : false,
6387     scrollBody : false,
6388     bordered: false,
6389     hover:  false,
6390     condensed : false,
6391     responsive : false,
6392     sm : false,
6393     cm : false,
6394     store : false,
6395     loadMask : false,
6396     footerShow : true,
6397     headerShow : true,
6398   
6399     rowSelection : false,
6400     cellSelection : false,
6401     layout : false,
6402     
6403     // Roo.Element - the tbody
6404     mainBody: false,
6405     // Roo.Element - thead element
6406     mainHead: false,
6407     
6408     container: false, // used by gridpanel...
6409     
6410     lazyLoad : false,
6411     
6412     CSS : Roo.util.CSS,
6413     
6414     auto_hide_footer : false,
6415     
6416     getAutoCreate : function()
6417     {
6418         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6419         
6420         cfg = {
6421             tag: 'table',
6422             cls : 'table',
6423             cn : []
6424         };
6425         if (this.scrollBody) {
6426             cfg.cls += ' table-body-fixed';
6427         }    
6428         if (this.striped) {
6429             cfg.cls += ' table-striped';
6430         }
6431         
6432         if (this.hover) {
6433             cfg.cls += ' table-hover';
6434         }
6435         if (this.bordered) {
6436             cfg.cls += ' table-bordered';
6437         }
6438         if (this.condensed) {
6439             cfg.cls += ' table-condensed';
6440         }
6441         if (this.responsive) {
6442             cfg.cls += ' table-responsive';
6443         }
6444         
6445         if (this.cls) {
6446             cfg.cls+=  ' ' +this.cls;
6447         }
6448         
6449         // this lot should be simplifed...
6450         var _t = this;
6451         var cp = [
6452             'align',
6453             'bgcolor',
6454             'border',
6455             'cellpadding',
6456             'cellspacing',
6457             'frame',
6458             'rules',
6459             'sortable',
6460             'summary',
6461             'width'
6462         ].forEach(function(k) {
6463             if (_t[k]) {
6464                 cfg[k] = _t[k];
6465             }
6466         });
6467         
6468         
6469         if (this.layout) {
6470             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6471         }
6472         
6473         if(this.store || this.cm){
6474             if(this.headerShow){
6475                 cfg.cn.push(this.renderHeader());
6476             }
6477             
6478             cfg.cn.push(this.renderBody());
6479             
6480             if(this.footerShow){
6481                 cfg.cn.push(this.renderFooter());
6482             }
6483             // where does this come from?
6484             //cfg.cls+=  ' TableGrid';
6485         }
6486         
6487         return { cn : [ cfg ] };
6488     },
6489     
6490     initEvents : function()
6491     {   
6492         if(!this.store || !this.cm){
6493             return;
6494         }
6495         if (this.selModel) {
6496             this.selModel.initEvents();
6497         }
6498         
6499         
6500         //Roo.log('initEvents with ds!!!!');
6501         
6502         this.mainBody = this.el.select('tbody', true).first();
6503         this.mainHead = this.el.select('thead', true).first();
6504         this.mainFoot = this.el.select('tfoot', true).first();
6505         
6506         
6507         
6508         var _this = this;
6509         
6510         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6511             e.on('click', _this.sort, _this);
6512         });
6513         
6514         this.mainBody.on("click", this.onClick, this);
6515         this.mainBody.on("dblclick", this.onDblClick, this);
6516         
6517         // why is this done????? = it breaks dialogs??
6518         //this.parent().el.setStyle('position', 'relative');
6519         
6520         
6521         if (this.footer) {
6522             this.footer.parentId = this.id;
6523             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6524             
6525             if(this.lazyLoad){
6526                 this.el.select('tfoot tr td').first().addClass('hide');
6527             }
6528         } 
6529         
6530         if(this.loadMask) {
6531             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6532         }
6533         
6534         this.store.on('load', this.onLoad, this);
6535         this.store.on('beforeload', this.onBeforeLoad, this);
6536         this.store.on('update', this.onUpdate, this);
6537         this.store.on('add', this.onAdd, this);
6538         this.store.on("clear", this.clear, this);
6539         
6540         this.el.on("contextmenu", this.onContextMenu, this);
6541         
6542         this.mainBody.on('scroll', this.onBodyScroll, this);
6543         
6544         this.cm.on("headerchange", this.onHeaderChange, this);
6545         
6546         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6547         
6548     },
6549     
6550     onContextMenu : function(e, t)
6551     {
6552         this.processEvent("contextmenu", e);
6553     },
6554     
6555     processEvent : function(name, e)
6556     {
6557         if (name != 'touchstart' ) {
6558             this.fireEvent(name, e);    
6559         }
6560         
6561         var t = e.getTarget();
6562         
6563         var cell = Roo.get(t);
6564         
6565         if(!cell){
6566             return;
6567         }
6568         
6569         if(cell.findParent('tfoot', false, true)){
6570             return;
6571         }
6572         
6573         if(cell.findParent('thead', false, true)){
6574             
6575             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6576                 cell = Roo.get(t).findParent('th', false, true);
6577                 if (!cell) {
6578                     Roo.log("failed to find th in thead?");
6579                     Roo.log(e.getTarget());
6580                     return;
6581                 }
6582             }
6583             
6584             var cellIndex = cell.dom.cellIndex;
6585             
6586             var ename = name == 'touchstart' ? 'click' : name;
6587             this.fireEvent("header" + ename, this, cellIndex, e);
6588             
6589             return;
6590         }
6591         
6592         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6593             cell = Roo.get(t).findParent('td', false, true);
6594             if (!cell) {
6595                 Roo.log("failed to find th in tbody?");
6596                 Roo.log(e.getTarget());
6597                 return;
6598             }
6599         }
6600         
6601         var row = cell.findParent('tr', false, true);
6602         var cellIndex = cell.dom.cellIndex;
6603         var rowIndex = row.dom.rowIndex - 1;
6604         
6605         if(row !== false){
6606             
6607             this.fireEvent("row" + name, this, rowIndex, e);
6608             
6609             if(cell !== false){
6610             
6611                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6612             }
6613         }
6614         
6615     },
6616     
6617     onMouseover : function(e, el)
6618     {
6619         var cell = Roo.get(el);
6620         
6621         if(!cell){
6622             return;
6623         }
6624         
6625         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6626             cell = cell.findParent('td', false, true);
6627         }
6628         
6629         var row = cell.findParent('tr', false, true);
6630         var cellIndex = cell.dom.cellIndex;
6631         var rowIndex = row.dom.rowIndex - 1; // start from 0
6632         
6633         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6634         
6635     },
6636     
6637     onMouseout : function(e, el)
6638     {
6639         var cell = Roo.get(el);
6640         
6641         if(!cell){
6642             return;
6643         }
6644         
6645         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6646             cell = cell.findParent('td', false, true);
6647         }
6648         
6649         var row = cell.findParent('tr', false, true);
6650         var cellIndex = cell.dom.cellIndex;
6651         var rowIndex = row.dom.rowIndex - 1; // start from 0
6652         
6653         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6654         
6655     },
6656     
6657     onClick : function(e, el)
6658     {
6659         var cell = Roo.get(el);
6660         
6661         if(!cell || (!this.cellSelection && !this.rowSelection)){
6662             return;
6663         }
6664         
6665         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6666             cell = cell.findParent('td', false, true);
6667         }
6668         
6669         if(!cell || typeof(cell) == 'undefined'){
6670             return;
6671         }
6672         
6673         var row = cell.findParent('tr', false, true);
6674         
6675         if(!row || typeof(row) == 'undefined'){
6676             return;
6677         }
6678         
6679         var cellIndex = cell.dom.cellIndex;
6680         var rowIndex = this.getRowIndex(row);
6681         
6682         // why??? - should these not be based on SelectionModel?
6683         if(this.cellSelection){
6684             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6685         }
6686         
6687         if(this.rowSelection){
6688             this.fireEvent('rowclick', this, row, rowIndex, e);
6689         }
6690         
6691         
6692     },
6693         
6694     onDblClick : function(e,el)
6695     {
6696         var cell = Roo.get(el);
6697         
6698         if(!cell || (!this.cellSelection && !this.rowSelection)){
6699             return;
6700         }
6701         
6702         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6703             cell = cell.findParent('td', false, true);
6704         }
6705         
6706         if(!cell || typeof(cell) == 'undefined'){
6707             return;
6708         }
6709         
6710         var row = cell.findParent('tr', false, true);
6711         
6712         if(!row || typeof(row) == 'undefined'){
6713             return;
6714         }
6715         
6716         var cellIndex = cell.dom.cellIndex;
6717         var rowIndex = this.getRowIndex(row);
6718         
6719         if(this.cellSelection){
6720             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6721         }
6722         
6723         if(this.rowSelection){
6724             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6725         }
6726     },
6727     
6728     sort : function(e,el)
6729     {
6730         var col = Roo.get(el);
6731         
6732         if(!col.hasClass('sortable')){
6733             return;
6734         }
6735         
6736         var sort = col.attr('sort');
6737         var dir = 'ASC';
6738         
6739         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6740             dir = 'DESC';
6741         }
6742         
6743         this.store.sortInfo = {field : sort, direction : dir};
6744         
6745         if (this.footer) {
6746             Roo.log("calling footer first");
6747             this.footer.onClick('first');
6748         } else {
6749         
6750             this.store.load({ params : { start : 0 } });
6751         }
6752     },
6753     
6754     renderHeader : function()
6755     {
6756         var header = {
6757             tag: 'thead',
6758             cn : []
6759         };
6760         
6761         var cm = this.cm;
6762         this.totalWidth = 0;
6763         
6764         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6765             
6766             var config = cm.config[i];
6767             
6768             var c = {
6769                 tag: 'th',
6770                 cls : 'x-hcol-' + i,
6771                 style : '',
6772                 html: cm.getColumnHeader(i)
6773             };
6774             
6775             var hh = '';
6776             
6777             if(typeof(config.sortable) != 'undefined' && config.sortable){
6778                 c.cls = 'sortable';
6779                 c.html = '<i class="glyphicon"></i>' + c.html;
6780             }
6781             
6782             if(typeof(config.lgHeader) != 'undefined'){
6783                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6784             }
6785             
6786             if(typeof(config.mdHeader) != 'undefined'){
6787                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6788             }
6789             
6790             if(typeof(config.smHeader) != 'undefined'){
6791                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6792             }
6793             
6794             if(typeof(config.xsHeader) != 'undefined'){
6795                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6796             }
6797             
6798             if(hh.length){
6799                 c.html = hh;
6800             }
6801             
6802             if(typeof(config.tooltip) != 'undefined'){
6803                 c.tooltip = config.tooltip;
6804             }
6805             
6806             if(typeof(config.colspan) != 'undefined'){
6807                 c.colspan = config.colspan;
6808             }
6809             
6810             if(typeof(config.hidden) != 'undefined' && config.hidden){
6811                 c.style += ' display:none;';
6812             }
6813             
6814             if(typeof(config.dataIndex) != 'undefined'){
6815                 c.sort = config.dataIndex;
6816             }
6817             
6818            
6819             
6820             if(typeof(config.align) != 'undefined' && config.align.length){
6821                 c.style += ' text-align:' + config.align + ';';
6822             }
6823             
6824             if(typeof(config.width) != 'undefined'){
6825                 c.style += ' width:' + config.width + 'px;';
6826                 this.totalWidth += config.width;
6827             } else {
6828                 this.totalWidth += 100; // assume minimum of 100 per column?
6829             }
6830             
6831             if(typeof(config.cls) != 'undefined'){
6832                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6833             }
6834             
6835             ['xs','sm','md','lg'].map(function(size){
6836                 
6837                 if(typeof(config[size]) == 'undefined'){
6838                     return;
6839                 }
6840                 
6841                 if (!config[size]) { // 0 = hidden
6842                     c.cls += ' hidden-' + size;
6843                     return;
6844                 }
6845                 
6846                 c.cls += ' col-' + size + '-' + config[size];
6847
6848             });
6849             
6850             header.cn.push(c)
6851         }
6852         
6853         return header;
6854     },
6855     
6856     renderBody : function()
6857     {
6858         var body = {
6859             tag: 'tbody',
6860             cn : [
6861                 {
6862                     tag: 'tr',
6863                     cn : [
6864                         {
6865                             tag : 'td',
6866                             colspan :  this.cm.getColumnCount()
6867                         }
6868                     ]
6869                 }
6870             ]
6871         };
6872         
6873         return body;
6874     },
6875     
6876     renderFooter : function()
6877     {
6878         var footer = {
6879             tag: 'tfoot',
6880             cn : [
6881                 {
6882                     tag: 'tr',
6883                     cn : [
6884                         {
6885                             tag : 'td',
6886                             colspan :  this.cm.getColumnCount()
6887                         }
6888                     ]
6889                 }
6890             ]
6891         };
6892         
6893         return footer;
6894     },
6895     
6896     
6897     
6898     onLoad : function()
6899     {
6900 //        Roo.log('ds onload');
6901         this.clear();
6902         
6903         var _this = this;
6904         var cm = this.cm;
6905         var ds = this.store;
6906         
6907         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6908             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6909             if (_this.store.sortInfo) {
6910                     
6911                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6912                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6913                 }
6914                 
6915                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6916                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6917                 }
6918             }
6919         });
6920         
6921         var tbody =  this.mainBody;
6922               
6923         if(ds.getCount() > 0){
6924             ds.data.each(function(d,rowIndex){
6925                 var row =  this.renderRow(cm, ds, rowIndex);
6926                 
6927                 tbody.createChild(row);
6928                 
6929                 var _this = this;
6930                 
6931                 if(row.cellObjects.length){
6932                     Roo.each(row.cellObjects, function(r){
6933                         _this.renderCellObject(r);
6934                     })
6935                 }
6936                 
6937             }, this);
6938         }
6939         
6940         var tfoot = this.el.select('tfoot', true).first();
6941         
6942         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6943             
6944             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6945             
6946             var total = this.ds.getTotalCount();
6947             
6948             if(this.footer.pageSize < total){
6949                 this.mainFoot.show();
6950             }
6951         }
6952         
6953         Roo.each(this.el.select('tbody td', true).elements, function(e){
6954             e.on('mouseover', _this.onMouseover, _this);
6955         });
6956         
6957         Roo.each(this.el.select('tbody td', true).elements, function(e){
6958             e.on('mouseout', _this.onMouseout, _this);
6959         });
6960         this.fireEvent('rowsrendered', this);
6961         
6962         this.autoSize();
6963     },
6964     
6965     
6966     onUpdate : function(ds,record)
6967     {
6968         this.refreshRow(record);
6969         this.autoSize();
6970     },
6971     
6972     onRemove : function(ds, record, index, isUpdate){
6973         if(isUpdate !== true){
6974             this.fireEvent("beforerowremoved", this, index, record);
6975         }
6976         var bt = this.mainBody.dom;
6977         
6978         var rows = this.el.select('tbody > tr', true).elements;
6979         
6980         if(typeof(rows[index]) != 'undefined'){
6981             bt.removeChild(rows[index].dom);
6982         }
6983         
6984 //        if(bt.rows[index]){
6985 //            bt.removeChild(bt.rows[index]);
6986 //        }
6987         
6988         if(isUpdate !== true){
6989             //this.stripeRows(index);
6990             //this.syncRowHeights(index, index);
6991             //this.layout();
6992             this.fireEvent("rowremoved", this, index, record);
6993         }
6994     },
6995     
6996     onAdd : function(ds, records, rowIndex)
6997     {
6998         //Roo.log('on Add called');
6999         // - note this does not handle multiple adding very well..
7000         var bt = this.mainBody.dom;
7001         for (var i =0 ; i < records.length;i++) {
7002             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7003             //Roo.log(records[i]);
7004             //Roo.log(this.store.getAt(rowIndex+i));
7005             this.insertRow(this.store, rowIndex + i, false);
7006             return;
7007         }
7008         
7009     },
7010     
7011     
7012     refreshRow : function(record){
7013         var ds = this.store, index;
7014         if(typeof record == 'number'){
7015             index = record;
7016             record = ds.getAt(index);
7017         }else{
7018             index = ds.indexOf(record);
7019         }
7020         this.insertRow(ds, index, true);
7021         this.autoSize();
7022         this.onRemove(ds, record, index+1, true);
7023         this.autoSize();
7024         //this.syncRowHeights(index, index);
7025         //this.layout();
7026         this.fireEvent("rowupdated", this, index, record);
7027     },
7028     
7029     insertRow : function(dm, rowIndex, isUpdate){
7030         
7031         if(!isUpdate){
7032             this.fireEvent("beforerowsinserted", this, rowIndex);
7033         }
7034             //var s = this.getScrollState();
7035         var row = this.renderRow(this.cm, this.store, rowIndex);
7036         // insert before rowIndex..
7037         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7038         
7039         var _this = this;
7040                 
7041         if(row.cellObjects.length){
7042             Roo.each(row.cellObjects, function(r){
7043                 _this.renderCellObject(r);
7044             })
7045         }
7046             
7047         if(!isUpdate){
7048             this.fireEvent("rowsinserted", this, rowIndex);
7049             //this.syncRowHeights(firstRow, lastRow);
7050             //this.stripeRows(firstRow);
7051             //this.layout();
7052         }
7053         
7054     },
7055     
7056     
7057     getRowDom : function(rowIndex)
7058     {
7059         var rows = this.el.select('tbody > tr', true).elements;
7060         
7061         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7062         
7063     },
7064     // returns the object tree for a tr..
7065   
7066     
7067     renderRow : function(cm, ds, rowIndex) 
7068     {
7069         var d = ds.getAt(rowIndex);
7070         
7071         var row = {
7072             tag : 'tr',
7073             cls : 'x-row-' + rowIndex,
7074             cn : []
7075         };
7076             
7077         var cellObjects = [];
7078         
7079         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7080             var config = cm.config[i];
7081             
7082             var renderer = cm.getRenderer(i);
7083             var value = '';
7084             var id = false;
7085             
7086             if(typeof(renderer) !== 'undefined'){
7087                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7088             }
7089             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7090             // and are rendered into the cells after the row is rendered - using the id for the element.
7091             
7092             if(typeof(value) === 'object'){
7093                 id = Roo.id();
7094                 cellObjects.push({
7095                     container : id,
7096                     cfg : value 
7097                 })
7098             }
7099             
7100             var rowcfg = {
7101                 record: d,
7102                 rowIndex : rowIndex,
7103                 colIndex : i,
7104                 rowClass : ''
7105             };
7106
7107             this.fireEvent('rowclass', this, rowcfg);
7108             
7109             var td = {
7110                 tag: 'td',
7111                 cls : rowcfg.rowClass + ' x-col-' + i,
7112                 style: '',
7113                 html: (typeof(value) === 'object') ? '' : value
7114             };
7115             
7116             if (id) {
7117                 td.id = id;
7118             }
7119             
7120             if(typeof(config.colspan) != 'undefined'){
7121                 td.colspan = config.colspan;
7122             }
7123             
7124             if(typeof(config.hidden) != 'undefined' && config.hidden){
7125                 td.style += ' display:none;';
7126             }
7127             
7128             if(typeof(config.align) != 'undefined' && config.align.length){
7129                 td.style += ' text-align:' + config.align + ';';
7130             }
7131             if(typeof(config.valign) != 'undefined' && config.valign.length){
7132                 td.style += ' vertical-align:' + config.valign + ';';
7133             }
7134             
7135             if(typeof(config.width) != 'undefined'){
7136                 td.style += ' width:' +  config.width + 'px;';
7137             }
7138             
7139             if(typeof(config.cursor) != 'undefined'){
7140                 td.style += ' cursor:' +  config.cursor + ';';
7141             }
7142             
7143             if(typeof(config.cls) != 'undefined'){
7144                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7145             }
7146             
7147             ['xs','sm','md','lg'].map(function(size){
7148                 
7149                 if(typeof(config[size]) == 'undefined'){
7150                     return;
7151                 }
7152                 
7153                 if (!config[size]) { // 0 = hidden
7154                     td.cls += ' hidden-' + size;
7155                     return;
7156                 }
7157                 
7158                 td.cls += ' col-' + size + '-' + config[size];
7159
7160             });
7161             
7162             row.cn.push(td);
7163            
7164         }
7165         
7166         row.cellObjects = cellObjects;
7167         
7168         return row;
7169           
7170     },
7171     
7172     
7173     
7174     onBeforeLoad : function()
7175     {
7176         
7177     },
7178      /**
7179      * Remove all rows
7180      */
7181     clear : function()
7182     {
7183         this.el.select('tbody', true).first().dom.innerHTML = '';
7184     },
7185     /**
7186      * Show or hide a row.
7187      * @param {Number} rowIndex to show or hide
7188      * @param {Boolean} state hide
7189      */
7190     setRowVisibility : function(rowIndex, state)
7191     {
7192         var bt = this.mainBody.dom;
7193         
7194         var rows = this.el.select('tbody > tr', true).elements;
7195         
7196         if(typeof(rows[rowIndex]) == 'undefined'){
7197             return;
7198         }
7199         rows[rowIndex].dom.style.display = state ? '' : 'none';
7200     },
7201     
7202     
7203     getSelectionModel : function(){
7204         if(!this.selModel){
7205             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7206         }
7207         return this.selModel;
7208     },
7209     /*
7210      * Render the Roo.bootstrap object from renderder
7211      */
7212     renderCellObject : function(r)
7213     {
7214         var _this = this;
7215         
7216         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7217         
7218         var t = r.cfg.render(r.container);
7219         
7220         if(r.cfg.cn){
7221             Roo.each(r.cfg.cn, function(c){
7222                 var child = {
7223                     container: t.getChildContainer(),
7224                     cfg: c
7225                 };
7226                 _this.renderCellObject(child);
7227             })
7228         }
7229     },
7230     
7231     getRowIndex : function(row)
7232     {
7233         var rowIndex = -1;
7234         
7235         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7236             if(el != row){
7237                 return;
7238             }
7239             
7240             rowIndex = index;
7241         });
7242         
7243         return rowIndex;
7244     },
7245      /**
7246      * Returns the grid's underlying element = used by panel.Grid
7247      * @return {Element} The element
7248      */
7249     getGridEl : function(){
7250         return this.el;
7251     },
7252      /**
7253      * Forces a resize - used by panel.Grid
7254      * @return {Element} The element
7255      */
7256     autoSize : function()
7257     {
7258         //var ctr = Roo.get(this.container.dom.parentElement);
7259         var ctr = Roo.get(this.el.dom);
7260         
7261         var thd = this.getGridEl().select('thead',true).first();
7262         var tbd = this.getGridEl().select('tbody', true).first();
7263         var tfd = this.getGridEl().select('tfoot', true).first();
7264         
7265         var cw = ctr.getWidth();
7266         
7267         if (tbd) {
7268             
7269             tbd.setSize(ctr.getWidth(),
7270                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7271             );
7272             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7273             cw -= barsize;
7274         }
7275         cw = Math.max(cw, this.totalWidth);
7276         this.getGridEl().select('tr',true).setWidth(cw);
7277         // resize 'expandable coloumn?
7278         
7279         return; // we doe not have a view in this design..
7280         
7281     },
7282     onBodyScroll: function()
7283     {
7284         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7285         if(this.mainHead){
7286             this.mainHead.setStyle({
7287                 'position' : 'relative',
7288                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7289             });
7290         }
7291         
7292         if(this.lazyLoad){
7293             
7294             var scrollHeight = this.mainBody.dom.scrollHeight;
7295             
7296             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7297             
7298             var height = this.mainBody.getHeight();
7299             
7300             if(scrollHeight - height == scrollTop) {
7301                 
7302                 var total = this.ds.getTotalCount();
7303                 
7304                 if(this.footer.cursor + this.footer.pageSize < total){
7305                     
7306                     this.footer.ds.load({
7307                         params : {
7308                             start : this.footer.cursor + this.footer.pageSize,
7309                             limit : this.footer.pageSize
7310                         },
7311                         add : true
7312                     });
7313                 }
7314             }
7315             
7316         }
7317     },
7318     
7319     onHeaderChange : function()
7320     {
7321         var header = this.renderHeader();
7322         var table = this.el.select('table', true).first();
7323         
7324         this.mainHead.remove();
7325         this.mainHead = table.createChild(header, this.mainBody, false);
7326     },
7327     
7328     onHiddenChange : function(colModel, colIndex, hidden)
7329     {
7330         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7331         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7332         
7333         this.CSS.updateRule(thSelector, "display", "");
7334         this.CSS.updateRule(tdSelector, "display", "");
7335         
7336         if(hidden){
7337             this.CSS.updateRule(thSelector, "display", "none");
7338             this.CSS.updateRule(tdSelector, "display", "none");
7339         }
7340         
7341         this.onHeaderChange();
7342         this.onLoad();
7343     },
7344     
7345     setColumnWidth: function(col_index, width)
7346     {
7347         // width = "md-2 xs-2..."
7348         if(!this.colModel.config[col_index]) {
7349             return;
7350         }
7351         
7352         var w = width.split(" ");
7353         
7354         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7355         
7356         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7357         
7358         
7359         for(var j = 0; j < w.length; j++) {
7360             
7361             if(!w[j]) {
7362                 continue;
7363             }
7364             
7365             var size_cls = w[j].split("-");
7366             
7367             if(!Number.isInteger(size_cls[1] * 1)) {
7368                 continue;
7369             }
7370             
7371             if(!this.colModel.config[col_index][size_cls[0]]) {
7372                 continue;
7373             }
7374             
7375             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7376                 continue;
7377             }
7378             
7379             h_row[0].classList.replace(
7380                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7381                 "col-"+size_cls[0]+"-"+size_cls[1]
7382             );
7383             
7384             for(var i = 0; i < rows.length; i++) {
7385                 
7386                 var size_cls = w[j].split("-");
7387                 
7388                 if(!Number.isInteger(size_cls[1] * 1)) {
7389                     continue;
7390                 }
7391                 
7392                 if(!this.colModel.config[col_index][size_cls[0]]) {
7393                     continue;
7394                 }
7395                 
7396                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7397                     continue;
7398                 }
7399                 
7400                 rows[i].classList.replace(
7401                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7402                     "col-"+size_cls[0]+"-"+size_cls[1]
7403                 );
7404             }
7405             
7406             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7407         }
7408     }
7409 });
7410
7411  
7412
7413  /*
7414  * - LGPL
7415  *
7416  * table cell
7417  * 
7418  */
7419
7420 /**
7421  * @class Roo.bootstrap.TableCell
7422  * @extends Roo.bootstrap.Component
7423  * Bootstrap TableCell class
7424  * @cfg {String} html cell contain text
7425  * @cfg {String} cls cell class
7426  * @cfg {String} tag cell tag (td|th) default td
7427  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7428  * @cfg {String} align Aligns the content in a cell
7429  * @cfg {String} axis Categorizes cells
7430  * @cfg {String} bgcolor Specifies the background color of a cell
7431  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7432  * @cfg {Number} colspan Specifies the number of columns a cell should span
7433  * @cfg {String} headers Specifies one or more header cells a cell is related to
7434  * @cfg {Number} height Sets the height of a cell
7435  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7436  * @cfg {Number} rowspan Sets the number of rows a cell should span
7437  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7438  * @cfg {String} valign Vertical aligns the content in a cell
7439  * @cfg {Number} width Specifies the width of a cell
7440  * 
7441  * @constructor
7442  * Create a new TableCell
7443  * @param {Object} config The config object
7444  */
7445
7446 Roo.bootstrap.TableCell = function(config){
7447     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7448 };
7449
7450 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7451     
7452     html: false,
7453     cls: false,
7454     tag: false,
7455     abbr: false,
7456     align: false,
7457     axis: false,
7458     bgcolor: false,
7459     charoff: false,
7460     colspan: false,
7461     headers: false,
7462     height: false,
7463     nowrap: false,
7464     rowspan: false,
7465     scope: false,
7466     valign: false,
7467     width: false,
7468     
7469     
7470     getAutoCreate : function(){
7471         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7472         
7473         cfg = {
7474             tag: 'td'
7475         };
7476         
7477         if(this.tag){
7478             cfg.tag = this.tag;
7479         }
7480         
7481         if (this.html) {
7482             cfg.html=this.html
7483         }
7484         if (this.cls) {
7485             cfg.cls=this.cls
7486         }
7487         if (this.abbr) {
7488             cfg.abbr=this.abbr
7489         }
7490         if (this.align) {
7491             cfg.align=this.align
7492         }
7493         if (this.axis) {
7494             cfg.axis=this.axis
7495         }
7496         if (this.bgcolor) {
7497             cfg.bgcolor=this.bgcolor
7498         }
7499         if (this.charoff) {
7500             cfg.charoff=this.charoff
7501         }
7502         if (this.colspan) {
7503             cfg.colspan=this.colspan
7504         }
7505         if (this.headers) {
7506             cfg.headers=this.headers
7507         }
7508         if (this.height) {
7509             cfg.height=this.height
7510         }
7511         if (this.nowrap) {
7512             cfg.nowrap=this.nowrap
7513         }
7514         if (this.rowspan) {
7515             cfg.rowspan=this.rowspan
7516         }
7517         if (this.scope) {
7518             cfg.scope=this.scope
7519         }
7520         if (this.valign) {
7521             cfg.valign=this.valign
7522         }
7523         if (this.width) {
7524             cfg.width=this.width
7525         }
7526         
7527         
7528         return cfg;
7529     }
7530    
7531 });
7532
7533  
7534
7535  /*
7536  * - LGPL
7537  *
7538  * table row
7539  * 
7540  */
7541
7542 /**
7543  * @class Roo.bootstrap.TableRow
7544  * @extends Roo.bootstrap.Component
7545  * Bootstrap TableRow class
7546  * @cfg {String} cls row class
7547  * @cfg {String} align Aligns the content in a table row
7548  * @cfg {String} bgcolor Specifies a background color for a table row
7549  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7550  * @cfg {String} valign Vertical aligns the content in a table row
7551  * 
7552  * @constructor
7553  * Create a new TableRow
7554  * @param {Object} config The config object
7555  */
7556
7557 Roo.bootstrap.TableRow = function(config){
7558     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7559 };
7560
7561 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7562     
7563     cls: false,
7564     align: false,
7565     bgcolor: false,
7566     charoff: false,
7567     valign: false,
7568     
7569     getAutoCreate : function(){
7570         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7571         
7572         cfg = {
7573             tag: 'tr'
7574         };
7575             
7576         if(this.cls){
7577             cfg.cls = this.cls;
7578         }
7579         if(this.align){
7580             cfg.align = this.align;
7581         }
7582         if(this.bgcolor){
7583             cfg.bgcolor = this.bgcolor;
7584         }
7585         if(this.charoff){
7586             cfg.charoff = this.charoff;
7587         }
7588         if(this.valign){
7589             cfg.valign = this.valign;
7590         }
7591         
7592         return cfg;
7593     }
7594    
7595 });
7596
7597  
7598
7599  /*
7600  * - LGPL
7601  *
7602  * table body
7603  * 
7604  */
7605
7606 /**
7607  * @class Roo.bootstrap.TableBody
7608  * @extends Roo.bootstrap.Component
7609  * Bootstrap TableBody class
7610  * @cfg {String} cls element class
7611  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7612  * @cfg {String} align Aligns the content inside the element
7613  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7614  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7615  * 
7616  * @constructor
7617  * Create a new TableBody
7618  * @param {Object} config The config object
7619  */
7620
7621 Roo.bootstrap.TableBody = function(config){
7622     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7623 };
7624
7625 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7626     
7627     cls: false,
7628     tag: false,
7629     align: false,
7630     charoff: false,
7631     valign: false,
7632     
7633     getAutoCreate : function(){
7634         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7635         
7636         cfg = {
7637             tag: 'tbody'
7638         };
7639             
7640         if (this.cls) {
7641             cfg.cls=this.cls
7642         }
7643         if(this.tag){
7644             cfg.tag = this.tag;
7645         }
7646         
7647         if(this.align){
7648             cfg.align = this.align;
7649         }
7650         if(this.charoff){
7651             cfg.charoff = this.charoff;
7652         }
7653         if(this.valign){
7654             cfg.valign = this.valign;
7655         }
7656         
7657         return cfg;
7658     }
7659     
7660     
7661 //    initEvents : function()
7662 //    {
7663 //        
7664 //        if(!this.store){
7665 //            return;
7666 //        }
7667 //        
7668 //        this.store = Roo.factory(this.store, Roo.data);
7669 //        this.store.on('load', this.onLoad, this);
7670 //        
7671 //        this.store.load();
7672 //        
7673 //    },
7674 //    
7675 //    onLoad: function () 
7676 //    {   
7677 //        this.fireEvent('load', this);
7678 //    }
7679 //    
7680 //   
7681 });
7682
7683  
7684
7685  /*
7686  * Based on:
7687  * Ext JS Library 1.1.1
7688  * Copyright(c) 2006-2007, Ext JS, LLC.
7689  *
7690  * Originally Released Under LGPL - original licence link has changed is not relivant.
7691  *
7692  * Fork - LGPL
7693  * <script type="text/javascript">
7694  */
7695
7696 // as we use this in bootstrap.
7697 Roo.namespace('Roo.form');
7698  /**
7699  * @class Roo.form.Action
7700  * Internal Class used to handle form actions
7701  * @constructor
7702  * @param {Roo.form.BasicForm} el The form element or its id
7703  * @param {Object} config Configuration options
7704  */
7705
7706  
7707  
7708 // define the action interface
7709 Roo.form.Action = function(form, options){
7710     this.form = form;
7711     this.options = options || {};
7712 };
7713 /**
7714  * Client Validation Failed
7715  * @const 
7716  */
7717 Roo.form.Action.CLIENT_INVALID = 'client';
7718 /**
7719  * Server Validation Failed
7720  * @const 
7721  */
7722 Roo.form.Action.SERVER_INVALID = 'server';
7723  /**
7724  * Connect to Server Failed
7725  * @const 
7726  */
7727 Roo.form.Action.CONNECT_FAILURE = 'connect';
7728 /**
7729  * Reading Data from Server Failed
7730  * @const 
7731  */
7732 Roo.form.Action.LOAD_FAILURE = 'load';
7733
7734 Roo.form.Action.prototype = {
7735     type : 'default',
7736     failureType : undefined,
7737     response : undefined,
7738     result : undefined,
7739
7740     // interface method
7741     run : function(options){
7742
7743     },
7744
7745     // interface method
7746     success : function(response){
7747
7748     },
7749
7750     // interface method
7751     handleResponse : function(response){
7752
7753     },
7754
7755     // default connection failure
7756     failure : function(response){
7757         
7758         this.response = response;
7759         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7760         this.form.afterAction(this, false);
7761     },
7762
7763     processResponse : function(response){
7764         this.response = response;
7765         if(!response.responseText){
7766             return true;
7767         }
7768         this.result = this.handleResponse(response);
7769         return this.result;
7770     },
7771
7772     // utility functions used internally
7773     getUrl : function(appendParams){
7774         var url = this.options.url || this.form.url || this.form.el.dom.action;
7775         if(appendParams){
7776             var p = this.getParams();
7777             if(p){
7778                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7779             }
7780         }
7781         return url;
7782     },
7783
7784     getMethod : function(){
7785         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7786     },
7787
7788     getParams : function(){
7789         var bp = this.form.baseParams;
7790         var p = this.options.params;
7791         if(p){
7792             if(typeof p == "object"){
7793                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7794             }else if(typeof p == 'string' && bp){
7795                 p += '&' + Roo.urlEncode(bp);
7796             }
7797         }else if(bp){
7798             p = Roo.urlEncode(bp);
7799         }
7800         return p;
7801     },
7802
7803     createCallback : function(){
7804         return {
7805             success: this.success,
7806             failure: this.failure,
7807             scope: this,
7808             timeout: (this.form.timeout*1000),
7809             upload: this.form.fileUpload ? this.success : undefined
7810         };
7811     }
7812 };
7813
7814 Roo.form.Action.Submit = function(form, options){
7815     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7816 };
7817
7818 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7819     type : 'submit',
7820
7821     haveProgress : false,
7822     uploadComplete : false,
7823     
7824     // uploadProgress indicator.
7825     uploadProgress : function()
7826     {
7827         if (!this.form.progressUrl) {
7828             return;
7829         }
7830         
7831         if (!this.haveProgress) {
7832             Roo.MessageBox.progress("Uploading", "Uploading");
7833         }
7834         if (this.uploadComplete) {
7835            Roo.MessageBox.hide();
7836            return;
7837         }
7838         
7839         this.haveProgress = true;
7840    
7841         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7842         
7843         var c = new Roo.data.Connection();
7844         c.request({
7845             url : this.form.progressUrl,
7846             params: {
7847                 id : uid
7848             },
7849             method: 'GET',
7850             success : function(req){
7851                //console.log(data);
7852                 var rdata = false;
7853                 var edata;
7854                 try  {
7855                    rdata = Roo.decode(req.responseText)
7856                 } catch (e) {
7857                     Roo.log("Invalid data from server..");
7858                     Roo.log(edata);
7859                     return;
7860                 }
7861                 if (!rdata || !rdata.success) {
7862                     Roo.log(rdata);
7863                     Roo.MessageBox.alert(Roo.encode(rdata));
7864                     return;
7865                 }
7866                 var data = rdata.data;
7867                 
7868                 if (this.uploadComplete) {
7869                    Roo.MessageBox.hide();
7870                    return;
7871                 }
7872                    
7873                 if (data){
7874                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7875                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7876                     );
7877                 }
7878                 this.uploadProgress.defer(2000,this);
7879             },
7880        
7881             failure: function(data) {
7882                 Roo.log('progress url failed ');
7883                 Roo.log(data);
7884             },
7885             scope : this
7886         });
7887            
7888     },
7889     
7890     
7891     run : function()
7892     {
7893         // run get Values on the form, so it syncs any secondary forms.
7894         this.form.getValues();
7895         
7896         var o = this.options;
7897         var method = this.getMethod();
7898         var isPost = method == 'POST';
7899         if(o.clientValidation === false || this.form.isValid()){
7900             
7901             if (this.form.progressUrl) {
7902                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7903                     (new Date() * 1) + '' + Math.random());
7904                     
7905             } 
7906             
7907             
7908             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7909                 form:this.form.el.dom,
7910                 url:this.getUrl(!isPost),
7911                 method: method,
7912                 params:isPost ? this.getParams() : null,
7913                 isUpload: this.form.fileUpload
7914             }));
7915             
7916             this.uploadProgress();
7917
7918         }else if (o.clientValidation !== false){ // client validation failed
7919             this.failureType = Roo.form.Action.CLIENT_INVALID;
7920             this.form.afterAction(this, false);
7921         }
7922     },
7923
7924     success : function(response)
7925     {
7926         this.uploadComplete= true;
7927         if (this.haveProgress) {
7928             Roo.MessageBox.hide();
7929         }
7930         
7931         
7932         var result = this.processResponse(response);
7933         if(result === true || result.success){
7934             this.form.afterAction(this, true);
7935             return;
7936         }
7937         if(result.errors){
7938             this.form.markInvalid(result.errors);
7939             this.failureType = Roo.form.Action.SERVER_INVALID;
7940         }
7941         this.form.afterAction(this, false);
7942     },
7943     failure : function(response)
7944     {
7945         this.uploadComplete= true;
7946         if (this.haveProgress) {
7947             Roo.MessageBox.hide();
7948         }
7949         
7950         this.response = response;
7951         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7952         this.form.afterAction(this, false);
7953     },
7954     
7955     handleResponse : function(response){
7956         if(this.form.errorReader){
7957             var rs = this.form.errorReader.read(response);
7958             var errors = [];
7959             if(rs.records){
7960                 for(var i = 0, len = rs.records.length; i < len; i++) {
7961                     var r = rs.records[i];
7962                     errors[i] = r.data;
7963                 }
7964             }
7965             if(errors.length < 1){
7966                 errors = null;
7967             }
7968             return {
7969                 success : rs.success,
7970                 errors : errors
7971             };
7972         }
7973         var ret = false;
7974         try {
7975             ret = Roo.decode(response.responseText);
7976         } catch (e) {
7977             ret = {
7978                 success: false,
7979                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7980                 errors : []
7981             };
7982         }
7983         return ret;
7984         
7985     }
7986 });
7987
7988
7989 Roo.form.Action.Load = function(form, options){
7990     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7991     this.reader = this.form.reader;
7992 };
7993
7994 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7995     type : 'load',
7996
7997     run : function(){
7998         
7999         Roo.Ajax.request(Roo.apply(
8000                 this.createCallback(), {
8001                     method:this.getMethod(),
8002                     url:this.getUrl(false),
8003                     params:this.getParams()
8004         }));
8005     },
8006
8007     success : function(response){
8008         
8009         var result = this.processResponse(response);
8010         if(result === true || !result.success || !result.data){
8011             this.failureType = Roo.form.Action.LOAD_FAILURE;
8012             this.form.afterAction(this, false);
8013             return;
8014         }
8015         this.form.clearInvalid();
8016         this.form.setValues(result.data);
8017         this.form.afterAction(this, true);
8018     },
8019
8020     handleResponse : function(response){
8021         if(this.form.reader){
8022             var rs = this.form.reader.read(response);
8023             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8024             return {
8025                 success : rs.success,
8026                 data : data
8027             };
8028         }
8029         return Roo.decode(response.responseText);
8030     }
8031 });
8032
8033 Roo.form.Action.ACTION_TYPES = {
8034     'load' : Roo.form.Action.Load,
8035     'submit' : Roo.form.Action.Submit
8036 };/*
8037  * - LGPL
8038  *
8039  * form
8040  *
8041  */
8042
8043 /**
8044  * @class Roo.bootstrap.Form
8045  * @extends Roo.bootstrap.Component
8046  * Bootstrap Form class
8047  * @cfg {String} method  GET | POST (default POST)
8048  * @cfg {String} labelAlign top | left (default top)
8049  * @cfg {String} align left  | right - for navbars
8050  * @cfg {Boolean} loadMask load mask when submit (default true)
8051
8052  *
8053  * @constructor
8054  * Create a new Form
8055  * @param {Object} config The config object
8056  */
8057
8058
8059 Roo.bootstrap.Form = function(config){
8060     
8061     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8062     
8063     Roo.bootstrap.Form.popover.apply();
8064     
8065     this.addEvents({
8066         /**
8067          * @event clientvalidation
8068          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8069          * @param {Form} this
8070          * @param {Boolean} valid true if the form has passed client-side validation
8071          */
8072         clientvalidation: true,
8073         /**
8074          * @event beforeaction
8075          * Fires before any action is performed. Return false to cancel the action.
8076          * @param {Form} this
8077          * @param {Action} action The action to be performed
8078          */
8079         beforeaction: true,
8080         /**
8081          * @event actionfailed
8082          * Fires when an action fails.
8083          * @param {Form} this
8084          * @param {Action} action The action that failed
8085          */
8086         actionfailed : true,
8087         /**
8088          * @event actioncomplete
8089          * Fires when an action is completed.
8090          * @param {Form} this
8091          * @param {Action} action The action that completed
8092          */
8093         actioncomplete : true
8094     });
8095 };
8096
8097 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8098
8099      /**
8100      * @cfg {String} method
8101      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8102      */
8103     method : 'POST',
8104     /**
8105      * @cfg {String} url
8106      * The URL to use for form actions if one isn't supplied in the action options.
8107      */
8108     /**
8109      * @cfg {Boolean} fileUpload
8110      * Set to true if this form is a file upload.
8111      */
8112
8113     /**
8114      * @cfg {Object} baseParams
8115      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8116      */
8117
8118     /**
8119      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8120      */
8121     timeout: 30,
8122     /**
8123      * @cfg {Sting} align (left|right) for navbar forms
8124      */
8125     align : 'left',
8126
8127     // private
8128     activeAction : null,
8129
8130     /**
8131      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8132      * element by passing it or its id or mask the form itself by passing in true.
8133      * @type Mixed
8134      */
8135     waitMsgTarget : false,
8136
8137     loadMask : true,
8138     
8139     /**
8140      * @cfg {Boolean} errorMask (true|false) default false
8141      */
8142     errorMask : false,
8143     
8144     /**
8145      * @cfg {Number} maskOffset Default 100
8146      */
8147     maskOffset : 100,
8148     
8149     /**
8150      * @cfg {Boolean} maskBody
8151      */
8152     maskBody : false,
8153
8154     getAutoCreate : function(){
8155
8156         var cfg = {
8157             tag: 'form',
8158             method : this.method || 'POST',
8159             id : this.id || Roo.id(),
8160             cls : ''
8161         };
8162         if (this.parent().xtype.match(/^Nav/)) {
8163             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8164
8165         }
8166
8167         if (this.labelAlign == 'left' ) {
8168             cfg.cls += ' form-horizontal';
8169         }
8170
8171
8172         return cfg;
8173     },
8174     initEvents : function()
8175     {
8176         this.el.on('submit', this.onSubmit, this);
8177         // this was added as random key presses on the form where triggering form submit.
8178         this.el.on('keypress', function(e) {
8179             if (e.getCharCode() != 13) {
8180                 return true;
8181             }
8182             // we might need to allow it for textareas.. and some other items.
8183             // check e.getTarget().
8184
8185             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8186                 return true;
8187             }
8188
8189             Roo.log("keypress blocked");
8190
8191             e.preventDefault();
8192             return false;
8193         });
8194         
8195     },
8196     // private
8197     onSubmit : function(e){
8198         e.stopEvent();
8199     },
8200
8201      /**
8202      * Returns true if client-side validation on the form is successful.
8203      * @return Boolean
8204      */
8205     isValid : function(){
8206         var items = this.getItems();
8207         var valid = true;
8208         var target = false;
8209         
8210         items.each(function(f){
8211             
8212             if(f.validate()){
8213                 return;
8214             }
8215             
8216             Roo.log('invalid field: ' + f.name);
8217             
8218             valid = false;
8219
8220             if(!target && f.el.isVisible(true)){
8221                 target = f;
8222             }
8223            
8224         });
8225         
8226         if(this.errorMask && !valid){
8227             Roo.bootstrap.Form.popover.mask(this, target);
8228         }
8229         
8230         return valid;
8231     },
8232     
8233     /**
8234      * Returns true if any fields in this form have changed since their original load.
8235      * @return Boolean
8236      */
8237     isDirty : function(){
8238         var dirty = false;
8239         var items = this.getItems();
8240         items.each(function(f){
8241            if(f.isDirty()){
8242                dirty = true;
8243                return false;
8244            }
8245            return true;
8246         });
8247         return dirty;
8248     },
8249      /**
8250      * Performs a predefined action (submit or load) or custom actions you define on this form.
8251      * @param {String} actionName The name of the action type
8252      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8253      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8254      * accept other config options):
8255      * <pre>
8256 Property          Type             Description
8257 ----------------  ---------------  ----------------------------------------------------------------------------------
8258 url               String           The url for the action (defaults to the form's url)
8259 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8260 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8261 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8262                                    validate the form on the client (defaults to false)
8263      * </pre>
8264      * @return {BasicForm} this
8265      */
8266     doAction : function(action, options){
8267         if(typeof action == 'string'){
8268             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8269         }
8270         if(this.fireEvent('beforeaction', this, action) !== false){
8271             this.beforeAction(action);
8272             action.run.defer(100, action);
8273         }
8274         return this;
8275     },
8276
8277     // private
8278     beforeAction : function(action){
8279         var o = action.options;
8280         
8281         if(this.loadMask){
8282             
8283             if(this.maskBody){
8284                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8285             } else {
8286                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8287             }
8288         }
8289         // not really supported yet.. ??
8290
8291         //if(this.waitMsgTarget === true){
8292         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8293         //}else if(this.waitMsgTarget){
8294         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8295         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8296         //}else {
8297         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8298        // }
8299
8300     },
8301
8302     // private
8303     afterAction : function(action, success){
8304         this.activeAction = null;
8305         var o = action.options;
8306
8307         if(this.loadMask){
8308             
8309             if(this.maskBody){
8310                 Roo.get(document.body).unmask();
8311             } else {
8312                 this.el.unmask();
8313             }
8314         }
8315         
8316         //if(this.waitMsgTarget === true){
8317 //            this.el.unmask();
8318         //}else if(this.waitMsgTarget){
8319         //    this.waitMsgTarget.unmask();
8320         //}else{
8321         //    Roo.MessageBox.updateProgress(1);
8322         //    Roo.MessageBox.hide();
8323        // }
8324         //
8325         if(success){
8326             if(o.reset){
8327                 this.reset();
8328             }
8329             Roo.callback(o.success, o.scope, [this, action]);
8330             this.fireEvent('actioncomplete', this, action);
8331
8332         }else{
8333
8334             // failure condition..
8335             // we have a scenario where updates need confirming.
8336             // eg. if a locking scenario exists..
8337             // we look for { errors : { needs_confirm : true }} in the response.
8338             if (
8339                 (typeof(action.result) != 'undefined')  &&
8340                 (typeof(action.result.errors) != 'undefined')  &&
8341                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8342            ){
8343                 var _t = this;
8344                 Roo.log("not supported yet");
8345                  /*
8346
8347                 Roo.MessageBox.confirm(
8348                     "Change requires confirmation",
8349                     action.result.errorMsg,
8350                     function(r) {
8351                         if (r != 'yes') {
8352                             return;
8353                         }
8354                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8355                     }
8356
8357                 );
8358                 */
8359
8360
8361                 return;
8362             }
8363
8364             Roo.callback(o.failure, o.scope, [this, action]);
8365             // show an error message if no failed handler is set..
8366             if (!this.hasListener('actionfailed')) {
8367                 Roo.log("need to add dialog support");
8368                 /*
8369                 Roo.MessageBox.alert("Error",
8370                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8371                         action.result.errorMsg :
8372                         "Saving Failed, please check your entries or try again"
8373                 );
8374                 */
8375             }
8376
8377             this.fireEvent('actionfailed', this, action);
8378         }
8379
8380     },
8381     /**
8382      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8383      * @param {String} id The value to search for
8384      * @return Field
8385      */
8386     findField : function(id){
8387         var items = this.getItems();
8388         var field = items.get(id);
8389         if(!field){
8390              items.each(function(f){
8391                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8392                     field = f;
8393                     return false;
8394                 }
8395                 return true;
8396             });
8397         }
8398         return field || null;
8399     },
8400      /**
8401      * Mark fields in this form invalid in bulk.
8402      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8403      * @return {BasicForm} this
8404      */
8405     markInvalid : function(errors){
8406         if(errors instanceof Array){
8407             for(var i = 0, len = errors.length; i < len; i++){
8408                 var fieldError = errors[i];
8409                 var f = this.findField(fieldError.id);
8410                 if(f){
8411                     f.markInvalid(fieldError.msg);
8412                 }
8413             }
8414         }else{
8415             var field, id;
8416             for(id in errors){
8417                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8418                     field.markInvalid(errors[id]);
8419                 }
8420             }
8421         }
8422         //Roo.each(this.childForms || [], function (f) {
8423         //    f.markInvalid(errors);
8424         //});
8425
8426         return this;
8427     },
8428
8429     /**
8430      * Set values for fields in this form in bulk.
8431      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8432      * @return {BasicForm} this
8433      */
8434     setValues : function(values){
8435         if(values instanceof Array){ // array of objects
8436             for(var i = 0, len = values.length; i < len; i++){
8437                 var v = values[i];
8438                 var f = this.findField(v.id);
8439                 if(f){
8440                     f.setValue(v.value);
8441                     if(this.trackResetOnLoad){
8442                         f.originalValue = f.getValue();
8443                     }
8444                 }
8445             }
8446         }else{ // object hash
8447             var field, id;
8448             for(id in values){
8449                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8450
8451                     if (field.setFromData &&
8452                         field.valueField &&
8453                         field.displayField &&
8454                         // combos' with local stores can
8455                         // be queried via setValue()
8456                         // to set their value..
8457                         (field.store && !field.store.isLocal)
8458                         ) {
8459                         // it's a combo
8460                         var sd = { };
8461                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8462                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8463                         field.setFromData(sd);
8464
8465                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8466                         
8467                         field.setFromData(values);
8468                         
8469                     } else {
8470                         field.setValue(values[id]);
8471                     }
8472
8473
8474                     if(this.trackResetOnLoad){
8475                         field.originalValue = field.getValue();
8476                     }
8477                 }
8478             }
8479         }
8480
8481         //Roo.each(this.childForms || [], function (f) {
8482         //    f.setValues(values);
8483         //});
8484
8485         return this;
8486     },
8487
8488     /**
8489      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8490      * they are returned as an array.
8491      * @param {Boolean} asString
8492      * @return {Object}
8493      */
8494     getValues : function(asString){
8495         //if (this.childForms) {
8496             // copy values from the child forms
8497         //    Roo.each(this.childForms, function (f) {
8498         //        this.setValues(f.getValues());
8499         //    }, this);
8500         //}
8501
8502
8503
8504         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8505         if(asString === true){
8506             return fs;
8507         }
8508         return Roo.urlDecode(fs);
8509     },
8510
8511     /**
8512      * Returns the fields in this form as an object with key/value pairs.
8513      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8514      * @return {Object}
8515      */
8516     getFieldValues : function(with_hidden)
8517     {
8518         var items = this.getItems();
8519         var ret = {};
8520         items.each(function(f){
8521             
8522             if (!f.getName()) {
8523                 return;
8524             }
8525             
8526             var v = f.getValue();
8527             
8528             if (f.inputType =='radio') {
8529                 if (typeof(ret[f.getName()]) == 'undefined') {
8530                     ret[f.getName()] = ''; // empty..
8531                 }
8532
8533                 if (!f.el.dom.checked) {
8534                     return;
8535
8536                 }
8537                 v = f.el.dom.value;
8538
8539             }
8540             
8541             if(f.xtype == 'MoneyField'){
8542                 ret[f.currencyName] = f.getCurrency();
8543             }
8544
8545             // not sure if this supported any more..
8546             if ((typeof(v) == 'object') && f.getRawValue) {
8547                 v = f.getRawValue() ; // dates..
8548             }
8549             // combo boxes where name != hiddenName...
8550             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8551                 ret[f.name] = f.getRawValue();
8552             }
8553             ret[f.getName()] = v;
8554         });
8555
8556         return ret;
8557     },
8558
8559     /**
8560      * Clears all invalid messages in this form.
8561      * @return {BasicForm} this
8562      */
8563     clearInvalid : function(){
8564         var items = this.getItems();
8565
8566         items.each(function(f){
8567            f.clearInvalid();
8568         });
8569
8570         return this;
8571     },
8572
8573     /**
8574      * Resets this form.
8575      * @return {BasicForm} this
8576      */
8577     reset : function(){
8578         var items = this.getItems();
8579         items.each(function(f){
8580             f.reset();
8581         });
8582
8583         Roo.each(this.childForms || [], function (f) {
8584             f.reset();
8585         });
8586
8587
8588         return this;
8589     },
8590     
8591     getItems : function()
8592     {
8593         var r=new Roo.util.MixedCollection(false, function(o){
8594             return o.id || (o.id = Roo.id());
8595         });
8596         var iter = function(el) {
8597             if (el.inputEl) {
8598                 r.add(el);
8599             }
8600             if (!el.items) {
8601                 return;
8602             }
8603             Roo.each(el.items,function(e) {
8604                 iter(e);
8605             });
8606         };
8607
8608         iter(this);
8609         return r;
8610     },
8611     
8612     hideFields : function(items)
8613     {
8614         Roo.each(items, function(i){
8615             
8616             var f = this.findField(i);
8617             
8618             if(!f){
8619                 return;
8620             }
8621             
8622             f.hide();
8623             
8624         }, this);
8625     },
8626     
8627     showFields : function(items)
8628     {
8629         Roo.each(items, function(i){
8630             
8631             var f = this.findField(i);
8632             
8633             if(!f){
8634                 return;
8635             }
8636             
8637             f.show();
8638             
8639         }, this);
8640     }
8641
8642 });
8643
8644 Roo.apply(Roo.bootstrap.Form, {
8645     
8646     popover : {
8647         
8648         padding : 5,
8649         
8650         isApplied : false,
8651         
8652         isMasked : false,
8653         
8654         form : false,
8655         
8656         target : false,
8657         
8658         toolTip : false,
8659         
8660         intervalID : false,
8661         
8662         maskEl : false,
8663         
8664         apply : function()
8665         {
8666             if(this.isApplied){
8667                 return;
8668             }
8669             
8670             this.maskEl = {
8671                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8672                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8673                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8674                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8675             };
8676             
8677             this.maskEl.top.enableDisplayMode("block");
8678             this.maskEl.left.enableDisplayMode("block");
8679             this.maskEl.bottom.enableDisplayMode("block");
8680             this.maskEl.right.enableDisplayMode("block");
8681             
8682             this.toolTip = new Roo.bootstrap.Tooltip({
8683                 cls : 'roo-form-error-popover',
8684                 alignment : {
8685                     'left' : ['r-l', [-2,0], 'right'],
8686                     'right' : ['l-r', [2,0], 'left'],
8687                     'bottom' : ['tl-bl', [0,2], 'top'],
8688                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8689                 }
8690             });
8691             
8692             this.toolTip.render(Roo.get(document.body));
8693
8694             this.toolTip.el.enableDisplayMode("block");
8695             
8696             Roo.get(document.body).on('click', function(){
8697                 this.unmask();
8698             }, this);
8699             
8700             Roo.get(document.body).on('touchstart', function(){
8701                 this.unmask();
8702             }, this);
8703             
8704             this.isApplied = true
8705         },
8706         
8707         mask : function(form, target)
8708         {
8709             this.form = form;
8710             
8711             this.target = target;
8712             
8713             if(!this.form.errorMask || !target.el){
8714                 return;
8715             }
8716             
8717             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8718             
8719             Roo.log(scrollable);
8720             
8721             var ot = this.target.el.calcOffsetsTo(scrollable);
8722             
8723             var scrollTo = ot[1] - this.form.maskOffset;
8724             
8725             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8726             
8727             scrollable.scrollTo('top', scrollTo);
8728             
8729             var box = this.target.el.getBox();
8730             Roo.log(box);
8731             var zIndex = Roo.bootstrap.Modal.zIndex++;
8732
8733             
8734             this.maskEl.top.setStyle('position', 'absolute');
8735             this.maskEl.top.setStyle('z-index', zIndex);
8736             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8737             this.maskEl.top.setLeft(0);
8738             this.maskEl.top.setTop(0);
8739             this.maskEl.top.show();
8740             
8741             this.maskEl.left.setStyle('position', 'absolute');
8742             this.maskEl.left.setStyle('z-index', zIndex);
8743             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8744             this.maskEl.left.setLeft(0);
8745             this.maskEl.left.setTop(box.y - this.padding);
8746             this.maskEl.left.show();
8747
8748             this.maskEl.bottom.setStyle('position', 'absolute');
8749             this.maskEl.bottom.setStyle('z-index', zIndex);
8750             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8751             this.maskEl.bottom.setLeft(0);
8752             this.maskEl.bottom.setTop(box.bottom + this.padding);
8753             this.maskEl.bottom.show();
8754
8755             this.maskEl.right.setStyle('position', 'absolute');
8756             this.maskEl.right.setStyle('z-index', zIndex);
8757             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8758             this.maskEl.right.setLeft(box.right + this.padding);
8759             this.maskEl.right.setTop(box.y - this.padding);
8760             this.maskEl.right.show();
8761
8762             this.toolTip.bindEl = this.target.el;
8763
8764             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8765
8766             var tip = this.target.blankText;
8767
8768             if(this.target.getValue() !== '' ) {
8769                 
8770                 if (this.target.invalidText.length) {
8771                     tip = this.target.invalidText;
8772                 } else if (this.target.regexText.length){
8773                     tip = this.target.regexText;
8774                 }
8775             }
8776
8777             this.toolTip.show(tip);
8778
8779             this.intervalID = window.setInterval(function() {
8780                 Roo.bootstrap.Form.popover.unmask();
8781             }, 10000);
8782
8783             window.onwheel = function(){ return false;};
8784             
8785             (function(){ this.isMasked = true; }).defer(500, this);
8786             
8787         },
8788         
8789         unmask : function()
8790         {
8791             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8792                 return;
8793             }
8794             
8795             this.maskEl.top.setStyle('position', 'absolute');
8796             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8797             this.maskEl.top.hide();
8798
8799             this.maskEl.left.setStyle('position', 'absolute');
8800             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8801             this.maskEl.left.hide();
8802
8803             this.maskEl.bottom.setStyle('position', 'absolute');
8804             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8805             this.maskEl.bottom.hide();
8806
8807             this.maskEl.right.setStyle('position', 'absolute');
8808             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8809             this.maskEl.right.hide();
8810             
8811             this.toolTip.hide();
8812             
8813             this.toolTip.el.hide();
8814             
8815             window.onwheel = function(){ return true;};
8816             
8817             if(this.intervalID){
8818                 window.clearInterval(this.intervalID);
8819                 this.intervalID = false;
8820             }
8821             
8822             this.isMasked = false;
8823             
8824         }
8825         
8826     }
8827     
8828 });
8829
8830 /*
8831  * Based on:
8832  * Ext JS Library 1.1.1
8833  * Copyright(c) 2006-2007, Ext JS, LLC.
8834  *
8835  * Originally Released Under LGPL - original licence link has changed is not relivant.
8836  *
8837  * Fork - LGPL
8838  * <script type="text/javascript">
8839  */
8840 /**
8841  * @class Roo.form.VTypes
8842  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8843  * @singleton
8844  */
8845 Roo.form.VTypes = function(){
8846     // closure these in so they are only created once.
8847     var alpha = /^[a-zA-Z_]+$/;
8848     var alphanum = /^[a-zA-Z0-9_]+$/;
8849     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8850     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8851
8852     // All these messages and functions are configurable
8853     return {
8854         /**
8855          * The function used to validate email addresses
8856          * @param {String} value The email address
8857          */
8858         'email' : function(v){
8859             return email.test(v);
8860         },
8861         /**
8862          * The error text to display when the email validation function returns false
8863          * @type String
8864          */
8865         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8866         /**
8867          * The keystroke filter mask to be applied on email input
8868          * @type RegExp
8869          */
8870         'emailMask' : /[a-z0-9_\.\-@]/i,
8871
8872         /**
8873          * The function used to validate URLs
8874          * @param {String} value The URL
8875          */
8876         'url' : function(v){
8877             return url.test(v);
8878         },
8879         /**
8880          * The error text to display when the url validation function returns false
8881          * @type String
8882          */
8883         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8884         
8885         /**
8886          * The function used to validate alpha values
8887          * @param {String} value The value
8888          */
8889         'alpha' : function(v){
8890             return alpha.test(v);
8891         },
8892         /**
8893          * The error text to display when the alpha validation function returns false
8894          * @type String
8895          */
8896         'alphaText' : 'This field should only contain letters and _',
8897         /**
8898          * The keystroke filter mask to be applied on alpha input
8899          * @type RegExp
8900          */
8901         'alphaMask' : /[a-z_]/i,
8902
8903         /**
8904          * The function used to validate alphanumeric values
8905          * @param {String} value The value
8906          */
8907         'alphanum' : function(v){
8908             return alphanum.test(v);
8909         },
8910         /**
8911          * The error text to display when the alphanumeric validation function returns false
8912          * @type String
8913          */
8914         'alphanumText' : 'This field should only contain letters, numbers and _',
8915         /**
8916          * The keystroke filter mask to be applied on alphanumeric input
8917          * @type RegExp
8918          */
8919         'alphanumMask' : /[a-z0-9_]/i
8920     };
8921 }();/*
8922  * - LGPL
8923  *
8924  * Input
8925  * 
8926  */
8927
8928 /**
8929  * @class Roo.bootstrap.Input
8930  * @extends Roo.bootstrap.Component
8931  * Bootstrap Input class
8932  * @cfg {Boolean} disabled is it disabled
8933  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8934  * @cfg {String} name name of the input
8935  * @cfg {string} fieldLabel - the label associated
8936  * @cfg {string} placeholder - placeholder to put in text.
8937  * @cfg {string}  before - input group add on before
8938  * @cfg {string} after - input group add on after
8939  * @cfg {string} size - (lg|sm) or leave empty..
8940  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8941  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8942  * @cfg {Number} md colspan out of 12 for computer-sized screens
8943  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8944  * @cfg {string} value default value of the input
8945  * @cfg {Number} labelWidth set the width of label 
8946  * @cfg {Number} labellg set the width of label (1-12)
8947  * @cfg {Number} labelmd set the width of label (1-12)
8948  * @cfg {Number} labelsm set the width of label (1-12)
8949  * @cfg {Number} labelxs set the width of label (1-12)
8950  * @cfg {String} labelAlign (top|left)
8951  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8952  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8953  * @cfg {String} indicatorpos (left|right) default left
8954  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8955  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8956
8957  * @cfg {String} align (left|center|right) Default left
8958  * @cfg {Boolean} forceFeedback (true|false) Default false
8959  * 
8960  * @constructor
8961  * Create a new Input
8962  * @param {Object} config The config object
8963  */
8964
8965 Roo.bootstrap.Input = function(config){
8966     
8967     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8968     
8969     this.addEvents({
8970         /**
8971          * @event focus
8972          * Fires when this field receives input focus.
8973          * @param {Roo.form.Field} this
8974          */
8975         focus : true,
8976         /**
8977          * @event blur
8978          * Fires when this field loses input focus.
8979          * @param {Roo.form.Field} this
8980          */
8981         blur : true,
8982         /**
8983          * @event specialkey
8984          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8985          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8986          * @param {Roo.form.Field} this
8987          * @param {Roo.EventObject} e The event object
8988          */
8989         specialkey : true,
8990         /**
8991          * @event change
8992          * Fires just before the field blurs if the field value has changed.
8993          * @param {Roo.form.Field} this
8994          * @param {Mixed} newValue The new value
8995          * @param {Mixed} oldValue The original value
8996          */
8997         change : true,
8998         /**
8999          * @event invalid
9000          * Fires after the field has been marked as invalid.
9001          * @param {Roo.form.Field} this
9002          * @param {String} msg The validation message
9003          */
9004         invalid : true,
9005         /**
9006          * @event valid
9007          * Fires after the field has been validated with no errors.
9008          * @param {Roo.form.Field} this
9009          */
9010         valid : true,
9011          /**
9012          * @event keyup
9013          * Fires after the key up
9014          * @param {Roo.form.Field} this
9015          * @param {Roo.EventObject}  e The event Object
9016          */
9017         keyup : true
9018     });
9019 };
9020
9021 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9022      /**
9023      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9024       automatic validation (defaults to "keyup").
9025      */
9026     validationEvent : "keyup",
9027      /**
9028      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9029      */
9030     validateOnBlur : true,
9031     /**
9032      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9033      */
9034     validationDelay : 250,
9035      /**
9036      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9037      */
9038     focusClass : "x-form-focus",  // not needed???
9039     
9040        
9041     /**
9042      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9043      */
9044     invalidClass : "has-warning",
9045     
9046     /**
9047      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9048      */
9049     validClass : "has-success",
9050     
9051     /**
9052      * @cfg {Boolean} hasFeedback (true|false) default true
9053      */
9054     hasFeedback : true,
9055     
9056     /**
9057      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9058      */
9059     invalidFeedbackClass : "glyphicon-warning-sign",
9060     
9061     /**
9062      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9063      */
9064     validFeedbackClass : "glyphicon-ok",
9065     
9066     /**
9067      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9068      */
9069     selectOnFocus : false,
9070     
9071      /**
9072      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9073      */
9074     maskRe : null,
9075        /**
9076      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9077      */
9078     vtype : null,
9079     
9080       /**
9081      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9082      */
9083     disableKeyFilter : false,
9084     
9085        /**
9086      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9087      */
9088     disabled : false,
9089      /**
9090      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9091      */
9092     allowBlank : true,
9093     /**
9094      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9095      */
9096     blankText : "Please complete this mandatory field",
9097     
9098      /**
9099      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9100      */
9101     minLength : 0,
9102     /**
9103      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9104      */
9105     maxLength : Number.MAX_VALUE,
9106     /**
9107      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9108      */
9109     minLengthText : "The minimum length for this field is {0}",
9110     /**
9111      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9112      */
9113     maxLengthText : "The maximum length for this field is {0}",
9114   
9115     
9116     /**
9117      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9118      * If available, this function will be called only after the basic validators all return true, and will be passed the
9119      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9120      */
9121     validator : null,
9122     /**
9123      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9124      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9125      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9126      */
9127     regex : null,
9128     /**
9129      * @cfg {String} regexText -- Depricated - use Invalid Text
9130      */
9131     regexText : "",
9132     
9133     /**
9134      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9135      */
9136     invalidText : "",
9137     
9138     
9139     
9140     autocomplete: false,
9141     
9142     
9143     fieldLabel : '',
9144     inputType : 'text',
9145     
9146     name : false,
9147     placeholder: false,
9148     before : false,
9149     after : false,
9150     size : false,
9151     hasFocus : false,
9152     preventMark: false,
9153     isFormField : true,
9154     value : '',
9155     labelWidth : 2,
9156     labelAlign : false,
9157     readOnly : false,
9158     align : false,
9159     formatedValue : false,
9160     forceFeedback : false,
9161     
9162     indicatorpos : 'left',
9163     
9164     labellg : 0,
9165     labelmd : 0,
9166     labelsm : 0,
9167     labelxs : 0,
9168     
9169     capture : '',
9170     accept : '',
9171     
9172     parentLabelAlign : function()
9173     {
9174         var parent = this;
9175         while (parent.parent()) {
9176             parent = parent.parent();
9177             if (typeof(parent.labelAlign) !='undefined') {
9178                 return parent.labelAlign;
9179             }
9180         }
9181         return 'left';
9182         
9183     },
9184     
9185     getAutoCreate : function()
9186     {
9187         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9188         
9189         var id = Roo.id();
9190         
9191         var cfg = {};
9192         
9193         if(this.inputType != 'hidden'){
9194             cfg.cls = 'form-group' //input-group
9195         }
9196         
9197         var input =  {
9198             tag: 'input',
9199             id : id,
9200             type : this.inputType,
9201             value : this.value,
9202             cls : 'form-control',
9203             placeholder : this.placeholder || '',
9204             autocomplete : this.autocomplete || 'new-password'
9205         };
9206         
9207         if(this.capture.length){
9208             input.capture = this.capture;
9209         }
9210         
9211         if(this.accept.length){
9212             input.accept = this.accept + "/*";
9213         }
9214         
9215         if(this.align){
9216             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9217         }
9218         
9219         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9220             input.maxLength = this.maxLength;
9221         }
9222         
9223         if (this.disabled) {
9224             input.disabled=true;
9225         }
9226         
9227         if (this.readOnly) {
9228             input.readonly=true;
9229         }
9230         
9231         if (this.name) {
9232             input.name = this.name;
9233         }
9234         
9235         if (this.size) {
9236             input.cls += ' input-' + this.size;
9237         }
9238         
9239         var settings=this;
9240         ['xs','sm','md','lg'].map(function(size){
9241             if (settings[size]) {
9242                 cfg.cls += ' col-' + size + '-' + settings[size];
9243             }
9244         });
9245         
9246         var inputblock = input;
9247         
9248         var feedback = {
9249             tag: 'span',
9250             cls: 'glyphicon form-control-feedback'
9251         };
9252             
9253         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9254             
9255             inputblock = {
9256                 cls : 'has-feedback',
9257                 cn :  [
9258                     input,
9259                     feedback
9260                 ] 
9261             };  
9262         }
9263         
9264         if (this.before || this.after) {
9265             
9266             inputblock = {
9267                 cls : 'input-group',
9268                 cn :  [] 
9269             };
9270             
9271             if (this.before && typeof(this.before) == 'string') {
9272                 
9273                 inputblock.cn.push({
9274                     tag :'span',
9275                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9276                     html : this.before
9277                 });
9278             }
9279             if (this.before && typeof(this.before) == 'object') {
9280                 this.before = Roo.factory(this.before);
9281                 
9282                 inputblock.cn.push({
9283                     tag :'span',
9284                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9285                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9286                 });
9287             }
9288             
9289             inputblock.cn.push(input);
9290             
9291             if (this.after && typeof(this.after) == 'string') {
9292                 inputblock.cn.push({
9293                     tag :'span',
9294                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9295                     html : this.after
9296                 });
9297             }
9298             if (this.after && typeof(this.after) == 'object') {
9299                 this.after = Roo.factory(this.after);
9300                 
9301                 inputblock.cn.push({
9302                     tag :'span',
9303                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9304                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9305                 });
9306             }
9307             
9308             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9309                 inputblock.cls += ' has-feedback';
9310                 inputblock.cn.push(feedback);
9311             }
9312         };
9313         var indicator = {
9314             tag : 'i',
9315             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9316             tooltip : 'This field is required'
9317         };
9318         if (Roo.bootstrap.version == 4) {
9319             indicator = {
9320                 tag : 'i',
9321                 style : 'display-none'
9322             };
9323         }
9324         if (align ==='left' && this.fieldLabel.length) {
9325             
9326             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9327             
9328             cfg.cn = [
9329                 indicator,
9330                 {
9331                     tag: 'label',
9332                     'for' :  id,
9333                     cls : 'control-label col-form-label',
9334                     html : this.fieldLabel
9335
9336                 },
9337                 {
9338                     cls : "", 
9339                     cn: [
9340                         inputblock
9341                     ]
9342                 }
9343             ];
9344             
9345             var labelCfg = cfg.cn[1];
9346             var contentCfg = cfg.cn[2];
9347             
9348             if(this.indicatorpos == 'right'){
9349                 cfg.cn = [
9350                     {
9351                         tag: 'label',
9352                         'for' :  id,
9353                         cls : 'control-label col-form-label',
9354                         cn : [
9355                             {
9356                                 tag : 'span',
9357                                 html : this.fieldLabel
9358                             },
9359                             indicator
9360                         ]
9361                     },
9362                     {
9363                         cls : "",
9364                         cn: [
9365                             inputblock
9366                         ]
9367                     }
9368
9369                 ];
9370                 
9371                 labelCfg = cfg.cn[0];
9372                 contentCfg = cfg.cn[1];
9373             
9374             }
9375             
9376             if(this.labelWidth > 12){
9377                 labelCfg.style = "width: " + this.labelWidth + 'px';
9378             }
9379             
9380             if(this.labelWidth < 13 && this.labelmd == 0){
9381                 this.labelmd = this.labelWidth;
9382             }
9383             
9384             if(this.labellg > 0){
9385                 labelCfg.cls += ' col-lg-' + this.labellg;
9386                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9387             }
9388             
9389             if(this.labelmd > 0){
9390                 labelCfg.cls += ' col-md-' + this.labelmd;
9391                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9392             }
9393             
9394             if(this.labelsm > 0){
9395                 labelCfg.cls += ' col-sm-' + this.labelsm;
9396                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9397             }
9398             
9399             if(this.labelxs > 0){
9400                 labelCfg.cls += ' col-xs-' + this.labelxs;
9401                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9402             }
9403             
9404             
9405         } else if ( this.fieldLabel.length) {
9406                 
9407             cfg.cn = [
9408                 {
9409                     tag : 'i',
9410                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9411                     tooltip : 'This field is required'
9412                 },
9413                 {
9414                     tag: 'label',
9415                    //cls : 'input-group-addon',
9416                     html : this.fieldLabel
9417
9418                 },
9419
9420                inputblock
9421
9422            ];
9423            
9424            if(this.indicatorpos == 'right'){
9425                 
9426                 cfg.cn = [
9427                     {
9428                         tag: 'label',
9429                        //cls : 'input-group-addon',
9430                         html : this.fieldLabel
9431
9432                     },
9433                     {
9434                         tag : 'i',
9435                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9436                         tooltip : 'This field is required'
9437                     },
9438
9439                    inputblock
9440
9441                ];
9442
9443             }
9444
9445         } else {
9446             
9447             cfg.cn = [
9448
9449                     inputblock
9450
9451             ];
9452                 
9453                 
9454         };
9455         
9456         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9457            cfg.cls += ' navbar-form';
9458         }
9459         
9460         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9461             // on BS4 we do this only if not form 
9462             cfg.cls += ' navbar-form';
9463             cfg.tag = 'li';
9464         }
9465         
9466         return cfg;
9467         
9468     },
9469     /**
9470      * return the real input element.
9471      */
9472     inputEl: function ()
9473     {
9474         return this.el.select('input.form-control',true).first();
9475     },
9476     
9477     tooltipEl : function()
9478     {
9479         return this.inputEl();
9480     },
9481     
9482     indicatorEl : function()
9483     {
9484         if (Roo.bootstrap.version == 4) {
9485             return false; // not enabled in v4 yet.
9486         }
9487         
9488         var indicator = this.el.select('i.roo-required-indicator',true).first();
9489         
9490         if(!indicator){
9491             return false;
9492         }
9493         
9494         return indicator;
9495         
9496     },
9497     
9498     setDisabled : function(v)
9499     {
9500         var i  = this.inputEl().dom;
9501         if (!v) {
9502             i.removeAttribute('disabled');
9503             return;
9504             
9505         }
9506         i.setAttribute('disabled','true');
9507     },
9508     initEvents : function()
9509     {
9510           
9511         this.inputEl().on("keydown" , this.fireKey,  this);
9512         this.inputEl().on("focus", this.onFocus,  this);
9513         this.inputEl().on("blur", this.onBlur,  this);
9514         
9515         this.inputEl().relayEvent('keyup', this);
9516         
9517         this.indicator = this.indicatorEl();
9518         
9519         if(this.indicator){
9520             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9521         }
9522  
9523         // reference to original value for reset
9524         this.originalValue = this.getValue();
9525         //Roo.form.TextField.superclass.initEvents.call(this);
9526         if(this.validationEvent == 'keyup'){
9527             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9528             this.inputEl().on('keyup', this.filterValidation, this);
9529         }
9530         else if(this.validationEvent !== false){
9531             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9532         }
9533         
9534         if(this.selectOnFocus){
9535             this.on("focus", this.preFocus, this);
9536             
9537         }
9538         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9539             this.inputEl().on("keypress", this.filterKeys, this);
9540         } else {
9541             this.inputEl().relayEvent('keypress', this);
9542         }
9543        /* if(this.grow){
9544             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9545             this.el.on("click", this.autoSize,  this);
9546         }
9547         */
9548         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9549             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9550         }
9551         
9552         if (typeof(this.before) == 'object') {
9553             this.before.render(this.el.select('.roo-input-before',true).first());
9554         }
9555         if (typeof(this.after) == 'object') {
9556             this.after.render(this.el.select('.roo-input-after',true).first());
9557         }
9558         
9559         this.inputEl().on('change', this.onChange, this);
9560         
9561     },
9562     filterValidation : function(e){
9563         if(!e.isNavKeyPress()){
9564             this.validationTask.delay(this.validationDelay);
9565         }
9566     },
9567      /**
9568      * Validates the field value
9569      * @return {Boolean} True if the value is valid, else false
9570      */
9571     validate : function(){
9572         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9573         if(this.disabled || this.validateValue(this.getRawValue())){
9574             this.markValid();
9575             return true;
9576         }
9577         
9578         this.markInvalid();
9579         return false;
9580     },
9581     
9582     
9583     /**
9584      * Validates a value according to the field's validation rules and marks the field as invalid
9585      * if the validation fails
9586      * @param {Mixed} value The value to validate
9587      * @return {Boolean} True if the value is valid, else false
9588      */
9589     validateValue : function(value)
9590     {
9591         if(this.getVisibilityEl().hasClass('hidden')){
9592             return true;
9593         }
9594         
9595         if(value.length < 1)  { // if it's blank
9596             if(this.allowBlank){
9597                 return true;
9598             }
9599             return false;
9600         }
9601         
9602         if(value.length < this.minLength){
9603             return false;
9604         }
9605         if(value.length > this.maxLength){
9606             return false;
9607         }
9608         if(this.vtype){
9609             var vt = Roo.form.VTypes;
9610             if(!vt[this.vtype](value, this)){
9611                 return false;
9612             }
9613         }
9614         if(typeof this.validator == "function"){
9615             var msg = this.validator(value);
9616             if(msg !== true){
9617                 return false;
9618             }
9619             if (typeof(msg) == 'string') {
9620                 this.invalidText = msg;
9621             }
9622         }
9623         
9624         if(this.regex && !this.regex.test(value)){
9625             return false;
9626         }
9627         
9628         return true;
9629     },
9630     
9631      // private
9632     fireKey : function(e){
9633         //Roo.log('field ' + e.getKey());
9634         if(e.isNavKeyPress()){
9635             this.fireEvent("specialkey", this, e);
9636         }
9637     },
9638     focus : function (selectText){
9639         if(this.rendered){
9640             this.inputEl().focus();
9641             if(selectText === true){
9642                 this.inputEl().dom.select();
9643             }
9644         }
9645         return this;
9646     } ,
9647     
9648     onFocus : function(){
9649         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9650            // this.el.addClass(this.focusClass);
9651         }
9652         if(!this.hasFocus){
9653             this.hasFocus = true;
9654             this.startValue = this.getValue();
9655             this.fireEvent("focus", this);
9656         }
9657     },
9658     
9659     beforeBlur : Roo.emptyFn,
9660
9661     
9662     // private
9663     onBlur : function(){
9664         this.beforeBlur();
9665         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9666             //this.el.removeClass(this.focusClass);
9667         }
9668         this.hasFocus = false;
9669         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9670             this.validate();
9671         }
9672         var v = this.getValue();
9673         if(String(v) !== String(this.startValue)){
9674             this.fireEvent('change', this, v, this.startValue);
9675         }
9676         this.fireEvent("blur", this);
9677     },
9678     
9679     onChange : function(e)
9680     {
9681         var v = this.getValue();
9682         if(String(v) !== String(this.startValue)){
9683             this.fireEvent('change', this, v, this.startValue);
9684         }
9685         
9686     },
9687     
9688     /**
9689      * Resets the current field value to the originally loaded value and clears any validation messages
9690      */
9691     reset : function(){
9692         this.setValue(this.originalValue);
9693         this.validate();
9694     },
9695      /**
9696      * Returns the name of the field
9697      * @return {Mixed} name The name field
9698      */
9699     getName: function(){
9700         return this.name;
9701     },
9702      /**
9703      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9704      * @return {Mixed} value The field value
9705      */
9706     getValue : function(){
9707         
9708         var v = this.inputEl().getValue();
9709         
9710         return v;
9711     },
9712     /**
9713      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9714      * @return {Mixed} value The field value
9715      */
9716     getRawValue : function(){
9717         var v = this.inputEl().getValue();
9718         
9719         return v;
9720     },
9721     
9722     /**
9723      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9724      * @param {Mixed} value The value to set
9725      */
9726     setRawValue : function(v){
9727         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9728     },
9729     
9730     selectText : function(start, end){
9731         var v = this.getRawValue();
9732         if(v.length > 0){
9733             start = start === undefined ? 0 : start;
9734             end = end === undefined ? v.length : end;
9735             var d = this.inputEl().dom;
9736             if(d.setSelectionRange){
9737                 d.setSelectionRange(start, end);
9738             }else if(d.createTextRange){
9739                 var range = d.createTextRange();
9740                 range.moveStart("character", start);
9741                 range.moveEnd("character", v.length-end);
9742                 range.select();
9743             }
9744         }
9745     },
9746     
9747     /**
9748      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9749      * @param {Mixed} value The value to set
9750      */
9751     setValue : function(v){
9752         this.value = v;
9753         if(this.rendered){
9754             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9755             this.validate();
9756         }
9757     },
9758     
9759     /*
9760     processValue : function(value){
9761         if(this.stripCharsRe){
9762             var newValue = value.replace(this.stripCharsRe, '');
9763             if(newValue !== value){
9764                 this.setRawValue(newValue);
9765                 return newValue;
9766             }
9767         }
9768         return value;
9769     },
9770   */
9771     preFocus : function(){
9772         
9773         if(this.selectOnFocus){
9774             this.inputEl().dom.select();
9775         }
9776     },
9777     filterKeys : function(e){
9778         var k = e.getKey();
9779         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9780             return;
9781         }
9782         var c = e.getCharCode(), cc = String.fromCharCode(c);
9783         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9784             return;
9785         }
9786         if(!this.maskRe.test(cc)){
9787             e.stopEvent();
9788         }
9789     },
9790      /**
9791      * Clear any invalid styles/messages for this field
9792      */
9793     clearInvalid : function(){
9794         
9795         if(!this.el || this.preventMark){ // not rendered
9796             return;
9797         }
9798         
9799         
9800         this.el.removeClass([this.invalidClass, 'is-invalid']);
9801         
9802         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9803             
9804             var feedback = this.el.select('.form-control-feedback', true).first();
9805             
9806             if(feedback){
9807                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9808             }
9809             
9810         }
9811         
9812         if(this.indicator){
9813             this.indicator.removeClass('visible');
9814             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9815         }
9816         
9817         this.fireEvent('valid', this);
9818     },
9819     
9820      /**
9821      * Mark this field as valid
9822      */
9823     markValid : function()
9824     {
9825         if(!this.el  || this.preventMark){ // not rendered...
9826             return;
9827         }
9828         
9829         this.el.removeClass([this.invalidClass, this.validClass]);
9830         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9831
9832         var feedback = this.el.select('.form-control-feedback', true).first();
9833             
9834         if(feedback){
9835             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9836         }
9837         
9838         if(this.indicator){
9839             this.indicator.removeClass('visible');
9840             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9841         }
9842         
9843         if(this.disabled){
9844             return;
9845         }
9846         
9847         if(this.allowBlank && !this.getRawValue().length){
9848             return;
9849         }
9850         if (Roo.bootstrap.version == 3) {
9851             this.el.addClass(this.validClass);
9852         } else {
9853             this.inputEl().addClass('is-valid');
9854         }
9855
9856         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9857             
9858             var feedback = this.el.select('.form-control-feedback', true).first();
9859             
9860             if(feedback){
9861                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9862                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9863             }
9864             
9865         }
9866         
9867         this.fireEvent('valid', this);
9868     },
9869     
9870      /**
9871      * Mark this field as invalid
9872      * @param {String} msg The validation message
9873      */
9874     markInvalid : function(msg)
9875     {
9876         if(!this.el  || this.preventMark){ // not rendered
9877             return;
9878         }
9879         
9880         this.el.removeClass([this.invalidClass, this.validClass]);
9881         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9882         
9883         var feedback = this.el.select('.form-control-feedback', true).first();
9884             
9885         if(feedback){
9886             this.el.select('.form-control-feedback', true).first().removeClass(
9887                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9888         }
9889
9890         if(this.disabled){
9891             return;
9892         }
9893         
9894         if(this.allowBlank && !this.getRawValue().length){
9895             return;
9896         }
9897         
9898         if(this.indicator){
9899             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9900             this.indicator.addClass('visible');
9901         }
9902         if (Roo.bootstrap.version == 3) {
9903             this.el.addClass(this.invalidClass);
9904         } else {
9905             this.inputEl().addClass('is-invalid');
9906         }
9907         
9908         
9909         
9910         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9911             
9912             var feedback = this.el.select('.form-control-feedback', true).first();
9913             
9914             if(feedback){
9915                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9916                 
9917                 if(this.getValue().length || this.forceFeedback){
9918                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9919                 }
9920                 
9921             }
9922             
9923         }
9924         
9925         this.fireEvent('invalid', this, msg);
9926     },
9927     // private
9928     SafariOnKeyDown : function(event)
9929     {
9930         // this is a workaround for a password hang bug on chrome/ webkit.
9931         if (this.inputEl().dom.type != 'password') {
9932             return;
9933         }
9934         
9935         var isSelectAll = false;
9936         
9937         if(this.inputEl().dom.selectionEnd > 0){
9938             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9939         }
9940         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9941             event.preventDefault();
9942             this.setValue('');
9943             return;
9944         }
9945         
9946         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9947             
9948             event.preventDefault();
9949             // this is very hacky as keydown always get's upper case.
9950             //
9951             var cc = String.fromCharCode(event.getCharCode());
9952             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9953             
9954         }
9955     },
9956     adjustWidth : function(tag, w){
9957         tag = tag.toLowerCase();
9958         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9959             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9960                 if(tag == 'input'){
9961                     return w + 2;
9962                 }
9963                 if(tag == 'textarea'){
9964                     return w-2;
9965                 }
9966             }else if(Roo.isOpera){
9967                 if(tag == 'input'){
9968                     return w + 2;
9969                 }
9970                 if(tag == 'textarea'){
9971                     return w-2;
9972                 }
9973             }
9974         }
9975         return w;
9976     },
9977     
9978     setFieldLabel : function(v)
9979     {
9980         if(!this.rendered){
9981             return;
9982         }
9983         
9984         if(this.indicatorEl()){
9985             var ar = this.el.select('label > span',true);
9986             
9987             if (ar.elements.length) {
9988                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9989                 this.fieldLabel = v;
9990                 return;
9991             }
9992             
9993             var br = this.el.select('label',true);
9994             
9995             if(br.elements.length) {
9996                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9997                 this.fieldLabel = v;
9998                 return;
9999             }
10000             
10001             Roo.log('Cannot Found any of label > span || label in input');
10002             return;
10003         }
10004         
10005         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10006         this.fieldLabel = v;
10007         
10008         
10009     }
10010 });
10011
10012  
10013 /*
10014  * - LGPL
10015  *
10016  * Input
10017  * 
10018  */
10019
10020 /**
10021  * @class Roo.bootstrap.TextArea
10022  * @extends Roo.bootstrap.Input
10023  * Bootstrap TextArea class
10024  * @cfg {Number} cols Specifies the visible width of a text area
10025  * @cfg {Number} rows Specifies the visible number of lines in a text area
10026  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10027  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10028  * @cfg {string} html text
10029  * 
10030  * @constructor
10031  * Create a new TextArea
10032  * @param {Object} config The config object
10033  */
10034
10035 Roo.bootstrap.TextArea = function(config){
10036     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10037    
10038 };
10039
10040 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10041      
10042     cols : false,
10043     rows : 5,
10044     readOnly : false,
10045     warp : 'soft',
10046     resize : false,
10047     value: false,
10048     html: false,
10049     
10050     getAutoCreate : function(){
10051         
10052         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10053         
10054         var id = Roo.id();
10055         
10056         var cfg = {};
10057         
10058         if(this.inputType != 'hidden'){
10059             cfg.cls = 'form-group' //input-group
10060         }
10061         
10062         var input =  {
10063             tag: 'textarea',
10064             id : id,
10065             warp : this.warp,
10066             rows : this.rows,
10067             value : this.value || '',
10068             html: this.html || '',
10069             cls : 'form-control',
10070             placeholder : this.placeholder || '' 
10071             
10072         };
10073         
10074         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10075             input.maxLength = this.maxLength;
10076         }
10077         
10078         if(this.resize){
10079             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10080         }
10081         
10082         if(this.cols){
10083             input.cols = this.cols;
10084         }
10085         
10086         if (this.readOnly) {
10087             input.readonly = true;
10088         }
10089         
10090         if (this.name) {
10091             input.name = this.name;
10092         }
10093         
10094         if (this.size) {
10095             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10096         }
10097         
10098         var settings=this;
10099         ['xs','sm','md','lg'].map(function(size){
10100             if (settings[size]) {
10101                 cfg.cls += ' col-' + size + '-' + settings[size];
10102             }
10103         });
10104         
10105         var inputblock = input;
10106         
10107         if(this.hasFeedback && !this.allowBlank){
10108             
10109             var feedback = {
10110                 tag: 'span',
10111                 cls: 'glyphicon form-control-feedback'
10112             };
10113
10114             inputblock = {
10115                 cls : 'has-feedback',
10116                 cn :  [
10117                     input,
10118                     feedback
10119                 ] 
10120             };  
10121         }
10122         
10123         
10124         if (this.before || this.after) {
10125             
10126             inputblock = {
10127                 cls : 'input-group',
10128                 cn :  [] 
10129             };
10130             if (this.before) {
10131                 inputblock.cn.push({
10132                     tag :'span',
10133                     cls : 'input-group-addon',
10134                     html : this.before
10135                 });
10136             }
10137             
10138             inputblock.cn.push(input);
10139             
10140             if(this.hasFeedback && !this.allowBlank){
10141                 inputblock.cls += ' has-feedback';
10142                 inputblock.cn.push(feedback);
10143             }
10144             
10145             if (this.after) {
10146                 inputblock.cn.push({
10147                     tag :'span',
10148                     cls : 'input-group-addon',
10149                     html : this.after
10150                 });
10151             }
10152             
10153         }
10154         
10155         if (align ==='left' && this.fieldLabel.length) {
10156             cfg.cn = [
10157                 {
10158                     tag: 'label',
10159                     'for' :  id,
10160                     cls : 'control-label',
10161                     html : this.fieldLabel
10162                 },
10163                 {
10164                     cls : "",
10165                     cn: [
10166                         inputblock
10167                     ]
10168                 }
10169
10170             ];
10171             
10172             if(this.labelWidth > 12){
10173                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10174             }
10175
10176             if(this.labelWidth < 13 && this.labelmd == 0){
10177                 this.labelmd = this.labelWidth;
10178             }
10179
10180             if(this.labellg > 0){
10181                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10182                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10183             }
10184
10185             if(this.labelmd > 0){
10186                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10187                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10188             }
10189
10190             if(this.labelsm > 0){
10191                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10192                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10193             }
10194
10195             if(this.labelxs > 0){
10196                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10197                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10198             }
10199             
10200         } else if ( this.fieldLabel.length) {
10201             cfg.cn = [
10202
10203                {
10204                    tag: 'label',
10205                    //cls : 'input-group-addon',
10206                    html : this.fieldLabel
10207
10208                },
10209
10210                inputblock
10211
10212            ];
10213
10214         } else {
10215
10216             cfg.cn = [
10217
10218                 inputblock
10219
10220             ];
10221                 
10222         }
10223         
10224         if (this.disabled) {
10225             input.disabled=true;
10226         }
10227         
10228         return cfg;
10229         
10230     },
10231     /**
10232      * return the real textarea element.
10233      */
10234     inputEl: function ()
10235     {
10236         return this.el.select('textarea.form-control',true).first();
10237     },
10238     
10239     /**
10240      * Clear any invalid styles/messages for this field
10241      */
10242     clearInvalid : function()
10243     {
10244         
10245         if(!this.el || this.preventMark){ // not rendered
10246             return;
10247         }
10248         
10249         var label = this.el.select('label', true).first();
10250         var icon = this.el.select('i.fa-star', true).first();
10251         
10252         if(label && icon){
10253             icon.remove();
10254         }
10255         this.el.removeClass( this.validClass);
10256         this.inputEl().removeClass('is-invalid');
10257          
10258         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10259             
10260             var feedback = this.el.select('.form-control-feedback', true).first();
10261             
10262             if(feedback){
10263                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10264             }
10265             
10266         }
10267         
10268         this.fireEvent('valid', this);
10269     },
10270     
10271      /**
10272      * Mark this field as valid
10273      */
10274     markValid : function()
10275     {
10276         if(!this.el  || this.preventMark){ // not rendered
10277             return;
10278         }
10279         
10280         this.el.removeClass([this.invalidClass, this.validClass]);
10281         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10282         
10283         var feedback = this.el.select('.form-control-feedback', true).first();
10284             
10285         if(feedback){
10286             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10287         }
10288
10289         if(this.disabled || this.allowBlank){
10290             return;
10291         }
10292         
10293         var label = this.el.select('label', true).first();
10294         var icon = this.el.select('i.fa-star', true).first();
10295         
10296         if(label && icon){
10297             icon.remove();
10298         }
10299         if (Roo.bootstrap.version == 3) {
10300             this.el.addClass(this.validClass);
10301         } else {
10302             this.inputEl().addClass('is-valid');
10303         }
10304         
10305         
10306         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10307             
10308             var feedback = this.el.select('.form-control-feedback', true).first();
10309             
10310             if(feedback){
10311                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10312                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10313             }
10314             
10315         }
10316         
10317         this.fireEvent('valid', this);
10318     },
10319     
10320      /**
10321      * Mark this field as invalid
10322      * @param {String} msg The validation message
10323      */
10324     markInvalid : function(msg)
10325     {
10326         if(!this.el  || this.preventMark){ // not rendered
10327             return;
10328         }
10329         
10330         this.el.removeClass([this.invalidClass, this.validClass]);
10331         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10332         
10333         var feedback = this.el.select('.form-control-feedback', true).first();
10334             
10335         if(feedback){
10336             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10337         }
10338
10339         if(this.disabled || this.allowBlank){
10340             return;
10341         }
10342         
10343         var label = this.el.select('label', true).first();
10344         var icon = this.el.select('i.fa-star', true).first();
10345         
10346         if(!this.getValue().length && label && !icon){
10347             this.el.createChild({
10348                 tag : 'i',
10349                 cls : 'text-danger fa fa-lg fa-star',
10350                 tooltip : 'This field is required',
10351                 style : 'margin-right:5px;'
10352             }, label, true);
10353         }
10354         
10355         if (Roo.bootstrap.version == 3) {
10356             this.el.addClass(this.invalidClass);
10357         } else {
10358             this.inputEl().addClass('is-invalid');
10359         }
10360         
10361         // fixme ... this may be depricated need to test..
10362         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10363             
10364             var feedback = this.el.select('.form-control-feedback', true).first();
10365             
10366             if(feedback){
10367                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10368                 
10369                 if(this.getValue().length || this.forceFeedback){
10370                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10371                 }
10372                 
10373             }
10374             
10375         }
10376         
10377         this.fireEvent('invalid', this, msg);
10378     }
10379 });
10380
10381  
10382 /*
10383  * - LGPL
10384  *
10385  * trigger field - base class for combo..
10386  * 
10387  */
10388  
10389 /**
10390  * @class Roo.bootstrap.TriggerField
10391  * @extends Roo.bootstrap.Input
10392  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10393  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10394  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10395  * for which you can provide a custom implementation.  For example:
10396  * <pre><code>
10397 var trigger = new Roo.bootstrap.TriggerField();
10398 trigger.onTriggerClick = myTriggerFn;
10399 trigger.applyTo('my-field');
10400 </code></pre>
10401  *
10402  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10403  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10404  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10405  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10406  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10407
10408  * @constructor
10409  * Create a new TriggerField.
10410  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10411  * to the base TextField)
10412  */
10413 Roo.bootstrap.TriggerField = function(config){
10414     this.mimicing = false;
10415     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10416 };
10417
10418 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10419     /**
10420      * @cfg {String} triggerClass A CSS class to apply to the trigger
10421      */
10422      /**
10423      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10424      */
10425     hideTrigger:false,
10426
10427     /**
10428      * @cfg {Boolean} removable (true|false) special filter default false
10429      */
10430     removable : false,
10431     
10432     /** @cfg {Boolean} grow @hide */
10433     /** @cfg {Number} growMin @hide */
10434     /** @cfg {Number} growMax @hide */
10435
10436     /**
10437      * @hide 
10438      * @method
10439      */
10440     autoSize: Roo.emptyFn,
10441     // private
10442     monitorTab : true,
10443     // private
10444     deferHeight : true,
10445
10446     
10447     actionMode : 'wrap',
10448     
10449     caret : false,
10450     
10451     
10452     getAutoCreate : function(){
10453        
10454         var align = this.labelAlign || this.parentLabelAlign();
10455         
10456         var id = Roo.id();
10457         
10458         var cfg = {
10459             cls: 'form-group' //input-group
10460         };
10461         
10462         
10463         var input =  {
10464             tag: 'input',
10465             id : id,
10466             type : this.inputType,
10467             cls : 'form-control',
10468             autocomplete: 'new-password',
10469             placeholder : this.placeholder || '' 
10470             
10471         };
10472         if (this.name) {
10473             input.name = this.name;
10474         }
10475         if (this.size) {
10476             input.cls += ' input-' + this.size;
10477         }
10478         
10479         if (this.disabled) {
10480             input.disabled=true;
10481         }
10482         
10483         var inputblock = input;
10484         
10485         if(this.hasFeedback && !this.allowBlank){
10486             
10487             var feedback = {
10488                 tag: 'span',
10489                 cls: 'glyphicon form-control-feedback'
10490             };
10491             
10492             if(this.removable && !this.editable && !this.tickable){
10493                 inputblock = {
10494                     cls : 'has-feedback',
10495                     cn :  [
10496                         inputblock,
10497                         {
10498                             tag: 'button',
10499                             html : 'x',
10500                             cls : 'roo-combo-removable-btn close'
10501                         },
10502                         feedback
10503                     ] 
10504                 };
10505             } else {
10506                 inputblock = {
10507                     cls : 'has-feedback',
10508                     cn :  [
10509                         inputblock,
10510                         feedback
10511                     ] 
10512                 };
10513             }
10514
10515         } else {
10516             if(this.removable && !this.editable && !this.tickable){
10517                 inputblock = {
10518                     cls : 'roo-removable',
10519                     cn :  [
10520                         inputblock,
10521                         {
10522                             tag: 'button',
10523                             html : 'x',
10524                             cls : 'roo-combo-removable-btn close'
10525                         }
10526                     ] 
10527                 };
10528             }
10529         }
10530         
10531         if (this.before || this.after) {
10532             
10533             inputblock = {
10534                 cls : 'input-group',
10535                 cn :  [] 
10536             };
10537             if (this.before) {
10538                 inputblock.cn.push({
10539                     tag :'span',
10540                     cls : 'input-group-addon input-group-prepend input-group-text',
10541                     html : this.before
10542                 });
10543             }
10544             
10545             inputblock.cn.push(input);
10546             
10547             if(this.hasFeedback && !this.allowBlank){
10548                 inputblock.cls += ' has-feedback';
10549                 inputblock.cn.push(feedback);
10550             }
10551             
10552             if (this.after) {
10553                 inputblock.cn.push({
10554                     tag :'span',
10555                     cls : 'input-group-addon input-group-append input-group-text',
10556                     html : this.after
10557                 });
10558             }
10559             
10560         };
10561         
10562       
10563         
10564         var ibwrap = inputblock;
10565         
10566         if(this.multiple){
10567             ibwrap = {
10568                 tag: 'ul',
10569                 cls: 'roo-select2-choices',
10570                 cn:[
10571                     {
10572                         tag: 'li',
10573                         cls: 'roo-select2-search-field',
10574                         cn: [
10575
10576                             inputblock
10577                         ]
10578                     }
10579                 ]
10580             };
10581                 
10582         }
10583         
10584         var combobox = {
10585             cls: 'roo-select2-container input-group',
10586             cn: [
10587                  {
10588                     tag: 'input',
10589                     type : 'hidden',
10590                     cls: 'form-hidden-field'
10591                 },
10592                 ibwrap
10593             ]
10594         };
10595         
10596         if(!this.multiple && this.showToggleBtn){
10597             
10598             var caret = {
10599                         tag: 'span',
10600                         cls: 'caret'
10601              };
10602             if (this.caret != false) {
10603                 caret = {
10604                      tag: 'i',
10605                      cls: 'fa fa-' + this.caret
10606                 };
10607                 
10608             }
10609             
10610             combobox.cn.push({
10611                 tag :'span',
10612                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10613                 cn : [
10614                     caret,
10615                     {
10616                         tag: 'span',
10617                         cls: 'combobox-clear',
10618                         cn  : [
10619                             {
10620                                 tag : 'i',
10621                                 cls: 'icon-remove'
10622                             }
10623                         ]
10624                     }
10625                 ]
10626
10627             })
10628         }
10629         
10630         if(this.multiple){
10631             combobox.cls += ' roo-select2-container-multi';
10632         }
10633          var indicator = {
10634             tag : 'i',
10635             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10636             tooltip : 'This field is required'
10637         };
10638         if (Roo.bootstrap.version == 4) {
10639             indicator = {
10640                 tag : 'i',
10641                 style : 'display:none'
10642             };
10643         }
10644         
10645         
10646         if (align ==='left' && this.fieldLabel.length) {
10647             
10648             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10649
10650             cfg.cn = [
10651                 indicator,
10652                 {
10653                     tag: 'label',
10654                     'for' :  id,
10655                     cls : 'control-label',
10656                     html : this.fieldLabel
10657
10658                 },
10659                 {
10660                     cls : "", 
10661                     cn: [
10662                         combobox
10663                     ]
10664                 }
10665
10666             ];
10667             
10668             var labelCfg = cfg.cn[1];
10669             var contentCfg = cfg.cn[2];
10670             
10671             if(this.indicatorpos == 'right'){
10672                 cfg.cn = [
10673                     {
10674                         tag: 'label',
10675                         'for' :  id,
10676                         cls : 'control-label',
10677                         cn : [
10678                             {
10679                                 tag : 'span',
10680                                 html : this.fieldLabel
10681                             },
10682                             indicator
10683                         ]
10684                     },
10685                     {
10686                         cls : "", 
10687                         cn: [
10688                             combobox
10689                         ]
10690                     }
10691
10692                 ];
10693                 
10694                 labelCfg = cfg.cn[0];
10695                 contentCfg = cfg.cn[1];
10696             }
10697             
10698             if(this.labelWidth > 12){
10699                 labelCfg.style = "width: " + this.labelWidth + 'px';
10700             }
10701             
10702             if(this.labelWidth < 13 && this.labelmd == 0){
10703                 this.labelmd = this.labelWidth;
10704             }
10705             
10706             if(this.labellg > 0){
10707                 labelCfg.cls += ' col-lg-' + this.labellg;
10708                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10709             }
10710             
10711             if(this.labelmd > 0){
10712                 labelCfg.cls += ' col-md-' + this.labelmd;
10713                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10714             }
10715             
10716             if(this.labelsm > 0){
10717                 labelCfg.cls += ' col-sm-' + this.labelsm;
10718                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10719             }
10720             
10721             if(this.labelxs > 0){
10722                 labelCfg.cls += ' col-xs-' + this.labelxs;
10723                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10724             }
10725             
10726         } else if ( this.fieldLabel.length) {
10727 //                Roo.log(" label");
10728             cfg.cn = [
10729                 indicator,
10730                {
10731                    tag: 'label',
10732                    //cls : 'input-group-addon',
10733                    html : this.fieldLabel
10734
10735                },
10736
10737                combobox
10738
10739             ];
10740             
10741             if(this.indicatorpos == 'right'){
10742                 
10743                 cfg.cn = [
10744                     {
10745                        tag: 'label',
10746                        cn : [
10747                            {
10748                                tag : 'span',
10749                                html : this.fieldLabel
10750                            },
10751                            indicator
10752                        ]
10753
10754                     },
10755                     combobox
10756
10757                 ];
10758
10759             }
10760
10761         } else {
10762             
10763 //                Roo.log(" no label && no align");
10764                 cfg = combobox
10765                      
10766                 
10767         }
10768         
10769         var settings=this;
10770         ['xs','sm','md','lg'].map(function(size){
10771             if (settings[size]) {
10772                 cfg.cls += ' col-' + size + '-' + settings[size];
10773             }
10774         });
10775         
10776         return cfg;
10777         
10778     },
10779     
10780     
10781     
10782     // private
10783     onResize : function(w, h){
10784 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10785 //        if(typeof w == 'number'){
10786 //            var x = w - this.trigger.getWidth();
10787 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10788 //            this.trigger.setStyle('left', x+'px');
10789 //        }
10790     },
10791
10792     // private
10793     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10794
10795     // private
10796     getResizeEl : function(){
10797         return this.inputEl();
10798     },
10799
10800     // private
10801     getPositionEl : function(){
10802         return this.inputEl();
10803     },
10804
10805     // private
10806     alignErrorIcon : function(){
10807         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10808     },
10809
10810     // private
10811     initEvents : function(){
10812         
10813         this.createList();
10814         
10815         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10816         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10817         if(!this.multiple && this.showToggleBtn){
10818             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10819             if(this.hideTrigger){
10820                 this.trigger.setDisplayed(false);
10821             }
10822             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10823         }
10824         
10825         if(this.multiple){
10826             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10827         }
10828         
10829         if(this.removable && !this.editable && !this.tickable){
10830             var close = this.closeTriggerEl();
10831             
10832             if(close){
10833                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10834                 close.on('click', this.removeBtnClick, this, close);
10835             }
10836         }
10837         
10838         //this.trigger.addClassOnOver('x-form-trigger-over');
10839         //this.trigger.addClassOnClick('x-form-trigger-click');
10840         
10841         //if(!this.width){
10842         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10843         //}
10844     },
10845     
10846     closeTriggerEl : function()
10847     {
10848         var close = this.el.select('.roo-combo-removable-btn', true).first();
10849         return close ? close : false;
10850     },
10851     
10852     removeBtnClick : function(e, h, el)
10853     {
10854         e.preventDefault();
10855         
10856         if(this.fireEvent("remove", this) !== false){
10857             this.reset();
10858             this.fireEvent("afterremove", this)
10859         }
10860     },
10861     
10862     createList : function()
10863     {
10864         this.list = Roo.get(document.body).createChild({
10865             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10866             cls: 'typeahead typeahead-long dropdown-menu',
10867             style: 'display:none'
10868         });
10869         
10870         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10871         
10872     },
10873
10874     // private
10875     initTrigger : function(){
10876        
10877     },
10878
10879     // private
10880     onDestroy : function(){
10881         if(this.trigger){
10882             this.trigger.removeAllListeners();
10883           //  this.trigger.remove();
10884         }
10885         //if(this.wrap){
10886         //    this.wrap.remove();
10887         //}
10888         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10889     },
10890
10891     // private
10892     onFocus : function(){
10893         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10894         /*
10895         if(!this.mimicing){
10896             this.wrap.addClass('x-trigger-wrap-focus');
10897             this.mimicing = true;
10898             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10899             if(this.monitorTab){
10900                 this.el.on("keydown", this.checkTab, this);
10901             }
10902         }
10903         */
10904     },
10905
10906     // private
10907     checkTab : function(e){
10908         if(e.getKey() == e.TAB){
10909             this.triggerBlur();
10910         }
10911     },
10912
10913     // private
10914     onBlur : function(){
10915         // do nothing
10916     },
10917
10918     // private
10919     mimicBlur : function(e, t){
10920         /*
10921         if(!this.wrap.contains(t) && this.validateBlur()){
10922             this.triggerBlur();
10923         }
10924         */
10925     },
10926
10927     // private
10928     triggerBlur : function(){
10929         this.mimicing = false;
10930         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10931         if(this.monitorTab){
10932             this.el.un("keydown", this.checkTab, this);
10933         }
10934         //this.wrap.removeClass('x-trigger-wrap-focus');
10935         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10936     },
10937
10938     // private
10939     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10940     validateBlur : function(e, t){
10941         return true;
10942     },
10943
10944     // private
10945     onDisable : function(){
10946         this.inputEl().dom.disabled = true;
10947         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10948         //if(this.wrap){
10949         //    this.wrap.addClass('x-item-disabled');
10950         //}
10951     },
10952
10953     // private
10954     onEnable : function(){
10955         this.inputEl().dom.disabled = false;
10956         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10957         //if(this.wrap){
10958         //    this.el.removeClass('x-item-disabled');
10959         //}
10960     },
10961
10962     // private
10963     onShow : function(){
10964         var ae = this.getActionEl();
10965         
10966         if(ae){
10967             ae.dom.style.display = '';
10968             ae.dom.style.visibility = 'visible';
10969         }
10970     },
10971
10972     // private
10973     
10974     onHide : function(){
10975         var ae = this.getActionEl();
10976         ae.dom.style.display = 'none';
10977     },
10978
10979     /**
10980      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10981      * by an implementing function.
10982      * @method
10983      * @param {EventObject} e
10984      */
10985     onTriggerClick : Roo.emptyFn
10986 });
10987  /*
10988  * Based on:
10989  * Ext JS Library 1.1.1
10990  * Copyright(c) 2006-2007, Ext JS, LLC.
10991  *
10992  * Originally Released Under LGPL - original licence link has changed is not relivant.
10993  *
10994  * Fork - LGPL
10995  * <script type="text/javascript">
10996  */
10997
10998
10999 /**
11000  * @class Roo.data.SortTypes
11001  * @singleton
11002  * Defines the default sorting (casting?) comparison functions used when sorting data.
11003  */
11004 Roo.data.SortTypes = {
11005     /**
11006      * Default sort that does nothing
11007      * @param {Mixed} s The value being converted
11008      * @return {Mixed} The comparison value
11009      */
11010     none : function(s){
11011         return s;
11012     },
11013     
11014     /**
11015      * The regular expression used to strip tags
11016      * @type {RegExp}
11017      * @property
11018      */
11019     stripTagsRE : /<\/?[^>]+>/gi,
11020     
11021     /**
11022      * Strips all HTML tags to sort on text only
11023      * @param {Mixed} s The value being converted
11024      * @return {String} The comparison value
11025      */
11026     asText : function(s){
11027         return String(s).replace(this.stripTagsRE, "");
11028     },
11029     
11030     /**
11031      * Strips all HTML tags to sort on text only - Case insensitive
11032      * @param {Mixed} s The value being converted
11033      * @return {String} The comparison value
11034      */
11035     asUCText : function(s){
11036         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11037     },
11038     
11039     /**
11040      * Case insensitive string
11041      * @param {Mixed} s The value being converted
11042      * @return {String} The comparison value
11043      */
11044     asUCString : function(s) {
11045         return String(s).toUpperCase();
11046     },
11047     
11048     /**
11049      * Date sorting
11050      * @param {Mixed} s The value being converted
11051      * @return {Number} The comparison value
11052      */
11053     asDate : function(s) {
11054         if(!s){
11055             return 0;
11056         }
11057         if(s instanceof Date){
11058             return s.getTime();
11059         }
11060         return Date.parse(String(s));
11061     },
11062     
11063     /**
11064      * Float sorting
11065      * @param {Mixed} s The value being converted
11066      * @return {Float} The comparison value
11067      */
11068     asFloat : function(s) {
11069         var val = parseFloat(String(s).replace(/,/g, ""));
11070         if(isNaN(val)) {
11071             val = 0;
11072         }
11073         return val;
11074     },
11075     
11076     /**
11077      * Integer sorting
11078      * @param {Mixed} s The value being converted
11079      * @return {Number} The comparison value
11080      */
11081     asInt : function(s) {
11082         var val = parseInt(String(s).replace(/,/g, ""));
11083         if(isNaN(val)) {
11084             val = 0;
11085         }
11086         return val;
11087     }
11088 };/*
11089  * Based on:
11090  * Ext JS Library 1.1.1
11091  * Copyright(c) 2006-2007, Ext JS, LLC.
11092  *
11093  * Originally Released Under LGPL - original licence link has changed is not relivant.
11094  *
11095  * Fork - LGPL
11096  * <script type="text/javascript">
11097  */
11098
11099 /**
11100 * @class Roo.data.Record
11101  * Instances of this class encapsulate both record <em>definition</em> information, and record
11102  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11103  * to access Records cached in an {@link Roo.data.Store} object.<br>
11104  * <p>
11105  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11106  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11107  * objects.<br>
11108  * <p>
11109  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11110  * @constructor
11111  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11112  * {@link #create}. The parameters are the same.
11113  * @param {Array} data An associative Array of data values keyed by the field name.
11114  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11115  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11116  * not specified an integer id is generated.
11117  */
11118 Roo.data.Record = function(data, id){
11119     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11120     this.data = data;
11121 };
11122
11123 /**
11124  * Generate a constructor for a specific record layout.
11125  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11126  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11127  * Each field definition object may contain the following properties: <ul>
11128  * <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,
11129  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11130  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11131  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11132  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11133  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11134  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11135  * this may be omitted.</p></li>
11136  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11137  * <ul><li>auto (Default, implies no conversion)</li>
11138  * <li>string</li>
11139  * <li>int</li>
11140  * <li>float</li>
11141  * <li>boolean</li>
11142  * <li>date</li></ul></p></li>
11143  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11144  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11145  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11146  * by the Reader into an object that will be stored in the Record. It is passed the
11147  * following parameters:<ul>
11148  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11149  * </ul></p></li>
11150  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11151  * </ul>
11152  * <br>usage:<br><pre><code>
11153 var TopicRecord = Roo.data.Record.create(
11154     {name: 'title', mapping: 'topic_title'},
11155     {name: 'author', mapping: 'username'},
11156     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11157     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11158     {name: 'lastPoster', mapping: 'user2'},
11159     {name: 'excerpt', mapping: 'post_text'}
11160 );
11161
11162 var myNewRecord = new TopicRecord({
11163     title: 'Do my job please',
11164     author: 'noobie',
11165     totalPosts: 1,
11166     lastPost: new Date(),
11167     lastPoster: 'Animal',
11168     excerpt: 'No way dude!'
11169 });
11170 myStore.add(myNewRecord);
11171 </code></pre>
11172  * @method create
11173  * @static
11174  */
11175 Roo.data.Record.create = function(o){
11176     var f = function(){
11177         f.superclass.constructor.apply(this, arguments);
11178     };
11179     Roo.extend(f, Roo.data.Record);
11180     var p = f.prototype;
11181     p.fields = new Roo.util.MixedCollection(false, function(field){
11182         return field.name;
11183     });
11184     for(var i = 0, len = o.length; i < len; i++){
11185         p.fields.add(new Roo.data.Field(o[i]));
11186     }
11187     f.getField = function(name){
11188         return p.fields.get(name);  
11189     };
11190     return f;
11191 };
11192
11193 Roo.data.Record.AUTO_ID = 1000;
11194 Roo.data.Record.EDIT = 'edit';
11195 Roo.data.Record.REJECT = 'reject';
11196 Roo.data.Record.COMMIT = 'commit';
11197
11198 Roo.data.Record.prototype = {
11199     /**
11200      * Readonly flag - true if this record has been modified.
11201      * @type Boolean
11202      */
11203     dirty : false,
11204     editing : false,
11205     error: null,
11206     modified: null,
11207
11208     // private
11209     join : function(store){
11210         this.store = store;
11211     },
11212
11213     /**
11214      * Set the named field to the specified value.
11215      * @param {String} name The name of the field to set.
11216      * @param {Object} value The value to set the field to.
11217      */
11218     set : function(name, value){
11219         if(this.data[name] == value){
11220             return;
11221         }
11222         this.dirty = true;
11223         if(!this.modified){
11224             this.modified = {};
11225         }
11226         if(typeof this.modified[name] == 'undefined'){
11227             this.modified[name] = this.data[name];
11228         }
11229         this.data[name] = value;
11230         if(!this.editing && this.store){
11231             this.store.afterEdit(this);
11232         }       
11233     },
11234
11235     /**
11236      * Get the value of the named field.
11237      * @param {String} name The name of the field to get the value of.
11238      * @return {Object} The value of the field.
11239      */
11240     get : function(name){
11241         return this.data[name]; 
11242     },
11243
11244     // private
11245     beginEdit : function(){
11246         this.editing = true;
11247         this.modified = {}; 
11248     },
11249
11250     // private
11251     cancelEdit : function(){
11252         this.editing = false;
11253         delete this.modified;
11254     },
11255
11256     // private
11257     endEdit : function(){
11258         this.editing = false;
11259         if(this.dirty && this.store){
11260             this.store.afterEdit(this);
11261         }
11262     },
11263
11264     /**
11265      * Usually called by the {@link Roo.data.Store} which owns the Record.
11266      * Rejects all changes made to the Record since either creation, or the last commit operation.
11267      * Modified fields are reverted to their original values.
11268      * <p>
11269      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11270      * of reject operations.
11271      */
11272     reject : function(){
11273         var m = this.modified;
11274         for(var n in m){
11275             if(typeof m[n] != "function"){
11276                 this.data[n] = m[n];
11277             }
11278         }
11279         this.dirty = false;
11280         delete this.modified;
11281         this.editing = false;
11282         if(this.store){
11283             this.store.afterReject(this);
11284         }
11285     },
11286
11287     /**
11288      * Usually called by the {@link Roo.data.Store} which owns the Record.
11289      * Commits all changes made to the Record since either creation, or the last commit operation.
11290      * <p>
11291      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11292      * of commit operations.
11293      */
11294     commit : function(){
11295         this.dirty = false;
11296         delete this.modified;
11297         this.editing = false;
11298         if(this.store){
11299             this.store.afterCommit(this);
11300         }
11301     },
11302
11303     // private
11304     hasError : function(){
11305         return this.error != null;
11306     },
11307
11308     // private
11309     clearError : function(){
11310         this.error = null;
11311     },
11312
11313     /**
11314      * Creates a copy of this record.
11315      * @param {String} id (optional) A new record id if you don't want to use this record's id
11316      * @return {Record}
11317      */
11318     copy : function(newId) {
11319         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11320     }
11321 };/*
11322  * Based on:
11323  * Ext JS Library 1.1.1
11324  * Copyright(c) 2006-2007, Ext JS, LLC.
11325  *
11326  * Originally Released Under LGPL - original licence link has changed is not relivant.
11327  *
11328  * Fork - LGPL
11329  * <script type="text/javascript">
11330  */
11331
11332
11333
11334 /**
11335  * @class Roo.data.Store
11336  * @extends Roo.util.Observable
11337  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11338  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11339  * <p>
11340  * 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
11341  * has no knowledge of the format of the data returned by the Proxy.<br>
11342  * <p>
11343  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11344  * instances from the data object. These records are cached and made available through accessor functions.
11345  * @constructor
11346  * Creates a new Store.
11347  * @param {Object} config A config object containing the objects needed for the Store to access data,
11348  * and read the data into Records.
11349  */
11350 Roo.data.Store = function(config){
11351     this.data = new Roo.util.MixedCollection(false);
11352     this.data.getKey = function(o){
11353         return o.id;
11354     };
11355     this.baseParams = {};
11356     // private
11357     this.paramNames = {
11358         "start" : "start",
11359         "limit" : "limit",
11360         "sort" : "sort",
11361         "dir" : "dir",
11362         "multisort" : "_multisort"
11363     };
11364
11365     if(config && config.data){
11366         this.inlineData = config.data;
11367         delete config.data;
11368     }
11369
11370     Roo.apply(this, config);
11371     
11372     if(this.reader){ // reader passed
11373         this.reader = Roo.factory(this.reader, Roo.data);
11374         this.reader.xmodule = this.xmodule || false;
11375         if(!this.recordType){
11376             this.recordType = this.reader.recordType;
11377         }
11378         if(this.reader.onMetaChange){
11379             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11380         }
11381     }
11382
11383     if(this.recordType){
11384         this.fields = this.recordType.prototype.fields;
11385     }
11386     this.modified = [];
11387
11388     this.addEvents({
11389         /**
11390          * @event datachanged
11391          * Fires when the data cache has changed, and a widget which is using this Store
11392          * as a Record cache should refresh its view.
11393          * @param {Store} this
11394          */
11395         datachanged : true,
11396         /**
11397          * @event metachange
11398          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11399          * @param {Store} this
11400          * @param {Object} meta The JSON metadata
11401          */
11402         metachange : true,
11403         /**
11404          * @event add
11405          * Fires when Records have been added to the Store
11406          * @param {Store} this
11407          * @param {Roo.data.Record[]} records The array of Records added
11408          * @param {Number} index The index at which the record(s) were added
11409          */
11410         add : true,
11411         /**
11412          * @event remove
11413          * Fires when a Record has been removed from the Store
11414          * @param {Store} this
11415          * @param {Roo.data.Record} record The Record that was removed
11416          * @param {Number} index The index at which the record was removed
11417          */
11418         remove : true,
11419         /**
11420          * @event update
11421          * Fires when a Record has been updated
11422          * @param {Store} this
11423          * @param {Roo.data.Record} record The Record that was updated
11424          * @param {String} operation The update operation being performed.  Value may be one of:
11425          * <pre><code>
11426  Roo.data.Record.EDIT
11427  Roo.data.Record.REJECT
11428  Roo.data.Record.COMMIT
11429          * </code></pre>
11430          */
11431         update : true,
11432         /**
11433          * @event clear
11434          * Fires when the data cache has been cleared.
11435          * @param {Store} this
11436          */
11437         clear : true,
11438         /**
11439          * @event beforeload
11440          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11441          * the load action will be canceled.
11442          * @param {Store} this
11443          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11444          */
11445         beforeload : true,
11446         /**
11447          * @event beforeloadadd
11448          * Fires after a new set of Records has been loaded.
11449          * @param {Store} this
11450          * @param {Roo.data.Record[]} records The Records that were loaded
11451          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11452          */
11453         beforeloadadd : true,
11454         /**
11455          * @event load
11456          * Fires after a new set of Records has been loaded, before they are added to the store.
11457          * @param {Store} this
11458          * @param {Roo.data.Record[]} records The Records that were loaded
11459          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11460          * @params {Object} return from reader
11461          */
11462         load : true,
11463         /**
11464          * @event loadexception
11465          * Fires if an exception occurs in the Proxy during loading.
11466          * Called with the signature of the Proxy's "loadexception" event.
11467          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11468          * 
11469          * @param {Proxy} 
11470          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11471          * @param {Object} load options 
11472          * @param {Object} jsonData from your request (normally this contains the Exception)
11473          */
11474         loadexception : true
11475     });
11476     
11477     if(this.proxy){
11478         this.proxy = Roo.factory(this.proxy, Roo.data);
11479         this.proxy.xmodule = this.xmodule || false;
11480         this.relayEvents(this.proxy,  ["loadexception"]);
11481     }
11482     this.sortToggle = {};
11483     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11484
11485     Roo.data.Store.superclass.constructor.call(this);
11486
11487     if(this.inlineData){
11488         this.loadData(this.inlineData);
11489         delete this.inlineData;
11490     }
11491 };
11492
11493 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11494      /**
11495     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11496     * without a remote query - used by combo/forms at present.
11497     */
11498     
11499     /**
11500     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11501     */
11502     /**
11503     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11504     */
11505     /**
11506     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11507     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11508     */
11509     /**
11510     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11511     * on any HTTP request
11512     */
11513     /**
11514     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11515     */
11516     /**
11517     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11518     */
11519     multiSort: false,
11520     /**
11521     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11522     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11523     */
11524     remoteSort : false,
11525
11526     /**
11527     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11528      * loaded or when a record is removed. (defaults to false).
11529     */
11530     pruneModifiedRecords : false,
11531
11532     // private
11533     lastOptions : null,
11534
11535     /**
11536      * Add Records to the Store and fires the add event.
11537      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11538      */
11539     add : function(records){
11540         records = [].concat(records);
11541         for(var i = 0, len = records.length; i < len; i++){
11542             records[i].join(this);
11543         }
11544         var index = this.data.length;
11545         this.data.addAll(records);
11546         this.fireEvent("add", this, records, index);
11547     },
11548
11549     /**
11550      * Remove a Record from the Store and fires the remove event.
11551      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11552      */
11553     remove : function(record){
11554         var index = this.data.indexOf(record);
11555         this.data.removeAt(index);
11556  
11557         if(this.pruneModifiedRecords){
11558             this.modified.remove(record);
11559         }
11560         this.fireEvent("remove", this, record, index);
11561     },
11562
11563     /**
11564      * Remove all Records from the Store and fires the clear event.
11565      */
11566     removeAll : function(){
11567         this.data.clear();
11568         if(this.pruneModifiedRecords){
11569             this.modified = [];
11570         }
11571         this.fireEvent("clear", this);
11572     },
11573
11574     /**
11575      * Inserts Records to the Store at the given index and fires the add event.
11576      * @param {Number} index The start index at which to insert the passed Records.
11577      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11578      */
11579     insert : function(index, records){
11580         records = [].concat(records);
11581         for(var i = 0, len = records.length; i < len; i++){
11582             this.data.insert(index, records[i]);
11583             records[i].join(this);
11584         }
11585         this.fireEvent("add", this, records, index);
11586     },
11587
11588     /**
11589      * Get the index within the cache of the passed Record.
11590      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11591      * @return {Number} The index of the passed Record. Returns -1 if not found.
11592      */
11593     indexOf : function(record){
11594         return this.data.indexOf(record);
11595     },
11596
11597     /**
11598      * Get the index within the cache of the Record with the passed id.
11599      * @param {String} id The id of the Record to find.
11600      * @return {Number} The index of the Record. Returns -1 if not found.
11601      */
11602     indexOfId : function(id){
11603         return this.data.indexOfKey(id);
11604     },
11605
11606     /**
11607      * Get the Record with the specified id.
11608      * @param {String} id The id of the Record to find.
11609      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11610      */
11611     getById : function(id){
11612         return this.data.key(id);
11613     },
11614
11615     /**
11616      * Get the Record at the specified index.
11617      * @param {Number} index The index of the Record to find.
11618      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11619      */
11620     getAt : function(index){
11621         return this.data.itemAt(index);
11622     },
11623
11624     /**
11625      * Returns a range of Records between specified indices.
11626      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11627      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11628      * @return {Roo.data.Record[]} An array of Records
11629      */
11630     getRange : function(start, end){
11631         return this.data.getRange(start, end);
11632     },
11633
11634     // private
11635     storeOptions : function(o){
11636         o = Roo.apply({}, o);
11637         delete o.callback;
11638         delete o.scope;
11639         this.lastOptions = o;
11640     },
11641
11642     /**
11643      * Loads the Record cache from the configured Proxy using the configured Reader.
11644      * <p>
11645      * If using remote paging, then the first load call must specify the <em>start</em>
11646      * and <em>limit</em> properties in the options.params property to establish the initial
11647      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11648      * <p>
11649      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11650      * and this call will return before the new data has been loaded. Perform any post-processing
11651      * in a callback function, or in a "load" event handler.</strong>
11652      * <p>
11653      * @param {Object} options An object containing properties which control loading options:<ul>
11654      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11655      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11656      * passed the following arguments:<ul>
11657      * <li>r : Roo.data.Record[]</li>
11658      * <li>options: Options object from the load call</li>
11659      * <li>success: Boolean success indicator</li></ul></li>
11660      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11661      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11662      * </ul>
11663      */
11664     load : function(options){
11665         options = options || {};
11666         if(this.fireEvent("beforeload", this, options) !== false){
11667             this.storeOptions(options);
11668             var p = Roo.apply(options.params || {}, this.baseParams);
11669             // if meta was not loaded from remote source.. try requesting it.
11670             if (!this.reader.metaFromRemote) {
11671                 p._requestMeta = 1;
11672             }
11673             if(this.sortInfo && this.remoteSort){
11674                 var pn = this.paramNames;
11675                 p[pn["sort"]] = this.sortInfo.field;
11676                 p[pn["dir"]] = this.sortInfo.direction;
11677             }
11678             if (this.multiSort) {
11679                 var pn = this.paramNames;
11680                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11681             }
11682             
11683             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11684         }
11685     },
11686
11687     /**
11688      * Reloads the Record cache from the configured Proxy using the configured Reader and
11689      * the options from the last load operation performed.
11690      * @param {Object} options (optional) An object containing properties which may override the options
11691      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11692      * the most recently used options are reused).
11693      */
11694     reload : function(options){
11695         this.load(Roo.applyIf(options||{}, this.lastOptions));
11696     },
11697
11698     // private
11699     // Called as a callback by the Reader during a load operation.
11700     loadRecords : function(o, options, success){
11701         if(!o || success === false){
11702             if(success !== false){
11703                 this.fireEvent("load", this, [], options, o);
11704             }
11705             if(options.callback){
11706                 options.callback.call(options.scope || this, [], options, false);
11707             }
11708             return;
11709         }
11710         // if data returned failure - throw an exception.
11711         if (o.success === false) {
11712             // show a message if no listener is registered.
11713             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11714                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11715             }
11716             // loadmask wil be hooked into this..
11717             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11718             return;
11719         }
11720         var r = o.records, t = o.totalRecords || r.length;
11721         
11722         this.fireEvent("beforeloadadd", this, r, options, o);
11723         
11724         if(!options || options.add !== true){
11725             if(this.pruneModifiedRecords){
11726                 this.modified = [];
11727             }
11728             for(var i = 0, len = r.length; i < len; i++){
11729                 r[i].join(this);
11730             }
11731             if(this.snapshot){
11732                 this.data = this.snapshot;
11733                 delete this.snapshot;
11734             }
11735             this.data.clear();
11736             this.data.addAll(r);
11737             this.totalLength = t;
11738             this.applySort();
11739             this.fireEvent("datachanged", this);
11740         }else{
11741             this.totalLength = Math.max(t, this.data.length+r.length);
11742             this.add(r);
11743         }
11744         
11745         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11746                 
11747             var e = new Roo.data.Record({});
11748
11749             e.set(this.parent.displayField, this.parent.emptyTitle);
11750             e.set(this.parent.valueField, '');
11751
11752             this.insert(0, e);
11753         }
11754             
11755         this.fireEvent("load", this, r, options, o);
11756         if(options.callback){
11757             options.callback.call(options.scope || this, r, options, true);
11758         }
11759     },
11760
11761
11762     /**
11763      * Loads data from a passed data block. A Reader which understands the format of the data
11764      * must have been configured in the constructor.
11765      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11766      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11767      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11768      */
11769     loadData : function(o, append){
11770         var r = this.reader.readRecords(o);
11771         this.loadRecords(r, {add: append}, true);
11772     },
11773
11774     /**
11775      * Gets the number of cached records.
11776      * <p>
11777      * <em>If using paging, this may not be the total size of the dataset. If the data object
11778      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11779      * the data set size</em>
11780      */
11781     getCount : function(){
11782         return this.data.length || 0;
11783     },
11784
11785     /**
11786      * Gets the total number of records in the dataset as returned by the server.
11787      * <p>
11788      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11789      * the dataset size</em>
11790      */
11791     getTotalCount : function(){
11792         return this.totalLength || 0;
11793     },
11794
11795     /**
11796      * Returns the sort state of the Store as an object with two properties:
11797      * <pre><code>
11798  field {String} The name of the field by which the Records are sorted
11799  direction {String} The sort order, "ASC" or "DESC"
11800      * </code></pre>
11801      */
11802     getSortState : function(){
11803         return this.sortInfo;
11804     },
11805
11806     // private
11807     applySort : function(){
11808         if(this.sortInfo && !this.remoteSort){
11809             var s = this.sortInfo, f = s.field;
11810             var st = this.fields.get(f).sortType;
11811             var fn = function(r1, r2){
11812                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11813                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11814             };
11815             this.data.sort(s.direction, fn);
11816             if(this.snapshot && this.snapshot != this.data){
11817                 this.snapshot.sort(s.direction, fn);
11818             }
11819         }
11820     },
11821
11822     /**
11823      * Sets the default sort column and order to be used by the next load operation.
11824      * @param {String} fieldName The name of the field to sort by.
11825      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11826      */
11827     setDefaultSort : function(field, dir){
11828         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11829     },
11830
11831     /**
11832      * Sort the Records.
11833      * If remote sorting is used, the sort is performed on the server, and the cache is
11834      * reloaded. If local sorting is used, the cache is sorted internally.
11835      * @param {String} fieldName The name of the field to sort by.
11836      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11837      */
11838     sort : function(fieldName, dir){
11839         var f = this.fields.get(fieldName);
11840         if(!dir){
11841             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11842             
11843             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11844                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11845             }else{
11846                 dir = f.sortDir;
11847             }
11848         }
11849         this.sortToggle[f.name] = dir;
11850         this.sortInfo = {field: f.name, direction: dir};
11851         if(!this.remoteSort){
11852             this.applySort();
11853             this.fireEvent("datachanged", this);
11854         }else{
11855             this.load(this.lastOptions);
11856         }
11857     },
11858
11859     /**
11860      * Calls the specified function for each of the Records in the cache.
11861      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11862      * Returning <em>false</em> aborts and exits the iteration.
11863      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11864      */
11865     each : function(fn, scope){
11866         this.data.each(fn, scope);
11867     },
11868
11869     /**
11870      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11871      * (e.g., during paging).
11872      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11873      */
11874     getModifiedRecords : function(){
11875         return this.modified;
11876     },
11877
11878     // private
11879     createFilterFn : function(property, value, anyMatch){
11880         if(!value.exec){ // not a regex
11881             value = String(value);
11882             if(value.length == 0){
11883                 return false;
11884             }
11885             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11886         }
11887         return function(r){
11888             return value.test(r.data[property]);
11889         };
11890     },
11891
11892     /**
11893      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11894      * @param {String} property A field on your records
11895      * @param {Number} start The record index to start at (defaults to 0)
11896      * @param {Number} end The last record index to include (defaults to length - 1)
11897      * @return {Number} The sum
11898      */
11899     sum : function(property, start, end){
11900         var rs = this.data.items, v = 0;
11901         start = start || 0;
11902         end = (end || end === 0) ? end : rs.length-1;
11903
11904         for(var i = start; i <= end; i++){
11905             v += (rs[i].data[property] || 0);
11906         }
11907         return v;
11908     },
11909
11910     /**
11911      * Filter the records by a specified property.
11912      * @param {String} field A field on your records
11913      * @param {String/RegExp} value Either a string that the field
11914      * should start with or a RegExp to test against the field
11915      * @param {Boolean} anyMatch True to match any part not just the beginning
11916      */
11917     filter : function(property, value, anyMatch){
11918         var fn = this.createFilterFn(property, value, anyMatch);
11919         return fn ? this.filterBy(fn) : this.clearFilter();
11920     },
11921
11922     /**
11923      * Filter by a function. The specified function will be called with each
11924      * record in this data source. If the function returns true the record is included,
11925      * otherwise it is filtered.
11926      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11927      * @param {Object} scope (optional) The scope of the function (defaults to this)
11928      */
11929     filterBy : function(fn, scope){
11930         this.snapshot = this.snapshot || this.data;
11931         this.data = this.queryBy(fn, scope||this);
11932         this.fireEvent("datachanged", this);
11933     },
11934
11935     /**
11936      * Query the records by a specified property.
11937      * @param {String} field A field on your records
11938      * @param {String/RegExp} value Either a string that the field
11939      * should start with or a RegExp to test against the field
11940      * @param {Boolean} anyMatch True to match any part not just the beginning
11941      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11942      */
11943     query : function(property, value, anyMatch){
11944         var fn = this.createFilterFn(property, value, anyMatch);
11945         return fn ? this.queryBy(fn) : this.data.clone();
11946     },
11947
11948     /**
11949      * Query by a function. The specified function will be called with each
11950      * record in this data source. If the function returns true the record is included
11951      * in the results.
11952      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11953      * @param {Object} scope (optional) The scope of the function (defaults to this)
11954       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11955      **/
11956     queryBy : function(fn, scope){
11957         var data = this.snapshot || this.data;
11958         return data.filterBy(fn, scope||this);
11959     },
11960
11961     /**
11962      * Collects unique values for a particular dataIndex from this store.
11963      * @param {String} dataIndex The property to collect
11964      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11965      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11966      * @return {Array} An array of the unique values
11967      **/
11968     collect : function(dataIndex, allowNull, bypassFilter){
11969         var d = (bypassFilter === true && this.snapshot) ?
11970                 this.snapshot.items : this.data.items;
11971         var v, sv, r = [], l = {};
11972         for(var i = 0, len = d.length; i < len; i++){
11973             v = d[i].data[dataIndex];
11974             sv = String(v);
11975             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11976                 l[sv] = true;
11977                 r[r.length] = v;
11978             }
11979         }
11980         return r;
11981     },
11982
11983     /**
11984      * Revert to a view of the Record cache with no filtering applied.
11985      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11986      */
11987     clearFilter : function(suppressEvent){
11988         if(this.snapshot && this.snapshot != this.data){
11989             this.data = this.snapshot;
11990             delete this.snapshot;
11991             if(suppressEvent !== true){
11992                 this.fireEvent("datachanged", this);
11993             }
11994         }
11995     },
11996
11997     // private
11998     afterEdit : function(record){
11999         if(this.modified.indexOf(record) == -1){
12000             this.modified.push(record);
12001         }
12002         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12003     },
12004     
12005     // private
12006     afterReject : function(record){
12007         this.modified.remove(record);
12008         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12009     },
12010
12011     // private
12012     afterCommit : function(record){
12013         this.modified.remove(record);
12014         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12015     },
12016
12017     /**
12018      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12019      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12020      */
12021     commitChanges : function(){
12022         var m = this.modified.slice(0);
12023         this.modified = [];
12024         for(var i = 0, len = m.length; i < len; i++){
12025             m[i].commit();
12026         }
12027     },
12028
12029     /**
12030      * Cancel outstanding changes on all changed records.
12031      */
12032     rejectChanges : function(){
12033         var m = this.modified.slice(0);
12034         this.modified = [];
12035         for(var i = 0, len = m.length; i < len; i++){
12036             m[i].reject();
12037         }
12038     },
12039
12040     onMetaChange : function(meta, rtype, o){
12041         this.recordType = rtype;
12042         this.fields = rtype.prototype.fields;
12043         delete this.snapshot;
12044         this.sortInfo = meta.sortInfo || this.sortInfo;
12045         this.modified = [];
12046         this.fireEvent('metachange', this, this.reader.meta);
12047     },
12048     
12049     moveIndex : function(data, type)
12050     {
12051         var index = this.indexOf(data);
12052         
12053         var newIndex = index + type;
12054         
12055         this.remove(data);
12056         
12057         this.insert(newIndex, data);
12058         
12059     }
12060 });/*
12061  * Based on:
12062  * Ext JS Library 1.1.1
12063  * Copyright(c) 2006-2007, Ext JS, LLC.
12064  *
12065  * Originally Released Under LGPL - original licence link has changed is not relivant.
12066  *
12067  * Fork - LGPL
12068  * <script type="text/javascript">
12069  */
12070
12071 /**
12072  * @class Roo.data.SimpleStore
12073  * @extends Roo.data.Store
12074  * Small helper class to make creating Stores from Array data easier.
12075  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12076  * @cfg {Array} fields An array of field definition objects, or field name strings.
12077  * @cfg {Array} data The multi-dimensional array of data
12078  * @constructor
12079  * @param {Object} config
12080  */
12081 Roo.data.SimpleStore = function(config){
12082     Roo.data.SimpleStore.superclass.constructor.call(this, {
12083         isLocal : true,
12084         reader: new Roo.data.ArrayReader({
12085                 id: config.id
12086             },
12087             Roo.data.Record.create(config.fields)
12088         ),
12089         proxy : new Roo.data.MemoryProxy(config.data)
12090     });
12091     this.load();
12092 };
12093 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12094  * Based on:
12095  * Ext JS Library 1.1.1
12096  * Copyright(c) 2006-2007, Ext JS, LLC.
12097  *
12098  * Originally Released Under LGPL - original licence link has changed is not relivant.
12099  *
12100  * Fork - LGPL
12101  * <script type="text/javascript">
12102  */
12103
12104 /**
12105 /**
12106  * @extends Roo.data.Store
12107  * @class Roo.data.JsonStore
12108  * Small helper class to make creating Stores for JSON data easier. <br/>
12109 <pre><code>
12110 var store = new Roo.data.JsonStore({
12111     url: 'get-images.php',
12112     root: 'images',
12113     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12114 });
12115 </code></pre>
12116  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12117  * JsonReader and HttpProxy (unless inline data is provided).</b>
12118  * @cfg {Array} fields An array of field definition objects, or field name strings.
12119  * @constructor
12120  * @param {Object} config
12121  */
12122 Roo.data.JsonStore = function(c){
12123     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12124         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12125         reader: new Roo.data.JsonReader(c, c.fields)
12126     }));
12127 };
12128 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12129  * Based on:
12130  * Ext JS Library 1.1.1
12131  * Copyright(c) 2006-2007, Ext JS, LLC.
12132  *
12133  * Originally Released Under LGPL - original licence link has changed is not relivant.
12134  *
12135  * Fork - LGPL
12136  * <script type="text/javascript">
12137  */
12138
12139  
12140 Roo.data.Field = function(config){
12141     if(typeof config == "string"){
12142         config = {name: config};
12143     }
12144     Roo.apply(this, config);
12145     
12146     if(!this.type){
12147         this.type = "auto";
12148     }
12149     
12150     var st = Roo.data.SortTypes;
12151     // named sortTypes are supported, here we look them up
12152     if(typeof this.sortType == "string"){
12153         this.sortType = st[this.sortType];
12154     }
12155     
12156     // set default sortType for strings and dates
12157     if(!this.sortType){
12158         switch(this.type){
12159             case "string":
12160                 this.sortType = st.asUCString;
12161                 break;
12162             case "date":
12163                 this.sortType = st.asDate;
12164                 break;
12165             default:
12166                 this.sortType = st.none;
12167         }
12168     }
12169
12170     // define once
12171     var stripRe = /[\$,%]/g;
12172
12173     // prebuilt conversion function for this field, instead of
12174     // switching every time we're reading a value
12175     if(!this.convert){
12176         var cv, dateFormat = this.dateFormat;
12177         switch(this.type){
12178             case "":
12179             case "auto":
12180             case undefined:
12181                 cv = function(v){ return v; };
12182                 break;
12183             case "string":
12184                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12185                 break;
12186             case "int":
12187                 cv = function(v){
12188                     return v !== undefined && v !== null && v !== '' ?
12189                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12190                     };
12191                 break;
12192             case "float":
12193                 cv = function(v){
12194                     return v !== undefined && v !== null && v !== '' ?
12195                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12196                     };
12197                 break;
12198             case "bool":
12199             case "boolean":
12200                 cv = function(v){ return v === true || v === "true" || v == 1; };
12201                 break;
12202             case "date":
12203                 cv = function(v){
12204                     if(!v){
12205                         return '';
12206                     }
12207                     if(v instanceof Date){
12208                         return v;
12209                     }
12210                     if(dateFormat){
12211                         if(dateFormat == "timestamp"){
12212                             return new Date(v*1000);
12213                         }
12214                         return Date.parseDate(v, dateFormat);
12215                     }
12216                     var parsed = Date.parse(v);
12217                     return parsed ? new Date(parsed) : null;
12218                 };
12219              break;
12220             
12221         }
12222         this.convert = cv;
12223     }
12224 };
12225
12226 Roo.data.Field.prototype = {
12227     dateFormat: null,
12228     defaultValue: "",
12229     mapping: null,
12230     sortType : null,
12231     sortDir : "ASC"
12232 };/*
12233  * Based on:
12234  * Ext JS Library 1.1.1
12235  * Copyright(c) 2006-2007, Ext JS, LLC.
12236  *
12237  * Originally Released Under LGPL - original licence link has changed is not relivant.
12238  *
12239  * Fork - LGPL
12240  * <script type="text/javascript">
12241  */
12242  
12243 // Base class for reading structured data from a data source.  This class is intended to be
12244 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12245
12246 /**
12247  * @class Roo.data.DataReader
12248  * Base class for reading structured data from a data source.  This class is intended to be
12249  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12250  */
12251
12252 Roo.data.DataReader = function(meta, recordType){
12253     
12254     this.meta = meta;
12255     
12256     this.recordType = recordType instanceof Array ? 
12257         Roo.data.Record.create(recordType) : recordType;
12258 };
12259
12260 Roo.data.DataReader.prototype = {
12261      /**
12262      * Create an empty record
12263      * @param {Object} data (optional) - overlay some values
12264      * @return {Roo.data.Record} record created.
12265      */
12266     newRow :  function(d) {
12267         var da =  {};
12268         this.recordType.prototype.fields.each(function(c) {
12269             switch( c.type) {
12270                 case 'int' : da[c.name] = 0; break;
12271                 case 'date' : da[c.name] = new Date(); break;
12272                 case 'float' : da[c.name] = 0.0; break;
12273                 case 'boolean' : da[c.name] = false; break;
12274                 default : da[c.name] = ""; break;
12275             }
12276             
12277         });
12278         return new this.recordType(Roo.apply(da, d));
12279     }
12280     
12281 };/*
12282  * Based on:
12283  * Ext JS Library 1.1.1
12284  * Copyright(c) 2006-2007, Ext JS, LLC.
12285  *
12286  * Originally Released Under LGPL - original licence link has changed is not relivant.
12287  *
12288  * Fork - LGPL
12289  * <script type="text/javascript">
12290  */
12291
12292 /**
12293  * @class Roo.data.DataProxy
12294  * @extends Roo.data.Observable
12295  * This class is an abstract base class for implementations which provide retrieval of
12296  * unformatted data objects.<br>
12297  * <p>
12298  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12299  * (of the appropriate type which knows how to parse the data object) to provide a block of
12300  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12301  * <p>
12302  * Custom implementations must implement the load method as described in
12303  * {@link Roo.data.HttpProxy#load}.
12304  */
12305 Roo.data.DataProxy = function(){
12306     this.addEvents({
12307         /**
12308          * @event beforeload
12309          * Fires before a network request is made to retrieve a data object.
12310          * @param {Object} This DataProxy object.
12311          * @param {Object} params The params parameter to the load function.
12312          */
12313         beforeload : true,
12314         /**
12315          * @event load
12316          * Fires before the load method's callback is called.
12317          * @param {Object} This DataProxy object.
12318          * @param {Object} o The data object.
12319          * @param {Object} arg The callback argument object passed to the load function.
12320          */
12321         load : true,
12322         /**
12323          * @event loadexception
12324          * Fires if an Exception occurs during data retrieval.
12325          * @param {Object} This DataProxy object.
12326          * @param {Object} o The data object.
12327          * @param {Object} arg The callback argument object passed to the load function.
12328          * @param {Object} e The Exception.
12329          */
12330         loadexception : true
12331     });
12332     Roo.data.DataProxy.superclass.constructor.call(this);
12333 };
12334
12335 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12336
12337     /**
12338      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12339      */
12340 /*
12341  * Based on:
12342  * Ext JS Library 1.1.1
12343  * Copyright(c) 2006-2007, Ext JS, LLC.
12344  *
12345  * Originally Released Under LGPL - original licence link has changed is not relivant.
12346  *
12347  * Fork - LGPL
12348  * <script type="text/javascript">
12349  */
12350 /**
12351  * @class Roo.data.MemoryProxy
12352  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12353  * to the Reader when its load method is called.
12354  * @constructor
12355  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12356  */
12357 Roo.data.MemoryProxy = function(data){
12358     if (data.data) {
12359         data = data.data;
12360     }
12361     Roo.data.MemoryProxy.superclass.constructor.call(this);
12362     this.data = data;
12363 };
12364
12365 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12366     
12367     /**
12368      * Load data from the requested source (in this case an in-memory
12369      * data object passed to the constructor), read the data object into
12370      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12371      * process that block using the passed callback.
12372      * @param {Object} params This parameter is not used by the MemoryProxy class.
12373      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12374      * object into a block of Roo.data.Records.
12375      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12376      * The function must be passed <ul>
12377      * <li>The Record block object</li>
12378      * <li>The "arg" argument from the load function</li>
12379      * <li>A boolean success indicator</li>
12380      * </ul>
12381      * @param {Object} scope The scope in which to call the callback
12382      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12383      */
12384     load : function(params, reader, callback, scope, arg){
12385         params = params || {};
12386         var result;
12387         try {
12388             result = reader.readRecords(this.data);
12389         }catch(e){
12390             this.fireEvent("loadexception", this, arg, null, e);
12391             callback.call(scope, null, arg, false);
12392             return;
12393         }
12394         callback.call(scope, result, arg, true);
12395     },
12396     
12397     // private
12398     update : function(params, records){
12399         
12400     }
12401 });/*
12402  * Based on:
12403  * Ext JS Library 1.1.1
12404  * Copyright(c) 2006-2007, Ext JS, LLC.
12405  *
12406  * Originally Released Under LGPL - original licence link has changed is not relivant.
12407  *
12408  * Fork - LGPL
12409  * <script type="text/javascript">
12410  */
12411 /**
12412  * @class Roo.data.HttpProxy
12413  * @extends Roo.data.DataProxy
12414  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12415  * configured to reference a certain URL.<br><br>
12416  * <p>
12417  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12418  * from which the running page was served.<br><br>
12419  * <p>
12420  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12421  * <p>
12422  * Be aware that to enable the browser to parse an XML document, the server must set
12423  * the Content-Type header in the HTTP response to "text/xml".
12424  * @constructor
12425  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12426  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12427  * will be used to make the request.
12428  */
12429 Roo.data.HttpProxy = function(conn){
12430     Roo.data.HttpProxy.superclass.constructor.call(this);
12431     // is conn a conn config or a real conn?
12432     this.conn = conn;
12433     this.useAjax = !conn || !conn.events;
12434   
12435 };
12436
12437 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12438     // thse are take from connection...
12439     
12440     /**
12441      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12442      */
12443     /**
12444      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12445      * extra parameters to each request made by this object. (defaults to undefined)
12446      */
12447     /**
12448      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12449      *  to each request made by this object. (defaults to undefined)
12450      */
12451     /**
12452      * @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)
12453      */
12454     /**
12455      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12456      */
12457      /**
12458      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12459      * @type Boolean
12460      */
12461   
12462
12463     /**
12464      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12465      * @type Boolean
12466      */
12467     /**
12468      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12469      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12470      * a finer-grained basis than the DataProxy events.
12471      */
12472     getConnection : function(){
12473         return this.useAjax ? Roo.Ajax : this.conn;
12474     },
12475
12476     /**
12477      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12478      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12479      * process that block using the passed callback.
12480      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12481      * for the request to the remote server.
12482      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12483      * object into a block of Roo.data.Records.
12484      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12485      * The function must be passed <ul>
12486      * <li>The Record block object</li>
12487      * <li>The "arg" argument from the load function</li>
12488      * <li>A boolean success indicator</li>
12489      * </ul>
12490      * @param {Object} scope The scope in which to call the callback
12491      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12492      */
12493     load : function(params, reader, callback, scope, arg){
12494         if(this.fireEvent("beforeload", this, params) !== false){
12495             var  o = {
12496                 params : params || {},
12497                 request: {
12498                     callback : callback,
12499                     scope : scope,
12500                     arg : arg
12501                 },
12502                 reader: reader,
12503                 callback : this.loadResponse,
12504                 scope: this
12505             };
12506             if(this.useAjax){
12507                 Roo.applyIf(o, this.conn);
12508                 if(this.activeRequest){
12509                     Roo.Ajax.abort(this.activeRequest);
12510                 }
12511                 this.activeRequest = Roo.Ajax.request(o);
12512             }else{
12513                 this.conn.request(o);
12514             }
12515         }else{
12516             callback.call(scope||this, null, arg, false);
12517         }
12518     },
12519
12520     // private
12521     loadResponse : function(o, success, response){
12522         delete this.activeRequest;
12523         if(!success){
12524             this.fireEvent("loadexception", this, o, response);
12525             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12526             return;
12527         }
12528         var result;
12529         try {
12530             result = o.reader.read(response);
12531         }catch(e){
12532             this.fireEvent("loadexception", this, o, response, e);
12533             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12534             return;
12535         }
12536         
12537         this.fireEvent("load", this, o, o.request.arg);
12538         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12539     },
12540
12541     // private
12542     update : function(dataSet){
12543
12544     },
12545
12546     // private
12547     updateResponse : function(dataSet){
12548
12549     }
12550 });/*
12551  * Based on:
12552  * Ext JS Library 1.1.1
12553  * Copyright(c) 2006-2007, Ext JS, LLC.
12554  *
12555  * Originally Released Under LGPL - original licence link has changed is not relivant.
12556  *
12557  * Fork - LGPL
12558  * <script type="text/javascript">
12559  */
12560
12561 /**
12562  * @class Roo.data.ScriptTagProxy
12563  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12564  * other than the originating domain of the running page.<br><br>
12565  * <p>
12566  * <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
12567  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12568  * <p>
12569  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12570  * source code that is used as the source inside a &lt;script> tag.<br><br>
12571  * <p>
12572  * In order for the browser to process the returned data, the server must wrap the data object
12573  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12574  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12575  * depending on whether the callback name was passed:
12576  * <p>
12577  * <pre><code>
12578 boolean scriptTag = false;
12579 String cb = request.getParameter("callback");
12580 if (cb != null) {
12581     scriptTag = true;
12582     response.setContentType("text/javascript");
12583 } else {
12584     response.setContentType("application/x-json");
12585 }
12586 Writer out = response.getWriter();
12587 if (scriptTag) {
12588     out.write(cb + "(");
12589 }
12590 out.print(dataBlock.toJsonString());
12591 if (scriptTag) {
12592     out.write(");");
12593 }
12594 </pre></code>
12595  *
12596  * @constructor
12597  * @param {Object} config A configuration object.
12598  */
12599 Roo.data.ScriptTagProxy = function(config){
12600     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12601     Roo.apply(this, config);
12602     this.head = document.getElementsByTagName("head")[0];
12603 };
12604
12605 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12606
12607 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12608     /**
12609      * @cfg {String} url The URL from which to request the data object.
12610      */
12611     /**
12612      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12613      */
12614     timeout : 30000,
12615     /**
12616      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12617      * the server the name of the callback function set up by the load call to process the returned data object.
12618      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12619      * javascript output which calls this named function passing the data object as its only parameter.
12620      */
12621     callbackParam : "callback",
12622     /**
12623      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12624      * name to the request.
12625      */
12626     nocache : true,
12627
12628     /**
12629      * Load data from the configured URL, read the data object into
12630      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12631      * process that block using the passed callback.
12632      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12633      * for the request to the remote server.
12634      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12635      * object into a block of Roo.data.Records.
12636      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12637      * The function must be passed <ul>
12638      * <li>The Record block object</li>
12639      * <li>The "arg" argument from the load function</li>
12640      * <li>A boolean success indicator</li>
12641      * </ul>
12642      * @param {Object} scope The scope in which to call the callback
12643      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12644      */
12645     load : function(params, reader, callback, scope, arg){
12646         if(this.fireEvent("beforeload", this, params) !== false){
12647
12648             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12649
12650             var url = this.url;
12651             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12652             if(this.nocache){
12653                 url += "&_dc=" + (new Date().getTime());
12654             }
12655             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12656             var trans = {
12657                 id : transId,
12658                 cb : "stcCallback"+transId,
12659                 scriptId : "stcScript"+transId,
12660                 params : params,
12661                 arg : arg,
12662                 url : url,
12663                 callback : callback,
12664                 scope : scope,
12665                 reader : reader
12666             };
12667             var conn = this;
12668
12669             window[trans.cb] = function(o){
12670                 conn.handleResponse(o, trans);
12671             };
12672
12673             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12674
12675             if(this.autoAbort !== false){
12676                 this.abort();
12677             }
12678
12679             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12680
12681             var script = document.createElement("script");
12682             script.setAttribute("src", url);
12683             script.setAttribute("type", "text/javascript");
12684             script.setAttribute("id", trans.scriptId);
12685             this.head.appendChild(script);
12686
12687             this.trans = trans;
12688         }else{
12689             callback.call(scope||this, null, arg, false);
12690         }
12691     },
12692
12693     // private
12694     isLoading : function(){
12695         return this.trans ? true : false;
12696     },
12697
12698     /**
12699      * Abort the current server request.
12700      */
12701     abort : function(){
12702         if(this.isLoading()){
12703             this.destroyTrans(this.trans);
12704         }
12705     },
12706
12707     // private
12708     destroyTrans : function(trans, isLoaded){
12709         this.head.removeChild(document.getElementById(trans.scriptId));
12710         clearTimeout(trans.timeoutId);
12711         if(isLoaded){
12712             window[trans.cb] = undefined;
12713             try{
12714                 delete window[trans.cb];
12715             }catch(e){}
12716         }else{
12717             // if hasn't been loaded, wait for load to remove it to prevent script error
12718             window[trans.cb] = function(){
12719                 window[trans.cb] = undefined;
12720                 try{
12721                     delete window[trans.cb];
12722                 }catch(e){}
12723             };
12724         }
12725     },
12726
12727     // private
12728     handleResponse : function(o, trans){
12729         this.trans = false;
12730         this.destroyTrans(trans, true);
12731         var result;
12732         try {
12733             result = trans.reader.readRecords(o);
12734         }catch(e){
12735             this.fireEvent("loadexception", this, o, trans.arg, e);
12736             trans.callback.call(trans.scope||window, null, trans.arg, false);
12737             return;
12738         }
12739         this.fireEvent("load", this, o, trans.arg);
12740         trans.callback.call(trans.scope||window, result, trans.arg, true);
12741     },
12742
12743     // private
12744     handleFailure : function(trans){
12745         this.trans = false;
12746         this.destroyTrans(trans, false);
12747         this.fireEvent("loadexception", this, null, trans.arg);
12748         trans.callback.call(trans.scope||window, null, trans.arg, false);
12749     }
12750 });/*
12751  * Based on:
12752  * Ext JS Library 1.1.1
12753  * Copyright(c) 2006-2007, Ext JS, LLC.
12754  *
12755  * Originally Released Under LGPL - original licence link has changed is not relivant.
12756  *
12757  * Fork - LGPL
12758  * <script type="text/javascript">
12759  */
12760
12761 /**
12762  * @class Roo.data.JsonReader
12763  * @extends Roo.data.DataReader
12764  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12765  * based on mappings in a provided Roo.data.Record constructor.
12766  * 
12767  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12768  * in the reply previously. 
12769  * 
12770  * <p>
12771  * Example code:
12772  * <pre><code>
12773 var RecordDef = Roo.data.Record.create([
12774     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12775     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12776 ]);
12777 var myReader = new Roo.data.JsonReader({
12778     totalProperty: "results",    // The property which contains the total dataset size (optional)
12779     root: "rows",                // The property which contains an Array of row objects
12780     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12781 }, RecordDef);
12782 </code></pre>
12783  * <p>
12784  * This would consume a JSON file like this:
12785  * <pre><code>
12786 { 'results': 2, 'rows': [
12787     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12788     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12789 }
12790 </code></pre>
12791  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12792  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12793  * paged from the remote server.
12794  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12795  * @cfg {String} root name of the property which contains the Array of row objects.
12796  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12797  * @cfg {Array} fields Array of field definition objects
12798  * @constructor
12799  * Create a new JsonReader
12800  * @param {Object} meta Metadata configuration options
12801  * @param {Object} recordType Either an Array of field definition objects,
12802  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12803  */
12804 Roo.data.JsonReader = function(meta, recordType){
12805     
12806     meta = meta || {};
12807     // set some defaults:
12808     Roo.applyIf(meta, {
12809         totalProperty: 'total',
12810         successProperty : 'success',
12811         root : 'data',
12812         id : 'id'
12813     });
12814     
12815     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12816 };
12817 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12818     
12819     /**
12820      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12821      * Used by Store query builder to append _requestMeta to params.
12822      * 
12823      */
12824     metaFromRemote : false,
12825     /**
12826      * This method is only used by a DataProxy which has retrieved data from a remote server.
12827      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12828      * @return {Object} data A data block which is used by an Roo.data.Store object as
12829      * a cache of Roo.data.Records.
12830      */
12831     read : function(response){
12832         var json = response.responseText;
12833        
12834         var o = /* eval:var:o */ eval("("+json+")");
12835         if(!o) {
12836             throw {message: "JsonReader.read: Json object not found"};
12837         }
12838         
12839         if(o.metaData){
12840             
12841             delete this.ef;
12842             this.metaFromRemote = true;
12843             this.meta = o.metaData;
12844             this.recordType = Roo.data.Record.create(o.metaData.fields);
12845             this.onMetaChange(this.meta, this.recordType, o);
12846         }
12847         return this.readRecords(o);
12848     },
12849
12850     // private function a store will implement
12851     onMetaChange : function(meta, recordType, o){
12852
12853     },
12854
12855     /**
12856          * @ignore
12857          */
12858     simpleAccess: function(obj, subsc) {
12859         return obj[subsc];
12860     },
12861
12862         /**
12863          * @ignore
12864          */
12865     getJsonAccessor: function(){
12866         var re = /[\[\.]/;
12867         return function(expr) {
12868             try {
12869                 return(re.test(expr))
12870                     ? new Function("obj", "return obj." + expr)
12871                     : function(obj){
12872                         return obj[expr];
12873                     };
12874             } catch(e){}
12875             return Roo.emptyFn;
12876         };
12877     }(),
12878
12879     /**
12880      * Create a data block containing Roo.data.Records from an XML document.
12881      * @param {Object} o An object which contains an Array of row objects in the property specified
12882      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12883      * which contains the total size of the dataset.
12884      * @return {Object} data A data block which is used by an Roo.data.Store object as
12885      * a cache of Roo.data.Records.
12886      */
12887     readRecords : function(o){
12888         /**
12889          * After any data loads, the raw JSON data is available for further custom processing.
12890          * @type Object
12891          */
12892         this.o = o;
12893         var s = this.meta, Record = this.recordType,
12894             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12895
12896 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12897         if (!this.ef) {
12898             if(s.totalProperty) {
12899                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12900                 }
12901                 if(s.successProperty) {
12902                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12903                 }
12904                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12905                 if (s.id) {
12906                         var g = this.getJsonAccessor(s.id);
12907                         this.getId = function(rec) {
12908                                 var r = g(rec);  
12909                                 return (r === undefined || r === "") ? null : r;
12910                         };
12911                 } else {
12912                         this.getId = function(){return null;};
12913                 }
12914             this.ef = [];
12915             for(var jj = 0; jj < fl; jj++){
12916                 f = fi[jj];
12917                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12918                 this.ef[jj] = this.getJsonAccessor(map);
12919             }
12920         }
12921
12922         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12923         if(s.totalProperty){
12924             var vt = parseInt(this.getTotal(o), 10);
12925             if(!isNaN(vt)){
12926                 totalRecords = vt;
12927             }
12928         }
12929         if(s.successProperty){
12930             var vs = this.getSuccess(o);
12931             if(vs === false || vs === 'false'){
12932                 success = false;
12933             }
12934         }
12935         var records = [];
12936         for(var i = 0; i < c; i++){
12937                 var n = root[i];
12938             var values = {};
12939             var id = this.getId(n);
12940             for(var j = 0; j < fl; j++){
12941                 f = fi[j];
12942             var v = this.ef[j](n);
12943             if (!f.convert) {
12944                 Roo.log('missing convert for ' + f.name);
12945                 Roo.log(f);
12946                 continue;
12947             }
12948             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12949             }
12950             var record = new Record(values, id);
12951             record.json = n;
12952             records[i] = record;
12953         }
12954         return {
12955             raw : o,
12956             success : success,
12957             records : records,
12958             totalRecords : totalRecords
12959         };
12960     }
12961 });/*
12962  * Based on:
12963  * Ext JS Library 1.1.1
12964  * Copyright(c) 2006-2007, Ext JS, LLC.
12965  *
12966  * Originally Released Under LGPL - original licence link has changed is not relivant.
12967  *
12968  * Fork - LGPL
12969  * <script type="text/javascript">
12970  */
12971
12972 /**
12973  * @class Roo.data.ArrayReader
12974  * @extends Roo.data.DataReader
12975  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12976  * Each element of that Array represents a row of data fields. The
12977  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12978  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12979  * <p>
12980  * Example code:.
12981  * <pre><code>
12982 var RecordDef = Roo.data.Record.create([
12983     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12984     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12985 ]);
12986 var myReader = new Roo.data.ArrayReader({
12987     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12988 }, RecordDef);
12989 </code></pre>
12990  * <p>
12991  * This would consume an Array like this:
12992  * <pre><code>
12993 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12994   </code></pre>
12995  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12996  * @constructor
12997  * Create a new JsonReader
12998  * @param {Object} meta Metadata configuration options.
12999  * @param {Object} recordType Either an Array of field definition objects
13000  * as specified to {@link Roo.data.Record#create},
13001  * or an {@link Roo.data.Record} object
13002  * created using {@link Roo.data.Record#create}.
13003  */
13004 Roo.data.ArrayReader = function(meta, recordType){
13005     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
13006 };
13007
13008 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13009     /**
13010      * Create a data block containing Roo.data.Records from an XML document.
13011      * @param {Object} o An Array of row objects which represents the dataset.
13012      * @return {Object} data A data block which is used by an Roo.data.Store object as
13013      * a cache of Roo.data.Records.
13014      */
13015     readRecords : function(o){
13016         var sid = this.meta ? this.meta.id : null;
13017         var recordType = this.recordType, fields = recordType.prototype.fields;
13018         var records = [];
13019         var root = o;
13020             for(var i = 0; i < root.length; i++){
13021                     var n = root[i];
13022                 var values = {};
13023                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13024                 for(var j = 0, jlen = fields.length; j < jlen; j++){
13025                 var f = fields.items[j];
13026                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13027                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13028                 v = f.convert(v);
13029                 values[f.name] = v;
13030             }
13031                 var record = new recordType(values, id);
13032                 record.json = n;
13033                 records[records.length] = record;
13034             }
13035             return {
13036                 records : records,
13037                 totalRecords : records.length
13038             };
13039     }
13040 });/*
13041  * - LGPL
13042  * * 
13043  */
13044
13045 /**
13046  * @class Roo.bootstrap.ComboBox
13047  * @extends Roo.bootstrap.TriggerField
13048  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13049  * @cfg {Boolean} append (true|false) default false
13050  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13051  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13052  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13053  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13054  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13055  * @cfg {Boolean} animate default true
13056  * @cfg {Boolean} emptyResultText only for touch device
13057  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13058  * @cfg {String} emptyTitle default ''
13059  * @constructor
13060  * Create a new ComboBox.
13061  * @param {Object} config Configuration options
13062  */
13063 Roo.bootstrap.ComboBox = function(config){
13064     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13065     this.addEvents({
13066         /**
13067          * @event expand
13068          * Fires when the dropdown list is expanded
13069         * @param {Roo.bootstrap.ComboBox} combo This combo box
13070         */
13071         'expand' : true,
13072         /**
13073          * @event collapse
13074          * Fires when the dropdown list is collapsed
13075         * @param {Roo.bootstrap.ComboBox} combo This combo box
13076         */
13077         'collapse' : true,
13078         /**
13079          * @event beforeselect
13080          * Fires before a list item is selected. Return false to cancel the selection.
13081         * @param {Roo.bootstrap.ComboBox} combo This combo box
13082         * @param {Roo.data.Record} record The data record returned from the underlying store
13083         * @param {Number} index The index of the selected item in the dropdown list
13084         */
13085         'beforeselect' : true,
13086         /**
13087          * @event select
13088          * Fires when a list item is selected
13089         * @param {Roo.bootstrap.ComboBox} combo This combo box
13090         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13091         * @param {Number} index The index of the selected item in the dropdown list
13092         */
13093         'select' : true,
13094         /**
13095          * @event beforequery
13096          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13097          * The event object passed has these properties:
13098         * @param {Roo.bootstrap.ComboBox} combo This combo box
13099         * @param {String} query The query
13100         * @param {Boolean} forceAll true to force "all" query
13101         * @param {Boolean} cancel true to cancel the query
13102         * @param {Object} e The query event object
13103         */
13104         'beforequery': true,
13105          /**
13106          * @event add
13107          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13108         * @param {Roo.bootstrap.ComboBox} combo This combo box
13109         */
13110         'add' : true,
13111         /**
13112          * @event edit
13113          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13114         * @param {Roo.bootstrap.ComboBox} combo This combo box
13115         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13116         */
13117         'edit' : true,
13118         /**
13119          * @event remove
13120          * Fires when the remove value from the combobox array
13121         * @param {Roo.bootstrap.ComboBox} combo This combo box
13122         */
13123         'remove' : true,
13124         /**
13125          * @event afterremove
13126          * Fires when the remove value from the combobox array
13127         * @param {Roo.bootstrap.ComboBox} combo This combo box
13128         */
13129         'afterremove' : true,
13130         /**
13131          * @event specialfilter
13132          * Fires when specialfilter
13133             * @param {Roo.bootstrap.ComboBox} combo This combo box
13134             */
13135         'specialfilter' : true,
13136         /**
13137          * @event tick
13138          * Fires when tick the element
13139             * @param {Roo.bootstrap.ComboBox} combo This combo box
13140             */
13141         'tick' : true,
13142         /**
13143          * @event touchviewdisplay
13144          * Fires when touch view require special display (default is using displayField)
13145             * @param {Roo.bootstrap.ComboBox} combo This combo box
13146             * @param {Object} cfg set html .
13147             */
13148         'touchviewdisplay' : true
13149         
13150     });
13151     
13152     this.item = [];
13153     this.tickItems = [];
13154     
13155     this.selectedIndex = -1;
13156     if(this.mode == 'local'){
13157         if(config.queryDelay === undefined){
13158             this.queryDelay = 10;
13159         }
13160         if(config.minChars === undefined){
13161             this.minChars = 0;
13162         }
13163     }
13164 };
13165
13166 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13167      
13168     /**
13169      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13170      * rendering into an Roo.Editor, defaults to false)
13171      */
13172     /**
13173      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13174      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13175      */
13176     /**
13177      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13178      */
13179     /**
13180      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13181      * the dropdown list (defaults to undefined, with no header element)
13182      */
13183
13184      /**
13185      * @cfg {String/Roo.Template} tpl The template to use to render the output
13186      */
13187      
13188      /**
13189      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13190      */
13191     listWidth: undefined,
13192     /**
13193      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13194      * mode = 'remote' or 'text' if mode = 'local')
13195      */
13196     displayField: undefined,
13197     
13198     /**
13199      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13200      * mode = 'remote' or 'value' if mode = 'local'). 
13201      * Note: use of a valueField requires the user make a selection
13202      * in order for a value to be mapped.
13203      */
13204     valueField: undefined,
13205     /**
13206      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13207      */
13208     modalTitle : '',
13209     
13210     /**
13211      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13212      * field's data value (defaults to the underlying DOM element's name)
13213      */
13214     hiddenName: undefined,
13215     /**
13216      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13217      */
13218     listClass: '',
13219     /**
13220      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13221      */
13222     selectedClass: 'active',
13223     
13224     /**
13225      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13226      */
13227     shadow:'sides',
13228     /**
13229      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13230      * anchor positions (defaults to 'tl-bl')
13231      */
13232     listAlign: 'tl-bl?',
13233     /**
13234      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13235      */
13236     maxHeight: 300,
13237     /**
13238      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13239      * query specified by the allQuery config option (defaults to 'query')
13240      */
13241     triggerAction: 'query',
13242     /**
13243      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13244      * (defaults to 4, does not apply if editable = false)
13245      */
13246     minChars : 4,
13247     /**
13248      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13249      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13250      */
13251     typeAhead: false,
13252     /**
13253      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13254      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13255      */
13256     queryDelay: 500,
13257     /**
13258      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13259      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13260      */
13261     pageSize: 0,
13262     /**
13263      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13264      * when editable = true (defaults to false)
13265      */
13266     selectOnFocus:false,
13267     /**
13268      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13269      */
13270     queryParam: 'query',
13271     /**
13272      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13273      * when mode = 'remote' (defaults to 'Loading...')
13274      */
13275     loadingText: 'Loading...',
13276     /**
13277      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13278      */
13279     resizable: false,
13280     /**
13281      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13282      */
13283     handleHeight : 8,
13284     /**
13285      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13286      * traditional select (defaults to true)
13287      */
13288     editable: true,
13289     /**
13290      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13291      */
13292     allQuery: '',
13293     /**
13294      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13295      */
13296     mode: 'remote',
13297     /**
13298      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13299      * listWidth has a higher value)
13300      */
13301     minListWidth : 70,
13302     /**
13303      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13304      * allow the user to set arbitrary text into the field (defaults to false)
13305      */
13306     forceSelection:false,
13307     /**
13308      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13309      * if typeAhead = true (defaults to 250)
13310      */
13311     typeAheadDelay : 250,
13312     /**
13313      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13314      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13315      */
13316     valueNotFoundText : undefined,
13317     /**
13318      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13319      */
13320     blockFocus : false,
13321     
13322     /**
13323      * @cfg {Boolean} disableClear Disable showing of clear button.
13324      */
13325     disableClear : false,
13326     /**
13327      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13328      */
13329     alwaysQuery : false,
13330     
13331     /**
13332      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13333      */
13334     multiple : false,
13335     
13336     /**
13337      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13338      */
13339     invalidClass : "has-warning",
13340     
13341     /**
13342      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13343      */
13344     validClass : "has-success",
13345     
13346     /**
13347      * @cfg {Boolean} specialFilter (true|false) special filter default false
13348      */
13349     specialFilter : false,
13350     
13351     /**
13352      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13353      */
13354     mobileTouchView : true,
13355     
13356     /**
13357      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13358      */
13359     useNativeIOS : false,
13360     
13361     /**
13362      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13363      */
13364     mobile_restrict_height : false,
13365     
13366     ios_options : false,
13367     
13368     //private
13369     addicon : false,
13370     editicon: false,
13371     
13372     page: 0,
13373     hasQuery: false,
13374     append: false,
13375     loadNext: false,
13376     autoFocus : true,
13377     tickable : false,
13378     btnPosition : 'right',
13379     triggerList : true,
13380     showToggleBtn : true,
13381     animate : true,
13382     emptyResultText: 'Empty',
13383     triggerText : 'Select',
13384     emptyTitle : '',
13385     
13386     // element that contains real text value.. (when hidden is used..)
13387     
13388     getAutoCreate : function()
13389     {   
13390         var cfg = false;
13391         //render
13392         /*
13393          * Render classic select for iso
13394          */
13395         
13396         if(Roo.isIOS && this.useNativeIOS){
13397             cfg = this.getAutoCreateNativeIOS();
13398             return cfg;
13399         }
13400         
13401         /*
13402          * Touch Devices
13403          */
13404         
13405         if(Roo.isTouch && this.mobileTouchView){
13406             cfg = this.getAutoCreateTouchView();
13407             return cfg;;
13408         }
13409         
13410         /*
13411          *  Normal ComboBox
13412          */
13413         if(!this.tickable){
13414             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13415             return cfg;
13416         }
13417         
13418         /*
13419          *  ComboBox with tickable selections
13420          */
13421              
13422         var align = this.labelAlign || this.parentLabelAlign();
13423         
13424         cfg = {
13425             cls : 'form-group roo-combobox-tickable' //input-group
13426         };
13427         
13428         var btn_text_select = '';
13429         var btn_text_done = '';
13430         var btn_text_cancel = '';
13431         
13432         if (this.btn_text_show) {
13433             btn_text_select = 'Select';
13434             btn_text_done = 'Done';
13435             btn_text_cancel = 'Cancel'; 
13436         }
13437         
13438         var buttons = {
13439             tag : 'div',
13440             cls : 'tickable-buttons',
13441             cn : [
13442                 {
13443                     tag : 'button',
13444                     type : 'button',
13445                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13446                     //html : this.triggerText
13447                     html: btn_text_select
13448                 },
13449                 {
13450                     tag : 'button',
13451                     type : 'button',
13452                     name : 'ok',
13453                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13454                     //html : 'Done'
13455                     html: btn_text_done
13456                 },
13457                 {
13458                     tag : 'button',
13459                     type : 'button',
13460                     name : 'cancel',
13461                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13462                     //html : 'Cancel'
13463                     html: btn_text_cancel
13464                 }
13465             ]
13466         };
13467         
13468         if(this.editable){
13469             buttons.cn.unshift({
13470                 tag: 'input',
13471                 cls: 'roo-select2-search-field-input'
13472             });
13473         }
13474         
13475         var _this = this;
13476         
13477         Roo.each(buttons.cn, function(c){
13478             if (_this.size) {
13479                 c.cls += ' btn-' + _this.size;
13480             }
13481
13482             if (_this.disabled) {
13483                 c.disabled = true;
13484             }
13485         });
13486         
13487         var box = {
13488             tag: 'div',
13489             style : 'display: contents',
13490             cn: [
13491                 {
13492                     tag: 'input',
13493                     type : 'hidden',
13494                     cls: 'form-hidden-field'
13495                 },
13496                 {
13497                     tag: 'ul',
13498                     cls: 'roo-select2-choices',
13499                     cn:[
13500                         {
13501                             tag: 'li',
13502                             cls: 'roo-select2-search-field',
13503                             cn: [
13504                                 buttons
13505                             ]
13506                         }
13507                     ]
13508                 }
13509             ]
13510         };
13511         
13512         var combobox = {
13513             cls: 'roo-select2-container input-group roo-select2-container-multi',
13514             cn: [
13515                 
13516                 box
13517 //                {
13518 //                    tag: 'ul',
13519 //                    cls: 'typeahead typeahead-long dropdown-menu',
13520 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13521 //                }
13522             ]
13523         };
13524         
13525         if(this.hasFeedback && !this.allowBlank){
13526             
13527             var feedback = {
13528                 tag: 'span',
13529                 cls: 'glyphicon form-control-feedback'
13530             };
13531
13532             combobox.cn.push(feedback);
13533         }
13534         
13535         var indicator = {
13536             tag : 'i',
13537             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13538             tooltip : 'This field is required'
13539         };
13540         if (Roo.bootstrap.version == 4) {
13541             indicator = {
13542                 tag : 'i',
13543                 style : 'display:none'
13544             };
13545         }
13546         if (align ==='left' && this.fieldLabel.length) {
13547             
13548             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13549             
13550             cfg.cn = [
13551                 indicator,
13552                 {
13553                     tag: 'label',
13554                     'for' :  id,
13555                     cls : 'control-label col-form-label',
13556                     html : this.fieldLabel
13557
13558                 },
13559                 {
13560                     cls : "", 
13561                     cn: [
13562                         combobox
13563                     ]
13564                 }
13565
13566             ];
13567             
13568             var labelCfg = cfg.cn[1];
13569             var contentCfg = cfg.cn[2];
13570             
13571
13572             if(this.indicatorpos == 'right'){
13573                 
13574                 cfg.cn = [
13575                     {
13576                         tag: 'label',
13577                         'for' :  id,
13578                         cls : 'control-label col-form-label',
13579                         cn : [
13580                             {
13581                                 tag : 'span',
13582                                 html : this.fieldLabel
13583                             },
13584                             indicator
13585                         ]
13586                     },
13587                     {
13588                         cls : "",
13589                         cn: [
13590                             combobox
13591                         ]
13592                     }
13593
13594                 ];
13595                 
13596                 
13597                 
13598                 labelCfg = cfg.cn[0];
13599                 contentCfg = cfg.cn[1];
13600             
13601             }
13602             
13603             if(this.labelWidth > 12){
13604                 labelCfg.style = "width: " + this.labelWidth + 'px';
13605             }
13606             
13607             if(this.labelWidth < 13 && this.labelmd == 0){
13608                 this.labelmd = this.labelWidth;
13609             }
13610             
13611             if(this.labellg > 0){
13612                 labelCfg.cls += ' col-lg-' + this.labellg;
13613                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13614             }
13615             
13616             if(this.labelmd > 0){
13617                 labelCfg.cls += ' col-md-' + this.labelmd;
13618                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13619             }
13620             
13621             if(this.labelsm > 0){
13622                 labelCfg.cls += ' col-sm-' + this.labelsm;
13623                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13624             }
13625             
13626             if(this.labelxs > 0){
13627                 labelCfg.cls += ' col-xs-' + this.labelxs;
13628                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13629             }
13630                 
13631                 
13632         } else if ( this.fieldLabel.length) {
13633 //                Roo.log(" label");
13634                  cfg.cn = [
13635                    indicator,
13636                     {
13637                         tag: 'label',
13638                         //cls : 'input-group-addon',
13639                         html : this.fieldLabel
13640                     },
13641                     combobox
13642                 ];
13643                 
13644                 if(this.indicatorpos == 'right'){
13645                     cfg.cn = [
13646                         {
13647                             tag: 'label',
13648                             //cls : 'input-group-addon',
13649                             html : this.fieldLabel
13650                         },
13651                         indicator,
13652                         combobox
13653                     ];
13654                     
13655                 }
13656
13657         } else {
13658             
13659 //                Roo.log(" no label && no align");
13660                 cfg = combobox
13661                      
13662                 
13663         }
13664          
13665         var settings=this;
13666         ['xs','sm','md','lg'].map(function(size){
13667             if (settings[size]) {
13668                 cfg.cls += ' col-' + size + '-' + settings[size];
13669             }
13670         });
13671         
13672         return cfg;
13673         
13674     },
13675     
13676     _initEventsCalled : false,
13677     
13678     // private
13679     initEvents: function()
13680     {   
13681         if (this._initEventsCalled) { // as we call render... prevent looping...
13682             return;
13683         }
13684         this._initEventsCalled = true;
13685         
13686         if (!this.store) {
13687             throw "can not find store for combo";
13688         }
13689         
13690         this.indicator = this.indicatorEl();
13691         
13692         this.store = Roo.factory(this.store, Roo.data);
13693         this.store.parent = this;
13694         
13695         // if we are building from html. then this element is so complex, that we can not really
13696         // use the rendered HTML.
13697         // so we have to trash and replace the previous code.
13698         if (Roo.XComponent.build_from_html) {
13699             // remove this element....
13700             var e = this.el.dom, k=0;
13701             while (e ) { e = e.previousSibling;  ++k;}
13702
13703             this.el.remove();
13704             
13705             this.el=false;
13706             this.rendered = false;
13707             
13708             this.render(this.parent().getChildContainer(true), k);
13709         }
13710         
13711         if(Roo.isIOS && this.useNativeIOS){
13712             this.initIOSView();
13713             return;
13714         }
13715         
13716         /*
13717          * Touch Devices
13718          */
13719         
13720         if(Roo.isTouch && this.mobileTouchView){
13721             this.initTouchView();
13722             return;
13723         }
13724         
13725         if(this.tickable){
13726             this.initTickableEvents();
13727             return;
13728         }
13729         
13730         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13731         
13732         if(this.hiddenName){
13733             
13734             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13735             
13736             this.hiddenField.dom.value =
13737                 this.hiddenValue !== undefined ? this.hiddenValue :
13738                 this.value !== undefined ? this.value : '';
13739
13740             // prevent input submission
13741             this.el.dom.removeAttribute('name');
13742             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13743              
13744              
13745         }
13746         //if(Roo.isGecko){
13747         //    this.el.dom.setAttribute('autocomplete', 'off');
13748         //}
13749         
13750         var cls = 'x-combo-list';
13751         
13752         //this.list = new Roo.Layer({
13753         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13754         //});
13755         
13756         var _this = this;
13757         
13758         (function(){
13759             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13760             _this.list.setWidth(lw);
13761         }).defer(100);
13762         
13763         this.list.on('mouseover', this.onViewOver, this);
13764         this.list.on('mousemove', this.onViewMove, this);
13765         this.list.on('scroll', this.onViewScroll, this);
13766         
13767         /*
13768         this.list.swallowEvent('mousewheel');
13769         this.assetHeight = 0;
13770
13771         if(this.title){
13772             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13773             this.assetHeight += this.header.getHeight();
13774         }
13775
13776         this.innerList = this.list.createChild({cls:cls+'-inner'});
13777         this.innerList.on('mouseover', this.onViewOver, this);
13778         this.innerList.on('mousemove', this.onViewMove, this);
13779         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13780         
13781         if(this.allowBlank && !this.pageSize && !this.disableClear){
13782             this.footer = this.list.createChild({cls:cls+'-ft'});
13783             this.pageTb = new Roo.Toolbar(this.footer);
13784            
13785         }
13786         if(this.pageSize){
13787             this.footer = this.list.createChild({cls:cls+'-ft'});
13788             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13789                     {pageSize: this.pageSize});
13790             
13791         }
13792         
13793         if (this.pageTb && this.allowBlank && !this.disableClear) {
13794             var _this = this;
13795             this.pageTb.add(new Roo.Toolbar.Fill(), {
13796                 cls: 'x-btn-icon x-btn-clear',
13797                 text: '&#160;',
13798                 handler: function()
13799                 {
13800                     _this.collapse();
13801                     _this.clearValue();
13802                     _this.onSelect(false, -1);
13803                 }
13804             });
13805         }
13806         if (this.footer) {
13807             this.assetHeight += this.footer.getHeight();
13808         }
13809         */
13810             
13811         if(!this.tpl){
13812             this.tpl = Roo.bootstrap.version == 4 ?
13813                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13814                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13815         }
13816
13817         this.view = new Roo.View(this.list, this.tpl, {
13818             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13819         });
13820         //this.view.wrapEl.setDisplayed(false);
13821         this.view.on('click', this.onViewClick, this);
13822         
13823         
13824         this.store.on('beforeload', this.onBeforeLoad, this);
13825         this.store.on('load', this.onLoad, this);
13826         this.store.on('loadexception', this.onLoadException, this);
13827         /*
13828         if(this.resizable){
13829             this.resizer = new Roo.Resizable(this.list,  {
13830                pinned:true, handles:'se'
13831             });
13832             this.resizer.on('resize', function(r, w, h){
13833                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13834                 this.listWidth = w;
13835                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13836                 this.restrictHeight();
13837             }, this);
13838             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13839         }
13840         */
13841         if(!this.editable){
13842             this.editable = true;
13843             this.setEditable(false);
13844         }
13845         
13846         /*
13847         
13848         if (typeof(this.events.add.listeners) != 'undefined') {
13849             
13850             this.addicon = this.wrap.createChild(
13851                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13852        
13853             this.addicon.on('click', function(e) {
13854                 this.fireEvent('add', this);
13855             }, this);
13856         }
13857         if (typeof(this.events.edit.listeners) != 'undefined') {
13858             
13859             this.editicon = this.wrap.createChild(
13860                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13861             if (this.addicon) {
13862                 this.editicon.setStyle('margin-left', '40px');
13863             }
13864             this.editicon.on('click', function(e) {
13865                 
13866                 // we fire even  if inothing is selected..
13867                 this.fireEvent('edit', this, this.lastData );
13868                 
13869             }, this);
13870         }
13871         */
13872         
13873         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13874             "up" : function(e){
13875                 this.inKeyMode = true;
13876                 this.selectPrev();
13877             },
13878
13879             "down" : function(e){
13880                 if(!this.isExpanded()){
13881                     this.onTriggerClick();
13882                 }else{
13883                     this.inKeyMode = true;
13884                     this.selectNext();
13885                 }
13886             },
13887
13888             "enter" : function(e){
13889 //                this.onViewClick();
13890                 //return true;
13891                 this.collapse();
13892                 
13893                 if(this.fireEvent("specialkey", this, e)){
13894                     this.onViewClick(false);
13895                 }
13896                 
13897                 return true;
13898             },
13899
13900             "esc" : function(e){
13901                 this.collapse();
13902             },
13903
13904             "tab" : function(e){
13905                 this.collapse();
13906                 
13907                 if(this.fireEvent("specialkey", this, e)){
13908                     this.onViewClick(false);
13909                 }
13910                 
13911                 return true;
13912             },
13913
13914             scope : this,
13915
13916             doRelay : function(foo, bar, hname){
13917                 if(hname == 'down' || this.scope.isExpanded()){
13918                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13919                 }
13920                 return true;
13921             },
13922
13923             forceKeyDown: true
13924         });
13925         
13926         
13927         this.queryDelay = Math.max(this.queryDelay || 10,
13928                 this.mode == 'local' ? 10 : 250);
13929         
13930         
13931         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13932         
13933         if(this.typeAhead){
13934             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13935         }
13936         if(this.editable !== false){
13937             this.inputEl().on("keyup", this.onKeyUp, this);
13938         }
13939         if(this.forceSelection){
13940             this.inputEl().on('blur', this.doForce, this);
13941         }
13942         
13943         if(this.multiple){
13944             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13945             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13946         }
13947     },
13948     
13949     initTickableEvents: function()
13950     {   
13951         this.createList();
13952         
13953         if(this.hiddenName){
13954             
13955             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13956             
13957             this.hiddenField.dom.value =
13958                 this.hiddenValue !== undefined ? this.hiddenValue :
13959                 this.value !== undefined ? this.value : '';
13960
13961             // prevent input submission
13962             this.el.dom.removeAttribute('name');
13963             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13964              
13965              
13966         }
13967         
13968 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13969         
13970         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13971         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13972         if(this.triggerList){
13973             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13974         }
13975          
13976         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13977         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13978         
13979         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13980         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13981         
13982         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13983         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13984         
13985         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13986         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13987         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13988         
13989         this.okBtn.hide();
13990         this.cancelBtn.hide();
13991         
13992         var _this = this;
13993         
13994         (function(){
13995             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13996             _this.list.setWidth(lw);
13997         }).defer(100);
13998         
13999         this.list.on('mouseover', this.onViewOver, this);
14000         this.list.on('mousemove', this.onViewMove, this);
14001         
14002         this.list.on('scroll', this.onViewScroll, this);
14003         
14004         if(!this.tpl){
14005             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14006                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14007         }
14008
14009         this.view = new Roo.View(this.list, this.tpl, {
14010             singleSelect:true,
14011             tickable:true,
14012             parent:this,
14013             store: this.store,
14014             selectedClass: this.selectedClass
14015         });
14016         
14017         //this.view.wrapEl.setDisplayed(false);
14018         this.view.on('click', this.onViewClick, this);
14019         
14020         
14021         
14022         this.store.on('beforeload', this.onBeforeLoad, this);
14023         this.store.on('load', this.onLoad, this);
14024         this.store.on('loadexception', this.onLoadException, this);
14025         
14026         if(this.editable){
14027             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14028                 "up" : function(e){
14029                     this.inKeyMode = true;
14030                     this.selectPrev();
14031                 },
14032
14033                 "down" : function(e){
14034                     this.inKeyMode = true;
14035                     this.selectNext();
14036                 },
14037
14038                 "enter" : function(e){
14039                     if(this.fireEvent("specialkey", this, e)){
14040                         this.onViewClick(false);
14041                     }
14042                     
14043                     return true;
14044                 },
14045
14046                 "esc" : function(e){
14047                     this.onTickableFooterButtonClick(e, false, false);
14048                 },
14049
14050                 "tab" : function(e){
14051                     this.fireEvent("specialkey", this, e);
14052                     
14053                     this.onTickableFooterButtonClick(e, false, false);
14054                     
14055                     return true;
14056                 },
14057
14058                 scope : this,
14059
14060                 doRelay : function(e, fn, key){
14061                     if(this.scope.isExpanded()){
14062                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14063                     }
14064                     return true;
14065                 },
14066
14067                 forceKeyDown: true
14068             });
14069         }
14070         
14071         this.queryDelay = Math.max(this.queryDelay || 10,
14072                 this.mode == 'local' ? 10 : 250);
14073         
14074         
14075         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14076         
14077         if(this.typeAhead){
14078             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14079         }
14080         
14081         if(this.editable !== false){
14082             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14083         }
14084         
14085         this.indicator = this.indicatorEl();
14086         
14087         if(this.indicator){
14088             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14089             this.indicator.hide();
14090         }
14091         
14092     },
14093
14094     onDestroy : function(){
14095         if(this.view){
14096             this.view.setStore(null);
14097             this.view.el.removeAllListeners();
14098             this.view.el.remove();
14099             this.view.purgeListeners();
14100         }
14101         if(this.list){
14102             this.list.dom.innerHTML  = '';
14103         }
14104         
14105         if(this.store){
14106             this.store.un('beforeload', this.onBeforeLoad, this);
14107             this.store.un('load', this.onLoad, this);
14108             this.store.un('loadexception', this.onLoadException, this);
14109         }
14110         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14111     },
14112
14113     // private
14114     fireKey : function(e){
14115         if(e.isNavKeyPress() && !this.list.isVisible()){
14116             this.fireEvent("specialkey", this, e);
14117         }
14118     },
14119
14120     // private
14121     onResize: function(w, h){
14122 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14123 //        
14124 //        if(typeof w != 'number'){
14125 //            // we do not handle it!?!?
14126 //            return;
14127 //        }
14128 //        var tw = this.trigger.getWidth();
14129 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14130 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14131 //        var x = w - tw;
14132 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14133 //            
14134 //        //this.trigger.setStyle('left', x+'px');
14135 //        
14136 //        if(this.list && this.listWidth === undefined){
14137 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14138 //            this.list.setWidth(lw);
14139 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14140 //        }
14141         
14142     
14143         
14144     },
14145
14146     /**
14147      * Allow or prevent the user from directly editing the field text.  If false is passed,
14148      * the user will only be able to select from the items defined in the dropdown list.  This method
14149      * is the runtime equivalent of setting the 'editable' config option at config time.
14150      * @param {Boolean} value True to allow the user to directly edit the field text
14151      */
14152     setEditable : function(value){
14153         if(value == this.editable){
14154             return;
14155         }
14156         this.editable = value;
14157         if(!value){
14158             this.inputEl().dom.setAttribute('readOnly', true);
14159             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14160             this.inputEl().addClass('x-combo-noedit');
14161         }else{
14162             this.inputEl().dom.setAttribute('readOnly', false);
14163             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14164             this.inputEl().removeClass('x-combo-noedit');
14165         }
14166     },
14167
14168     // private
14169     
14170     onBeforeLoad : function(combo,opts){
14171         if(!this.hasFocus){
14172             return;
14173         }
14174          if (!opts.add) {
14175             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14176          }
14177         this.restrictHeight();
14178         this.selectedIndex = -1;
14179     },
14180
14181     // private
14182     onLoad : function(){
14183         
14184         this.hasQuery = false;
14185         
14186         if(!this.hasFocus){
14187             return;
14188         }
14189         
14190         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14191             this.loading.hide();
14192         }
14193         
14194         if(this.store.getCount() > 0){
14195             
14196             this.expand();
14197             this.restrictHeight();
14198             if(this.lastQuery == this.allQuery){
14199                 if(this.editable && !this.tickable){
14200                     this.inputEl().dom.select();
14201                 }
14202                 
14203                 if(
14204                     !this.selectByValue(this.value, true) &&
14205                     this.autoFocus && 
14206                     (
14207                         !this.store.lastOptions ||
14208                         typeof(this.store.lastOptions.add) == 'undefined' || 
14209                         this.store.lastOptions.add != true
14210                     )
14211                 ){
14212                     this.select(0, true);
14213                 }
14214             }else{
14215                 if(this.autoFocus){
14216                     this.selectNext();
14217                 }
14218                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14219                     this.taTask.delay(this.typeAheadDelay);
14220                 }
14221             }
14222         }else{
14223             this.onEmptyResults();
14224         }
14225         
14226         //this.el.focus();
14227     },
14228     // private
14229     onLoadException : function()
14230     {
14231         this.hasQuery = false;
14232         
14233         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14234             this.loading.hide();
14235         }
14236         
14237         if(this.tickable && this.editable){
14238             return;
14239         }
14240         
14241         this.collapse();
14242         // only causes errors at present
14243         //Roo.log(this.store.reader.jsonData);
14244         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14245             // fixme
14246             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14247         //}
14248         
14249         
14250     },
14251     // private
14252     onTypeAhead : function(){
14253         if(this.store.getCount() > 0){
14254             var r = this.store.getAt(0);
14255             var newValue = r.data[this.displayField];
14256             var len = newValue.length;
14257             var selStart = this.getRawValue().length;
14258             
14259             if(selStart != len){
14260                 this.setRawValue(newValue);
14261                 this.selectText(selStart, newValue.length);
14262             }
14263         }
14264     },
14265
14266     // private
14267     onSelect : function(record, index){
14268         
14269         if(this.fireEvent('beforeselect', this, record, index) !== false){
14270         
14271             this.setFromData(index > -1 ? record.data : false);
14272             
14273             this.collapse();
14274             this.fireEvent('select', this, record, index);
14275         }
14276     },
14277
14278     /**
14279      * Returns the currently selected field value or empty string if no value is set.
14280      * @return {String} value The selected value
14281      */
14282     getValue : function()
14283     {
14284         if(Roo.isIOS && this.useNativeIOS){
14285             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14286         }
14287         
14288         if(this.multiple){
14289             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14290         }
14291         
14292         if(this.valueField){
14293             return typeof this.value != 'undefined' ? this.value : '';
14294         }else{
14295             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14296         }
14297     },
14298     
14299     getRawValue : function()
14300     {
14301         if(Roo.isIOS && this.useNativeIOS){
14302             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14303         }
14304         
14305         var v = this.inputEl().getValue();
14306         
14307         return v;
14308     },
14309
14310     /**
14311      * Clears any text/value currently set in the field
14312      */
14313     clearValue : function(){
14314         
14315         if(this.hiddenField){
14316             this.hiddenField.dom.value = '';
14317         }
14318         this.value = '';
14319         this.setRawValue('');
14320         this.lastSelectionText = '';
14321         this.lastData = false;
14322         
14323         var close = this.closeTriggerEl();
14324         
14325         if(close){
14326             close.hide();
14327         }
14328         
14329         this.validate();
14330         
14331     },
14332
14333     /**
14334      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14335      * will be displayed in the field.  If the value does not match the data value of an existing item,
14336      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14337      * Otherwise the field will be blank (although the value will still be set).
14338      * @param {String} value The value to match
14339      */
14340     setValue : function(v)
14341     {
14342         if(Roo.isIOS && this.useNativeIOS){
14343             this.setIOSValue(v);
14344             return;
14345         }
14346         
14347         if(this.multiple){
14348             this.syncValue();
14349             return;
14350         }
14351         
14352         var text = v;
14353         if(this.valueField){
14354             var r = this.findRecord(this.valueField, v);
14355             if(r){
14356                 text = r.data[this.displayField];
14357             }else if(this.valueNotFoundText !== undefined){
14358                 text = this.valueNotFoundText;
14359             }
14360         }
14361         this.lastSelectionText = text;
14362         if(this.hiddenField){
14363             this.hiddenField.dom.value = v;
14364         }
14365         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14366         this.value = v;
14367         
14368         var close = this.closeTriggerEl();
14369         
14370         if(close){
14371             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14372         }
14373         
14374         this.validate();
14375     },
14376     /**
14377      * @property {Object} the last set data for the element
14378      */
14379     
14380     lastData : false,
14381     /**
14382      * Sets the value of the field based on a object which is related to the record format for the store.
14383      * @param {Object} value the value to set as. or false on reset?
14384      */
14385     setFromData : function(o){
14386         
14387         if(this.multiple){
14388             this.addItem(o);
14389             return;
14390         }
14391             
14392         var dv = ''; // display value
14393         var vv = ''; // value value..
14394         this.lastData = o;
14395         if (this.displayField) {
14396             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14397         } else {
14398             // this is an error condition!!!
14399             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14400         }
14401         
14402         if(this.valueField){
14403             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14404         }
14405         
14406         var close = this.closeTriggerEl();
14407         
14408         if(close){
14409             if(dv.length || vv * 1 > 0){
14410                 close.show() ;
14411                 this.blockFocus=true;
14412             } else {
14413                 close.hide();
14414             }             
14415         }
14416         
14417         if(this.hiddenField){
14418             this.hiddenField.dom.value = vv;
14419             
14420             this.lastSelectionText = dv;
14421             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14422             this.value = vv;
14423             return;
14424         }
14425         // no hidden field.. - we store the value in 'value', but still display
14426         // display field!!!!
14427         this.lastSelectionText = dv;
14428         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14429         this.value = vv;
14430         
14431         
14432         
14433     },
14434     // private
14435     reset : function(){
14436         // overridden so that last data is reset..
14437         
14438         if(this.multiple){
14439             this.clearItem();
14440             return;
14441         }
14442         
14443         this.setValue(this.originalValue);
14444         //this.clearInvalid();
14445         this.lastData = false;
14446         if (this.view) {
14447             this.view.clearSelections();
14448         }
14449         
14450         this.validate();
14451     },
14452     // private
14453     findRecord : function(prop, value){
14454         var record;
14455         if(this.store.getCount() > 0){
14456             this.store.each(function(r){
14457                 if(r.data[prop] == value){
14458                     record = r;
14459                     return false;
14460                 }
14461                 return true;
14462             });
14463         }
14464         return record;
14465     },
14466     
14467     getName: function()
14468     {
14469         // returns hidden if it's set..
14470         if (!this.rendered) {return ''};
14471         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14472         
14473     },
14474     // private
14475     onViewMove : function(e, t){
14476         this.inKeyMode = false;
14477     },
14478
14479     // private
14480     onViewOver : function(e, t){
14481         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14482             return;
14483         }
14484         var item = this.view.findItemFromChild(t);
14485         
14486         if(item){
14487             var index = this.view.indexOf(item);
14488             this.select(index, false);
14489         }
14490     },
14491
14492     // private
14493     onViewClick : function(view, doFocus, el, e)
14494     {
14495         var index = this.view.getSelectedIndexes()[0];
14496         
14497         var r = this.store.getAt(index);
14498         
14499         if(this.tickable){
14500             
14501             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14502                 return;
14503             }
14504             
14505             var rm = false;
14506             var _this = this;
14507             
14508             Roo.each(this.tickItems, function(v,k){
14509                 
14510                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14511                     Roo.log(v);
14512                     _this.tickItems.splice(k, 1);
14513                     
14514                     if(typeof(e) == 'undefined' && view == false){
14515                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14516                     }
14517                     
14518                     rm = true;
14519                     return;
14520                 }
14521             });
14522             
14523             if(rm){
14524                 return;
14525             }
14526             
14527             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14528                 this.tickItems.push(r.data);
14529             }
14530             
14531             if(typeof(e) == 'undefined' && view == false){
14532                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14533             }
14534                     
14535             return;
14536         }
14537         
14538         if(r){
14539             this.onSelect(r, index);
14540         }
14541         if(doFocus !== false && !this.blockFocus){
14542             this.inputEl().focus();
14543         }
14544     },
14545
14546     // private
14547     restrictHeight : function(){
14548         //this.innerList.dom.style.height = '';
14549         //var inner = this.innerList.dom;
14550         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14551         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14552         //this.list.beginUpdate();
14553         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14554         this.list.alignTo(this.inputEl(), this.listAlign);
14555         this.list.alignTo(this.inputEl(), this.listAlign);
14556         //this.list.endUpdate();
14557     },
14558
14559     // private
14560     onEmptyResults : function(){
14561         
14562         if(this.tickable && this.editable){
14563             this.hasFocus = false;
14564             this.restrictHeight();
14565             return;
14566         }
14567         
14568         this.collapse();
14569     },
14570
14571     /**
14572      * Returns true if the dropdown list is expanded, else false.
14573      */
14574     isExpanded : function(){
14575         return this.list.isVisible();
14576     },
14577
14578     /**
14579      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14580      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14581      * @param {String} value The data value of the item to select
14582      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14583      * selected item if it is not currently in view (defaults to true)
14584      * @return {Boolean} True if the value matched an item in the list, else false
14585      */
14586     selectByValue : function(v, scrollIntoView){
14587         if(v !== undefined && v !== null){
14588             var r = this.findRecord(this.valueField || this.displayField, v);
14589             if(r){
14590                 this.select(this.store.indexOf(r), scrollIntoView);
14591                 return true;
14592             }
14593         }
14594         return false;
14595     },
14596
14597     /**
14598      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14599      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14600      * @param {Number} index The zero-based index of the list item to select
14601      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14602      * selected item if it is not currently in view (defaults to true)
14603      */
14604     select : function(index, scrollIntoView){
14605         this.selectedIndex = index;
14606         this.view.select(index);
14607         if(scrollIntoView !== false){
14608             var el = this.view.getNode(index);
14609             /*
14610              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14611              */
14612             if(el){
14613                 this.list.scrollChildIntoView(el, false);
14614             }
14615         }
14616     },
14617
14618     // private
14619     selectNext : function(){
14620         var ct = this.store.getCount();
14621         if(ct > 0){
14622             if(this.selectedIndex == -1){
14623                 this.select(0);
14624             }else if(this.selectedIndex < ct-1){
14625                 this.select(this.selectedIndex+1);
14626             }
14627         }
14628     },
14629
14630     // private
14631     selectPrev : function(){
14632         var ct = this.store.getCount();
14633         if(ct > 0){
14634             if(this.selectedIndex == -1){
14635                 this.select(0);
14636             }else if(this.selectedIndex != 0){
14637                 this.select(this.selectedIndex-1);
14638             }
14639         }
14640     },
14641
14642     // private
14643     onKeyUp : function(e){
14644         if(this.editable !== false && !e.isSpecialKey()){
14645             this.lastKey = e.getKey();
14646             this.dqTask.delay(this.queryDelay);
14647         }
14648     },
14649
14650     // private
14651     validateBlur : function(){
14652         return !this.list || !this.list.isVisible();   
14653     },
14654
14655     // private
14656     initQuery : function(){
14657         
14658         var v = this.getRawValue();
14659         
14660         if(this.tickable && this.editable){
14661             v = this.tickableInputEl().getValue();
14662         }
14663         
14664         this.doQuery(v);
14665     },
14666
14667     // private
14668     doForce : function(){
14669         if(this.inputEl().dom.value.length > 0){
14670             this.inputEl().dom.value =
14671                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14672              
14673         }
14674     },
14675
14676     /**
14677      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14678      * query allowing the query action to be canceled if needed.
14679      * @param {String} query The SQL query to execute
14680      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14681      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14682      * saved in the current store (defaults to false)
14683      */
14684     doQuery : function(q, forceAll){
14685         
14686         if(q === undefined || q === null){
14687             q = '';
14688         }
14689         var qe = {
14690             query: q,
14691             forceAll: forceAll,
14692             combo: this,
14693             cancel:false
14694         };
14695         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14696             return false;
14697         }
14698         q = qe.query;
14699         
14700         forceAll = qe.forceAll;
14701         if(forceAll === true || (q.length >= this.minChars)){
14702             
14703             this.hasQuery = true;
14704             
14705             if(this.lastQuery != q || this.alwaysQuery){
14706                 this.lastQuery = q;
14707                 if(this.mode == 'local'){
14708                     this.selectedIndex = -1;
14709                     if(forceAll){
14710                         this.store.clearFilter();
14711                     }else{
14712                         
14713                         if(this.specialFilter){
14714                             this.fireEvent('specialfilter', this);
14715                             this.onLoad();
14716                             return;
14717                         }
14718                         
14719                         this.store.filter(this.displayField, q);
14720                     }
14721                     
14722                     this.store.fireEvent("datachanged", this.store);
14723                     
14724                     this.onLoad();
14725                     
14726                     
14727                 }else{
14728                     
14729                     this.store.baseParams[this.queryParam] = q;
14730                     
14731                     var options = {params : this.getParams(q)};
14732                     
14733                     if(this.loadNext){
14734                         options.add = true;
14735                         options.params.start = this.page * this.pageSize;
14736                     }
14737                     
14738                     this.store.load(options);
14739                     
14740                     /*
14741                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14742                      *  we should expand the list on onLoad
14743                      *  so command out it
14744                      */
14745 //                    this.expand();
14746                 }
14747             }else{
14748                 this.selectedIndex = -1;
14749                 this.onLoad();   
14750             }
14751         }
14752         
14753         this.loadNext = false;
14754     },
14755     
14756     // private
14757     getParams : function(q){
14758         var p = {};
14759         //p[this.queryParam] = q;
14760         
14761         if(this.pageSize){
14762             p.start = 0;
14763             p.limit = this.pageSize;
14764         }
14765         return p;
14766     },
14767
14768     /**
14769      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14770      */
14771     collapse : function(){
14772         if(!this.isExpanded()){
14773             return;
14774         }
14775         
14776         this.list.hide();
14777         
14778         this.hasFocus = false;
14779         
14780         if(this.tickable){
14781             this.okBtn.hide();
14782             this.cancelBtn.hide();
14783             this.trigger.show();
14784             
14785             if(this.editable){
14786                 this.tickableInputEl().dom.value = '';
14787                 this.tickableInputEl().blur();
14788             }
14789             
14790         }
14791         
14792         Roo.get(document).un('mousedown', this.collapseIf, this);
14793         Roo.get(document).un('mousewheel', this.collapseIf, this);
14794         if (!this.editable) {
14795             Roo.get(document).un('keydown', this.listKeyPress, this);
14796         }
14797         this.fireEvent('collapse', this);
14798         
14799         this.validate();
14800     },
14801
14802     // private
14803     collapseIf : function(e){
14804         var in_combo  = e.within(this.el);
14805         var in_list =  e.within(this.list);
14806         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14807         
14808         if (in_combo || in_list || is_list) {
14809             //e.stopPropagation();
14810             return;
14811         }
14812         
14813         if(this.tickable){
14814             this.onTickableFooterButtonClick(e, false, false);
14815         }
14816
14817         this.collapse();
14818         
14819     },
14820
14821     /**
14822      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14823      */
14824     expand : function(){
14825        
14826         if(this.isExpanded() || !this.hasFocus){
14827             return;
14828         }
14829         
14830         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14831         this.list.setWidth(lw);
14832         
14833         Roo.log('expand');
14834         
14835         this.list.show();
14836         
14837         this.restrictHeight();
14838         
14839         if(this.tickable){
14840             
14841             this.tickItems = Roo.apply([], this.item);
14842             
14843             this.okBtn.show();
14844             this.cancelBtn.show();
14845             this.trigger.hide();
14846             
14847             if(this.editable){
14848                 this.tickableInputEl().focus();
14849             }
14850             
14851         }
14852         
14853         Roo.get(document).on('mousedown', this.collapseIf, this);
14854         Roo.get(document).on('mousewheel', this.collapseIf, this);
14855         if (!this.editable) {
14856             Roo.get(document).on('keydown', this.listKeyPress, this);
14857         }
14858         
14859         this.fireEvent('expand', this);
14860     },
14861
14862     // private
14863     // Implements the default empty TriggerField.onTriggerClick function
14864     onTriggerClick : function(e)
14865     {
14866         Roo.log('trigger click');
14867         
14868         if(this.disabled || !this.triggerList){
14869             return;
14870         }
14871         
14872         this.page = 0;
14873         this.loadNext = false;
14874         
14875         if(this.isExpanded()){
14876             this.collapse();
14877             if (!this.blockFocus) {
14878                 this.inputEl().focus();
14879             }
14880             
14881         }else {
14882             this.hasFocus = true;
14883             if(this.triggerAction == 'all') {
14884                 this.doQuery(this.allQuery, true);
14885             } else {
14886                 this.doQuery(this.getRawValue());
14887             }
14888             if (!this.blockFocus) {
14889                 this.inputEl().focus();
14890             }
14891         }
14892     },
14893     
14894     onTickableTriggerClick : function(e)
14895     {
14896         if(this.disabled){
14897             return;
14898         }
14899         
14900         this.page = 0;
14901         this.loadNext = false;
14902         this.hasFocus = true;
14903         
14904         if(this.triggerAction == 'all') {
14905             this.doQuery(this.allQuery, true);
14906         } else {
14907             this.doQuery(this.getRawValue());
14908         }
14909     },
14910     
14911     onSearchFieldClick : function(e)
14912     {
14913         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14914             this.onTickableFooterButtonClick(e, false, false);
14915             return;
14916         }
14917         
14918         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14919             return;
14920         }
14921         
14922         this.page = 0;
14923         this.loadNext = false;
14924         this.hasFocus = true;
14925         
14926         if(this.triggerAction == 'all') {
14927             this.doQuery(this.allQuery, true);
14928         } else {
14929             this.doQuery(this.getRawValue());
14930         }
14931     },
14932     
14933     listKeyPress : function(e)
14934     {
14935         //Roo.log('listkeypress');
14936         // scroll to first matching element based on key pres..
14937         if (e.isSpecialKey()) {
14938             return false;
14939         }
14940         var k = String.fromCharCode(e.getKey()).toUpperCase();
14941         //Roo.log(k);
14942         var match  = false;
14943         var csel = this.view.getSelectedNodes();
14944         var cselitem = false;
14945         if (csel.length) {
14946             var ix = this.view.indexOf(csel[0]);
14947             cselitem  = this.store.getAt(ix);
14948             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14949                 cselitem = false;
14950             }
14951             
14952         }
14953         
14954         this.store.each(function(v) { 
14955             if (cselitem) {
14956                 // start at existing selection.
14957                 if (cselitem.id == v.id) {
14958                     cselitem = false;
14959                 }
14960                 return true;
14961             }
14962                 
14963             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14964                 match = this.store.indexOf(v);
14965                 return false;
14966             }
14967             return true;
14968         }, this);
14969         
14970         if (match === false) {
14971             return true; // no more action?
14972         }
14973         // scroll to?
14974         this.view.select(match);
14975         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14976         sn.scrollIntoView(sn.dom.parentNode, false);
14977     },
14978     
14979     onViewScroll : function(e, t){
14980         
14981         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){
14982             return;
14983         }
14984         
14985         this.hasQuery = true;
14986         
14987         this.loading = this.list.select('.loading', true).first();
14988         
14989         if(this.loading === null){
14990             this.list.createChild({
14991                 tag: 'div',
14992                 cls: 'loading roo-select2-more-results roo-select2-active',
14993                 html: 'Loading more results...'
14994             });
14995             
14996             this.loading = this.list.select('.loading', true).first();
14997             
14998             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14999             
15000             this.loading.hide();
15001         }
15002         
15003         this.loading.show();
15004         
15005         var _combo = this;
15006         
15007         this.page++;
15008         this.loadNext = true;
15009         
15010         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15011         
15012         return;
15013     },
15014     
15015     addItem : function(o)
15016     {   
15017         var dv = ''; // display value
15018         
15019         if (this.displayField) {
15020             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15021         } else {
15022             // this is an error condition!!!
15023             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15024         }
15025         
15026         if(!dv.length){
15027             return;
15028         }
15029         
15030         var choice = this.choices.createChild({
15031             tag: 'li',
15032             cls: 'roo-select2-search-choice',
15033             cn: [
15034                 {
15035                     tag: 'div',
15036                     html: dv
15037                 },
15038                 {
15039                     tag: 'a',
15040                     href: '#',
15041                     cls: 'roo-select2-search-choice-close fa fa-times',
15042                     tabindex: '-1'
15043                 }
15044             ]
15045             
15046         }, this.searchField);
15047         
15048         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15049         
15050         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15051         
15052         this.item.push(o);
15053         
15054         this.lastData = o;
15055         
15056         this.syncValue();
15057         
15058         this.inputEl().dom.value = '';
15059         
15060         this.validate();
15061     },
15062     
15063     onRemoveItem : function(e, _self, o)
15064     {
15065         e.preventDefault();
15066         
15067         this.lastItem = Roo.apply([], this.item);
15068         
15069         var index = this.item.indexOf(o.data) * 1;
15070         
15071         if( index < 0){
15072             Roo.log('not this item?!');
15073             return;
15074         }
15075         
15076         this.item.splice(index, 1);
15077         o.item.remove();
15078         
15079         this.syncValue();
15080         
15081         this.fireEvent('remove', this, e);
15082         
15083         this.validate();
15084         
15085     },
15086     
15087     syncValue : function()
15088     {
15089         if(!this.item.length){
15090             this.clearValue();
15091             return;
15092         }
15093             
15094         var value = [];
15095         var _this = this;
15096         Roo.each(this.item, function(i){
15097             if(_this.valueField){
15098                 value.push(i[_this.valueField]);
15099                 return;
15100             }
15101
15102             value.push(i);
15103         });
15104
15105         this.value = value.join(',');
15106
15107         if(this.hiddenField){
15108             this.hiddenField.dom.value = this.value;
15109         }
15110         
15111         this.store.fireEvent("datachanged", this.store);
15112         
15113         this.validate();
15114     },
15115     
15116     clearItem : function()
15117     {
15118         if(!this.multiple){
15119             return;
15120         }
15121         
15122         this.item = [];
15123         
15124         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15125            c.remove();
15126         });
15127         
15128         this.syncValue();
15129         
15130         this.validate();
15131         
15132         if(this.tickable && !Roo.isTouch){
15133             this.view.refresh();
15134         }
15135     },
15136     
15137     inputEl: function ()
15138     {
15139         if(Roo.isIOS && this.useNativeIOS){
15140             return this.el.select('select.roo-ios-select', true).first();
15141         }
15142         
15143         if(Roo.isTouch && this.mobileTouchView){
15144             return this.el.select('input.form-control',true).first();
15145         }
15146         
15147         if(this.tickable){
15148             return this.searchField;
15149         }
15150         
15151         return this.el.select('input.form-control',true).first();
15152     },
15153     
15154     onTickableFooterButtonClick : function(e, btn, el)
15155     {
15156         e.preventDefault();
15157         
15158         this.lastItem = Roo.apply([], this.item);
15159         
15160         if(btn && btn.name == 'cancel'){
15161             this.tickItems = Roo.apply([], this.item);
15162             this.collapse();
15163             return;
15164         }
15165         
15166         this.clearItem();
15167         
15168         var _this = this;
15169         
15170         Roo.each(this.tickItems, function(o){
15171             _this.addItem(o);
15172         });
15173         
15174         this.collapse();
15175         
15176     },
15177     
15178     validate : function()
15179     {
15180         if(this.getVisibilityEl().hasClass('hidden')){
15181             return true;
15182         }
15183         
15184         var v = this.getRawValue();
15185         
15186         if(this.multiple){
15187             v = this.getValue();
15188         }
15189         
15190         if(this.disabled || this.allowBlank || v.length){
15191             this.markValid();
15192             return true;
15193         }
15194         
15195         this.markInvalid();
15196         return false;
15197     },
15198     
15199     tickableInputEl : function()
15200     {
15201         if(!this.tickable || !this.editable){
15202             return this.inputEl();
15203         }
15204         
15205         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15206     },
15207     
15208     
15209     getAutoCreateTouchView : function()
15210     {
15211         var id = Roo.id();
15212         
15213         var cfg = {
15214             cls: 'form-group' //input-group
15215         };
15216         
15217         var input =  {
15218             tag: 'input',
15219             id : id,
15220             type : this.inputType,
15221             cls : 'form-control x-combo-noedit',
15222             autocomplete: 'new-password',
15223             placeholder : this.placeholder || '',
15224             readonly : true
15225         };
15226         
15227         if (this.name) {
15228             input.name = this.name;
15229         }
15230         
15231         if (this.size) {
15232             input.cls += ' input-' + this.size;
15233         }
15234         
15235         if (this.disabled) {
15236             input.disabled = true;
15237         }
15238         
15239         var inputblock = {
15240             cls : '',
15241             cn : [
15242                 input
15243             ]
15244         };
15245         
15246         if(this.before){
15247             inputblock.cls += ' input-group';
15248             
15249             inputblock.cn.unshift({
15250                 tag :'span',
15251                 cls : 'input-group-addon input-group-prepend input-group-text',
15252                 html : this.before
15253             });
15254         }
15255         
15256         if(this.removable && !this.multiple){
15257             inputblock.cls += ' roo-removable';
15258             
15259             inputblock.cn.push({
15260                 tag: 'button',
15261                 html : 'x',
15262                 cls : 'roo-combo-removable-btn close'
15263             });
15264         }
15265
15266         if(this.hasFeedback && !this.allowBlank){
15267             
15268             inputblock.cls += ' has-feedback';
15269             
15270             inputblock.cn.push({
15271                 tag: 'span',
15272                 cls: 'glyphicon form-control-feedback'
15273             });
15274             
15275         }
15276         
15277         if (this.after) {
15278             
15279             inputblock.cls += (this.before) ? '' : ' input-group';
15280             
15281             inputblock.cn.push({
15282                 tag :'span',
15283                 cls : 'input-group-addon input-group-append input-group-text',
15284                 html : this.after
15285             });
15286         }
15287
15288         
15289         var ibwrap = inputblock;
15290         
15291         if(this.multiple){
15292             ibwrap = {
15293                 tag: 'ul',
15294                 cls: 'roo-select2-choices',
15295                 cn:[
15296                     {
15297                         tag: 'li',
15298                         cls: 'roo-select2-search-field',
15299                         cn: [
15300
15301                             inputblock
15302                         ]
15303                     }
15304                 ]
15305             };
15306         
15307             
15308         }
15309         
15310         var combobox = {
15311             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15312             cn: [
15313                 {
15314                     tag: 'input',
15315                     type : 'hidden',
15316                     cls: 'form-hidden-field'
15317                 },
15318                 ibwrap
15319             ]
15320         };
15321         
15322         if(!this.multiple && this.showToggleBtn){
15323             
15324             var caret = {
15325                         tag: 'span',
15326                         cls: 'caret'
15327             };
15328             
15329             if (this.caret != false) {
15330                 caret = {
15331                      tag: 'i',
15332                      cls: 'fa fa-' + this.caret
15333                 };
15334                 
15335             }
15336             
15337             combobox.cn.push({
15338                 tag :'span',
15339                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15340                 cn : [
15341                     caret,
15342                     {
15343                         tag: 'span',
15344                         cls: 'combobox-clear',
15345                         cn  : [
15346                             {
15347                                 tag : 'i',
15348                                 cls: 'icon-remove'
15349                             }
15350                         ]
15351                     }
15352                 ]
15353
15354             })
15355         }
15356         
15357         if(this.multiple){
15358             combobox.cls += ' roo-select2-container-multi';
15359         }
15360         
15361         var align = this.labelAlign || this.parentLabelAlign();
15362         
15363         if (align ==='left' && this.fieldLabel.length) {
15364
15365             cfg.cn = [
15366                 {
15367                    tag : 'i',
15368                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15369                    tooltip : 'This field is required'
15370                 },
15371                 {
15372                     tag: 'label',
15373                     cls : 'control-label col-form-label',
15374                     html : this.fieldLabel
15375
15376                 },
15377                 {
15378                     cls : '', 
15379                     cn: [
15380                         combobox
15381                     ]
15382                 }
15383             ];
15384             
15385             var labelCfg = cfg.cn[1];
15386             var contentCfg = cfg.cn[2];
15387             
15388
15389             if(this.indicatorpos == 'right'){
15390                 cfg.cn = [
15391                     {
15392                         tag: 'label',
15393                         'for' :  id,
15394                         cls : 'control-label col-form-label',
15395                         cn : [
15396                             {
15397                                 tag : 'span',
15398                                 html : this.fieldLabel
15399                             },
15400                             {
15401                                 tag : 'i',
15402                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15403                                 tooltip : 'This field is required'
15404                             }
15405                         ]
15406                     },
15407                     {
15408                         cls : "",
15409                         cn: [
15410                             combobox
15411                         ]
15412                     }
15413
15414                 ];
15415                 
15416                 labelCfg = cfg.cn[0];
15417                 contentCfg = cfg.cn[1];
15418             }
15419             
15420            
15421             
15422             if(this.labelWidth > 12){
15423                 labelCfg.style = "width: " + this.labelWidth + 'px';
15424             }
15425             
15426             if(this.labelWidth < 13 && this.labelmd == 0){
15427                 this.labelmd = this.labelWidth;
15428             }
15429             
15430             if(this.labellg > 0){
15431                 labelCfg.cls += ' col-lg-' + this.labellg;
15432                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15433             }
15434             
15435             if(this.labelmd > 0){
15436                 labelCfg.cls += ' col-md-' + this.labelmd;
15437                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15438             }
15439             
15440             if(this.labelsm > 0){
15441                 labelCfg.cls += ' col-sm-' + this.labelsm;
15442                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15443             }
15444             
15445             if(this.labelxs > 0){
15446                 labelCfg.cls += ' col-xs-' + this.labelxs;
15447                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15448             }
15449                 
15450                 
15451         } else if ( this.fieldLabel.length) {
15452             cfg.cn = [
15453                 {
15454                    tag : 'i',
15455                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15456                    tooltip : 'This field is required'
15457                 },
15458                 {
15459                     tag: 'label',
15460                     cls : 'control-label',
15461                     html : this.fieldLabel
15462
15463                 },
15464                 {
15465                     cls : '', 
15466                     cn: [
15467                         combobox
15468                     ]
15469                 }
15470             ];
15471             
15472             if(this.indicatorpos == 'right'){
15473                 cfg.cn = [
15474                     {
15475                         tag: 'label',
15476                         cls : 'control-label',
15477                         html : this.fieldLabel,
15478                         cn : [
15479                             {
15480                                tag : 'i',
15481                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15482                                tooltip : 'This field is required'
15483                             }
15484                         ]
15485                     },
15486                     {
15487                         cls : '', 
15488                         cn: [
15489                             combobox
15490                         ]
15491                     }
15492                 ];
15493             }
15494         } else {
15495             cfg.cn = combobox;    
15496         }
15497         
15498         
15499         var settings = this;
15500         
15501         ['xs','sm','md','lg'].map(function(size){
15502             if (settings[size]) {
15503                 cfg.cls += ' col-' + size + '-' + settings[size];
15504             }
15505         });
15506         
15507         return cfg;
15508     },
15509     
15510     initTouchView : function()
15511     {
15512         this.renderTouchView();
15513         
15514         this.touchViewEl.on('scroll', function(){
15515             this.el.dom.scrollTop = 0;
15516         }, this);
15517         
15518         this.originalValue = this.getValue();
15519         
15520         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15521         
15522         this.inputEl().on("click", this.showTouchView, this);
15523         if (this.triggerEl) {
15524             this.triggerEl.on("click", this.showTouchView, this);
15525         }
15526         
15527         
15528         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15529         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15530         
15531         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15532         
15533         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15534         this.store.on('load', this.onTouchViewLoad, this);
15535         this.store.on('loadexception', this.onTouchViewLoadException, this);
15536         
15537         if(this.hiddenName){
15538             
15539             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15540             
15541             this.hiddenField.dom.value =
15542                 this.hiddenValue !== undefined ? this.hiddenValue :
15543                 this.value !== undefined ? this.value : '';
15544         
15545             this.el.dom.removeAttribute('name');
15546             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15547         }
15548         
15549         if(this.multiple){
15550             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15551             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15552         }
15553         
15554         if(this.removable && !this.multiple){
15555             var close = this.closeTriggerEl();
15556             if(close){
15557                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15558                 close.on('click', this.removeBtnClick, this, close);
15559             }
15560         }
15561         /*
15562          * fix the bug in Safari iOS8
15563          */
15564         this.inputEl().on("focus", function(e){
15565             document.activeElement.blur();
15566         }, this);
15567         
15568         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15569         
15570         return;
15571         
15572         
15573     },
15574     
15575     renderTouchView : function()
15576     {
15577         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15578         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15579         
15580         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15581         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15582         
15583         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15584         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15585         this.touchViewBodyEl.setStyle('overflow', 'auto');
15586         
15587         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15588         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15589         
15590         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15591         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15592         
15593     },
15594     
15595     showTouchView : function()
15596     {
15597         if(this.disabled){
15598             return;
15599         }
15600         
15601         this.touchViewHeaderEl.hide();
15602
15603         if(this.modalTitle.length){
15604             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15605             this.touchViewHeaderEl.show();
15606         }
15607
15608         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15609         this.touchViewEl.show();
15610
15611         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15612         
15613         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15614         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15615
15616         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15617
15618         if(this.modalTitle.length){
15619             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15620         }
15621         
15622         this.touchViewBodyEl.setHeight(bodyHeight);
15623
15624         if(this.animate){
15625             var _this = this;
15626             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15627         }else{
15628             this.touchViewEl.addClass('in');
15629         }
15630         
15631         if(this._touchViewMask){
15632             Roo.get(document.body).addClass("x-body-masked");
15633             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15634             this._touchViewMask.setStyle('z-index', 10000);
15635             this._touchViewMask.addClass('show');
15636         }
15637         
15638         this.doTouchViewQuery();
15639         
15640     },
15641     
15642     hideTouchView : function()
15643     {
15644         this.touchViewEl.removeClass('in');
15645
15646         if(this.animate){
15647             var _this = this;
15648             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15649         }else{
15650             this.touchViewEl.setStyle('display', 'none');
15651         }
15652         
15653         if(this._touchViewMask){
15654             this._touchViewMask.removeClass('show');
15655             Roo.get(document.body).removeClass("x-body-masked");
15656         }
15657     },
15658     
15659     setTouchViewValue : function()
15660     {
15661         if(this.multiple){
15662             this.clearItem();
15663         
15664             var _this = this;
15665
15666             Roo.each(this.tickItems, function(o){
15667                 this.addItem(o);
15668             }, this);
15669         }
15670         
15671         this.hideTouchView();
15672     },
15673     
15674     doTouchViewQuery : function()
15675     {
15676         var qe = {
15677             query: '',
15678             forceAll: true,
15679             combo: this,
15680             cancel:false
15681         };
15682         
15683         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15684             return false;
15685         }
15686         
15687         if(!this.alwaysQuery || this.mode == 'local'){
15688             this.onTouchViewLoad();
15689             return;
15690         }
15691         
15692         this.store.load();
15693     },
15694     
15695     onTouchViewBeforeLoad : function(combo,opts)
15696     {
15697         return;
15698     },
15699
15700     // private
15701     onTouchViewLoad : function()
15702     {
15703         if(this.store.getCount() < 1){
15704             this.onTouchViewEmptyResults();
15705             return;
15706         }
15707         
15708         this.clearTouchView();
15709         
15710         var rawValue = this.getRawValue();
15711         
15712         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15713         
15714         this.tickItems = [];
15715         
15716         this.store.data.each(function(d, rowIndex){
15717             var row = this.touchViewListGroup.createChild(template);
15718             
15719             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15720                 row.addClass(d.data.cls);
15721             }
15722             
15723             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15724                 var cfg = {
15725                     data : d.data,
15726                     html : d.data[this.displayField]
15727                 };
15728                 
15729                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15730                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15731                 }
15732             }
15733             row.removeClass('selected');
15734             if(!this.multiple && this.valueField &&
15735                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15736             {
15737                 // radio buttons..
15738                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15739                 row.addClass('selected');
15740             }
15741             
15742             if(this.multiple && this.valueField &&
15743                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15744             {
15745                 
15746                 // checkboxes...
15747                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15748                 this.tickItems.push(d.data);
15749             }
15750             
15751             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15752             
15753         }, this);
15754         
15755         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15756         
15757         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15758
15759         if(this.modalTitle.length){
15760             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15761         }
15762
15763         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15764         
15765         if(this.mobile_restrict_height && listHeight < bodyHeight){
15766             this.touchViewBodyEl.setHeight(listHeight);
15767         }
15768         
15769         var _this = this;
15770         
15771         if(firstChecked && listHeight > bodyHeight){
15772             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15773         }
15774         
15775     },
15776     
15777     onTouchViewLoadException : function()
15778     {
15779         this.hideTouchView();
15780     },
15781     
15782     onTouchViewEmptyResults : function()
15783     {
15784         this.clearTouchView();
15785         
15786         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15787         
15788         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15789         
15790     },
15791     
15792     clearTouchView : function()
15793     {
15794         this.touchViewListGroup.dom.innerHTML = '';
15795     },
15796     
15797     onTouchViewClick : function(e, el, o)
15798     {
15799         e.preventDefault();
15800         
15801         var row = o.row;
15802         var rowIndex = o.rowIndex;
15803         
15804         var r = this.store.getAt(rowIndex);
15805         
15806         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15807             
15808             if(!this.multiple){
15809                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15810                     c.dom.removeAttribute('checked');
15811                 }, this);
15812
15813                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15814
15815                 this.setFromData(r.data);
15816
15817                 var close = this.closeTriggerEl();
15818
15819                 if(close){
15820                     close.show();
15821                 }
15822
15823                 this.hideTouchView();
15824
15825                 this.fireEvent('select', this, r, rowIndex);
15826
15827                 return;
15828             }
15829
15830             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15831                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15832                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15833                 return;
15834             }
15835
15836             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15837             this.addItem(r.data);
15838             this.tickItems.push(r.data);
15839         }
15840     },
15841     
15842     getAutoCreateNativeIOS : function()
15843     {
15844         var cfg = {
15845             cls: 'form-group' //input-group,
15846         };
15847         
15848         var combobox =  {
15849             tag: 'select',
15850             cls : 'roo-ios-select'
15851         };
15852         
15853         if (this.name) {
15854             combobox.name = this.name;
15855         }
15856         
15857         if (this.disabled) {
15858             combobox.disabled = true;
15859         }
15860         
15861         var settings = this;
15862         
15863         ['xs','sm','md','lg'].map(function(size){
15864             if (settings[size]) {
15865                 cfg.cls += ' col-' + size + '-' + settings[size];
15866             }
15867         });
15868         
15869         cfg.cn = combobox;
15870         
15871         return cfg;
15872         
15873     },
15874     
15875     initIOSView : function()
15876     {
15877         this.store.on('load', this.onIOSViewLoad, this);
15878         
15879         return;
15880     },
15881     
15882     onIOSViewLoad : function()
15883     {
15884         if(this.store.getCount() < 1){
15885             return;
15886         }
15887         
15888         this.clearIOSView();
15889         
15890         if(this.allowBlank) {
15891             
15892             var default_text = '-- SELECT --';
15893             
15894             if(this.placeholder.length){
15895                 default_text = this.placeholder;
15896             }
15897             
15898             if(this.emptyTitle.length){
15899                 default_text += ' - ' + this.emptyTitle + ' -';
15900             }
15901             
15902             var opt = this.inputEl().createChild({
15903                 tag: 'option',
15904                 value : 0,
15905                 html : default_text
15906             });
15907             
15908             var o = {};
15909             o[this.valueField] = 0;
15910             o[this.displayField] = default_text;
15911             
15912             this.ios_options.push({
15913                 data : o,
15914                 el : opt
15915             });
15916             
15917         }
15918         
15919         this.store.data.each(function(d, rowIndex){
15920             
15921             var html = '';
15922             
15923             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15924                 html = d.data[this.displayField];
15925             }
15926             
15927             var value = '';
15928             
15929             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15930                 value = d.data[this.valueField];
15931             }
15932             
15933             var option = {
15934                 tag: 'option',
15935                 value : value,
15936                 html : html
15937             };
15938             
15939             if(this.value == d.data[this.valueField]){
15940                 option['selected'] = true;
15941             }
15942             
15943             var opt = this.inputEl().createChild(option);
15944             
15945             this.ios_options.push({
15946                 data : d.data,
15947                 el : opt
15948             });
15949             
15950         }, this);
15951         
15952         this.inputEl().on('change', function(){
15953            this.fireEvent('select', this);
15954         }, this);
15955         
15956     },
15957     
15958     clearIOSView: function()
15959     {
15960         this.inputEl().dom.innerHTML = '';
15961         
15962         this.ios_options = [];
15963     },
15964     
15965     setIOSValue: function(v)
15966     {
15967         this.value = v;
15968         
15969         if(!this.ios_options){
15970             return;
15971         }
15972         
15973         Roo.each(this.ios_options, function(opts){
15974            
15975            opts.el.dom.removeAttribute('selected');
15976            
15977            if(opts.data[this.valueField] != v){
15978                return;
15979            }
15980            
15981            opts.el.dom.setAttribute('selected', true);
15982            
15983         }, this);
15984     }
15985
15986     /** 
15987     * @cfg {Boolean} grow 
15988     * @hide 
15989     */
15990     /** 
15991     * @cfg {Number} growMin 
15992     * @hide 
15993     */
15994     /** 
15995     * @cfg {Number} growMax 
15996     * @hide 
15997     */
15998     /**
15999      * @hide
16000      * @method autoSize
16001      */
16002 });
16003
16004 Roo.apply(Roo.bootstrap.ComboBox,  {
16005     
16006     header : {
16007         tag: 'div',
16008         cls: 'modal-header',
16009         cn: [
16010             {
16011                 tag: 'h4',
16012                 cls: 'modal-title'
16013             }
16014         ]
16015     },
16016     
16017     body : {
16018         tag: 'div',
16019         cls: 'modal-body',
16020         cn: [
16021             {
16022                 tag: 'ul',
16023                 cls: 'list-group'
16024             }
16025         ]
16026     },
16027     
16028     listItemRadio : {
16029         tag: 'li',
16030         cls: 'list-group-item',
16031         cn: [
16032             {
16033                 tag: 'span',
16034                 cls: 'roo-combobox-list-group-item-value'
16035             },
16036             {
16037                 tag: 'div',
16038                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16039                 cn: [
16040                     {
16041                         tag: 'input',
16042                         type: 'radio'
16043                     },
16044                     {
16045                         tag: 'label'
16046                     }
16047                 ]
16048             }
16049         ]
16050     },
16051     
16052     listItemCheckbox : {
16053         tag: 'li',
16054         cls: 'list-group-item',
16055         cn: [
16056             {
16057                 tag: 'span',
16058                 cls: 'roo-combobox-list-group-item-value'
16059             },
16060             {
16061                 tag: 'div',
16062                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16063                 cn: [
16064                     {
16065                         tag: 'input',
16066                         type: 'checkbox'
16067                     },
16068                     {
16069                         tag: 'label'
16070                     }
16071                 ]
16072             }
16073         ]
16074     },
16075     
16076     emptyResult : {
16077         tag: 'div',
16078         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16079     },
16080     
16081     footer : {
16082         tag: 'div',
16083         cls: 'modal-footer',
16084         cn: [
16085             {
16086                 tag: 'div',
16087                 cls: 'row',
16088                 cn: [
16089                     {
16090                         tag: 'div',
16091                         cls: 'col-xs-6 text-left',
16092                         cn: {
16093                             tag: 'button',
16094                             cls: 'btn btn-danger roo-touch-view-cancel',
16095                             html: 'Cancel'
16096                         }
16097                     },
16098                     {
16099                         tag: 'div',
16100                         cls: 'col-xs-6 text-right',
16101                         cn: {
16102                             tag: 'button',
16103                             cls: 'btn btn-success roo-touch-view-ok',
16104                             html: 'OK'
16105                         }
16106                     }
16107                 ]
16108             }
16109         ]
16110         
16111     }
16112 });
16113
16114 Roo.apply(Roo.bootstrap.ComboBox,  {
16115     
16116     touchViewTemplate : {
16117         tag: 'div',
16118         cls: 'modal fade roo-combobox-touch-view',
16119         cn: [
16120             {
16121                 tag: 'div',
16122                 cls: 'modal-dialog',
16123                 style : 'position:fixed', // we have to fix position....
16124                 cn: [
16125                     {
16126                         tag: 'div',
16127                         cls: 'modal-content',
16128                         cn: [
16129                             Roo.bootstrap.ComboBox.header,
16130                             Roo.bootstrap.ComboBox.body,
16131                             Roo.bootstrap.ComboBox.footer
16132                         ]
16133                     }
16134                 ]
16135             }
16136         ]
16137     }
16138 });/*
16139  * Based on:
16140  * Ext JS Library 1.1.1
16141  * Copyright(c) 2006-2007, Ext JS, LLC.
16142  *
16143  * Originally Released Under LGPL - original licence link has changed is not relivant.
16144  *
16145  * Fork - LGPL
16146  * <script type="text/javascript">
16147  */
16148
16149 /**
16150  * @class Roo.View
16151  * @extends Roo.util.Observable
16152  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16153  * This class also supports single and multi selection modes. <br>
16154  * Create a data model bound view:
16155  <pre><code>
16156  var store = new Roo.data.Store(...);
16157
16158  var view = new Roo.View({
16159     el : "my-element",
16160     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16161  
16162     singleSelect: true,
16163     selectedClass: "ydataview-selected",
16164     store: store
16165  });
16166
16167  // listen for node click?
16168  view.on("click", function(vw, index, node, e){
16169  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16170  });
16171
16172  // load XML data
16173  dataModel.load("foobar.xml");
16174  </code></pre>
16175  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16176  * <br><br>
16177  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16178  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16179  * 
16180  * Note: old style constructor is still suported (container, template, config)
16181  * 
16182  * @constructor
16183  * Create a new View
16184  * @param {Object} config The config object
16185  * 
16186  */
16187 Roo.View = function(config, depreciated_tpl, depreciated_config){
16188     
16189     this.parent = false;
16190     
16191     if (typeof(depreciated_tpl) == 'undefined') {
16192         // new way.. - universal constructor.
16193         Roo.apply(this, config);
16194         this.el  = Roo.get(this.el);
16195     } else {
16196         // old format..
16197         this.el  = Roo.get(config);
16198         this.tpl = depreciated_tpl;
16199         Roo.apply(this, depreciated_config);
16200     }
16201     this.wrapEl  = this.el.wrap().wrap();
16202     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16203     
16204     
16205     if(typeof(this.tpl) == "string"){
16206         this.tpl = new Roo.Template(this.tpl);
16207     } else {
16208         // support xtype ctors..
16209         this.tpl = new Roo.factory(this.tpl, Roo);
16210     }
16211     
16212     
16213     this.tpl.compile();
16214     
16215     /** @private */
16216     this.addEvents({
16217         /**
16218          * @event beforeclick
16219          * Fires before a click is processed. Returns false to cancel the default action.
16220          * @param {Roo.View} this
16221          * @param {Number} index The index of the target node
16222          * @param {HTMLElement} node The target node
16223          * @param {Roo.EventObject} e The raw event object
16224          */
16225             "beforeclick" : true,
16226         /**
16227          * @event click
16228          * Fires when a template node is clicked.
16229          * @param {Roo.View} this
16230          * @param {Number} index The index of the target node
16231          * @param {HTMLElement} node The target node
16232          * @param {Roo.EventObject} e The raw event object
16233          */
16234             "click" : true,
16235         /**
16236          * @event dblclick
16237          * Fires when a template node is double clicked.
16238          * @param {Roo.View} this
16239          * @param {Number} index The index of the target node
16240          * @param {HTMLElement} node The target node
16241          * @param {Roo.EventObject} e The raw event object
16242          */
16243             "dblclick" : true,
16244         /**
16245          * @event contextmenu
16246          * Fires when a template node is right clicked.
16247          * @param {Roo.View} this
16248          * @param {Number} index The index of the target node
16249          * @param {HTMLElement} node The target node
16250          * @param {Roo.EventObject} e The raw event object
16251          */
16252             "contextmenu" : true,
16253         /**
16254          * @event selectionchange
16255          * Fires when the selected nodes change.
16256          * @param {Roo.View} this
16257          * @param {Array} selections Array of the selected nodes
16258          */
16259             "selectionchange" : true,
16260     
16261         /**
16262          * @event beforeselect
16263          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16264          * @param {Roo.View} this
16265          * @param {HTMLElement} node The node to be selected
16266          * @param {Array} selections Array of currently selected nodes
16267          */
16268             "beforeselect" : true,
16269         /**
16270          * @event preparedata
16271          * Fires on every row to render, to allow you to change the data.
16272          * @param {Roo.View} this
16273          * @param {Object} data to be rendered (change this)
16274          */
16275           "preparedata" : true
16276           
16277           
16278         });
16279
16280
16281
16282     this.el.on({
16283         "click": this.onClick,
16284         "dblclick": this.onDblClick,
16285         "contextmenu": this.onContextMenu,
16286         scope:this
16287     });
16288
16289     this.selections = [];
16290     this.nodes = [];
16291     this.cmp = new Roo.CompositeElementLite([]);
16292     if(this.store){
16293         this.store = Roo.factory(this.store, Roo.data);
16294         this.setStore(this.store, true);
16295     }
16296     
16297     if ( this.footer && this.footer.xtype) {
16298            
16299          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16300         
16301         this.footer.dataSource = this.store;
16302         this.footer.container = fctr;
16303         this.footer = Roo.factory(this.footer, Roo);
16304         fctr.insertFirst(this.el);
16305         
16306         // this is a bit insane - as the paging toolbar seems to detach the el..
16307 //        dom.parentNode.parentNode.parentNode
16308          // they get detached?
16309     }
16310     
16311     
16312     Roo.View.superclass.constructor.call(this);
16313     
16314     
16315 };
16316
16317 Roo.extend(Roo.View, Roo.util.Observable, {
16318     
16319      /**
16320      * @cfg {Roo.data.Store} store Data store to load data from.
16321      */
16322     store : false,
16323     
16324     /**
16325      * @cfg {String|Roo.Element} el The container element.
16326      */
16327     el : '',
16328     
16329     /**
16330      * @cfg {String|Roo.Template} tpl The template used by this View 
16331      */
16332     tpl : false,
16333     /**
16334      * @cfg {String} dataName the named area of the template to use as the data area
16335      *                          Works with domtemplates roo-name="name"
16336      */
16337     dataName: false,
16338     /**
16339      * @cfg {String} selectedClass The css class to add to selected nodes
16340      */
16341     selectedClass : "x-view-selected",
16342      /**
16343      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16344      */
16345     emptyText : "",
16346     
16347     /**
16348      * @cfg {String} text to display on mask (default Loading)
16349      */
16350     mask : false,
16351     /**
16352      * @cfg {Boolean} multiSelect Allow multiple selection
16353      */
16354     multiSelect : false,
16355     /**
16356      * @cfg {Boolean} singleSelect Allow single selection
16357      */
16358     singleSelect:  false,
16359     
16360     /**
16361      * @cfg {Boolean} toggleSelect - selecting 
16362      */
16363     toggleSelect : false,
16364     
16365     /**
16366      * @cfg {Boolean} tickable - selecting 
16367      */
16368     tickable : false,
16369     
16370     /**
16371      * Returns the element this view is bound to.
16372      * @return {Roo.Element}
16373      */
16374     getEl : function(){
16375         return this.wrapEl;
16376     },
16377     
16378     
16379
16380     /**
16381      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16382      */
16383     refresh : function(){
16384         //Roo.log('refresh');
16385         var t = this.tpl;
16386         
16387         // if we are using something like 'domtemplate', then
16388         // the what gets used is:
16389         // t.applySubtemplate(NAME, data, wrapping data..)
16390         // the outer template then get' applied with
16391         //     the store 'extra data'
16392         // and the body get's added to the
16393         //      roo-name="data" node?
16394         //      <span class='roo-tpl-{name}'></span> ?????
16395         
16396         
16397         
16398         this.clearSelections();
16399         this.el.update("");
16400         var html = [];
16401         var records = this.store.getRange();
16402         if(records.length < 1) {
16403             
16404             // is this valid??  = should it render a template??
16405             
16406             this.el.update(this.emptyText);
16407             return;
16408         }
16409         var el = this.el;
16410         if (this.dataName) {
16411             this.el.update(t.apply(this.store.meta)); //????
16412             el = this.el.child('.roo-tpl-' + this.dataName);
16413         }
16414         
16415         for(var i = 0, len = records.length; i < len; i++){
16416             var data = this.prepareData(records[i].data, i, records[i]);
16417             this.fireEvent("preparedata", this, data, i, records[i]);
16418             
16419             var d = Roo.apply({}, data);
16420             
16421             if(this.tickable){
16422                 Roo.apply(d, {'roo-id' : Roo.id()});
16423                 
16424                 var _this = this;
16425             
16426                 Roo.each(this.parent.item, function(item){
16427                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16428                         return;
16429                     }
16430                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16431                 });
16432             }
16433             
16434             html[html.length] = Roo.util.Format.trim(
16435                 this.dataName ?
16436                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16437                     t.apply(d)
16438             );
16439         }
16440         
16441         
16442         
16443         el.update(html.join(""));
16444         this.nodes = el.dom.childNodes;
16445         this.updateIndexes(0);
16446     },
16447     
16448
16449     /**
16450      * Function to override to reformat the data that is sent to
16451      * the template for each node.
16452      * DEPRICATED - use the preparedata event handler.
16453      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16454      * a JSON object for an UpdateManager bound view).
16455      */
16456     prepareData : function(data, index, record)
16457     {
16458         this.fireEvent("preparedata", this, data, index, record);
16459         return data;
16460     },
16461
16462     onUpdate : function(ds, record){
16463         // Roo.log('on update');   
16464         this.clearSelections();
16465         var index = this.store.indexOf(record);
16466         var n = this.nodes[index];
16467         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16468         n.parentNode.removeChild(n);
16469         this.updateIndexes(index, index);
16470     },
16471
16472     
16473     
16474 // --------- FIXME     
16475     onAdd : function(ds, records, index)
16476     {
16477         //Roo.log(['on Add', ds, records, index] );        
16478         this.clearSelections();
16479         if(this.nodes.length == 0){
16480             this.refresh();
16481             return;
16482         }
16483         var n = this.nodes[index];
16484         for(var i = 0, len = records.length; i < len; i++){
16485             var d = this.prepareData(records[i].data, i, records[i]);
16486             if(n){
16487                 this.tpl.insertBefore(n, d);
16488             }else{
16489                 
16490                 this.tpl.append(this.el, d);
16491             }
16492         }
16493         this.updateIndexes(index);
16494     },
16495
16496     onRemove : function(ds, record, index){
16497        // Roo.log('onRemove');
16498         this.clearSelections();
16499         var el = this.dataName  ?
16500             this.el.child('.roo-tpl-' + this.dataName) :
16501             this.el; 
16502         
16503         el.dom.removeChild(this.nodes[index]);
16504         this.updateIndexes(index);
16505     },
16506
16507     /**
16508      * Refresh an individual node.
16509      * @param {Number} index
16510      */
16511     refreshNode : function(index){
16512         this.onUpdate(this.store, this.store.getAt(index));
16513     },
16514
16515     updateIndexes : function(startIndex, endIndex){
16516         var ns = this.nodes;
16517         startIndex = startIndex || 0;
16518         endIndex = endIndex || ns.length - 1;
16519         for(var i = startIndex; i <= endIndex; i++){
16520             ns[i].nodeIndex = i;
16521         }
16522     },
16523
16524     /**
16525      * Changes the data store this view uses and refresh the view.
16526      * @param {Store} store
16527      */
16528     setStore : function(store, initial){
16529         if(!initial && this.store){
16530             this.store.un("datachanged", this.refresh);
16531             this.store.un("add", this.onAdd);
16532             this.store.un("remove", this.onRemove);
16533             this.store.un("update", this.onUpdate);
16534             this.store.un("clear", this.refresh);
16535             this.store.un("beforeload", this.onBeforeLoad);
16536             this.store.un("load", this.onLoad);
16537             this.store.un("loadexception", this.onLoad);
16538         }
16539         if(store){
16540           
16541             store.on("datachanged", this.refresh, this);
16542             store.on("add", this.onAdd, this);
16543             store.on("remove", this.onRemove, this);
16544             store.on("update", this.onUpdate, this);
16545             store.on("clear", this.refresh, this);
16546             store.on("beforeload", this.onBeforeLoad, this);
16547             store.on("load", this.onLoad, this);
16548             store.on("loadexception", this.onLoad, this);
16549         }
16550         
16551         if(store){
16552             this.refresh();
16553         }
16554     },
16555     /**
16556      * onbeforeLoad - masks the loading area.
16557      *
16558      */
16559     onBeforeLoad : function(store,opts)
16560     {
16561          //Roo.log('onBeforeLoad');   
16562         if (!opts.add) {
16563             this.el.update("");
16564         }
16565         this.el.mask(this.mask ? this.mask : "Loading" ); 
16566     },
16567     onLoad : function ()
16568     {
16569         this.el.unmask();
16570     },
16571     
16572
16573     /**
16574      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16575      * @param {HTMLElement} node
16576      * @return {HTMLElement} The template node
16577      */
16578     findItemFromChild : function(node){
16579         var el = this.dataName  ?
16580             this.el.child('.roo-tpl-' + this.dataName,true) :
16581             this.el.dom; 
16582         
16583         if(!node || node.parentNode == el){
16584                     return node;
16585             }
16586             var p = node.parentNode;
16587             while(p && p != el){
16588             if(p.parentNode == el){
16589                 return p;
16590             }
16591             p = p.parentNode;
16592         }
16593             return null;
16594     },
16595
16596     /** @ignore */
16597     onClick : function(e){
16598         var item = this.findItemFromChild(e.getTarget());
16599         if(item){
16600             var index = this.indexOf(item);
16601             if(this.onItemClick(item, index, e) !== false){
16602                 this.fireEvent("click", this, index, item, e);
16603             }
16604         }else{
16605             this.clearSelections();
16606         }
16607     },
16608
16609     /** @ignore */
16610     onContextMenu : function(e){
16611         var item = this.findItemFromChild(e.getTarget());
16612         if(item){
16613             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16614         }
16615     },
16616
16617     /** @ignore */
16618     onDblClick : function(e){
16619         var item = this.findItemFromChild(e.getTarget());
16620         if(item){
16621             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16622         }
16623     },
16624
16625     onItemClick : function(item, index, e)
16626     {
16627         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16628             return false;
16629         }
16630         if (this.toggleSelect) {
16631             var m = this.isSelected(item) ? 'unselect' : 'select';
16632             //Roo.log(m);
16633             var _t = this;
16634             _t[m](item, true, false);
16635             return true;
16636         }
16637         if(this.multiSelect || this.singleSelect){
16638             if(this.multiSelect && e.shiftKey && this.lastSelection){
16639                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16640             }else{
16641                 this.select(item, this.multiSelect && e.ctrlKey);
16642                 this.lastSelection = item;
16643             }
16644             
16645             if(!this.tickable){
16646                 e.preventDefault();
16647             }
16648             
16649         }
16650         return true;
16651     },
16652
16653     /**
16654      * Get the number of selected nodes.
16655      * @return {Number}
16656      */
16657     getSelectionCount : function(){
16658         return this.selections.length;
16659     },
16660
16661     /**
16662      * Get the currently selected nodes.
16663      * @return {Array} An array of HTMLElements
16664      */
16665     getSelectedNodes : function(){
16666         return this.selections;
16667     },
16668
16669     /**
16670      * Get the indexes of the selected nodes.
16671      * @return {Array}
16672      */
16673     getSelectedIndexes : function(){
16674         var indexes = [], s = this.selections;
16675         for(var i = 0, len = s.length; i < len; i++){
16676             indexes.push(s[i].nodeIndex);
16677         }
16678         return indexes;
16679     },
16680
16681     /**
16682      * Clear all selections
16683      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16684      */
16685     clearSelections : function(suppressEvent){
16686         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16687             this.cmp.elements = this.selections;
16688             this.cmp.removeClass(this.selectedClass);
16689             this.selections = [];
16690             if(!suppressEvent){
16691                 this.fireEvent("selectionchange", this, this.selections);
16692             }
16693         }
16694     },
16695
16696     /**
16697      * Returns true if the passed node is selected
16698      * @param {HTMLElement/Number} node The node or node index
16699      * @return {Boolean}
16700      */
16701     isSelected : function(node){
16702         var s = this.selections;
16703         if(s.length < 1){
16704             return false;
16705         }
16706         node = this.getNode(node);
16707         return s.indexOf(node) !== -1;
16708     },
16709
16710     /**
16711      * Selects nodes.
16712      * @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
16713      * @param {Boolean} keepExisting (optional) true to keep existing selections
16714      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16715      */
16716     select : function(nodeInfo, keepExisting, suppressEvent){
16717         if(nodeInfo instanceof Array){
16718             if(!keepExisting){
16719                 this.clearSelections(true);
16720             }
16721             for(var i = 0, len = nodeInfo.length; i < len; i++){
16722                 this.select(nodeInfo[i], true, true);
16723             }
16724             return;
16725         } 
16726         var node = this.getNode(nodeInfo);
16727         if(!node || this.isSelected(node)){
16728             return; // already selected.
16729         }
16730         if(!keepExisting){
16731             this.clearSelections(true);
16732         }
16733         
16734         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16735             Roo.fly(node).addClass(this.selectedClass);
16736             this.selections.push(node);
16737             if(!suppressEvent){
16738                 this.fireEvent("selectionchange", this, this.selections);
16739             }
16740         }
16741         
16742         
16743     },
16744       /**
16745      * Unselects nodes.
16746      * @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
16747      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16748      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16749      */
16750     unselect : function(nodeInfo, keepExisting, suppressEvent)
16751     {
16752         if(nodeInfo instanceof Array){
16753             Roo.each(this.selections, function(s) {
16754                 this.unselect(s, nodeInfo);
16755             }, this);
16756             return;
16757         }
16758         var node = this.getNode(nodeInfo);
16759         if(!node || !this.isSelected(node)){
16760             //Roo.log("not selected");
16761             return; // not selected.
16762         }
16763         // fireevent???
16764         var ns = [];
16765         Roo.each(this.selections, function(s) {
16766             if (s == node ) {
16767                 Roo.fly(node).removeClass(this.selectedClass);
16768
16769                 return;
16770             }
16771             ns.push(s);
16772         },this);
16773         
16774         this.selections= ns;
16775         this.fireEvent("selectionchange", this, this.selections);
16776     },
16777
16778     /**
16779      * Gets a template node.
16780      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16781      * @return {HTMLElement} The node or null if it wasn't found
16782      */
16783     getNode : function(nodeInfo){
16784         if(typeof nodeInfo == "string"){
16785             return document.getElementById(nodeInfo);
16786         }else if(typeof nodeInfo == "number"){
16787             return this.nodes[nodeInfo];
16788         }
16789         return nodeInfo;
16790     },
16791
16792     /**
16793      * Gets a range template nodes.
16794      * @param {Number} startIndex
16795      * @param {Number} endIndex
16796      * @return {Array} An array of nodes
16797      */
16798     getNodes : function(start, end){
16799         var ns = this.nodes;
16800         start = start || 0;
16801         end = typeof end == "undefined" ? ns.length - 1 : end;
16802         var nodes = [];
16803         if(start <= end){
16804             for(var i = start; i <= end; i++){
16805                 nodes.push(ns[i]);
16806             }
16807         } else{
16808             for(var i = start; i >= end; i--){
16809                 nodes.push(ns[i]);
16810             }
16811         }
16812         return nodes;
16813     },
16814
16815     /**
16816      * Finds the index of the passed node
16817      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16818      * @return {Number} The index of the node or -1
16819      */
16820     indexOf : function(node){
16821         node = this.getNode(node);
16822         if(typeof node.nodeIndex == "number"){
16823             return node.nodeIndex;
16824         }
16825         var ns = this.nodes;
16826         for(var i = 0, len = ns.length; i < len; i++){
16827             if(ns[i] == node){
16828                 return i;
16829             }
16830         }
16831         return -1;
16832     }
16833 });
16834 /*
16835  * - LGPL
16836  *
16837  * based on jquery fullcalendar
16838  * 
16839  */
16840
16841 Roo.bootstrap = Roo.bootstrap || {};
16842 /**
16843  * @class Roo.bootstrap.Calendar
16844  * @extends Roo.bootstrap.Component
16845  * Bootstrap Calendar class
16846  * @cfg {Boolean} loadMask (true|false) default false
16847  * @cfg {Object} header generate the user specific header of the calendar, default false
16848
16849  * @constructor
16850  * Create a new Container
16851  * @param {Object} config The config object
16852  */
16853
16854
16855
16856 Roo.bootstrap.Calendar = function(config){
16857     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16858      this.addEvents({
16859         /**
16860              * @event select
16861              * Fires when a date is selected
16862              * @param {DatePicker} this
16863              * @param {Date} date The selected date
16864              */
16865         'select': true,
16866         /**
16867              * @event monthchange
16868              * Fires when the displayed month changes 
16869              * @param {DatePicker} this
16870              * @param {Date} date The selected month
16871              */
16872         'monthchange': true,
16873         /**
16874              * @event evententer
16875              * Fires when mouse over an event
16876              * @param {Calendar} this
16877              * @param {event} Event
16878              */
16879         'evententer': true,
16880         /**
16881              * @event eventleave
16882              * Fires when the mouse leaves an
16883              * @param {Calendar} this
16884              * @param {event}
16885              */
16886         'eventleave': true,
16887         /**
16888              * @event eventclick
16889              * Fires when the mouse click an
16890              * @param {Calendar} this
16891              * @param {event}
16892              */
16893         'eventclick': true
16894         
16895     });
16896
16897 };
16898
16899 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16900     
16901      /**
16902      * @cfg {Number} startDay
16903      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16904      */
16905     startDay : 0,
16906     
16907     loadMask : false,
16908     
16909     header : false,
16910       
16911     getAutoCreate : function(){
16912         
16913         
16914         var fc_button = function(name, corner, style, content ) {
16915             return Roo.apply({},{
16916                 tag : 'span',
16917                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16918                          (corner.length ?
16919                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16920                             ''
16921                         ),
16922                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16923                 unselectable: 'on'
16924             });
16925         };
16926         
16927         var header = {};
16928         
16929         if(!this.header){
16930             header = {
16931                 tag : 'table',
16932                 cls : 'fc-header',
16933                 style : 'width:100%',
16934                 cn : [
16935                     {
16936                         tag: 'tr',
16937                         cn : [
16938                             {
16939                                 tag : 'td',
16940                                 cls : 'fc-header-left',
16941                                 cn : [
16942                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16943                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16944                                     { tag: 'span', cls: 'fc-header-space' },
16945                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16946
16947
16948                                 ]
16949                             },
16950
16951                             {
16952                                 tag : 'td',
16953                                 cls : 'fc-header-center',
16954                                 cn : [
16955                                     {
16956                                         tag: 'span',
16957                                         cls: 'fc-header-title',
16958                                         cn : {
16959                                             tag: 'H2',
16960                                             html : 'month / year'
16961                                         }
16962                                     }
16963
16964                                 ]
16965                             },
16966                             {
16967                                 tag : 'td',
16968                                 cls : 'fc-header-right',
16969                                 cn : [
16970                               /*      fc_button('month', 'left', '', 'month' ),
16971                                     fc_button('week', '', '', 'week' ),
16972                                     fc_button('day', 'right', '', 'day' )
16973                                 */    
16974
16975                                 ]
16976                             }
16977
16978                         ]
16979                     }
16980                 ]
16981             };
16982         }
16983         
16984         header = this.header;
16985         
16986        
16987         var cal_heads = function() {
16988             var ret = [];
16989             // fixme - handle this.
16990             
16991             for (var i =0; i < Date.dayNames.length; i++) {
16992                 var d = Date.dayNames[i];
16993                 ret.push({
16994                     tag: 'th',
16995                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16996                     html : d.substring(0,3)
16997                 });
16998                 
16999             }
17000             ret[0].cls += ' fc-first';
17001             ret[6].cls += ' fc-last';
17002             return ret;
17003         };
17004         var cal_cell = function(n) {
17005             return  {
17006                 tag: 'td',
17007                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17008                 cn : [
17009                     {
17010                         cn : [
17011                             {
17012                                 cls: 'fc-day-number',
17013                                 html: 'D'
17014                             },
17015                             {
17016                                 cls: 'fc-day-content',
17017                              
17018                                 cn : [
17019                                      {
17020                                         style: 'position: relative;' // height: 17px;
17021                                     }
17022                                 ]
17023                             }
17024                             
17025                             
17026                         ]
17027                     }
17028                 ]
17029                 
17030             }
17031         };
17032         var cal_rows = function() {
17033             
17034             var ret = [];
17035             for (var r = 0; r < 6; r++) {
17036                 var row= {
17037                     tag : 'tr',
17038                     cls : 'fc-week',
17039                     cn : []
17040                 };
17041                 
17042                 for (var i =0; i < Date.dayNames.length; i++) {
17043                     var d = Date.dayNames[i];
17044                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17045
17046                 }
17047                 row.cn[0].cls+=' fc-first';
17048                 row.cn[0].cn[0].style = 'min-height:90px';
17049                 row.cn[6].cls+=' fc-last';
17050                 ret.push(row);
17051                 
17052             }
17053             ret[0].cls += ' fc-first';
17054             ret[4].cls += ' fc-prev-last';
17055             ret[5].cls += ' fc-last';
17056             return ret;
17057             
17058         };
17059         
17060         var cal_table = {
17061             tag: 'table',
17062             cls: 'fc-border-separate',
17063             style : 'width:100%',
17064             cellspacing  : 0,
17065             cn : [
17066                 { 
17067                     tag: 'thead',
17068                     cn : [
17069                         { 
17070                             tag: 'tr',
17071                             cls : 'fc-first fc-last',
17072                             cn : cal_heads()
17073                         }
17074                     ]
17075                 },
17076                 { 
17077                     tag: 'tbody',
17078                     cn : cal_rows()
17079                 }
17080                   
17081             ]
17082         };
17083          
17084          var cfg = {
17085             cls : 'fc fc-ltr',
17086             cn : [
17087                 header,
17088                 {
17089                     cls : 'fc-content',
17090                     style : "position: relative;",
17091                     cn : [
17092                         {
17093                             cls : 'fc-view fc-view-month fc-grid',
17094                             style : 'position: relative',
17095                             unselectable : 'on',
17096                             cn : [
17097                                 {
17098                                     cls : 'fc-event-container',
17099                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17100                                 },
17101                                 cal_table
17102                             ]
17103                         }
17104                     ]
17105     
17106                 }
17107            ] 
17108             
17109         };
17110         
17111          
17112         
17113         return cfg;
17114     },
17115     
17116     
17117     initEvents : function()
17118     {
17119         if(!this.store){
17120             throw "can not find store for calendar";
17121         }
17122         
17123         var mark = {
17124             tag: "div",
17125             cls:"x-dlg-mask",
17126             style: "text-align:center",
17127             cn: [
17128                 {
17129                     tag: "div",
17130                     style: "background-color:white;width:50%;margin:250 auto",
17131                     cn: [
17132                         {
17133                             tag: "img",
17134                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17135                         },
17136                         {
17137                             tag: "span",
17138                             html: "Loading"
17139                         }
17140                         
17141                     ]
17142                 }
17143             ]
17144         };
17145         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17146         
17147         var size = this.el.select('.fc-content', true).first().getSize();
17148         this.maskEl.setSize(size.width, size.height);
17149         this.maskEl.enableDisplayMode("block");
17150         if(!this.loadMask){
17151             this.maskEl.hide();
17152         }
17153         
17154         this.store = Roo.factory(this.store, Roo.data);
17155         this.store.on('load', this.onLoad, this);
17156         this.store.on('beforeload', this.onBeforeLoad, this);
17157         
17158         this.resize();
17159         
17160         this.cells = this.el.select('.fc-day',true);
17161         //Roo.log(this.cells);
17162         this.textNodes = this.el.query('.fc-day-number');
17163         this.cells.addClassOnOver('fc-state-hover');
17164         
17165         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17166         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17167         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17168         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17169         
17170         this.on('monthchange', this.onMonthChange, this);
17171         
17172         this.update(new Date().clearTime());
17173     },
17174     
17175     resize : function() {
17176         var sz  = this.el.getSize();
17177         
17178         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17179         this.el.select('.fc-day-content div',true).setHeight(34);
17180     },
17181     
17182     
17183     // private
17184     showPrevMonth : function(e){
17185         this.update(this.activeDate.add("mo", -1));
17186     },
17187     showToday : function(e){
17188         this.update(new Date().clearTime());
17189     },
17190     // private
17191     showNextMonth : function(e){
17192         this.update(this.activeDate.add("mo", 1));
17193     },
17194
17195     // private
17196     showPrevYear : function(){
17197         this.update(this.activeDate.add("y", -1));
17198     },
17199
17200     // private
17201     showNextYear : function(){
17202         this.update(this.activeDate.add("y", 1));
17203     },
17204
17205     
17206    // private
17207     update : function(date)
17208     {
17209         var vd = this.activeDate;
17210         this.activeDate = date;
17211 //        if(vd && this.el){
17212 //            var t = date.getTime();
17213 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17214 //                Roo.log('using add remove');
17215 //                
17216 //                this.fireEvent('monthchange', this, date);
17217 //                
17218 //                this.cells.removeClass("fc-state-highlight");
17219 //                this.cells.each(function(c){
17220 //                   if(c.dateValue == t){
17221 //                       c.addClass("fc-state-highlight");
17222 //                       setTimeout(function(){
17223 //                            try{c.dom.firstChild.focus();}catch(e){}
17224 //                       }, 50);
17225 //                       return false;
17226 //                   }
17227 //                   return true;
17228 //                });
17229 //                return;
17230 //            }
17231 //        }
17232         
17233         var days = date.getDaysInMonth();
17234         
17235         var firstOfMonth = date.getFirstDateOfMonth();
17236         var startingPos = firstOfMonth.getDay()-this.startDay;
17237         
17238         if(startingPos < this.startDay){
17239             startingPos += 7;
17240         }
17241         
17242         var pm = date.add(Date.MONTH, -1);
17243         var prevStart = pm.getDaysInMonth()-startingPos;
17244 //        
17245         this.cells = this.el.select('.fc-day',true);
17246         this.textNodes = this.el.query('.fc-day-number');
17247         this.cells.addClassOnOver('fc-state-hover');
17248         
17249         var cells = this.cells.elements;
17250         var textEls = this.textNodes;
17251         
17252         Roo.each(cells, function(cell){
17253             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17254         });
17255         
17256         days += startingPos;
17257
17258         // convert everything to numbers so it's fast
17259         var day = 86400000;
17260         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17261         //Roo.log(d);
17262         //Roo.log(pm);
17263         //Roo.log(prevStart);
17264         
17265         var today = new Date().clearTime().getTime();
17266         var sel = date.clearTime().getTime();
17267         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17268         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17269         var ddMatch = this.disabledDatesRE;
17270         var ddText = this.disabledDatesText;
17271         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17272         var ddaysText = this.disabledDaysText;
17273         var format = this.format;
17274         
17275         var setCellClass = function(cal, cell){
17276             cell.row = 0;
17277             cell.events = [];
17278             cell.more = [];
17279             //Roo.log('set Cell Class');
17280             cell.title = "";
17281             var t = d.getTime();
17282             
17283             //Roo.log(d);
17284             
17285             cell.dateValue = t;
17286             if(t == today){
17287                 cell.className += " fc-today";
17288                 cell.className += " fc-state-highlight";
17289                 cell.title = cal.todayText;
17290             }
17291             if(t == sel){
17292                 // disable highlight in other month..
17293                 //cell.className += " fc-state-highlight";
17294                 
17295             }
17296             // disabling
17297             if(t < min) {
17298                 cell.className = " fc-state-disabled";
17299                 cell.title = cal.minText;
17300                 return;
17301             }
17302             if(t > max) {
17303                 cell.className = " fc-state-disabled";
17304                 cell.title = cal.maxText;
17305                 return;
17306             }
17307             if(ddays){
17308                 if(ddays.indexOf(d.getDay()) != -1){
17309                     cell.title = ddaysText;
17310                     cell.className = " fc-state-disabled";
17311                 }
17312             }
17313             if(ddMatch && format){
17314                 var fvalue = d.dateFormat(format);
17315                 if(ddMatch.test(fvalue)){
17316                     cell.title = ddText.replace("%0", fvalue);
17317                     cell.className = " fc-state-disabled";
17318                 }
17319             }
17320             
17321             if (!cell.initialClassName) {
17322                 cell.initialClassName = cell.dom.className;
17323             }
17324             
17325             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17326         };
17327
17328         var i = 0;
17329         
17330         for(; i < startingPos; i++) {
17331             textEls[i].innerHTML = (++prevStart);
17332             d.setDate(d.getDate()+1);
17333             
17334             cells[i].className = "fc-past fc-other-month";
17335             setCellClass(this, cells[i]);
17336         }
17337         
17338         var intDay = 0;
17339         
17340         for(; i < days; i++){
17341             intDay = i - startingPos + 1;
17342             textEls[i].innerHTML = (intDay);
17343             d.setDate(d.getDate()+1);
17344             
17345             cells[i].className = ''; // "x-date-active";
17346             setCellClass(this, cells[i]);
17347         }
17348         var extraDays = 0;
17349         
17350         for(; i < 42; i++) {
17351             textEls[i].innerHTML = (++extraDays);
17352             d.setDate(d.getDate()+1);
17353             
17354             cells[i].className = "fc-future fc-other-month";
17355             setCellClass(this, cells[i]);
17356         }
17357         
17358         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17359         
17360         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17361         
17362         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17363         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17364         
17365         if(totalRows != 6){
17366             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17367             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17368         }
17369         
17370         this.fireEvent('monthchange', this, date);
17371         
17372         
17373         /*
17374         if(!this.internalRender){
17375             var main = this.el.dom.firstChild;
17376             var w = main.offsetWidth;
17377             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17378             Roo.fly(main).setWidth(w);
17379             this.internalRender = true;
17380             // opera does not respect the auto grow header center column
17381             // then, after it gets a width opera refuses to recalculate
17382             // without a second pass
17383             if(Roo.isOpera && !this.secondPass){
17384                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17385                 this.secondPass = true;
17386                 this.update.defer(10, this, [date]);
17387             }
17388         }
17389         */
17390         
17391     },
17392     
17393     findCell : function(dt) {
17394         dt = dt.clearTime().getTime();
17395         var ret = false;
17396         this.cells.each(function(c){
17397             //Roo.log("check " +c.dateValue + '?=' + dt);
17398             if(c.dateValue == dt){
17399                 ret = c;
17400                 return false;
17401             }
17402             return true;
17403         });
17404         
17405         return ret;
17406     },
17407     
17408     findCells : function(ev) {
17409         var s = ev.start.clone().clearTime().getTime();
17410        // Roo.log(s);
17411         var e= ev.end.clone().clearTime().getTime();
17412        // Roo.log(e);
17413         var ret = [];
17414         this.cells.each(function(c){
17415              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17416             
17417             if(c.dateValue > e){
17418                 return ;
17419             }
17420             if(c.dateValue < s){
17421                 return ;
17422             }
17423             ret.push(c);
17424         });
17425         
17426         return ret;    
17427     },
17428     
17429 //    findBestRow: function(cells)
17430 //    {
17431 //        var ret = 0;
17432 //        
17433 //        for (var i =0 ; i < cells.length;i++) {
17434 //            ret  = Math.max(cells[i].rows || 0,ret);
17435 //        }
17436 //        return ret;
17437 //        
17438 //    },
17439     
17440     
17441     addItem : function(ev)
17442     {
17443         // look for vertical location slot in
17444         var cells = this.findCells(ev);
17445         
17446 //        ev.row = this.findBestRow(cells);
17447         
17448         // work out the location.
17449         
17450         var crow = false;
17451         var rows = [];
17452         for(var i =0; i < cells.length; i++) {
17453             
17454             cells[i].row = cells[0].row;
17455             
17456             if(i == 0){
17457                 cells[i].row = cells[i].row + 1;
17458             }
17459             
17460             if (!crow) {
17461                 crow = {
17462                     start : cells[i],
17463                     end :  cells[i]
17464                 };
17465                 continue;
17466             }
17467             if (crow.start.getY() == cells[i].getY()) {
17468                 // on same row.
17469                 crow.end = cells[i];
17470                 continue;
17471             }
17472             // different row.
17473             rows.push(crow);
17474             crow = {
17475                 start: cells[i],
17476                 end : cells[i]
17477             };
17478             
17479         }
17480         
17481         rows.push(crow);
17482         ev.els = [];
17483         ev.rows = rows;
17484         ev.cells = cells;
17485         
17486         cells[0].events.push(ev);
17487         
17488         this.calevents.push(ev);
17489     },
17490     
17491     clearEvents: function() {
17492         
17493         if(!this.calevents){
17494             return;
17495         }
17496         
17497         Roo.each(this.cells.elements, function(c){
17498             c.row = 0;
17499             c.events = [];
17500             c.more = [];
17501         });
17502         
17503         Roo.each(this.calevents, function(e) {
17504             Roo.each(e.els, function(el) {
17505                 el.un('mouseenter' ,this.onEventEnter, this);
17506                 el.un('mouseleave' ,this.onEventLeave, this);
17507                 el.remove();
17508             },this);
17509         },this);
17510         
17511         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17512             e.remove();
17513         });
17514         
17515     },
17516     
17517     renderEvents: function()
17518     {   
17519         var _this = this;
17520         
17521         this.cells.each(function(c) {
17522             
17523             if(c.row < 5){
17524                 return;
17525             }
17526             
17527             var ev = c.events;
17528             
17529             var r = 4;
17530             if(c.row != c.events.length){
17531                 r = 4 - (4 - (c.row - c.events.length));
17532             }
17533             
17534             c.events = ev.slice(0, r);
17535             c.more = ev.slice(r);
17536             
17537             if(c.more.length && c.more.length == 1){
17538                 c.events.push(c.more.pop());
17539             }
17540             
17541             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17542             
17543         });
17544             
17545         this.cells.each(function(c) {
17546             
17547             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17548             
17549             
17550             for (var e = 0; e < c.events.length; e++){
17551                 var ev = c.events[e];
17552                 var rows = ev.rows;
17553                 
17554                 for(var i = 0; i < rows.length; i++) {
17555                 
17556                     // how many rows should it span..
17557
17558                     var  cfg = {
17559                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17560                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17561
17562                         unselectable : "on",
17563                         cn : [
17564                             {
17565                                 cls: 'fc-event-inner',
17566                                 cn : [
17567     //                                {
17568     //                                  tag:'span',
17569     //                                  cls: 'fc-event-time',
17570     //                                  html : cells.length > 1 ? '' : ev.time
17571     //                                },
17572                                     {
17573                                       tag:'span',
17574                                       cls: 'fc-event-title',
17575                                       html : String.format('{0}', ev.title)
17576                                     }
17577
17578
17579                                 ]
17580                             },
17581                             {
17582                                 cls: 'ui-resizable-handle ui-resizable-e',
17583                                 html : '&nbsp;&nbsp;&nbsp'
17584                             }
17585
17586                         ]
17587                     };
17588
17589                     if (i == 0) {
17590                         cfg.cls += ' fc-event-start';
17591                     }
17592                     if ((i+1) == rows.length) {
17593                         cfg.cls += ' fc-event-end';
17594                     }
17595
17596                     var ctr = _this.el.select('.fc-event-container',true).first();
17597                     var cg = ctr.createChild(cfg);
17598
17599                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17600                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17601
17602                     var r = (c.more.length) ? 1 : 0;
17603                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17604                     cg.setWidth(ebox.right - sbox.x -2);
17605
17606                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17607                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17608                     cg.on('click', _this.onEventClick, _this, ev);
17609
17610                     ev.els.push(cg);
17611                     
17612                 }
17613                 
17614             }
17615             
17616             
17617             if(c.more.length){
17618                 var  cfg = {
17619                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17620                     style : 'position: absolute',
17621                     unselectable : "on",
17622                     cn : [
17623                         {
17624                             cls: 'fc-event-inner',
17625                             cn : [
17626                                 {
17627                                   tag:'span',
17628                                   cls: 'fc-event-title',
17629                                   html : 'More'
17630                                 }
17631
17632
17633                             ]
17634                         },
17635                         {
17636                             cls: 'ui-resizable-handle ui-resizable-e',
17637                             html : '&nbsp;&nbsp;&nbsp'
17638                         }
17639
17640                     ]
17641                 };
17642
17643                 var ctr = _this.el.select('.fc-event-container',true).first();
17644                 var cg = ctr.createChild(cfg);
17645
17646                 var sbox = c.select('.fc-day-content',true).first().getBox();
17647                 var ebox = c.select('.fc-day-content',true).first().getBox();
17648                 //Roo.log(cg);
17649                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17650                 cg.setWidth(ebox.right - sbox.x -2);
17651
17652                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17653                 
17654             }
17655             
17656         });
17657         
17658         
17659         
17660     },
17661     
17662     onEventEnter: function (e, el,event,d) {
17663         this.fireEvent('evententer', this, el, event);
17664     },
17665     
17666     onEventLeave: function (e, el,event,d) {
17667         this.fireEvent('eventleave', this, el, event);
17668     },
17669     
17670     onEventClick: function (e, el,event,d) {
17671         this.fireEvent('eventclick', this, el, event);
17672     },
17673     
17674     onMonthChange: function () {
17675         this.store.load();
17676     },
17677     
17678     onMoreEventClick: function(e, el, more)
17679     {
17680         var _this = this;
17681         
17682         this.calpopover.placement = 'right';
17683         this.calpopover.setTitle('More');
17684         
17685         this.calpopover.setContent('');
17686         
17687         var ctr = this.calpopover.el.select('.popover-content', true).first();
17688         
17689         Roo.each(more, function(m){
17690             var cfg = {
17691                 cls : 'fc-event-hori fc-event-draggable',
17692                 html : m.title
17693             };
17694             var cg = ctr.createChild(cfg);
17695             
17696             cg.on('click', _this.onEventClick, _this, m);
17697         });
17698         
17699         this.calpopover.show(el);
17700         
17701         
17702     },
17703     
17704     onLoad: function () 
17705     {   
17706         this.calevents = [];
17707         var cal = this;
17708         
17709         if(this.store.getCount() > 0){
17710             this.store.data.each(function(d){
17711                cal.addItem({
17712                     id : d.data.id,
17713                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17714                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17715                     time : d.data.start_time,
17716                     title : d.data.title,
17717                     description : d.data.description,
17718                     venue : d.data.venue
17719                 });
17720             });
17721         }
17722         
17723         this.renderEvents();
17724         
17725         if(this.calevents.length && this.loadMask){
17726             this.maskEl.hide();
17727         }
17728     },
17729     
17730     onBeforeLoad: function()
17731     {
17732         this.clearEvents();
17733         if(this.loadMask){
17734             this.maskEl.show();
17735         }
17736     }
17737 });
17738
17739  
17740  /*
17741  * - LGPL
17742  *
17743  * element
17744  * 
17745  */
17746
17747 /**
17748  * @class Roo.bootstrap.Popover
17749  * @extends Roo.bootstrap.Component
17750  * Bootstrap Popover class
17751  * @cfg {String} html contents of the popover   (or false to use children..)
17752  * @cfg {String} title of popover (or false to hide)
17753  * @cfg {String} placement how it is placed
17754  * @cfg {String} trigger click || hover (or false to trigger manually)
17755  * @cfg {String} over what (parent or false to trigger manually.)
17756  * @cfg {Number} delay - delay before showing
17757  
17758  * @constructor
17759  * Create a new Popover
17760  * @param {Object} config The config object
17761  */
17762
17763 Roo.bootstrap.Popover = function(config){
17764     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17765     
17766     this.addEvents({
17767         // raw events
17768          /**
17769          * @event show
17770          * After the popover show
17771          * 
17772          * @param {Roo.bootstrap.Popover} this
17773          */
17774         "show" : true,
17775         /**
17776          * @event hide
17777          * After the popover hide
17778          * 
17779          * @param {Roo.bootstrap.Popover} this
17780          */
17781         "hide" : true
17782     });
17783 };
17784
17785 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17786     
17787     title: 'Fill in a title',
17788     html: false,
17789     
17790     placement : 'right',
17791     trigger : 'hover', // hover
17792     
17793     delay : 0,
17794     
17795     over: 'parent',
17796     
17797     can_build_overlaid : false,
17798     
17799     getChildContainer : function()
17800     {
17801         return this.el.select('.popover-content',true).first();
17802     },
17803     
17804     getAutoCreate : function(){
17805          
17806         var cfg = {
17807            cls : 'popover roo-dynamic',
17808            style: 'display:block',
17809            cn : [
17810                 {
17811                     cls : 'arrow'
17812                 },
17813                 {
17814                     cls : 'popover-inner',
17815                     cn : [
17816                         {
17817                             tag: 'h3',
17818                             cls: 'popover-title popover-header',
17819                             html : this.title
17820                         },
17821                         {
17822                             cls : 'popover-content popover-body',
17823                             html : this.html
17824                         }
17825                     ]
17826                     
17827                 }
17828            ]
17829         };
17830         
17831         return cfg;
17832     },
17833     setTitle: function(str)
17834     {
17835         this.title = str;
17836         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17837     },
17838     setContent: function(str)
17839     {
17840         this.html = str;
17841         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17842     },
17843     // as it get's added to the bottom of the page.
17844     onRender : function(ct, position)
17845     {
17846         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17847         if(!this.el){
17848             var cfg = Roo.apply({},  this.getAutoCreate());
17849             cfg.id = Roo.id();
17850             
17851             if (this.cls) {
17852                 cfg.cls += ' ' + this.cls;
17853             }
17854             if (this.style) {
17855                 cfg.style = this.style;
17856             }
17857             //Roo.log("adding to ");
17858             this.el = Roo.get(document.body).createChild(cfg, position);
17859 //            Roo.log(this.el);
17860         }
17861         this.initEvents();
17862     },
17863     
17864     initEvents : function()
17865     {
17866         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17867         this.el.enableDisplayMode('block');
17868         this.el.hide();
17869         if (this.over === false) {
17870             return; 
17871         }
17872         if (this.triggers === false) {
17873             return;
17874         }
17875         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17876         var triggers = this.trigger ? this.trigger.split(' ') : [];
17877         Roo.each(triggers, function(trigger) {
17878         
17879             if (trigger == 'click') {
17880                 on_el.on('click', this.toggle, this);
17881             } else if (trigger != 'manual') {
17882                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17883                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17884       
17885                 on_el.on(eventIn  ,this.enter, this);
17886                 on_el.on(eventOut, this.leave, this);
17887             }
17888         }, this);
17889         
17890     },
17891     
17892     
17893     // private
17894     timeout : null,
17895     hoverState : null,
17896     
17897     toggle : function () {
17898         this.hoverState == 'in' ? this.leave() : this.enter();
17899     },
17900     
17901     enter : function () {
17902         
17903         clearTimeout(this.timeout);
17904     
17905         this.hoverState = 'in';
17906     
17907         if (!this.delay || !this.delay.show) {
17908             this.show();
17909             return;
17910         }
17911         var _t = this;
17912         this.timeout = setTimeout(function () {
17913             if (_t.hoverState == 'in') {
17914                 _t.show();
17915             }
17916         }, this.delay.show)
17917     },
17918     
17919     leave : function() {
17920         clearTimeout(this.timeout);
17921     
17922         this.hoverState = 'out';
17923     
17924         if (!this.delay || !this.delay.hide) {
17925             this.hide();
17926             return;
17927         }
17928         var _t = this;
17929         this.timeout = setTimeout(function () {
17930             if (_t.hoverState == 'out') {
17931                 _t.hide();
17932             }
17933         }, this.delay.hide)
17934     },
17935     
17936     show : function (on_el)
17937     {
17938         if (!on_el) {
17939             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17940         }
17941         
17942         // set content.
17943         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17944         if (this.html !== false) {
17945             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17946         }
17947         this.el.removeClass([
17948             'fade','top','bottom', 'left', 'right','in',
17949             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17950         ]);
17951         if (!this.title.length) {
17952             this.el.select('.popover-title',true).hide();
17953         }
17954         
17955         var placement = typeof this.placement == 'function' ?
17956             this.placement.call(this, this.el, on_el) :
17957             this.placement;
17958             
17959         var autoToken = /\s?auto?\s?/i;
17960         var autoPlace = autoToken.test(placement);
17961         if (autoPlace) {
17962             placement = placement.replace(autoToken, '') || 'top';
17963         }
17964         
17965         //this.el.detach()
17966         //this.el.setXY([0,0]);
17967         this.el.show();
17968         this.el.dom.style.display='block';
17969         this.el.addClass(placement);
17970         
17971         //this.el.appendTo(on_el);
17972         
17973         var p = this.getPosition();
17974         var box = this.el.getBox();
17975         
17976         if (autoPlace) {
17977             // fixme..
17978         }
17979         var align = Roo.bootstrap.Popover.alignment[placement];
17980         
17981 //        Roo.log(align);
17982         this.el.alignTo(on_el, align[0],align[1]);
17983         //var arrow = this.el.select('.arrow',true).first();
17984         //arrow.set(align[2], 
17985         
17986         this.el.addClass('in');
17987         
17988         
17989         if (this.el.hasClass('fade')) {
17990             // fade it?
17991         }
17992         
17993         this.hoverState = 'in';
17994         
17995         this.fireEvent('show', this);
17996         
17997     },
17998     hide : function()
17999     {
18000         this.el.setXY([0,0]);
18001         this.el.removeClass('in');
18002         this.el.hide();
18003         this.hoverState = null;
18004         
18005         this.fireEvent('hide', this);
18006     }
18007     
18008 });
18009
18010 Roo.bootstrap.Popover.alignment = {
18011     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18012     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18013     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18014     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18015 };
18016
18017  /*
18018  * - LGPL
18019  *
18020  * Progress
18021  * 
18022  */
18023
18024 /**
18025  * @class Roo.bootstrap.Progress
18026  * @extends Roo.bootstrap.Component
18027  * Bootstrap Progress class
18028  * @cfg {Boolean} striped striped of the progress bar
18029  * @cfg {Boolean} active animated of the progress bar
18030  * 
18031  * 
18032  * @constructor
18033  * Create a new Progress
18034  * @param {Object} config The config object
18035  */
18036
18037 Roo.bootstrap.Progress = function(config){
18038     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18039 };
18040
18041 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18042     
18043     striped : false,
18044     active: false,
18045     
18046     getAutoCreate : function(){
18047         var cfg = {
18048             tag: 'div',
18049             cls: 'progress'
18050         };
18051         
18052         
18053         if(this.striped){
18054             cfg.cls += ' progress-striped';
18055         }
18056       
18057         if(this.active){
18058             cfg.cls += ' active';
18059         }
18060         
18061         
18062         return cfg;
18063     }
18064    
18065 });
18066
18067  
18068
18069  /*
18070  * - LGPL
18071  *
18072  * ProgressBar
18073  * 
18074  */
18075
18076 /**
18077  * @class Roo.bootstrap.ProgressBar
18078  * @extends Roo.bootstrap.Component
18079  * Bootstrap ProgressBar class
18080  * @cfg {Number} aria_valuenow aria-value now
18081  * @cfg {Number} aria_valuemin aria-value min
18082  * @cfg {Number} aria_valuemax aria-value max
18083  * @cfg {String} label label for the progress bar
18084  * @cfg {String} panel (success | info | warning | danger )
18085  * @cfg {String} role role of the progress bar
18086  * @cfg {String} sr_only text
18087  * 
18088  * 
18089  * @constructor
18090  * Create a new ProgressBar
18091  * @param {Object} config The config object
18092  */
18093
18094 Roo.bootstrap.ProgressBar = function(config){
18095     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18096 };
18097
18098 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18099     
18100     aria_valuenow : 0,
18101     aria_valuemin : 0,
18102     aria_valuemax : 100,
18103     label : false,
18104     panel : false,
18105     role : false,
18106     sr_only: false,
18107     
18108     getAutoCreate : function()
18109     {
18110         
18111         var cfg = {
18112             tag: 'div',
18113             cls: 'progress-bar',
18114             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18115         };
18116         
18117         if(this.sr_only){
18118             cfg.cn = {
18119                 tag: 'span',
18120                 cls: 'sr-only',
18121                 html: this.sr_only
18122             }
18123         }
18124         
18125         if(this.role){
18126             cfg.role = this.role;
18127         }
18128         
18129         if(this.aria_valuenow){
18130             cfg['aria-valuenow'] = this.aria_valuenow;
18131         }
18132         
18133         if(this.aria_valuemin){
18134             cfg['aria-valuemin'] = this.aria_valuemin;
18135         }
18136         
18137         if(this.aria_valuemax){
18138             cfg['aria-valuemax'] = this.aria_valuemax;
18139         }
18140         
18141         if(this.label && !this.sr_only){
18142             cfg.html = this.label;
18143         }
18144         
18145         if(this.panel){
18146             cfg.cls += ' progress-bar-' + this.panel;
18147         }
18148         
18149         return cfg;
18150     },
18151     
18152     update : function(aria_valuenow)
18153     {
18154         this.aria_valuenow = aria_valuenow;
18155         
18156         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18157     }
18158    
18159 });
18160
18161  
18162
18163  /*
18164  * - LGPL
18165  *
18166  * column
18167  * 
18168  */
18169
18170 /**
18171  * @class Roo.bootstrap.TabGroup
18172  * @extends Roo.bootstrap.Column
18173  * Bootstrap Column class
18174  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18175  * @cfg {Boolean} carousel true to make the group behave like a carousel
18176  * @cfg {Boolean} bullets show bullets for the panels
18177  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18178  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18179  * @cfg {Boolean} showarrow (true|false) show arrow default true
18180  * 
18181  * @constructor
18182  * Create a new TabGroup
18183  * @param {Object} config The config object
18184  */
18185
18186 Roo.bootstrap.TabGroup = function(config){
18187     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18188     if (!this.navId) {
18189         this.navId = Roo.id();
18190     }
18191     this.tabs = [];
18192     Roo.bootstrap.TabGroup.register(this);
18193     
18194 };
18195
18196 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18197     
18198     carousel : false,
18199     transition : false,
18200     bullets : 0,
18201     timer : 0,
18202     autoslide : false,
18203     slideFn : false,
18204     slideOnTouch : false,
18205     showarrow : true,
18206     
18207     getAutoCreate : function()
18208     {
18209         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18210         
18211         cfg.cls += ' tab-content';
18212         
18213         if (this.carousel) {
18214             cfg.cls += ' carousel slide';
18215             
18216             cfg.cn = [{
18217                cls : 'carousel-inner',
18218                cn : []
18219             }];
18220         
18221             if(this.bullets  && !Roo.isTouch){
18222                 
18223                 var bullets = {
18224                     cls : 'carousel-bullets',
18225                     cn : []
18226                 };
18227                
18228                 if(this.bullets_cls){
18229                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18230                 }
18231                 
18232                 bullets.cn.push({
18233                     cls : 'clear'
18234                 });
18235                 
18236                 cfg.cn[0].cn.push(bullets);
18237             }
18238             
18239             if(this.showarrow){
18240                 cfg.cn[0].cn.push({
18241                     tag : 'div',
18242                     class : 'carousel-arrow',
18243                     cn : [
18244                         {
18245                             tag : 'div',
18246                             class : 'carousel-prev',
18247                             cn : [
18248                                 {
18249                                     tag : 'i',
18250                                     class : 'fa fa-chevron-left'
18251                                 }
18252                             ]
18253                         },
18254                         {
18255                             tag : 'div',
18256                             class : 'carousel-next',
18257                             cn : [
18258                                 {
18259                                     tag : 'i',
18260                                     class : 'fa fa-chevron-right'
18261                                 }
18262                             ]
18263                         }
18264                     ]
18265                 });
18266             }
18267             
18268         }
18269         
18270         return cfg;
18271     },
18272     
18273     initEvents:  function()
18274     {
18275 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18276 //            this.el.on("touchstart", this.onTouchStart, this);
18277 //        }
18278         
18279         if(this.autoslide){
18280             var _this = this;
18281             
18282             this.slideFn = window.setInterval(function() {
18283                 _this.showPanelNext();
18284             }, this.timer);
18285         }
18286         
18287         if(this.showarrow){
18288             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18289             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18290         }
18291         
18292         
18293     },
18294     
18295 //    onTouchStart : function(e, el, o)
18296 //    {
18297 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18298 //            return;
18299 //        }
18300 //        
18301 //        this.showPanelNext();
18302 //    },
18303     
18304     
18305     getChildContainer : function()
18306     {
18307         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18308     },
18309     
18310     /**
18311     * register a Navigation item
18312     * @param {Roo.bootstrap.NavItem} the navitem to add
18313     */
18314     register : function(item)
18315     {
18316         this.tabs.push( item);
18317         item.navId = this.navId; // not really needed..
18318         this.addBullet();
18319     
18320     },
18321     
18322     getActivePanel : function()
18323     {
18324         var r = false;
18325         Roo.each(this.tabs, function(t) {
18326             if (t.active) {
18327                 r = t;
18328                 return false;
18329             }
18330             return null;
18331         });
18332         return r;
18333         
18334     },
18335     getPanelByName : function(n)
18336     {
18337         var r = false;
18338         Roo.each(this.tabs, function(t) {
18339             if (t.tabId == n) {
18340                 r = t;
18341                 return false;
18342             }
18343             return null;
18344         });
18345         return r;
18346     },
18347     indexOfPanel : function(p)
18348     {
18349         var r = false;
18350         Roo.each(this.tabs, function(t,i) {
18351             if (t.tabId == p.tabId) {
18352                 r = i;
18353                 return false;
18354             }
18355             return null;
18356         });
18357         return r;
18358     },
18359     /**
18360      * show a specific panel
18361      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18362      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18363      */
18364     showPanel : function (pan)
18365     {
18366         if(this.transition || typeof(pan) == 'undefined'){
18367             Roo.log("waiting for the transitionend");
18368             return false;
18369         }
18370         
18371         if (typeof(pan) == 'number') {
18372             pan = this.tabs[pan];
18373         }
18374         
18375         if (typeof(pan) == 'string') {
18376             pan = this.getPanelByName(pan);
18377         }
18378         
18379         var cur = this.getActivePanel();
18380         
18381         if(!pan || !cur){
18382             Roo.log('pan or acitve pan is undefined');
18383             return false;
18384         }
18385         
18386         if (pan.tabId == this.getActivePanel().tabId) {
18387             return true;
18388         }
18389         
18390         if (false === cur.fireEvent('beforedeactivate')) {
18391             return false;
18392         }
18393         
18394         if(this.bullets > 0 && !Roo.isTouch){
18395             this.setActiveBullet(this.indexOfPanel(pan));
18396         }
18397         
18398         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18399             
18400             //class="carousel-item carousel-item-next carousel-item-left"
18401             
18402             this.transition = true;
18403             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18404             var lr = dir == 'next' ? 'left' : 'right';
18405             pan.el.addClass(dir); // or prev
18406             pan.el.addClass('carousel-item-' + dir); // or prev
18407             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18408             cur.el.addClass(lr); // or right
18409             pan.el.addClass(lr);
18410             cur.el.addClass('carousel-item-' +lr); // or right
18411             pan.el.addClass('carousel-item-' +lr);
18412             
18413             
18414             var _this = this;
18415             cur.el.on('transitionend', function() {
18416                 Roo.log("trans end?");
18417                 
18418                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18419                 pan.setActive(true);
18420                 
18421                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18422                 cur.setActive(false);
18423                 
18424                 _this.transition = false;
18425                 
18426             }, this, { single:  true } );
18427             
18428             return true;
18429         }
18430         
18431         cur.setActive(false);
18432         pan.setActive(true);
18433         
18434         return true;
18435         
18436     },
18437     showPanelNext : function()
18438     {
18439         var i = this.indexOfPanel(this.getActivePanel());
18440         
18441         if (i >= this.tabs.length - 1 && !this.autoslide) {
18442             return;
18443         }
18444         
18445         if (i >= this.tabs.length - 1 && this.autoslide) {
18446             i = -1;
18447         }
18448         
18449         this.showPanel(this.tabs[i+1]);
18450     },
18451     
18452     showPanelPrev : function()
18453     {
18454         var i = this.indexOfPanel(this.getActivePanel());
18455         
18456         if (i  < 1 && !this.autoslide) {
18457             return;
18458         }
18459         
18460         if (i < 1 && this.autoslide) {
18461             i = this.tabs.length;
18462         }
18463         
18464         this.showPanel(this.tabs[i-1]);
18465     },
18466     
18467     
18468     addBullet: function()
18469     {
18470         if(!this.bullets || Roo.isTouch){
18471             return;
18472         }
18473         var ctr = this.el.select('.carousel-bullets',true).first();
18474         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18475         var bullet = ctr.createChild({
18476             cls : 'bullet bullet-' + i
18477         },ctr.dom.lastChild);
18478         
18479         
18480         var _this = this;
18481         
18482         bullet.on('click', (function(e, el, o, ii, t){
18483
18484             e.preventDefault();
18485
18486             this.showPanel(ii);
18487
18488             if(this.autoslide && this.slideFn){
18489                 clearInterval(this.slideFn);
18490                 this.slideFn = window.setInterval(function() {
18491                     _this.showPanelNext();
18492                 }, this.timer);
18493             }
18494
18495         }).createDelegate(this, [i, bullet], true));
18496                 
18497         
18498     },
18499      
18500     setActiveBullet : function(i)
18501     {
18502         if(Roo.isTouch){
18503             return;
18504         }
18505         
18506         Roo.each(this.el.select('.bullet', true).elements, function(el){
18507             el.removeClass('selected');
18508         });
18509
18510         var bullet = this.el.select('.bullet-' + i, true).first();
18511         
18512         if(!bullet){
18513             return;
18514         }
18515         
18516         bullet.addClass('selected');
18517     }
18518     
18519     
18520   
18521 });
18522
18523  
18524
18525  
18526  
18527 Roo.apply(Roo.bootstrap.TabGroup, {
18528     
18529     groups: {},
18530      /**
18531     * register a Navigation Group
18532     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18533     */
18534     register : function(navgrp)
18535     {
18536         this.groups[navgrp.navId] = navgrp;
18537         
18538     },
18539     /**
18540     * fetch a Navigation Group based on the navigation ID
18541     * if one does not exist , it will get created.
18542     * @param {string} the navgroup to add
18543     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18544     */
18545     get: function(navId) {
18546         if (typeof(this.groups[navId]) == 'undefined') {
18547             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18548         }
18549         return this.groups[navId] ;
18550     }
18551     
18552     
18553     
18554 });
18555
18556  /*
18557  * - LGPL
18558  *
18559  * TabPanel
18560  * 
18561  */
18562
18563 /**
18564  * @class Roo.bootstrap.TabPanel
18565  * @extends Roo.bootstrap.Component
18566  * Bootstrap TabPanel class
18567  * @cfg {Boolean} active panel active
18568  * @cfg {String} html panel content
18569  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18570  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18571  * @cfg {String} href click to link..
18572  * 
18573  * 
18574  * @constructor
18575  * Create a new TabPanel
18576  * @param {Object} config The config object
18577  */
18578
18579 Roo.bootstrap.TabPanel = function(config){
18580     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18581     this.addEvents({
18582         /**
18583              * @event changed
18584              * Fires when the active status changes
18585              * @param {Roo.bootstrap.TabPanel} this
18586              * @param {Boolean} state the new state
18587             
18588          */
18589         'changed': true,
18590         /**
18591              * @event beforedeactivate
18592              * Fires before a tab is de-activated - can be used to do validation on a form.
18593              * @param {Roo.bootstrap.TabPanel} this
18594              * @return {Boolean} false if there is an error
18595             
18596          */
18597         'beforedeactivate': true
18598      });
18599     
18600     this.tabId = this.tabId || Roo.id();
18601   
18602 };
18603
18604 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18605     
18606     active: false,
18607     html: false,
18608     tabId: false,
18609     navId : false,
18610     href : '',
18611     
18612     getAutoCreate : function(){
18613         
18614         
18615         var cfg = {
18616             tag: 'div',
18617             // item is needed for carousel - not sure if it has any effect otherwise
18618             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18619             html: this.html || ''
18620         };
18621         
18622         if(this.active){
18623             cfg.cls += ' active';
18624         }
18625         
18626         if(this.tabId){
18627             cfg.tabId = this.tabId;
18628         }
18629         
18630         
18631         
18632         return cfg;
18633     },
18634     
18635     initEvents:  function()
18636     {
18637         var p = this.parent();
18638         
18639         this.navId = this.navId || p.navId;
18640         
18641         if (typeof(this.navId) != 'undefined') {
18642             // not really needed.. but just in case.. parent should be a NavGroup.
18643             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18644             
18645             tg.register(this);
18646             
18647             var i = tg.tabs.length - 1;
18648             
18649             if(this.active && tg.bullets > 0 && i < tg.bullets){
18650                 tg.setActiveBullet(i);
18651             }
18652         }
18653         
18654         this.el.on('click', this.onClick, this);
18655         
18656         if(Roo.isTouch){
18657             this.el.on("touchstart", this.onTouchStart, this);
18658             this.el.on("touchmove", this.onTouchMove, this);
18659             this.el.on("touchend", this.onTouchEnd, this);
18660         }
18661         
18662     },
18663     
18664     onRender : function(ct, position)
18665     {
18666         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18667     },
18668     
18669     setActive : function(state)
18670     {
18671         Roo.log("panel - set active " + this.tabId + "=" + state);
18672         
18673         this.active = state;
18674         if (!state) {
18675             this.el.removeClass('active');
18676             
18677         } else  if (!this.el.hasClass('active')) {
18678             this.el.addClass('active');
18679         }
18680         
18681         this.fireEvent('changed', this, state);
18682     },
18683     
18684     onClick : function(e)
18685     {
18686         e.preventDefault();
18687         
18688         if(!this.href.length){
18689             return;
18690         }
18691         
18692         window.location.href = this.href;
18693     },
18694     
18695     startX : 0,
18696     startY : 0,
18697     endX : 0,
18698     endY : 0,
18699     swiping : false,
18700     
18701     onTouchStart : function(e)
18702     {
18703         this.swiping = false;
18704         
18705         this.startX = e.browserEvent.touches[0].clientX;
18706         this.startY = e.browserEvent.touches[0].clientY;
18707     },
18708     
18709     onTouchMove : function(e)
18710     {
18711         this.swiping = true;
18712         
18713         this.endX = e.browserEvent.touches[0].clientX;
18714         this.endY = e.browserEvent.touches[0].clientY;
18715     },
18716     
18717     onTouchEnd : function(e)
18718     {
18719         if(!this.swiping){
18720             this.onClick(e);
18721             return;
18722         }
18723         
18724         var tabGroup = this.parent();
18725         
18726         if(this.endX > this.startX){ // swiping right
18727             tabGroup.showPanelPrev();
18728             return;
18729         }
18730         
18731         if(this.startX > this.endX){ // swiping left
18732             tabGroup.showPanelNext();
18733             return;
18734         }
18735     }
18736     
18737     
18738 });
18739  
18740
18741  
18742
18743  /*
18744  * - LGPL
18745  *
18746  * DateField
18747  * 
18748  */
18749
18750 /**
18751  * @class Roo.bootstrap.DateField
18752  * @extends Roo.bootstrap.Input
18753  * Bootstrap DateField class
18754  * @cfg {Number} weekStart default 0
18755  * @cfg {String} viewMode default empty, (months|years)
18756  * @cfg {String} minViewMode default empty, (months|years)
18757  * @cfg {Number} startDate default -Infinity
18758  * @cfg {Number} endDate default Infinity
18759  * @cfg {Boolean} todayHighlight default false
18760  * @cfg {Boolean} todayBtn default false
18761  * @cfg {Boolean} calendarWeeks default false
18762  * @cfg {Object} daysOfWeekDisabled default empty
18763  * @cfg {Boolean} singleMode default false (true | false)
18764  * 
18765  * @cfg {Boolean} keyboardNavigation default true
18766  * @cfg {String} language default en
18767  * 
18768  * @constructor
18769  * Create a new DateField
18770  * @param {Object} config The config object
18771  */
18772
18773 Roo.bootstrap.DateField = function(config){
18774     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18775      this.addEvents({
18776             /**
18777              * @event show
18778              * Fires when this field show.
18779              * @param {Roo.bootstrap.DateField} this
18780              * @param {Mixed} date The date value
18781              */
18782             show : true,
18783             /**
18784              * @event show
18785              * Fires when this field hide.
18786              * @param {Roo.bootstrap.DateField} this
18787              * @param {Mixed} date The date value
18788              */
18789             hide : true,
18790             /**
18791              * @event select
18792              * Fires when select a date.
18793              * @param {Roo.bootstrap.DateField} this
18794              * @param {Mixed} date The date value
18795              */
18796             select : true,
18797             /**
18798              * @event beforeselect
18799              * Fires when before select a date.
18800              * @param {Roo.bootstrap.DateField} this
18801              * @param {Mixed} date The date value
18802              */
18803             beforeselect : true
18804         });
18805 };
18806
18807 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18808     
18809     /**
18810      * @cfg {String} format
18811      * The default date format string which can be overriden for localization support.  The format must be
18812      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18813      */
18814     format : "m/d/y",
18815     /**
18816      * @cfg {String} altFormats
18817      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18818      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18819      */
18820     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18821     
18822     weekStart : 0,
18823     
18824     viewMode : '',
18825     
18826     minViewMode : '',
18827     
18828     todayHighlight : false,
18829     
18830     todayBtn: false,
18831     
18832     language: 'en',
18833     
18834     keyboardNavigation: true,
18835     
18836     calendarWeeks: false,
18837     
18838     startDate: -Infinity,
18839     
18840     endDate: Infinity,
18841     
18842     daysOfWeekDisabled: [],
18843     
18844     _events: [],
18845     
18846     singleMode : false,
18847     
18848     UTCDate: function()
18849     {
18850         return new Date(Date.UTC.apply(Date, arguments));
18851     },
18852     
18853     UTCToday: function()
18854     {
18855         var today = new Date();
18856         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18857     },
18858     
18859     getDate: function() {
18860             var d = this.getUTCDate();
18861             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18862     },
18863     
18864     getUTCDate: function() {
18865             return this.date;
18866     },
18867     
18868     setDate: function(d) {
18869             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18870     },
18871     
18872     setUTCDate: function(d) {
18873             this.date = d;
18874             this.setValue(this.formatDate(this.date));
18875     },
18876         
18877     onRender: function(ct, position)
18878     {
18879         
18880         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18881         
18882         this.language = this.language || 'en';
18883         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18884         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18885         
18886         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18887         this.format = this.format || 'm/d/y';
18888         this.isInline = false;
18889         this.isInput = true;
18890         this.component = this.el.select('.add-on', true).first() || false;
18891         this.component = (this.component && this.component.length === 0) ? false : this.component;
18892         this.hasInput = this.component && this.inputEl().length;
18893         
18894         if (typeof(this.minViewMode === 'string')) {
18895             switch (this.minViewMode) {
18896                 case 'months':
18897                     this.minViewMode = 1;
18898                     break;
18899                 case 'years':
18900                     this.minViewMode = 2;
18901                     break;
18902                 default:
18903                     this.minViewMode = 0;
18904                     break;
18905             }
18906         }
18907         
18908         if (typeof(this.viewMode === 'string')) {
18909             switch (this.viewMode) {
18910                 case 'months':
18911                     this.viewMode = 1;
18912                     break;
18913                 case 'years':
18914                     this.viewMode = 2;
18915                     break;
18916                 default:
18917                     this.viewMode = 0;
18918                     break;
18919             }
18920         }
18921                 
18922         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18923         
18924 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18925         
18926         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18927         
18928         this.picker().on('mousedown', this.onMousedown, this);
18929         this.picker().on('click', this.onClick, this);
18930         
18931         this.picker().addClass('datepicker-dropdown');
18932         
18933         this.startViewMode = this.viewMode;
18934         
18935         if(this.singleMode){
18936             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18937                 v.setVisibilityMode(Roo.Element.DISPLAY);
18938                 v.hide();
18939             });
18940             
18941             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18942                 v.setStyle('width', '189px');
18943             });
18944         }
18945         
18946         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18947             if(!this.calendarWeeks){
18948                 v.remove();
18949                 return;
18950             }
18951             
18952             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18953             v.attr('colspan', function(i, val){
18954                 return parseInt(val) + 1;
18955             });
18956         });
18957                         
18958         
18959         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18960         
18961         this.setStartDate(this.startDate);
18962         this.setEndDate(this.endDate);
18963         
18964         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18965         
18966         this.fillDow();
18967         this.fillMonths();
18968         this.update();
18969         this.showMode();
18970         
18971         if(this.isInline) {
18972             this.showPopup();
18973         }
18974     },
18975     
18976     picker : function()
18977     {
18978         return this.pickerEl;
18979 //        return this.el.select('.datepicker', true).first();
18980     },
18981     
18982     fillDow: function()
18983     {
18984         var dowCnt = this.weekStart;
18985         
18986         var dow = {
18987             tag: 'tr',
18988             cn: [
18989                 
18990             ]
18991         };
18992         
18993         if(this.calendarWeeks){
18994             dow.cn.push({
18995                 tag: 'th',
18996                 cls: 'cw',
18997                 html: '&nbsp;'
18998             })
18999         }
19000         
19001         while (dowCnt < this.weekStart + 7) {
19002             dow.cn.push({
19003                 tag: 'th',
19004                 cls: 'dow',
19005                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19006             });
19007         }
19008         
19009         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19010     },
19011     
19012     fillMonths: function()
19013     {    
19014         var i = 0;
19015         var months = this.picker().select('>.datepicker-months td', true).first();
19016         
19017         months.dom.innerHTML = '';
19018         
19019         while (i < 12) {
19020             var month = {
19021                 tag: 'span',
19022                 cls: 'month',
19023                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19024             };
19025             
19026             months.createChild(month);
19027         }
19028         
19029     },
19030     
19031     update: function()
19032     {
19033         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;
19034         
19035         if (this.date < this.startDate) {
19036             this.viewDate = new Date(this.startDate);
19037         } else if (this.date > this.endDate) {
19038             this.viewDate = new Date(this.endDate);
19039         } else {
19040             this.viewDate = new Date(this.date);
19041         }
19042         
19043         this.fill();
19044     },
19045     
19046     fill: function() 
19047     {
19048         var d = new Date(this.viewDate),
19049                 year = d.getUTCFullYear(),
19050                 month = d.getUTCMonth(),
19051                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19052                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19053                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19054                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19055                 currentDate = this.date && this.date.valueOf(),
19056                 today = this.UTCToday();
19057         
19058         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19059         
19060 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19061         
19062 //        this.picker.select('>tfoot th.today').
19063 //                                              .text(dates[this.language].today)
19064 //                                              .toggle(this.todayBtn !== false);
19065     
19066         this.updateNavArrows();
19067         this.fillMonths();
19068                                                 
19069         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19070         
19071         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19072          
19073         prevMonth.setUTCDate(day);
19074         
19075         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19076         
19077         var nextMonth = new Date(prevMonth);
19078         
19079         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19080         
19081         nextMonth = nextMonth.valueOf();
19082         
19083         var fillMonths = false;
19084         
19085         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19086         
19087         while(prevMonth.valueOf() <= nextMonth) {
19088             var clsName = '';
19089             
19090             if (prevMonth.getUTCDay() === this.weekStart) {
19091                 if(fillMonths){
19092                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19093                 }
19094                     
19095                 fillMonths = {
19096                     tag: 'tr',
19097                     cn: []
19098                 };
19099                 
19100                 if(this.calendarWeeks){
19101                     // ISO 8601: First week contains first thursday.
19102                     // ISO also states week starts on Monday, but we can be more abstract here.
19103                     var
19104                     // Start of current week: based on weekstart/current date
19105                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19106                     // Thursday of this week
19107                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19108                     // First Thursday of year, year from thursday
19109                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19110                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19111                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19112                     
19113                     fillMonths.cn.push({
19114                         tag: 'td',
19115                         cls: 'cw',
19116                         html: calWeek
19117                     });
19118                 }
19119             }
19120             
19121             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19122                 clsName += ' old';
19123             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19124                 clsName += ' new';
19125             }
19126             if (this.todayHighlight &&
19127                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19128                 prevMonth.getUTCMonth() == today.getMonth() &&
19129                 prevMonth.getUTCDate() == today.getDate()) {
19130                 clsName += ' today';
19131             }
19132             
19133             if (currentDate && prevMonth.valueOf() === currentDate) {
19134                 clsName += ' active';
19135             }
19136             
19137             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19138                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19139                     clsName += ' disabled';
19140             }
19141             
19142             fillMonths.cn.push({
19143                 tag: 'td',
19144                 cls: 'day ' + clsName,
19145                 html: prevMonth.getDate()
19146             });
19147             
19148             prevMonth.setDate(prevMonth.getDate()+1);
19149         }
19150           
19151         var currentYear = this.date && this.date.getUTCFullYear();
19152         var currentMonth = this.date && this.date.getUTCMonth();
19153         
19154         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19155         
19156         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19157             v.removeClass('active');
19158             
19159             if(currentYear === year && k === currentMonth){
19160                 v.addClass('active');
19161             }
19162             
19163             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19164                 v.addClass('disabled');
19165             }
19166             
19167         });
19168         
19169         
19170         year = parseInt(year/10, 10) * 10;
19171         
19172         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19173         
19174         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19175         
19176         year -= 1;
19177         for (var i = -1; i < 11; i++) {
19178             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19179                 tag: 'span',
19180                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19181                 html: year
19182             });
19183             
19184             year += 1;
19185         }
19186     },
19187     
19188     showMode: function(dir) 
19189     {
19190         if (dir) {
19191             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19192         }
19193         
19194         Roo.each(this.picker().select('>div',true).elements, function(v){
19195             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19196             v.hide();
19197         });
19198         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19199     },
19200     
19201     place: function()
19202     {
19203         if(this.isInline) {
19204             return;
19205         }
19206         
19207         this.picker().removeClass(['bottom', 'top']);
19208         
19209         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19210             /*
19211              * place to the top of element!
19212              *
19213              */
19214             
19215             this.picker().addClass('top');
19216             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19217             
19218             return;
19219         }
19220         
19221         this.picker().addClass('bottom');
19222         
19223         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19224     },
19225     
19226     parseDate : function(value)
19227     {
19228         if(!value || value instanceof Date){
19229             return value;
19230         }
19231         var v = Date.parseDate(value, this.format);
19232         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19233             v = Date.parseDate(value, 'Y-m-d');
19234         }
19235         if(!v && this.altFormats){
19236             if(!this.altFormatsArray){
19237                 this.altFormatsArray = this.altFormats.split("|");
19238             }
19239             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19240                 v = Date.parseDate(value, this.altFormatsArray[i]);
19241             }
19242         }
19243         return v;
19244     },
19245     
19246     formatDate : function(date, fmt)
19247     {   
19248         return (!date || !(date instanceof Date)) ?
19249         date : date.dateFormat(fmt || this.format);
19250     },
19251     
19252     onFocus : function()
19253     {
19254         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19255         this.showPopup();
19256     },
19257     
19258     onBlur : function()
19259     {
19260         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19261         
19262         var d = this.inputEl().getValue();
19263         
19264         this.setValue(d);
19265                 
19266         this.hidePopup();
19267     },
19268     
19269     showPopup : function()
19270     {
19271         this.picker().show();
19272         this.update();
19273         this.place();
19274         
19275         this.fireEvent('showpopup', this, this.date);
19276     },
19277     
19278     hidePopup : function()
19279     {
19280         if(this.isInline) {
19281             return;
19282         }
19283         this.picker().hide();
19284         this.viewMode = this.startViewMode;
19285         this.showMode();
19286         
19287         this.fireEvent('hidepopup', this, this.date);
19288         
19289     },
19290     
19291     onMousedown: function(e)
19292     {
19293         e.stopPropagation();
19294         e.preventDefault();
19295     },
19296     
19297     keyup: function(e)
19298     {
19299         Roo.bootstrap.DateField.superclass.keyup.call(this);
19300         this.update();
19301     },
19302
19303     setValue: function(v)
19304     {
19305         if(this.fireEvent('beforeselect', this, v) !== false){
19306             var d = new Date(this.parseDate(v) ).clearTime();
19307         
19308             if(isNaN(d.getTime())){
19309                 this.date = this.viewDate = '';
19310                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19311                 return;
19312             }
19313
19314             v = this.formatDate(d);
19315
19316             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19317
19318             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19319
19320             this.update();
19321
19322             this.fireEvent('select', this, this.date);
19323         }
19324     },
19325     
19326     getValue: function()
19327     {
19328         return this.formatDate(this.date);
19329     },
19330     
19331     fireKey: function(e)
19332     {
19333         if (!this.picker().isVisible()){
19334             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19335                 this.showPopup();
19336             }
19337             return;
19338         }
19339         
19340         var dateChanged = false,
19341         dir, day, month,
19342         newDate, newViewDate;
19343         
19344         switch(e.keyCode){
19345             case 27: // escape
19346                 this.hidePopup();
19347                 e.preventDefault();
19348                 break;
19349             case 37: // left
19350             case 39: // right
19351                 if (!this.keyboardNavigation) {
19352                     break;
19353                 }
19354                 dir = e.keyCode == 37 ? -1 : 1;
19355                 
19356                 if (e.ctrlKey){
19357                     newDate = this.moveYear(this.date, dir);
19358                     newViewDate = this.moveYear(this.viewDate, dir);
19359                 } else if (e.shiftKey){
19360                     newDate = this.moveMonth(this.date, dir);
19361                     newViewDate = this.moveMonth(this.viewDate, dir);
19362                 } else {
19363                     newDate = new Date(this.date);
19364                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19365                     newViewDate = new Date(this.viewDate);
19366                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19367                 }
19368                 if (this.dateWithinRange(newDate)){
19369                     this.date = newDate;
19370                     this.viewDate = newViewDate;
19371                     this.setValue(this.formatDate(this.date));
19372 //                    this.update();
19373                     e.preventDefault();
19374                     dateChanged = true;
19375                 }
19376                 break;
19377             case 38: // up
19378             case 40: // down
19379                 if (!this.keyboardNavigation) {
19380                     break;
19381                 }
19382                 dir = e.keyCode == 38 ? -1 : 1;
19383                 if (e.ctrlKey){
19384                     newDate = this.moveYear(this.date, dir);
19385                     newViewDate = this.moveYear(this.viewDate, dir);
19386                 } else if (e.shiftKey){
19387                     newDate = this.moveMonth(this.date, dir);
19388                     newViewDate = this.moveMonth(this.viewDate, dir);
19389                 } else {
19390                     newDate = new Date(this.date);
19391                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19392                     newViewDate = new Date(this.viewDate);
19393                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19394                 }
19395                 if (this.dateWithinRange(newDate)){
19396                     this.date = newDate;
19397                     this.viewDate = newViewDate;
19398                     this.setValue(this.formatDate(this.date));
19399 //                    this.update();
19400                     e.preventDefault();
19401                     dateChanged = true;
19402                 }
19403                 break;
19404             case 13: // enter
19405                 this.setValue(this.formatDate(this.date));
19406                 this.hidePopup();
19407                 e.preventDefault();
19408                 break;
19409             case 9: // tab
19410                 this.setValue(this.formatDate(this.date));
19411                 this.hidePopup();
19412                 break;
19413             case 16: // shift
19414             case 17: // ctrl
19415             case 18: // alt
19416                 break;
19417             default :
19418                 this.hidePopup();
19419                 
19420         }
19421     },
19422     
19423     
19424     onClick: function(e) 
19425     {
19426         e.stopPropagation();
19427         e.preventDefault();
19428         
19429         var target = e.getTarget();
19430         
19431         if(target.nodeName.toLowerCase() === 'i'){
19432             target = Roo.get(target).dom.parentNode;
19433         }
19434         
19435         var nodeName = target.nodeName;
19436         var className = target.className;
19437         var html = target.innerHTML;
19438         //Roo.log(nodeName);
19439         
19440         switch(nodeName.toLowerCase()) {
19441             case 'th':
19442                 switch(className) {
19443                     case 'switch':
19444                         this.showMode(1);
19445                         break;
19446                     case 'prev':
19447                     case 'next':
19448                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19449                         switch(this.viewMode){
19450                                 case 0:
19451                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19452                                         break;
19453                                 case 1:
19454                                 case 2:
19455                                         this.viewDate = this.moveYear(this.viewDate, dir);
19456                                         break;
19457                         }
19458                         this.fill();
19459                         break;
19460                     case 'today':
19461                         var date = new Date();
19462                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19463 //                        this.fill()
19464                         this.setValue(this.formatDate(this.date));
19465                         
19466                         this.hidePopup();
19467                         break;
19468                 }
19469                 break;
19470             case 'span':
19471                 if (className.indexOf('disabled') < 0) {
19472                     this.viewDate.setUTCDate(1);
19473                     if (className.indexOf('month') > -1) {
19474                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19475                     } else {
19476                         var year = parseInt(html, 10) || 0;
19477                         this.viewDate.setUTCFullYear(year);
19478                         
19479                     }
19480                     
19481                     if(this.singleMode){
19482                         this.setValue(this.formatDate(this.viewDate));
19483                         this.hidePopup();
19484                         return;
19485                     }
19486                     
19487                     this.showMode(-1);
19488                     this.fill();
19489                 }
19490                 break;
19491                 
19492             case 'td':
19493                 //Roo.log(className);
19494                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19495                     var day = parseInt(html, 10) || 1;
19496                     var year = this.viewDate.getUTCFullYear(),
19497                         month = this.viewDate.getUTCMonth();
19498
19499                     if (className.indexOf('old') > -1) {
19500                         if(month === 0 ){
19501                             month = 11;
19502                             year -= 1;
19503                         }else{
19504                             month -= 1;
19505                         }
19506                     } else if (className.indexOf('new') > -1) {
19507                         if (month == 11) {
19508                             month = 0;
19509                             year += 1;
19510                         } else {
19511                             month += 1;
19512                         }
19513                     }
19514                     //Roo.log([year,month,day]);
19515                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19516                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19517 //                    this.fill();
19518                     //Roo.log(this.formatDate(this.date));
19519                     this.setValue(this.formatDate(this.date));
19520                     this.hidePopup();
19521                 }
19522                 break;
19523         }
19524     },
19525     
19526     setStartDate: function(startDate)
19527     {
19528         this.startDate = startDate || -Infinity;
19529         if (this.startDate !== -Infinity) {
19530             this.startDate = this.parseDate(this.startDate);
19531         }
19532         this.update();
19533         this.updateNavArrows();
19534     },
19535
19536     setEndDate: function(endDate)
19537     {
19538         this.endDate = endDate || Infinity;
19539         if (this.endDate !== Infinity) {
19540             this.endDate = this.parseDate(this.endDate);
19541         }
19542         this.update();
19543         this.updateNavArrows();
19544     },
19545     
19546     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19547     {
19548         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19549         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19550             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19551         }
19552         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19553             return parseInt(d, 10);
19554         });
19555         this.update();
19556         this.updateNavArrows();
19557     },
19558     
19559     updateNavArrows: function() 
19560     {
19561         if(this.singleMode){
19562             return;
19563         }
19564         
19565         var d = new Date(this.viewDate),
19566         year = d.getUTCFullYear(),
19567         month = d.getUTCMonth();
19568         
19569         Roo.each(this.picker().select('.prev', true).elements, function(v){
19570             v.show();
19571             switch (this.viewMode) {
19572                 case 0:
19573
19574                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19575                         v.hide();
19576                     }
19577                     break;
19578                 case 1:
19579                 case 2:
19580                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19581                         v.hide();
19582                     }
19583                     break;
19584             }
19585         });
19586         
19587         Roo.each(this.picker().select('.next', true).elements, function(v){
19588             v.show();
19589             switch (this.viewMode) {
19590                 case 0:
19591
19592                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19593                         v.hide();
19594                     }
19595                     break;
19596                 case 1:
19597                 case 2:
19598                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19599                         v.hide();
19600                     }
19601                     break;
19602             }
19603         })
19604     },
19605     
19606     moveMonth: function(date, dir)
19607     {
19608         if (!dir) {
19609             return date;
19610         }
19611         var new_date = new Date(date.valueOf()),
19612         day = new_date.getUTCDate(),
19613         month = new_date.getUTCMonth(),
19614         mag = Math.abs(dir),
19615         new_month, test;
19616         dir = dir > 0 ? 1 : -1;
19617         if (mag == 1){
19618             test = dir == -1
19619             // If going back one month, make sure month is not current month
19620             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19621             ? function(){
19622                 return new_date.getUTCMonth() == month;
19623             }
19624             // If going forward one month, make sure month is as expected
19625             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19626             : function(){
19627                 return new_date.getUTCMonth() != new_month;
19628             };
19629             new_month = month + dir;
19630             new_date.setUTCMonth(new_month);
19631             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19632             if (new_month < 0 || new_month > 11) {
19633                 new_month = (new_month + 12) % 12;
19634             }
19635         } else {
19636             // For magnitudes >1, move one month at a time...
19637             for (var i=0; i<mag; i++) {
19638                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19639                 new_date = this.moveMonth(new_date, dir);
19640             }
19641             // ...then reset the day, keeping it in the new month
19642             new_month = new_date.getUTCMonth();
19643             new_date.setUTCDate(day);
19644             test = function(){
19645                 return new_month != new_date.getUTCMonth();
19646             };
19647         }
19648         // Common date-resetting loop -- if date is beyond end of month, make it
19649         // end of month
19650         while (test()){
19651             new_date.setUTCDate(--day);
19652             new_date.setUTCMonth(new_month);
19653         }
19654         return new_date;
19655     },
19656
19657     moveYear: function(date, dir)
19658     {
19659         return this.moveMonth(date, dir*12);
19660     },
19661
19662     dateWithinRange: function(date)
19663     {
19664         return date >= this.startDate && date <= this.endDate;
19665     },
19666
19667     
19668     remove: function() 
19669     {
19670         this.picker().remove();
19671     },
19672     
19673     validateValue : function(value)
19674     {
19675         if(this.getVisibilityEl().hasClass('hidden')){
19676             return true;
19677         }
19678         
19679         if(value.length < 1)  {
19680             if(this.allowBlank){
19681                 return true;
19682             }
19683             return false;
19684         }
19685         
19686         if(value.length < this.minLength){
19687             return false;
19688         }
19689         if(value.length > this.maxLength){
19690             return false;
19691         }
19692         if(this.vtype){
19693             var vt = Roo.form.VTypes;
19694             if(!vt[this.vtype](value, this)){
19695                 return false;
19696             }
19697         }
19698         if(typeof this.validator == "function"){
19699             var msg = this.validator(value);
19700             if(msg !== true){
19701                 return false;
19702             }
19703         }
19704         
19705         if(this.regex && !this.regex.test(value)){
19706             return false;
19707         }
19708         
19709         if(typeof(this.parseDate(value)) == 'undefined'){
19710             return false;
19711         }
19712         
19713         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19714             return false;
19715         }      
19716         
19717         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19718             return false;
19719         } 
19720         
19721         
19722         return true;
19723     },
19724     
19725     reset : function()
19726     {
19727         this.date = this.viewDate = '';
19728         
19729         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19730     }
19731    
19732 });
19733
19734 Roo.apply(Roo.bootstrap.DateField,  {
19735     
19736     head : {
19737         tag: 'thead',
19738         cn: [
19739         {
19740             tag: 'tr',
19741             cn: [
19742             {
19743                 tag: 'th',
19744                 cls: 'prev',
19745                 html: '<i class="fa fa-arrow-left"/>'
19746             },
19747             {
19748                 tag: 'th',
19749                 cls: 'switch',
19750                 colspan: '5'
19751             },
19752             {
19753                 tag: 'th',
19754                 cls: 'next',
19755                 html: '<i class="fa fa-arrow-right"/>'
19756             }
19757
19758             ]
19759         }
19760         ]
19761     },
19762     
19763     content : {
19764         tag: 'tbody',
19765         cn: [
19766         {
19767             tag: 'tr',
19768             cn: [
19769             {
19770                 tag: 'td',
19771                 colspan: '7'
19772             }
19773             ]
19774         }
19775         ]
19776     },
19777     
19778     footer : {
19779         tag: 'tfoot',
19780         cn: [
19781         {
19782             tag: 'tr',
19783             cn: [
19784             {
19785                 tag: 'th',
19786                 colspan: '7',
19787                 cls: 'today'
19788             }
19789                     
19790             ]
19791         }
19792         ]
19793     },
19794     
19795     dates:{
19796         en: {
19797             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19798             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19799             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19800             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19801             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19802             today: "Today"
19803         }
19804     },
19805     
19806     modes: [
19807     {
19808         clsName: 'days',
19809         navFnc: 'Month',
19810         navStep: 1
19811     },
19812     {
19813         clsName: 'months',
19814         navFnc: 'FullYear',
19815         navStep: 1
19816     },
19817     {
19818         clsName: 'years',
19819         navFnc: 'FullYear',
19820         navStep: 10
19821     }]
19822 });
19823
19824 Roo.apply(Roo.bootstrap.DateField,  {
19825   
19826     template : {
19827         tag: 'div',
19828         cls: 'datepicker dropdown-menu roo-dynamic',
19829         cn: [
19830         {
19831             tag: 'div',
19832             cls: 'datepicker-days',
19833             cn: [
19834             {
19835                 tag: 'table',
19836                 cls: 'table-condensed',
19837                 cn:[
19838                 Roo.bootstrap.DateField.head,
19839                 {
19840                     tag: 'tbody'
19841                 },
19842                 Roo.bootstrap.DateField.footer
19843                 ]
19844             }
19845             ]
19846         },
19847         {
19848             tag: 'div',
19849             cls: 'datepicker-months',
19850             cn: [
19851             {
19852                 tag: 'table',
19853                 cls: 'table-condensed',
19854                 cn:[
19855                 Roo.bootstrap.DateField.head,
19856                 Roo.bootstrap.DateField.content,
19857                 Roo.bootstrap.DateField.footer
19858                 ]
19859             }
19860             ]
19861         },
19862         {
19863             tag: 'div',
19864             cls: 'datepicker-years',
19865             cn: [
19866             {
19867                 tag: 'table',
19868                 cls: 'table-condensed',
19869                 cn:[
19870                 Roo.bootstrap.DateField.head,
19871                 Roo.bootstrap.DateField.content,
19872                 Roo.bootstrap.DateField.footer
19873                 ]
19874             }
19875             ]
19876         }
19877         ]
19878     }
19879 });
19880
19881  
19882
19883  /*
19884  * - LGPL
19885  *
19886  * TimeField
19887  * 
19888  */
19889
19890 /**
19891  * @class Roo.bootstrap.TimeField
19892  * @extends Roo.bootstrap.Input
19893  * Bootstrap DateField class
19894  * 
19895  * 
19896  * @constructor
19897  * Create a new TimeField
19898  * @param {Object} config The config object
19899  */
19900
19901 Roo.bootstrap.TimeField = function(config){
19902     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19903     this.addEvents({
19904             /**
19905              * @event show
19906              * Fires when this field show.
19907              * @param {Roo.bootstrap.DateField} thisthis
19908              * @param {Mixed} date The date value
19909              */
19910             show : true,
19911             /**
19912              * @event show
19913              * Fires when this field hide.
19914              * @param {Roo.bootstrap.DateField} this
19915              * @param {Mixed} date The date value
19916              */
19917             hide : true,
19918             /**
19919              * @event select
19920              * Fires when select a date.
19921              * @param {Roo.bootstrap.DateField} this
19922              * @param {Mixed} date The date value
19923              */
19924             select : true
19925         });
19926 };
19927
19928 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19929     
19930     /**
19931      * @cfg {String} format
19932      * The default time format string which can be overriden for localization support.  The format must be
19933      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19934      */
19935     format : "H:i",
19936        
19937     onRender: function(ct, position)
19938     {
19939         
19940         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19941                 
19942         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19943         
19944         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19945         
19946         this.pop = this.picker().select('>.datepicker-time',true).first();
19947         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19948         
19949         this.picker().on('mousedown', this.onMousedown, this);
19950         this.picker().on('click', this.onClick, this);
19951         
19952         this.picker().addClass('datepicker-dropdown');
19953     
19954         this.fillTime();
19955         this.update();
19956             
19957         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19958         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19959         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19960         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19961         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19962         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19963
19964     },
19965     
19966     fireKey: function(e){
19967         if (!this.picker().isVisible()){
19968             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19969                 this.show();
19970             }
19971             return;
19972         }
19973
19974         e.preventDefault();
19975         
19976         switch(e.keyCode){
19977             case 27: // escape
19978                 this.hide();
19979                 break;
19980             case 37: // left
19981             case 39: // right
19982                 this.onTogglePeriod();
19983                 break;
19984             case 38: // up
19985                 this.onIncrementMinutes();
19986                 break;
19987             case 40: // down
19988                 this.onDecrementMinutes();
19989                 break;
19990             case 13: // enter
19991             case 9: // tab
19992                 this.setTime();
19993                 break;
19994         }
19995     },
19996     
19997     onClick: function(e) {
19998         e.stopPropagation();
19999         e.preventDefault();
20000     },
20001     
20002     picker : function()
20003     {
20004         return this.el.select('.datepicker', true).first();
20005     },
20006     
20007     fillTime: function()
20008     {    
20009         var time = this.pop.select('tbody', true).first();
20010         
20011         time.dom.innerHTML = '';
20012         
20013         time.createChild({
20014             tag: 'tr',
20015             cn: [
20016                 {
20017                     tag: 'td',
20018                     cn: [
20019                         {
20020                             tag: 'a',
20021                             href: '#',
20022                             cls: 'btn',
20023                             cn: [
20024                                 {
20025                                     tag: 'span',
20026                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20027                                 }
20028                             ]
20029                         } 
20030                     ]
20031                 },
20032                 {
20033                     tag: 'td',
20034                     cls: 'separator'
20035                 },
20036                 {
20037                     tag: 'td',
20038                     cn: [
20039                         {
20040                             tag: 'a',
20041                             href: '#',
20042                             cls: 'btn',
20043                             cn: [
20044                                 {
20045                                     tag: 'span',
20046                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20047                                 }
20048                             ]
20049                         }
20050                     ]
20051                 },
20052                 {
20053                     tag: 'td',
20054                     cls: 'separator'
20055                 }
20056             ]
20057         });
20058         
20059         time.createChild({
20060             tag: 'tr',
20061             cn: [
20062                 {
20063                     tag: 'td',
20064                     cn: [
20065                         {
20066                             tag: 'span',
20067                             cls: 'timepicker-hour',
20068                             html: '00'
20069                         }  
20070                     ]
20071                 },
20072                 {
20073                     tag: 'td',
20074                     cls: 'separator',
20075                     html: ':'
20076                 },
20077                 {
20078                     tag: 'td',
20079                     cn: [
20080                         {
20081                             tag: 'span',
20082                             cls: 'timepicker-minute',
20083                             html: '00'
20084                         }  
20085                     ]
20086                 },
20087                 {
20088                     tag: 'td',
20089                     cls: 'separator'
20090                 },
20091                 {
20092                     tag: 'td',
20093                     cn: [
20094                         {
20095                             tag: 'button',
20096                             type: 'button',
20097                             cls: 'btn btn-primary period',
20098                             html: 'AM'
20099                             
20100                         }
20101                     ]
20102                 }
20103             ]
20104         });
20105         
20106         time.createChild({
20107             tag: 'tr',
20108             cn: [
20109                 {
20110                     tag: 'td',
20111                     cn: [
20112                         {
20113                             tag: 'a',
20114                             href: '#',
20115                             cls: 'btn',
20116                             cn: [
20117                                 {
20118                                     tag: 'span',
20119                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20120                                 }
20121                             ]
20122                         }
20123                     ]
20124                 },
20125                 {
20126                     tag: 'td',
20127                     cls: 'separator'
20128                 },
20129                 {
20130                     tag: 'td',
20131                     cn: [
20132                         {
20133                             tag: 'a',
20134                             href: '#',
20135                             cls: 'btn',
20136                             cn: [
20137                                 {
20138                                     tag: 'span',
20139                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20140                                 }
20141                             ]
20142                         }
20143                     ]
20144                 },
20145                 {
20146                     tag: 'td',
20147                     cls: 'separator'
20148                 }
20149             ]
20150         });
20151         
20152     },
20153     
20154     update: function()
20155     {
20156         
20157         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20158         
20159         this.fill();
20160     },
20161     
20162     fill: function() 
20163     {
20164         var hours = this.time.getHours();
20165         var minutes = this.time.getMinutes();
20166         var period = 'AM';
20167         
20168         if(hours > 11){
20169             period = 'PM';
20170         }
20171         
20172         if(hours == 0){
20173             hours = 12;
20174         }
20175         
20176         
20177         if(hours > 12){
20178             hours = hours - 12;
20179         }
20180         
20181         if(hours < 10){
20182             hours = '0' + hours;
20183         }
20184         
20185         if(minutes < 10){
20186             minutes = '0' + minutes;
20187         }
20188         
20189         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20190         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20191         this.pop.select('button', true).first().dom.innerHTML = period;
20192         
20193     },
20194     
20195     place: function()
20196     {   
20197         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20198         
20199         var cls = ['bottom'];
20200         
20201         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20202             cls.pop();
20203             cls.push('top');
20204         }
20205         
20206         cls.push('right');
20207         
20208         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20209             cls.pop();
20210             cls.push('left');
20211         }
20212         
20213         this.picker().addClass(cls.join('-'));
20214         
20215         var _this = this;
20216         
20217         Roo.each(cls, function(c){
20218             if(c == 'bottom'){
20219                 _this.picker().setTop(_this.inputEl().getHeight());
20220                 return;
20221             }
20222             if(c == 'top'){
20223                 _this.picker().setTop(0 - _this.picker().getHeight());
20224                 return;
20225             }
20226             
20227             if(c == 'left'){
20228                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20229                 return;
20230             }
20231             if(c == 'right'){
20232                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20233                 return;
20234             }
20235         });
20236         
20237     },
20238   
20239     onFocus : function()
20240     {
20241         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20242         this.show();
20243     },
20244     
20245     onBlur : function()
20246     {
20247         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20248         this.hide();
20249     },
20250     
20251     show : function()
20252     {
20253         this.picker().show();
20254         this.pop.show();
20255         this.update();
20256         this.place();
20257         
20258         this.fireEvent('show', this, this.date);
20259     },
20260     
20261     hide : function()
20262     {
20263         this.picker().hide();
20264         this.pop.hide();
20265         
20266         this.fireEvent('hide', this, this.date);
20267     },
20268     
20269     setTime : function()
20270     {
20271         this.hide();
20272         this.setValue(this.time.format(this.format));
20273         
20274         this.fireEvent('select', this, this.date);
20275         
20276         
20277     },
20278     
20279     onMousedown: function(e){
20280         e.stopPropagation();
20281         e.preventDefault();
20282     },
20283     
20284     onIncrementHours: function()
20285     {
20286         Roo.log('onIncrementHours');
20287         this.time = this.time.add(Date.HOUR, 1);
20288         this.update();
20289         
20290     },
20291     
20292     onDecrementHours: function()
20293     {
20294         Roo.log('onDecrementHours');
20295         this.time = this.time.add(Date.HOUR, -1);
20296         this.update();
20297     },
20298     
20299     onIncrementMinutes: function()
20300     {
20301         Roo.log('onIncrementMinutes');
20302         this.time = this.time.add(Date.MINUTE, 1);
20303         this.update();
20304     },
20305     
20306     onDecrementMinutes: function()
20307     {
20308         Roo.log('onDecrementMinutes');
20309         this.time = this.time.add(Date.MINUTE, -1);
20310         this.update();
20311     },
20312     
20313     onTogglePeriod: function()
20314     {
20315         Roo.log('onTogglePeriod');
20316         this.time = this.time.add(Date.HOUR, 12);
20317         this.update();
20318     }
20319     
20320    
20321 });
20322
20323 Roo.apply(Roo.bootstrap.TimeField,  {
20324     
20325     content : {
20326         tag: 'tbody',
20327         cn: [
20328             {
20329                 tag: 'tr',
20330                 cn: [
20331                 {
20332                     tag: 'td',
20333                     colspan: '7'
20334                 }
20335                 ]
20336             }
20337         ]
20338     },
20339     
20340     footer : {
20341         tag: 'tfoot',
20342         cn: [
20343             {
20344                 tag: 'tr',
20345                 cn: [
20346                 {
20347                     tag: 'th',
20348                     colspan: '7',
20349                     cls: '',
20350                     cn: [
20351                         {
20352                             tag: 'button',
20353                             cls: 'btn btn-info ok',
20354                             html: 'OK'
20355                         }
20356                     ]
20357                 }
20358
20359                 ]
20360             }
20361         ]
20362     }
20363 });
20364
20365 Roo.apply(Roo.bootstrap.TimeField,  {
20366   
20367     template : {
20368         tag: 'div',
20369         cls: 'datepicker dropdown-menu',
20370         cn: [
20371             {
20372                 tag: 'div',
20373                 cls: 'datepicker-time',
20374                 cn: [
20375                 {
20376                     tag: 'table',
20377                     cls: 'table-condensed',
20378                     cn:[
20379                     Roo.bootstrap.TimeField.content,
20380                     Roo.bootstrap.TimeField.footer
20381                     ]
20382                 }
20383                 ]
20384             }
20385         ]
20386     }
20387 });
20388
20389  
20390
20391  /*
20392  * - LGPL
20393  *
20394  * MonthField
20395  * 
20396  */
20397
20398 /**
20399  * @class Roo.bootstrap.MonthField
20400  * @extends Roo.bootstrap.Input
20401  * Bootstrap MonthField class
20402  * 
20403  * @cfg {String} language default en
20404  * 
20405  * @constructor
20406  * Create a new MonthField
20407  * @param {Object} config The config object
20408  */
20409
20410 Roo.bootstrap.MonthField = function(config){
20411     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20412     
20413     this.addEvents({
20414         /**
20415          * @event show
20416          * Fires when this field show.
20417          * @param {Roo.bootstrap.MonthField} this
20418          * @param {Mixed} date The date value
20419          */
20420         show : true,
20421         /**
20422          * @event show
20423          * Fires when this field hide.
20424          * @param {Roo.bootstrap.MonthField} this
20425          * @param {Mixed} date The date value
20426          */
20427         hide : true,
20428         /**
20429          * @event select
20430          * Fires when select a date.
20431          * @param {Roo.bootstrap.MonthField} this
20432          * @param {String} oldvalue The old value
20433          * @param {String} newvalue The new value
20434          */
20435         select : true
20436     });
20437 };
20438
20439 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20440     
20441     onRender: function(ct, position)
20442     {
20443         
20444         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20445         
20446         this.language = this.language || 'en';
20447         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20448         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20449         
20450         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20451         this.isInline = false;
20452         this.isInput = true;
20453         this.component = this.el.select('.add-on', true).first() || false;
20454         this.component = (this.component && this.component.length === 0) ? false : this.component;
20455         this.hasInput = this.component && this.inputEL().length;
20456         
20457         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20458         
20459         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20460         
20461         this.picker().on('mousedown', this.onMousedown, this);
20462         this.picker().on('click', this.onClick, this);
20463         
20464         this.picker().addClass('datepicker-dropdown');
20465         
20466         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20467             v.setStyle('width', '189px');
20468         });
20469         
20470         this.fillMonths();
20471         
20472         this.update();
20473         
20474         if(this.isInline) {
20475             this.show();
20476         }
20477         
20478     },
20479     
20480     setValue: function(v, suppressEvent)
20481     {   
20482         var o = this.getValue();
20483         
20484         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20485         
20486         this.update();
20487
20488         if(suppressEvent !== true){
20489             this.fireEvent('select', this, o, v);
20490         }
20491         
20492     },
20493     
20494     getValue: function()
20495     {
20496         return this.value;
20497     },
20498     
20499     onClick: function(e) 
20500     {
20501         e.stopPropagation();
20502         e.preventDefault();
20503         
20504         var target = e.getTarget();
20505         
20506         if(target.nodeName.toLowerCase() === 'i'){
20507             target = Roo.get(target).dom.parentNode;
20508         }
20509         
20510         var nodeName = target.nodeName;
20511         var className = target.className;
20512         var html = target.innerHTML;
20513         
20514         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20515             return;
20516         }
20517         
20518         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20519         
20520         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20521         
20522         this.hide();
20523                         
20524     },
20525     
20526     picker : function()
20527     {
20528         return this.pickerEl;
20529     },
20530     
20531     fillMonths: function()
20532     {    
20533         var i = 0;
20534         var months = this.picker().select('>.datepicker-months td', true).first();
20535         
20536         months.dom.innerHTML = '';
20537         
20538         while (i < 12) {
20539             var month = {
20540                 tag: 'span',
20541                 cls: 'month',
20542                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20543             };
20544             
20545             months.createChild(month);
20546         }
20547         
20548     },
20549     
20550     update: function()
20551     {
20552         var _this = this;
20553         
20554         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20555             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20556         }
20557         
20558         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20559             e.removeClass('active');
20560             
20561             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20562                 e.addClass('active');
20563             }
20564         })
20565     },
20566     
20567     place: function()
20568     {
20569         if(this.isInline) {
20570             return;
20571         }
20572         
20573         this.picker().removeClass(['bottom', 'top']);
20574         
20575         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20576             /*
20577              * place to the top of element!
20578              *
20579              */
20580             
20581             this.picker().addClass('top');
20582             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20583             
20584             return;
20585         }
20586         
20587         this.picker().addClass('bottom');
20588         
20589         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20590     },
20591     
20592     onFocus : function()
20593     {
20594         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20595         this.show();
20596     },
20597     
20598     onBlur : function()
20599     {
20600         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20601         
20602         var d = this.inputEl().getValue();
20603         
20604         this.setValue(d);
20605                 
20606         this.hide();
20607     },
20608     
20609     show : function()
20610     {
20611         this.picker().show();
20612         this.picker().select('>.datepicker-months', true).first().show();
20613         this.update();
20614         this.place();
20615         
20616         this.fireEvent('show', this, this.date);
20617     },
20618     
20619     hide : function()
20620     {
20621         if(this.isInline) {
20622             return;
20623         }
20624         this.picker().hide();
20625         this.fireEvent('hide', this, this.date);
20626         
20627     },
20628     
20629     onMousedown: function(e)
20630     {
20631         e.stopPropagation();
20632         e.preventDefault();
20633     },
20634     
20635     keyup: function(e)
20636     {
20637         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20638         this.update();
20639     },
20640
20641     fireKey: function(e)
20642     {
20643         if (!this.picker().isVisible()){
20644             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20645                 this.show();
20646             }
20647             return;
20648         }
20649         
20650         var dir;
20651         
20652         switch(e.keyCode){
20653             case 27: // escape
20654                 this.hide();
20655                 e.preventDefault();
20656                 break;
20657             case 37: // left
20658             case 39: // right
20659                 dir = e.keyCode == 37 ? -1 : 1;
20660                 
20661                 this.vIndex = this.vIndex + dir;
20662                 
20663                 if(this.vIndex < 0){
20664                     this.vIndex = 0;
20665                 }
20666                 
20667                 if(this.vIndex > 11){
20668                     this.vIndex = 11;
20669                 }
20670                 
20671                 if(isNaN(this.vIndex)){
20672                     this.vIndex = 0;
20673                 }
20674                 
20675                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20676                 
20677                 break;
20678             case 38: // up
20679             case 40: // down
20680                 
20681                 dir = e.keyCode == 38 ? -1 : 1;
20682                 
20683                 this.vIndex = this.vIndex + dir * 4;
20684                 
20685                 if(this.vIndex < 0){
20686                     this.vIndex = 0;
20687                 }
20688                 
20689                 if(this.vIndex > 11){
20690                     this.vIndex = 11;
20691                 }
20692                 
20693                 if(isNaN(this.vIndex)){
20694                     this.vIndex = 0;
20695                 }
20696                 
20697                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20698                 break;
20699                 
20700             case 13: // enter
20701                 
20702                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20703                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20704                 }
20705                 
20706                 this.hide();
20707                 e.preventDefault();
20708                 break;
20709             case 9: // tab
20710                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20711                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20712                 }
20713                 this.hide();
20714                 break;
20715             case 16: // shift
20716             case 17: // ctrl
20717             case 18: // alt
20718                 break;
20719             default :
20720                 this.hide();
20721                 
20722         }
20723     },
20724     
20725     remove: function() 
20726     {
20727         this.picker().remove();
20728     }
20729    
20730 });
20731
20732 Roo.apply(Roo.bootstrap.MonthField,  {
20733     
20734     content : {
20735         tag: 'tbody',
20736         cn: [
20737         {
20738             tag: 'tr',
20739             cn: [
20740             {
20741                 tag: 'td',
20742                 colspan: '7'
20743             }
20744             ]
20745         }
20746         ]
20747     },
20748     
20749     dates:{
20750         en: {
20751             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20752             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20753         }
20754     }
20755 });
20756
20757 Roo.apply(Roo.bootstrap.MonthField,  {
20758   
20759     template : {
20760         tag: 'div',
20761         cls: 'datepicker dropdown-menu roo-dynamic',
20762         cn: [
20763             {
20764                 tag: 'div',
20765                 cls: 'datepicker-months',
20766                 cn: [
20767                 {
20768                     tag: 'table',
20769                     cls: 'table-condensed',
20770                     cn:[
20771                         Roo.bootstrap.DateField.content
20772                     ]
20773                 }
20774                 ]
20775             }
20776         ]
20777     }
20778 });
20779
20780  
20781
20782  
20783  /*
20784  * - LGPL
20785  *
20786  * CheckBox
20787  * 
20788  */
20789
20790 /**
20791  * @class Roo.bootstrap.CheckBox
20792  * @extends Roo.bootstrap.Input
20793  * Bootstrap CheckBox class
20794  * 
20795  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20796  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20797  * @cfg {String} boxLabel The text that appears beside the checkbox
20798  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20799  * @cfg {Boolean} checked initnal the element
20800  * @cfg {Boolean} inline inline the element (default false)
20801  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20802  * @cfg {String} tooltip label tooltip
20803  * 
20804  * @constructor
20805  * Create a new CheckBox
20806  * @param {Object} config The config object
20807  */
20808
20809 Roo.bootstrap.CheckBox = function(config){
20810     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20811    
20812     this.addEvents({
20813         /**
20814         * @event check
20815         * Fires when the element is checked or unchecked.
20816         * @param {Roo.bootstrap.CheckBox} this This input
20817         * @param {Boolean} checked The new checked value
20818         */
20819        check : true,
20820        /**
20821         * @event click
20822         * Fires when the element is click.
20823         * @param {Roo.bootstrap.CheckBox} this This input
20824         */
20825        click : true
20826     });
20827     
20828 };
20829
20830 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20831   
20832     inputType: 'checkbox',
20833     inputValue: 1,
20834     valueOff: 0,
20835     boxLabel: false,
20836     checked: false,
20837     weight : false,
20838     inline: false,
20839     tooltip : '',
20840     
20841     getAutoCreate : function()
20842     {
20843         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20844         
20845         var id = Roo.id();
20846         
20847         var cfg = {};
20848         
20849         cfg.cls = 'form-group ' + this.inputType; //input-group
20850         
20851         if(this.inline){
20852             cfg.cls += ' ' + this.inputType + '-inline';
20853         }
20854         
20855         var input =  {
20856             tag: 'input',
20857             id : id,
20858             type : this.inputType,
20859             value : this.inputValue,
20860             cls : 'roo-' + this.inputType, //'form-box',
20861             placeholder : this.placeholder || ''
20862             
20863         };
20864         
20865         if(this.inputType != 'radio'){
20866             var hidden =  {
20867                 tag: 'input',
20868                 type : 'hidden',
20869                 cls : 'roo-hidden-value',
20870                 value : this.checked ? this.inputValue : this.valueOff
20871             };
20872         }
20873         
20874             
20875         if (this.weight) { // Validity check?
20876             cfg.cls += " " + this.inputType + "-" + this.weight;
20877         }
20878         
20879         if (this.disabled) {
20880             input.disabled=true;
20881         }
20882         
20883         if(this.checked){
20884             input.checked = this.checked;
20885         }
20886         
20887         if (this.name) {
20888             
20889             input.name = this.name;
20890             
20891             if(this.inputType != 'radio'){
20892                 hidden.name = this.name;
20893                 input.name = '_hidden_' + this.name;
20894             }
20895         }
20896         
20897         if (this.size) {
20898             input.cls += ' input-' + this.size;
20899         }
20900         
20901         var settings=this;
20902         
20903         ['xs','sm','md','lg'].map(function(size){
20904             if (settings[size]) {
20905                 cfg.cls += ' col-' + size + '-' + settings[size];
20906             }
20907         });
20908         
20909         var inputblock = input;
20910          
20911         if (this.before || this.after) {
20912             
20913             inputblock = {
20914                 cls : 'input-group',
20915                 cn :  [] 
20916             };
20917             
20918             if (this.before) {
20919                 inputblock.cn.push({
20920                     tag :'span',
20921                     cls : 'input-group-addon',
20922                     html : this.before
20923                 });
20924             }
20925             
20926             inputblock.cn.push(input);
20927             
20928             if(this.inputType != 'radio'){
20929                 inputblock.cn.push(hidden);
20930             }
20931             
20932             if (this.after) {
20933                 inputblock.cn.push({
20934                     tag :'span',
20935                     cls : 'input-group-addon',
20936                     html : this.after
20937                 });
20938             }
20939             
20940         }
20941         
20942         if (align ==='left' && this.fieldLabel.length) {
20943 //                Roo.log("left and has label");
20944             cfg.cn = [
20945                 {
20946                     tag: 'label',
20947                     'for' :  id,
20948                     cls : 'control-label',
20949                     html : this.fieldLabel
20950                 },
20951                 {
20952                     cls : "", 
20953                     cn: [
20954                         inputblock
20955                     ]
20956                 }
20957             ];
20958             
20959             if(this.labelWidth > 12){
20960                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20961             }
20962             
20963             if(this.labelWidth < 13 && this.labelmd == 0){
20964                 this.labelmd = this.labelWidth;
20965             }
20966             
20967             if(this.labellg > 0){
20968                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20969                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20970             }
20971             
20972             if(this.labelmd > 0){
20973                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20974                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20975             }
20976             
20977             if(this.labelsm > 0){
20978                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20979                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20980             }
20981             
20982             if(this.labelxs > 0){
20983                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20984                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20985             }
20986             
20987         } else if ( this.fieldLabel.length) {
20988 //                Roo.log(" label");
20989                 cfg.cn = [
20990                    
20991                     {
20992                         tag: this.boxLabel ? 'span' : 'label',
20993                         'for': id,
20994                         cls: 'control-label box-input-label',
20995                         //cls : 'input-group-addon',
20996                         html : this.fieldLabel
20997                     },
20998                     
20999                     inputblock
21000                     
21001                 ];
21002
21003         } else {
21004             
21005 //                Roo.log(" no label && no align");
21006                 cfg.cn = [  inputblock ] ;
21007                 
21008                 
21009         }
21010         
21011         if(this.boxLabel){
21012              var boxLabelCfg = {
21013                 tag: 'label',
21014                 //'for': id, // box label is handled by onclick - so no for...
21015                 cls: 'box-label',
21016                 html: this.boxLabel
21017             };
21018             
21019             if(this.tooltip){
21020                 boxLabelCfg.tooltip = this.tooltip;
21021             }
21022              
21023             cfg.cn.push(boxLabelCfg);
21024         }
21025         
21026         if(this.inputType != 'radio'){
21027             cfg.cn.push(hidden);
21028         }
21029         
21030         return cfg;
21031         
21032     },
21033     
21034     /**
21035      * return the real input element.
21036      */
21037     inputEl: function ()
21038     {
21039         return this.el.select('input.roo-' + this.inputType,true).first();
21040     },
21041     hiddenEl: function ()
21042     {
21043         return this.el.select('input.roo-hidden-value',true).first();
21044     },
21045     
21046     labelEl: function()
21047     {
21048         return this.el.select('label.control-label',true).first();
21049     },
21050     /* depricated... */
21051     
21052     label: function()
21053     {
21054         return this.labelEl();
21055     },
21056     
21057     boxLabelEl: function()
21058     {
21059         return this.el.select('label.box-label',true).first();
21060     },
21061     
21062     initEvents : function()
21063     {
21064 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21065         
21066         this.inputEl().on('click', this.onClick,  this);
21067         
21068         if (this.boxLabel) { 
21069             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21070         }
21071         
21072         this.startValue = this.getValue();
21073         
21074         if(this.groupId){
21075             Roo.bootstrap.CheckBox.register(this);
21076         }
21077     },
21078     
21079     onClick : function(e)
21080     {   
21081         if(this.fireEvent('click', this, e) !== false){
21082             this.setChecked(!this.checked);
21083         }
21084         
21085     },
21086     
21087     setChecked : function(state,suppressEvent)
21088     {
21089         this.startValue = this.getValue();
21090
21091         if(this.inputType == 'radio'){
21092             
21093             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21094                 e.dom.checked = false;
21095             });
21096             
21097             this.inputEl().dom.checked = true;
21098             
21099             this.inputEl().dom.value = this.inputValue;
21100             
21101             if(suppressEvent !== true){
21102                 this.fireEvent('check', this, true);
21103             }
21104             
21105             this.validate();
21106             
21107             return;
21108         }
21109         
21110         this.checked = state;
21111         
21112         this.inputEl().dom.checked = state;
21113         
21114         
21115         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21116         
21117         if(suppressEvent !== true){
21118             this.fireEvent('check', this, state);
21119         }
21120         
21121         this.validate();
21122     },
21123     
21124     getValue : function()
21125     {
21126         if(this.inputType == 'radio'){
21127             return this.getGroupValue();
21128         }
21129         
21130         return this.hiddenEl().dom.value;
21131         
21132     },
21133     
21134     getGroupValue : function()
21135     {
21136         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21137             return '';
21138         }
21139         
21140         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21141     },
21142     
21143     setValue : function(v,suppressEvent)
21144     {
21145         if(this.inputType == 'radio'){
21146             this.setGroupValue(v, suppressEvent);
21147             return;
21148         }
21149         
21150         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21151         
21152         this.validate();
21153     },
21154     
21155     setGroupValue : function(v, suppressEvent)
21156     {
21157         this.startValue = this.getValue();
21158         
21159         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21160             e.dom.checked = false;
21161             
21162             if(e.dom.value == v){
21163                 e.dom.checked = true;
21164             }
21165         });
21166         
21167         if(suppressEvent !== true){
21168             this.fireEvent('check', this, true);
21169         }
21170
21171         this.validate();
21172         
21173         return;
21174     },
21175     
21176     validate : function()
21177     {
21178         if(this.getVisibilityEl().hasClass('hidden')){
21179             return true;
21180         }
21181         
21182         if(
21183                 this.disabled || 
21184                 (this.inputType == 'radio' && this.validateRadio()) ||
21185                 (this.inputType == 'checkbox' && this.validateCheckbox())
21186         ){
21187             this.markValid();
21188             return true;
21189         }
21190         
21191         this.markInvalid();
21192         return false;
21193     },
21194     
21195     validateRadio : function()
21196     {
21197         if(this.getVisibilityEl().hasClass('hidden')){
21198             return true;
21199         }
21200         
21201         if(this.allowBlank){
21202             return true;
21203         }
21204         
21205         var valid = false;
21206         
21207         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21208             if(!e.dom.checked){
21209                 return;
21210             }
21211             
21212             valid = true;
21213             
21214             return false;
21215         });
21216         
21217         return valid;
21218     },
21219     
21220     validateCheckbox : function()
21221     {
21222         if(!this.groupId){
21223             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21224             //return (this.getValue() == this.inputValue) ? true : false;
21225         }
21226         
21227         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21228         
21229         if(!group){
21230             return false;
21231         }
21232         
21233         var r = false;
21234         
21235         for(var i in group){
21236             if(group[i].el.isVisible(true)){
21237                 r = false;
21238                 break;
21239             }
21240             
21241             r = true;
21242         }
21243         
21244         for(var i in group){
21245             if(r){
21246                 break;
21247             }
21248             
21249             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21250         }
21251         
21252         return r;
21253     },
21254     
21255     /**
21256      * Mark this field as valid
21257      */
21258     markValid : function()
21259     {
21260         var _this = this;
21261         
21262         this.fireEvent('valid', this);
21263         
21264         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21265         
21266         if(this.groupId){
21267             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21268         }
21269         
21270         if(label){
21271             label.markValid();
21272         }
21273
21274         if(this.inputType == 'radio'){
21275             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21276                 var fg = e.findParent('.form-group', false, true);
21277                 if (Roo.bootstrap.version == 3) {
21278                     fg.removeClass([_this.invalidClass, _this.validClass]);
21279                     fg.addClass(_this.validClass);
21280                 } else {
21281                     fg.removeClass(['is-valid', 'is-invalid']);
21282                     fg.addClass('is-valid');
21283                 }
21284             });
21285             
21286             return;
21287         }
21288
21289         if(!this.groupId){
21290             var fg = this.el.findParent('.form-group', false, true);
21291             if (Roo.bootstrap.version == 3) {
21292                 fg.removeClass([this.invalidClass, this.validClass]);
21293                 fg.addClass(this.validClass);
21294             } else {
21295                 fg.removeClass(['is-valid', 'is-invalid']);
21296                 fg.addClass('is-valid');
21297             }
21298             return;
21299         }
21300         
21301         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21302         
21303         if(!group){
21304             return;
21305         }
21306         
21307         for(var i in group){
21308             var fg = group[i].el.findParent('.form-group', false, true);
21309             if (Roo.bootstrap.version == 3) {
21310                 fg.removeClass([this.invalidClass, this.validClass]);
21311                 fg.addClass(this.validClass);
21312             } else {
21313                 fg.removeClass(['is-valid', 'is-invalid']);
21314                 fg.addClass('is-valid');
21315             }
21316         }
21317     },
21318     
21319      /**
21320      * Mark this field as invalid
21321      * @param {String} msg The validation message
21322      */
21323     markInvalid : function(msg)
21324     {
21325         if(this.allowBlank){
21326             return;
21327         }
21328         
21329         var _this = this;
21330         
21331         this.fireEvent('invalid', this, msg);
21332         
21333         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21334         
21335         if(this.groupId){
21336             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21337         }
21338         
21339         if(label){
21340             label.markInvalid();
21341         }
21342             
21343         if(this.inputType == 'radio'){
21344             
21345             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21346                 var fg = e.findParent('.form-group', false, true);
21347                 if (Roo.bootstrap.version == 3) {
21348                     fg.removeClass([_this.invalidClass, _this.validClass]);
21349                     fg.addClass(_this.invalidClass);
21350                 } else {
21351                     fg.removeClass(['is-invalid', 'is-valid']);
21352                     fg.addClass('is-invalid');
21353                 }
21354             });
21355             
21356             return;
21357         }
21358         
21359         if(!this.groupId){
21360             var fg = this.el.findParent('.form-group', false, true);
21361             if (Roo.bootstrap.version == 3) {
21362                 fg.removeClass([_this.invalidClass, _this.validClass]);
21363                 fg.addClass(_this.invalidClass);
21364             } else {
21365                 fg.removeClass(['is-invalid', 'is-valid']);
21366                 fg.addClass('is-invalid');
21367             }
21368             return;
21369         }
21370         
21371         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21372         
21373         if(!group){
21374             return;
21375         }
21376         
21377         for(var i in group){
21378             var fg = group[i].el.findParent('.form-group', false, true);
21379             if (Roo.bootstrap.version == 3) {
21380                 fg.removeClass([_this.invalidClass, _this.validClass]);
21381                 fg.addClass(_this.invalidClass);
21382             } else {
21383                 fg.removeClass(['is-invalid', 'is-valid']);
21384                 fg.addClass('is-invalid');
21385             }
21386         }
21387         
21388     },
21389     
21390     clearInvalid : function()
21391     {
21392         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21393         
21394         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21395         
21396         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21397         
21398         if (label && label.iconEl) {
21399             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21400             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21401         }
21402     },
21403     
21404     disable : function()
21405     {
21406         if(this.inputType != 'radio'){
21407             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21408             return;
21409         }
21410         
21411         var _this = this;
21412         
21413         if(this.rendered){
21414             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21415                 _this.getActionEl().addClass(this.disabledClass);
21416                 e.dom.disabled = true;
21417             });
21418         }
21419         
21420         this.disabled = true;
21421         this.fireEvent("disable", this);
21422         return this;
21423     },
21424
21425     enable : function()
21426     {
21427         if(this.inputType != 'radio'){
21428             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21429             return;
21430         }
21431         
21432         var _this = this;
21433         
21434         if(this.rendered){
21435             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21436                 _this.getActionEl().removeClass(this.disabledClass);
21437                 e.dom.disabled = false;
21438             });
21439         }
21440         
21441         this.disabled = false;
21442         this.fireEvent("enable", this);
21443         return this;
21444     },
21445     
21446     setBoxLabel : function(v)
21447     {
21448         this.boxLabel = v;
21449         
21450         if(this.rendered){
21451             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21452         }
21453     }
21454
21455 });
21456
21457 Roo.apply(Roo.bootstrap.CheckBox, {
21458     
21459     groups: {},
21460     
21461      /**
21462     * register a CheckBox Group
21463     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21464     */
21465     register : function(checkbox)
21466     {
21467         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21468             this.groups[checkbox.groupId] = {};
21469         }
21470         
21471         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21472             return;
21473         }
21474         
21475         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21476         
21477     },
21478     /**
21479     * fetch a CheckBox Group based on the group ID
21480     * @param {string} the group ID
21481     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21482     */
21483     get: function(groupId) {
21484         if (typeof(this.groups[groupId]) == 'undefined') {
21485             return false;
21486         }
21487         
21488         return this.groups[groupId] ;
21489     }
21490     
21491     
21492 });
21493 /*
21494  * - LGPL
21495  *
21496  * RadioItem
21497  * 
21498  */
21499
21500 /**
21501  * @class Roo.bootstrap.Radio
21502  * @extends Roo.bootstrap.Component
21503  * Bootstrap Radio class
21504  * @cfg {String} boxLabel - the label associated
21505  * @cfg {String} value - the value of radio
21506  * 
21507  * @constructor
21508  * Create a new Radio
21509  * @param {Object} config The config object
21510  */
21511 Roo.bootstrap.Radio = function(config){
21512     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21513     
21514 };
21515
21516 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21517     
21518     boxLabel : '',
21519     
21520     value : '',
21521     
21522     getAutoCreate : function()
21523     {
21524         var cfg = {
21525             tag : 'div',
21526             cls : 'form-group radio',
21527             cn : [
21528                 {
21529                     tag : 'label',
21530                     cls : 'box-label',
21531                     html : this.boxLabel
21532                 }
21533             ]
21534         };
21535         
21536         return cfg;
21537     },
21538     
21539     initEvents : function() 
21540     {
21541         this.parent().register(this);
21542         
21543         this.el.on('click', this.onClick, this);
21544         
21545     },
21546     
21547     onClick : function(e)
21548     {
21549         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21550             this.setChecked(true);
21551         }
21552     },
21553     
21554     setChecked : function(state, suppressEvent)
21555     {
21556         this.parent().setValue(this.value, suppressEvent);
21557         
21558     },
21559     
21560     setBoxLabel : function(v)
21561     {
21562         this.boxLabel = v;
21563         
21564         if(this.rendered){
21565             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21566         }
21567     }
21568     
21569 });
21570  
21571
21572  /*
21573  * - LGPL
21574  *
21575  * Input
21576  * 
21577  */
21578
21579 /**
21580  * @class Roo.bootstrap.SecurePass
21581  * @extends Roo.bootstrap.Input
21582  * Bootstrap SecurePass class
21583  *
21584  * 
21585  * @constructor
21586  * Create a new SecurePass
21587  * @param {Object} config The config object
21588  */
21589  
21590 Roo.bootstrap.SecurePass = function (config) {
21591     // these go here, so the translation tool can replace them..
21592     this.errors = {
21593         PwdEmpty: "Please type a password, and then retype it to confirm.",
21594         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21595         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21596         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21597         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21598         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21599         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21600         TooWeak: "Your password is Too Weak."
21601     },
21602     this.meterLabel = "Password strength:";
21603     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21604     this.meterClass = [
21605         "roo-password-meter-tooweak", 
21606         "roo-password-meter-weak", 
21607         "roo-password-meter-medium", 
21608         "roo-password-meter-strong", 
21609         "roo-password-meter-grey"
21610     ];
21611     
21612     this.errors = {};
21613     
21614     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21615 }
21616
21617 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21618     /**
21619      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21620      * {
21621      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21622      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21623      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21624      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21625      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21626      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21627      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21628      * })
21629      */
21630     // private
21631     
21632     meterWidth: 300,
21633     errorMsg :'',    
21634     errors: false,
21635     imageRoot: '/',
21636     /**
21637      * @cfg {String/Object} Label for the strength meter (defaults to
21638      * 'Password strength:')
21639      */
21640     // private
21641     meterLabel: '',
21642     /**
21643      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21644      * ['Weak', 'Medium', 'Strong'])
21645      */
21646     // private    
21647     pwdStrengths: false,    
21648     // private
21649     strength: 0,
21650     // private
21651     _lastPwd: null,
21652     // private
21653     kCapitalLetter: 0,
21654     kSmallLetter: 1,
21655     kDigit: 2,
21656     kPunctuation: 3,
21657     
21658     insecure: false,
21659     // private
21660     initEvents: function ()
21661     {
21662         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21663
21664         if (this.el.is('input[type=password]') && Roo.isSafari) {
21665             this.el.on('keydown', this.SafariOnKeyDown, this);
21666         }
21667
21668         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21669     },
21670     // private
21671     onRender: function (ct, position)
21672     {
21673         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21674         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21675         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21676
21677         this.trigger.createChild({
21678                    cn: [
21679                     {
21680                     //id: 'PwdMeter',
21681                     tag: 'div',
21682                     cls: 'roo-password-meter-grey col-xs-12',
21683                     style: {
21684                         //width: 0,
21685                         //width: this.meterWidth + 'px'                                                
21686                         }
21687                     },
21688                     {                            
21689                          cls: 'roo-password-meter-text'                          
21690                     }
21691                 ]            
21692         });
21693
21694          
21695         if (this.hideTrigger) {
21696             this.trigger.setDisplayed(false);
21697         }
21698         this.setSize(this.width || '', this.height || '');
21699     },
21700     // private
21701     onDestroy: function ()
21702     {
21703         if (this.trigger) {
21704             this.trigger.removeAllListeners();
21705             this.trigger.remove();
21706         }
21707         if (this.wrap) {
21708             this.wrap.remove();
21709         }
21710         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21711     },
21712     // private
21713     checkStrength: function ()
21714     {
21715         var pwd = this.inputEl().getValue();
21716         if (pwd == this._lastPwd) {
21717             return;
21718         }
21719
21720         var strength;
21721         if (this.ClientSideStrongPassword(pwd)) {
21722             strength = 3;
21723         } else if (this.ClientSideMediumPassword(pwd)) {
21724             strength = 2;
21725         } else if (this.ClientSideWeakPassword(pwd)) {
21726             strength = 1;
21727         } else {
21728             strength = 0;
21729         }
21730         
21731         Roo.log('strength1: ' + strength);
21732         
21733         //var pm = this.trigger.child('div/div/div').dom;
21734         var pm = this.trigger.child('div/div');
21735         pm.removeClass(this.meterClass);
21736         pm.addClass(this.meterClass[strength]);
21737                 
21738         
21739         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21740                 
21741         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21742         
21743         this._lastPwd = pwd;
21744     },
21745     reset: function ()
21746     {
21747         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21748         
21749         this._lastPwd = '';
21750         
21751         var pm = this.trigger.child('div/div');
21752         pm.removeClass(this.meterClass);
21753         pm.addClass('roo-password-meter-grey');        
21754         
21755         
21756         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21757         
21758         pt.innerHTML = '';
21759         this.inputEl().dom.type='password';
21760     },
21761     // private
21762     validateValue: function (value)
21763     {
21764         
21765         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21766             return false;
21767         }
21768         if (value.length == 0) {
21769             if (this.allowBlank) {
21770                 this.clearInvalid();
21771                 return true;
21772             }
21773
21774             this.markInvalid(this.errors.PwdEmpty);
21775             this.errorMsg = this.errors.PwdEmpty;
21776             return false;
21777         }
21778         
21779         if(this.insecure){
21780             return true;
21781         }
21782         
21783         if ('[\x21-\x7e]*'.match(value)) {
21784             this.markInvalid(this.errors.PwdBadChar);
21785             this.errorMsg = this.errors.PwdBadChar;
21786             return false;
21787         }
21788         if (value.length < 6) {
21789             this.markInvalid(this.errors.PwdShort);
21790             this.errorMsg = this.errors.PwdShort;
21791             return false;
21792         }
21793         if (value.length > 16) {
21794             this.markInvalid(this.errors.PwdLong);
21795             this.errorMsg = this.errors.PwdLong;
21796             return false;
21797         }
21798         var strength;
21799         if (this.ClientSideStrongPassword(value)) {
21800             strength = 3;
21801         } else if (this.ClientSideMediumPassword(value)) {
21802             strength = 2;
21803         } else if (this.ClientSideWeakPassword(value)) {
21804             strength = 1;
21805         } else {
21806             strength = 0;
21807         }
21808
21809         
21810         if (strength < 2) {
21811             //this.markInvalid(this.errors.TooWeak);
21812             this.errorMsg = this.errors.TooWeak;
21813             //return false;
21814         }
21815         
21816         
21817         console.log('strength2: ' + strength);
21818         
21819         //var pm = this.trigger.child('div/div/div').dom;
21820         
21821         var pm = this.trigger.child('div/div');
21822         pm.removeClass(this.meterClass);
21823         pm.addClass(this.meterClass[strength]);
21824                 
21825         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21826                 
21827         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21828         
21829         this.errorMsg = ''; 
21830         return true;
21831     },
21832     // private
21833     CharacterSetChecks: function (type)
21834     {
21835         this.type = type;
21836         this.fResult = false;
21837     },
21838     // private
21839     isctype: function (character, type)
21840     {
21841         switch (type) {  
21842             case this.kCapitalLetter:
21843                 if (character >= 'A' && character <= 'Z') {
21844                     return true;
21845                 }
21846                 break;
21847             
21848             case this.kSmallLetter:
21849                 if (character >= 'a' && character <= 'z') {
21850                     return true;
21851                 }
21852                 break;
21853             
21854             case this.kDigit:
21855                 if (character >= '0' && character <= '9') {
21856                     return true;
21857                 }
21858                 break;
21859             
21860             case this.kPunctuation:
21861                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21862                     return true;
21863                 }
21864                 break;
21865             
21866             default:
21867                 return false;
21868         }
21869
21870     },
21871     // private
21872     IsLongEnough: function (pwd, size)
21873     {
21874         return !(pwd == null || isNaN(size) || pwd.length < size);
21875     },
21876     // private
21877     SpansEnoughCharacterSets: function (word, nb)
21878     {
21879         if (!this.IsLongEnough(word, nb))
21880         {
21881             return false;
21882         }
21883
21884         var characterSetChecks = new Array(
21885             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21886             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21887         );
21888         
21889         for (var index = 0; index < word.length; ++index) {
21890             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21891                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21892                     characterSetChecks[nCharSet].fResult = true;
21893                     break;
21894                 }
21895             }
21896         }
21897
21898         var nCharSets = 0;
21899         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21900             if (characterSetChecks[nCharSet].fResult) {
21901                 ++nCharSets;
21902             }
21903         }
21904
21905         if (nCharSets < nb) {
21906             return false;
21907         }
21908         return true;
21909     },
21910     // private
21911     ClientSideStrongPassword: function (pwd)
21912     {
21913         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21914     },
21915     // private
21916     ClientSideMediumPassword: function (pwd)
21917     {
21918         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21919     },
21920     // private
21921     ClientSideWeakPassword: function (pwd)
21922     {
21923         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21924     }
21925           
21926 })//<script type="text/javascript">
21927
21928 /*
21929  * Based  Ext JS Library 1.1.1
21930  * Copyright(c) 2006-2007, Ext JS, LLC.
21931  * LGPL
21932  *
21933  */
21934  
21935 /**
21936  * @class Roo.HtmlEditorCore
21937  * @extends Roo.Component
21938  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21939  *
21940  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21941  */
21942
21943 Roo.HtmlEditorCore = function(config){
21944     
21945     
21946     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21947     
21948     
21949     this.addEvents({
21950         /**
21951          * @event initialize
21952          * Fires when the editor is fully initialized (including the iframe)
21953          * @param {Roo.HtmlEditorCore} this
21954          */
21955         initialize: true,
21956         /**
21957          * @event activate
21958          * Fires when the editor is first receives the focus. Any insertion must wait
21959          * until after this event.
21960          * @param {Roo.HtmlEditorCore} this
21961          */
21962         activate: true,
21963          /**
21964          * @event beforesync
21965          * Fires before the textarea is updated with content from the editor iframe. Return false
21966          * to cancel the sync.
21967          * @param {Roo.HtmlEditorCore} this
21968          * @param {String} html
21969          */
21970         beforesync: true,
21971          /**
21972          * @event beforepush
21973          * Fires before the iframe editor is updated with content from the textarea. Return false
21974          * to cancel the push.
21975          * @param {Roo.HtmlEditorCore} this
21976          * @param {String} html
21977          */
21978         beforepush: true,
21979          /**
21980          * @event sync
21981          * Fires when the textarea is updated with content from the editor iframe.
21982          * @param {Roo.HtmlEditorCore} this
21983          * @param {String} html
21984          */
21985         sync: true,
21986          /**
21987          * @event push
21988          * Fires when the iframe editor is updated with content from the textarea.
21989          * @param {Roo.HtmlEditorCore} this
21990          * @param {String} html
21991          */
21992         push: true,
21993         
21994         /**
21995          * @event editorevent
21996          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21997          * @param {Roo.HtmlEditorCore} this
21998          */
21999         editorevent: true
22000         
22001     });
22002     
22003     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22004     
22005     // defaults : white / black...
22006     this.applyBlacklists();
22007     
22008     
22009     
22010 };
22011
22012
22013 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22014
22015
22016      /**
22017      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22018      */
22019     
22020     owner : false,
22021     
22022      /**
22023      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22024      *                        Roo.resizable.
22025      */
22026     resizable : false,
22027      /**
22028      * @cfg {Number} height (in pixels)
22029      */   
22030     height: 300,
22031    /**
22032      * @cfg {Number} width (in pixels)
22033      */   
22034     width: 500,
22035     
22036     /**
22037      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22038      * 
22039      */
22040     stylesheets: false,
22041     
22042     // id of frame..
22043     frameId: false,
22044     
22045     // private properties
22046     validationEvent : false,
22047     deferHeight: true,
22048     initialized : false,
22049     activated : false,
22050     sourceEditMode : false,
22051     onFocus : Roo.emptyFn,
22052     iframePad:3,
22053     hideMode:'offsets',
22054     
22055     clearUp: true,
22056     
22057     // blacklist + whitelisted elements..
22058     black: false,
22059     white: false,
22060      
22061     bodyCls : '',
22062
22063     /**
22064      * Protected method that will not generally be called directly. It
22065      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22066      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22067      */
22068     getDocMarkup : function(){
22069         // body styles..
22070         var st = '';
22071         
22072         // inherit styels from page...?? 
22073         if (this.stylesheets === false) {
22074             
22075             Roo.get(document.head).select('style').each(function(node) {
22076                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22077             });
22078             
22079             Roo.get(document.head).select('link').each(function(node) { 
22080                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22081             });
22082             
22083         } else if (!this.stylesheets.length) {
22084                 // simple..
22085                 st = '<style type="text/css">' +
22086                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22087                    '</style>';
22088         } else { 
22089             st = '<style type="text/css">' +
22090                     this.stylesheets +
22091                 '</style>';
22092         }
22093         
22094         st +=  '<style type="text/css">' +
22095             'IMG { cursor: pointer } ' +
22096         '</style>';
22097
22098         var cls = 'roo-htmleditor-body';
22099         
22100         if(this.bodyCls.length){
22101             cls += ' ' + this.bodyCls;
22102         }
22103         
22104         return '<html><head>' + st  +
22105             //<style type="text/css">' +
22106             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22107             //'</style>' +
22108             ' </head><body class="' +  cls + '"></body></html>';
22109     },
22110
22111     // private
22112     onRender : function(ct, position)
22113     {
22114         var _t = this;
22115         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22116         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22117         
22118         
22119         this.el.dom.style.border = '0 none';
22120         this.el.dom.setAttribute('tabIndex', -1);
22121         this.el.addClass('x-hidden hide');
22122         
22123         
22124         
22125         if(Roo.isIE){ // fix IE 1px bogus margin
22126             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22127         }
22128        
22129         
22130         this.frameId = Roo.id();
22131         
22132          
22133         
22134         var iframe = this.owner.wrap.createChild({
22135             tag: 'iframe',
22136             cls: 'form-control', // bootstrap..
22137             id: this.frameId,
22138             name: this.frameId,
22139             frameBorder : 'no',
22140             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22141         }, this.el
22142         );
22143         
22144         
22145         this.iframe = iframe.dom;
22146
22147          this.assignDocWin();
22148         
22149         this.doc.designMode = 'on';
22150        
22151         this.doc.open();
22152         this.doc.write(this.getDocMarkup());
22153         this.doc.close();
22154
22155         
22156         var task = { // must defer to wait for browser to be ready
22157             run : function(){
22158                 //console.log("run task?" + this.doc.readyState);
22159                 this.assignDocWin();
22160                 if(this.doc.body || this.doc.readyState == 'complete'){
22161                     try {
22162                         this.doc.designMode="on";
22163                     } catch (e) {
22164                         return;
22165                     }
22166                     Roo.TaskMgr.stop(task);
22167                     this.initEditor.defer(10, this);
22168                 }
22169             },
22170             interval : 10,
22171             duration: 10000,
22172             scope: this
22173         };
22174         Roo.TaskMgr.start(task);
22175
22176     },
22177
22178     // private
22179     onResize : function(w, h)
22180     {
22181          Roo.log('resize: ' +w + ',' + h );
22182         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22183         if(!this.iframe){
22184             return;
22185         }
22186         if(typeof w == 'number'){
22187             
22188             this.iframe.style.width = w + 'px';
22189         }
22190         if(typeof h == 'number'){
22191             
22192             this.iframe.style.height = h + 'px';
22193             if(this.doc){
22194                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22195             }
22196         }
22197         
22198     },
22199
22200     /**
22201      * Toggles the editor between standard and source edit mode.
22202      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22203      */
22204     toggleSourceEdit : function(sourceEditMode){
22205         
22206         this.sourceEditMode = sourceEditMode === true;
22207         
22208         if(this.sourceEditMode){
22209  
22210             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22211             
22212         }else{
22213             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22214             //this.iframe.className = '';
22215             this.deferFocus();
22216         }
22217         //this.setSize(this.owner.wrap.getSize());
22218         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22219     },
22220
22221     
22222   
22223
22224     /**
22225      * Protected method that will not generally be called directly. If you need/want
22226      * custom HTML cleanup, this is the method you should override.
22227      * @param {String} html The HTML to be cleaned
22228      * return {String} The cleaned HTML
22229      */
22230     cleanHtml : function(html){
22231         html = String(html);
22232         if(html.length > 5){
22233             if(Roo.isSafari){ // strip safari nonsense
22234                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22235             }
22236         }
22237         if(html == '&nbsp;'){
22238             html = '';
22239         }
22240         return html;
22241     },
22242
22243     /**
22244      * HTML Editor -> Textarea
22245      * Protected method that will not generally be called directly. Syncs the contents
22246      * of the editor iframe with the textarea.
22247      */
22248     syncValue : function(){
22249         if(this.initialized){
22250             var bd = (this.doc.body || this.doc.documentElement);
22251             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22252             var html = bd.innerHTML;
22253             if(Roo.isSafari){
22254                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22255                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22256                 if(m && m[1]){
22257                     html = '<div style="'+m[0]+'">' + html + '</div>';
22258                 }
22259             }
22260             html = this.cleanHtml(html);
22261             // fix up the special chars.. normaly like back quotes in word...
22262             // however we do not want to do this with chinese..
22263             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22264                 var cc = b.charCodeAt();
22265                 if (
22266                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22267                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22268                     (cc >= 0xf900 && cc < 0xfb00 )
22269                 ) {
22270                         return b;
22271                 }
22272                 return "&#"+cc+";" 
22273             });
22274             if(this.owner.fireEvent('beforesync', this, html) !== false){
22275                 this.el.dom.value = html;
22276                 this.owner.fireEvent('sync', this, html);
22277             }
22278         }
22279     },
22280
22281     /**
22282      * Protected method that will not generally be called directly. Pushes the value of the textarea
22283      * into the iframe editor.
22284      */
22285     pushValue : function(){
22286         if(this.initialized){
22287             var v = this.el.dom.value.trim();
22288             
22289 //            if(v.length < 1){
22290 //                v = '&#160;';
22291 //            }
22292             
22293             if(this.owner.fireEvent('beforepush', this, v) !== false){
22294                 var d = (this.doc.body || this.doc.documentElement);
22295                 d.innerHTML = v;
22296                 this.cleanUpPaste();
22297                 this.el.dom.value = d.innerHTML;
22298                 this.owner.fireEvent('push', this, v);
22299             }
22300         }
22301     },
22302
22303     // private
22304     deferFocus : function(){
22305         this.focus.defer(10, this);
22306     },
22307
22308     // doc'ed in Field
22309     focus : function(){
22310         if(this.win && !this.sourceEditMode){
22311             this.win.focus();
22312         }else{
22313             this.el.focus();
22314         }
22315     },
22316     
22317     assignDocWin: function()
22318     {
22319         var iframe = this.iframe;
22320         
22321          if(Roo.isIE){
22322             this.doc = iframe.contentWindow.document;
22323             this.win = iframe.contentWindow;
22324         } else {
22325 //            if (!Roo.get(this.frameId)) {
22326 //                return;
22327 //            }
22328 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22329 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22330             
22331             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22332                 return;
22333             }
22334             
22335             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22336             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22337         }
22338     },
22339     
22340     // private
22341     initEditor : function(){
22342         //console.log("INIT EDITOR");
22343         this.assignDocWin();
22344         
22345         
22346         
22347         this.doc.designMode="on";
22348         this.doc.open();
22349         this.doc.write(this.getDocMarkup());
22350         this.doc.close();
22351         
22352         var dbody = (this.doc.body || this.doc.documentElement);
22353         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22354         // this copies styles from the containing element into thsi one..
22355         // not sure why we need all of this..
22356         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22357         
22358         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22359         //ss['background-attachment'] = 'fixed'; // w3c
22360         dbody.bgProperties = 'fixed'; // ie
22361         //Roo.DomHelper.applyStyles(dbody, ss);
22362         Roo.EventManager.on(this.doc, {
22363             //'mousedown': this.onEditorEvent,
22364             'mouseup': this.onEditorEvent,
22365             'dblclick': this.onEditorEvent,
22366             'click': this.onEditorEvent,
22367             'keyup': this.onEditorEvent,
22368             buffer:100,
22369             scope: this
22370         });
22371         if(Roo.isGecko){
22372             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22373         }
22374         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22375             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22376         }
22377         this.initialized = true;
22378
22379         this.owner.fireEvent('initialize', this);
22380         this.pushValue();
22381     },
22382
22383     // private
22384     onDestroy : function(){
22385         
22386         
22387         
22388         if(this.rendered){
22389             
22390             //for (var i =0; i < this.toolbars.length;i++) {
22391             //    // fixme - ask toolbars for heights?
22392             //    this.toolbars[i].onDestroy();
22393            // }
22394             
22395             //this.wrap.dom.innerHTML = '';
22396             //this.wrap.remove();
22397         }
22398     },
22399
22400     // private
22401     onFirstFocus : function(){
22402         
22403         this.assignDocWin();
22404         
22405         
22406         this.activated = true;
22407          
22408     
22409         if(Roo.isGecko){ // prevent silly gecko errors
22410             this.win.focus();
22411             var s = this.win.getSelection();
22412             if(!s.focusNode || s.focusNode.nodeType != 3){
22413                 var r = s.getRangeAt(0);
22414                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22415                 r.collapse(true);
22416                 this.deferFocus();
22417             }
22418             try{
22419                 this.execCmd('useCSS', true);
22420                 this.execCmd('styleWithCSS', false);
22421             }catch(e){}
22422         }
22423         this.owner.fireEvent('activate', this);
22424     },
22425
22426     // private
22427     adjustFont: function(btn){
22428         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22429         //if(Roo.isSafari){ // safari
22430         //    adjust *= 2;
22431        // }
22432         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22433         if(Roo.isSafari){ // safari
22434             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22435             v =  (v < 10) ? 10 : v;
22436             v =  (v > 48) ? 48 : v;
22437             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22438             
22439         }
22440         
22441         
22442         v = Math.max(1, v+adjust);
22443         
22444         this.execCmd('FontSize', v  );
22445     },
22446
22447     onEditorEvent : function(e)
22448     {
22449         this.owner.fireEvent('editorevent', this, e);
22450       //  this.updateToolbar();
22451         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22452     },
22453
22454     insertTag : function(tg)
22455     {
22456         // could be a bit smarter... -> wrap the current selected tRoo..
22457         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22458             
22459             range = this.createRange(this.getSelection());
22460             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22461             wrappingNode.appendChild(range.extractContents());
22462             range.insertNode(wrappingNode);
22463
22464             return;
22465             
22466             
22467             
22468         }
22469         this.execCmd("formatblock",   tg);
22470         
22471     },
22472     
22473     insertText : function(txt)
22474     {
22475         
22476         
22477         var range = this.createRange();
22478         range.deleteContents();
22479                //alert(Sender.getAttribute('label'));
22480                
22481         range.insertNode(this.doc.createTextNode(txt));
22482     } ,
22483     
22484      
22485
22486     /**
22487      * Executes a Midas editor command on the editor document and performs necessary focus and
22488      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22489      * @param {String} cmd The Midas command
22490      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22491      */
22492     relayCmd : function(cmd, value){
22493         this.win.focus();
22494         this.execCmd(cmd, value);
22495         this.owner.fireEvent('editorevent', this);
22496         //this.updateToolbar();
22497         this.owner.deferFocus();
22498     },
22499
22500     /**
22501      * Executes a Midas editor command directly on the editor document.
22502      * For visual commands, you should use {@link #relayCmd} instead.
22503      * <b>This should only be called after the editor is initialized.</b>
22504      * @param {String} cmd The Midas command
22505      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22506      */
22507     execCmd : function(cmd, value){
22508         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22509         this.syncValue();
22510     },
22511  
22512  
22513    
22514     /**
22515      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22516      * to insert tRoo.
22517      * @param {String} text | dom node.. 
22518      */
22519     insertAtCursor : function(text)
22520     {
22521         
22522         if(!this.activated){
22523             return;
22524         }
22525         /*
22526         if(Roo.isIE){
22527             this.win.focus();
22528             var r = this.doc.selection.createRange();
22529             if(r){
22530                 r.collapse(true);
22531                 r.pasteHTML(text);
22532                 this.syncValue();
22533                 this.deferFocus();
22534             
22535             }
22536             return;
22537         }
22538         */
22539         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22540             this.win.focus();
22541             
22542             
22543             // from jquery ui (MIT licenced)
22544             var range, node;
22545             var win = this.win;
22546             
22547             if (win.getSelection && win.getSelection().getRangeAt) {
22548                 range = win.getSelection().getRangeAt(0);
22549                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22550                 range.insertNode(node);
22551             } else if (win.document.selection && win.document.selection.createRange) {
22552                 // no firefox support
22553                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22554                 win.document.selection.createRange().pasteHTML(txt);
22555             } else {
22556                 // no firefox support
22557                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22558                 this.execCmd('InsertHTML', txt);
22559             } 
22560             
22561             this.syncValue();
22562             
22563             this.deferFocus();
22564         }
22565     },
22566  // private
22567     mozKeyPress : function(e){
22568         if(e.ctrlKey){
22569             var c = e.getCharCode(), cmd;
22570           
22571             if(c > 0){
22572                 c = String.fromCharCode(c).toLowerCase();
22573                 switch(c){
22574                     case 'b':
22575                         cmd = 'bold';
22576                         break;
22577                     case 'i':
22578                         cmd = 'italic';
22579                         break;
22580                     
22581                     case 'u':
22582                         cmd = 'underline';
22583                         break;
22584                     
22585                     case 'v':
22586                         this.cleanUpPaste.defer(100, this);
22587                         return;
22588                         
22589                 }
22590                 if(cmd){
22591                     this.win.focus();
22592                     this.execCmd(cmd);
22593                     this.deferFocus();
22594                     e.preventDefault();
22595                 }
22596                 
22597             }
22598         }
22599     },
22600
22601     // private
22602     fixKeys : function(){ // load time branching for fastest keydown performance
22603         if(Roo.isIE){
22604             return function(e){
22605                 var k = e.getKey(), r;
22606                 if(k == e.TAB){
22607                     e.stopEvent();
22608                     r = this.doc.selection.createRange();
22609                     if(r){
22610                         r.collapse(true);
22611                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22612                         this.deferFocus();
22613                     }
22614                     return;
22615                 }
22616                 
22617                 if(k == e.ENTER){
22618                     r = this.doc.selection.createRange();
22619                     if(r){
22620                         var target = r.parentElement();
22621                         if(!target || target.tagName.toLowerCase() != 'li'){
22622                             e.stopEvent();
22623                             r.pasteHTML('<br />');
22624                             r.collapse(false);
22625                             r.select();
22626                         }
22627                     }
22628                 }
22629                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22630                     this.cleanUpPaste.defer(100, this);
22631                     return;
22632                 }
22633                 
22634                 
22635             };
22636         }else if(Roo.isOpera){
22637             return function(e){
22638                 var k = e.getKey();
22639                 if(k == e.TAB){
22640                     e.stopEvent();
22641                     this.win.focus();
22642                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22643                     this.deferFocus();
22644                 }
22645                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22646                     this.cleanUpPaste.defer(100, this);
22647                     return;
22648                 }
22649                 
22650             };
22651         }else if(Roo.isSafari){
22652             return function(e){
22653                 var k = e.getKey();
22654                 
22655                 if(k == e.TAB){
22656                     e.stopEvent();
22657                     this.execCmd('InsertText','\t');
22658                     this.deferFocus();
22659                     return;
22660                 }
22661                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22662                     this.cleanUpPaste.defer(100, this);
22663                     return;
22664                 }
22665                 
22666              };
22667         }
22668     }(),
22669     
22670     getAllAncestors: function()
22671     {
22672         var p = this.getSelectedNode();
22673         var a = [];
22674         if (!p) {
22675             a.push(p); // push blank onto stack..
22676             p = this.getParentElement();
22677         }
22678         
22679         
22680         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22681             a.push(p);
22682             p = p.parentNode;
22683         }
22684         a.push(this.doc.body);
22685         return a;
22686     },
22687     lastSel : false,
22688     lastSelNode : false,
22689     
22690     
22691     getSelection : function() 
22692     {
22693         this.assignDocWin();
22694         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22695     },
22696     
22697     getSelectedNode: function() 
22698     {
22699         // this may only work on Gecko!!!
22700         
22701         // should we cache this!!!!
22702         
22703         
22704         
22705          
22706         var range = this.createRange(this.getSelection()).cloneRange();
22707         
22708         if (Roo.isIE) {
22709             var parent = range.parentElement();
22710             while (true) {
22711                 var testRange = range.duplicate();
22712                 testRange.moveToElementText(parent);
22713                 if (testRange.inRange(range)) {
22714                     break;
22715                 }
22716                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22717                     break;
22718                 }
22719                 parent = parent.parentElement;
22720             }
22721             return parent;
22722         }
22723         
22724         // is ancestor a text element.
22725         var ac =  range.commonAncestorContainer;
22726         if (ac.nodeType == 3) {
22727             ac = ac.parentNode;
22728         }
22729         
22730         var ar = ac.childNodes;
22731          
22732         var nodes = [];
22733         var other_nodes = [];
22734         var has_other_nodes = false;
22735         for (var i=0;i<ar.length;i++) {
22736             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22737                 continue;
22738             }
22739             // fullly contained node.
22740             
22741             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22742                 nodes.push(ar[i]);
22743                 continue;
22744             }
22745             
22746             // probably selected..
22747             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22748                 other_nodes.push(ar[i]);
22749                 continue;
22750             }
22751             // outer..
22752             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22753                 continue;
22754             }
22755             
22756             
22757             has_other_nodes = true;
22758         }
22759         if (!nodes.length && other_nodes.length) {
22760             nodes= other_nodes;
22761         }
22762         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22763             return false;
22764         }
22765         
22766         return nodes[0];
22767     },
22768     createRange: function(sel)
22769     {
22770         // this has strange effects when using with 
22771         // top toolbar - not sure if it's a great idea.
22772         //this.editor.contentWindow.focus();
22773         if (typeof sel != "undefined") {
22774             try {
22775                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22776             } catch(e) {
22777                 return this.doc.createRange();
22778             }
22779         } else {
22780             return this.doc.createRange();
22781         }
22782     },
22783     getParentElement: function()
22784     {
22785         
22786         this.assignDocWin();
22787         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22788         
22789         var range = this.createRange(sel);
22790          
22791         try {
22792             var p = range.commonAncestorContainer;
22793             while (p.nodeType == 3) { // text node
22794                 p = p.parentNode;
22795             }
22796             return p;
22797         } catch (e) {
22798             return null;
22799         }
22800     
22801     },
22802     /***
22803      *
22804      * Range intersection.. the hard stuff...
22805      *  '-1' = before
22806      *  '0' = hits..
22807      *  '1' = after.
22808      *         [ -- selected range --- ]
22809      *   [fail]                        [fail]
22810      *
22811      *    basically..
22812      *      if end is before start or  hits it. fail.
22813      *      if start is after end or hits it fail.
22814      *
22815      *   if either hits (but other is outside. - then it's not 
22816      *   
22817      *    
22818      **/
22819     
22820     
22821     // @see http://www.thismuchiknow.co.uk/?p=64.
22822     rangeIntersectsNode : function(range, node)
22823     {
22824         var nodeRange = node.ownerDocument.createRange();
22825         try {
22826             nodeRange.selectNode(node);
22827         } catch (e) {
22828             nodeRange.selectNodeContents(node);
22829         }
22830     
22831         var rangeStartRange = range.cloneRange();
22832         rangeStartRange.collapse(true);
22833     
22834         var rangeEndRange = range.cloneRange();
22835         rangeEndRange.collapse(false);
22836     
22837         var nodeStartRange = nodeRange.cloneRange();
22838         nodeStartRange.collapse(true);
22839     
22840         var nodeEndRange = nodeRange.cloneRange();
22841         nodeEndRange.collapse(false);
22842     
22843         return rangeStartRange.compareBoundaryPoints(
22844                  Range.START_TO_START, nodeEndRange) == -1 &&
22845                rangeEndRange.compareBoundaryPoints(
22846                  Range.START_TO_START, nodeStartRange) == 1;
22847         
22848          
22849     },
22850     rangeCompareNode : function(range, node)
22851     {
22852         var nodeRange = node.ownerDocument.createRange();
22853         try {
22854             nodeRange.selectNode(node);
22855         } catch (e) {
22856             nodeRange.selectNodeContents(node);
22857         }
22858         
22859         
22860         range.collapse(true);
22861     
22862         nodeRange.collapse(true);
22863      
22864         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22865         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22866          
22867         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22868         
22869         var nodeIsBefore   =  ss == 1;
22870         var nodeIsAfter    = ee == -1;
22871         
22872         if (nodeIsBefore && nodeIsAfter) {
22873             return 0; // outer
22874         }
22875         if (!nodeIsBefore && nodeIsAfter) {
22876             return 1; //right trailed.
22877         }
22878         
22879         if (nodeIsBefore && !nodeIsAfter) {
22880             return 2;  // left trailed.
22881         }
22882         // fully contined.
22883         return 3;
22884     },
22885
22886     // private? - in a new class?
22887     cleanUpPaste :  function()
22888     {
22889         // cleans up the whole document..
22890         Roo.log('cleanuppaste');
22891         
22892         this.cleanUpChildren(this.doc.body);
22893         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22894         if (clean != this.doc.body.innerHTML) {
22895             this.doc.body.innerHTML = clean;
22896         }
22897         
22898     },
22899     
22900     cleanWordChars : function(input) {// change the chars to hex code
22901         var he = Roo.HtmlEditorCore;
22902         
22903         var output = input;
22904         Roo.each(he.swapCodes, function(sw) { 
22905             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22906             
22907             output = output.replace(swapper, sw[1]);
22908         });
22909         
22910         return output;
22911     },
22912     
22913     
22914     cleanUpChildren : function (n)
22915     {
22916         if (!n.childNodes.length) {
22917             return;
22918         }
22919         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22920            this.cleanUpChild(n.childNodes[i]);
22921         }
22922     },
22923     
22924     
22925         
22926     
22927     cleanUpChild : function (node)
22928     {
22929         var ed = this;
22930         //console.log(node);
22931         if (node.nodeName == "#text") {
22932             // clean up silly Windows -- stuff?
22933             return; 
22934         }
22935         if (node.nodeName == "#comment") {
22936             node.parentNode.removeChild(node);
22937             // clean up silly Windows -- stuff?
22938             return; 
22939         }
22940         var lcname = node.tagName.toLowerCase();
22941         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22942         // whitelist of tags..
22943         
22944         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22945             // remove node.
22946             node.parentNode.removeChild(node);
22947             return;
22948             
22949         }
22950         
22951         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22952         
22953         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22954         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22955         
22956         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22957         //    remove_keep_children = true;
22958         //}
22959         
22960         if (remove_keep_children) {
22961             this.cleanUpChildren(node);
22962             // inserts everything just before this node...
22963             while (node.childNodes.length) {
22964                 var cn = node.childNodes[0];
22965                 node.removeChild(cn);
22966                 node.parentNode.insertBefore(cn, node);
22967             }
22968             node.parentNode.removeChild(node);
22969             return;
22970         }
22971         
22972         if (!node.attributes || !node.attributes.length) {
22973             this.cleanUpChildren(node);
22974             return;
22975         }
22976         
22977         function cleanAttr(n,v)
22978         {
22979             
22980             if (v.match(/^\./) || v.match(/^\//)) {
22981                 return;
22982             }
22983             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22984                 return;
22985             }
22986             if (v.match(/^#/)) {
22987                 return;
22988             }
22989 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22990             node.removeAttribute(n);
22991             
22992         }
22993         
22994         var cwhite = this.cwhite;
22995         var cblack = this.cblack;
22996             
22997         function cleanStyle(n,v)
22998         {
22999             if (v.match(/expression/)) { //XSS?? should we even bother..
23000                 node.removeAttribute(n);
23001                 return;
23002             }
23003             
23004             var parts = v.split(/;/);
23005             var clean = [];
23006             
23007             Roo.each(parts, function(p) {
23008                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23009                 if (!p.length) {
23010                     return true;
23011                 }
23012                 var l = p.split(':').shift().replace(/\s+/g,'');
23013                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23014                 
23015                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23016 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23017                     //node.removeAttribute(n);
23018                     return true;
23019                 }
23020                 //Roo.log()
23021                 // only allow 'c whitelisted system attributes'
23022                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23023 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23024                     //node.removeAttribute(n);
23025                     return true;
23026                 }
23027                 
23028                 
23029                  
23030                 
23031                 clean.push(p);
23032                 return true;
23033             });
23034             if (clean.length) { 
23035                 node.setAttribute(n, clean.join(';'));
23036             } else {
23037                 node.removeAttribute(n);
23038             }
23039             
23040         }
23041         
23042         
23043         for (var i = node.attributes.length-1; i > -1 ; i--) {
23044             var a = node.attributes[i];
23045             //console.log(a);
23046             
23047             if (a.name.toLowerCase().substr(0,2)=='on')  {
23048                 node.removeAttribute(a.name);
23049                 continue;
23050             }
23051             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23052                 node.removeAttribute(a.name);
23053                 continue;
23054             }
23055             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23056                 cleanAttr(a.name,a.value); // fixme..
23057                 continue;
23058             }
23059             if (a.name == 'style') {
23060                 cleanStyle(a.name,a.value);
23061                 continue;
23062             }
23063             /// clean up MS crap..
23064             // tecnically this should be a list of valid class'es..
23065             
23066             
23067             if (a.name == 'class') {
23068                 if (a.value.match(/^Mso/)) {
23069                     node.className = '';
23070                 }
23071                 
23072                 if (a.value.match(/^body$/)) {
23073                     node.className = '';
23074                 }
23075                 continue;
23076             }
23077             
23078             // style cleanup!?
23079             // class cleanup?
23080             
23081         }
23082         
23083         
23084         this.cleanUpChildren(node);
23085         
23086         
23087     },
23088     
23089     /**
23090      * Clean up MS wordisms...
23091      */
23092     cleanWord : function(node)
23093     {
23094         
23095         
23096         if (!node) {
23097             this.cleanWord(this.doc.body);
23098             return;
23099         }
23100         if (node.nodeName == "#text") {
23101             // clean up silly Windows -- stuff?
23102             return; 
23103         }
23104         if (node.nodeName == "#comment") {
23105             node.parentNode.removeChild(node);
23106             // clean up silly Windows -- stuff?
23107             return; 
23108         }
23109         
23110         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23111             node.parentNode.removeChild(node);
23112             return;
23113         }
23114         
23115         // remove - but keep children..
23116         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23117             while (node.childNodes.length) {
23118                 var cn = node.childNodes[0];
23119                 node.removeChild(cn);
23120                 node.parentNode.insertBefore(cn, node);
23121             }
23122             node.parentNode.removeChild(node);
23123             this.iterateChildren(node, this.cleanWord);
23124             return;
23125         }
23126         // clean styles
23127         if (node.className.length) {
23128             
23129             var cn = node.className.split(/\W+/);
23130             var cna = [];
23131             Roo.each(cn, function(cls) {
23132                 if (cls.match(/Mso[a-zA-Z]+/)) {
23133                     return;
23134                 }
23135                 cna.push(cls);
23136             });
23137             node.className = cna.length ? cna.join(' ') : '';
23138             if (!cna.length) {
23139                 node.removeAttribute("class");
23140             }
23141         }
23142         
23143         if (node.hasAttribute("lang")) {
23144             node.removeAttribute("lang");
23145         }
23146         
23147         if (node.hasAttribute("style")) {
23148             
23149             var styles = node.getAttribute("style").split(";");
23150             var nstyle = [];
23151             Roo.each(styles, function(s) {
23152                 if (!s.match(/:/)) {
23153                     return;
23154                 }
23155                 var kv = s.split(":");
23156                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23157                     return;
23158                 }
23159                 // what ever is left... we allow.
23160                 nstyle.push(s);
23161             });
23162             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23163             if (!nstyle.length) {
23164                 node.removeAttribute('style');
23165             }
23166         }
23167         this.iterateChildren(node, this.cleanWord);
23168         
23169         
23170         
23171     },
23172     /**
23173      * iterateChildren of a Node, calling fn each time, using this as the scole..
23174      * @param {DomNode} node node to iterate children of.
23175      * @param {Function} fn method of this class to call on each item.
23176      */
23177     iterateChildren : function(node, fn)
23178     {
23179         if (!node.childNodes.length) {
23180                 return;
23181         }
23182         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23183            fn.call(this, node.childNodes[i])
23184         }
23185     },
23186     
23187     
23188     /**
23189      * cleanTableWidths.
23190      *
23191      * Quite often pasting from word etc.. results in tables with column and widths.
23192      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23193      *
23194      */
23195     cleanTableWidths : function(node)
23196     {
23197          
23198          
23199         if (!node) {
23200             this.cleanTableWidths(this.doc.body);
23201             return;
23202         }
23203         
23204         // ignore list...
23205         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23206             return; 
23207         }
23208         Roo.log(node.tagName);
23209         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23210             this.iterateChildren(node, this.cleanTableWidths);
23211             return;
23212         }
23213         if (node.hasAttribute('width')) {
23214             node.removeAttribute('width');
23215         }
23216         
23217          
23218         if (node.hasAttribute("style")) {
23219             // pretty basic...
23220             
23221             var styles = node.getAttribute("style").split(";");
23222             var nstyle = [];
23223             Roo.each(styles, function(s) {
23224                 if (!s.match(/:/)) {
23225                     return;
23226                 }
23227                 var kv = s.split(":");
23228                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23229                     return;
23230                 }
23231                 // what ever is left... we allow.
23232                 nstyle.push(s);
23233             });
23234             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23235             if (!nstyle.length) {
23236                 node.removeAttribute('style');
23237             }
23238         }
23239         
23240         this.iterateChildren(node, this.cleanTableWidths);
23241         
23242         
23243     },
23244     
23245     
23246     
23247     
23248     domToHTML : function(currentElement, depth, nopadtext) {
23249         
23250         depth = depth || 0;
23251         nopadtext = nopadtext || false;
23252     
23253         if (!currentElement) {
23254             return this.domToHTML(this.doc.body);
23255         }
23256         
23257         //Roo.log(currentElement);
23258         var j;
23259         var allText = false;
23260         var nodeName = currentElement.nodeName;
23261         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23262         
23263         if  (nodeName == '#text') {
23264             
23265             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23266         }
23267         
23268         
23269         var ret = '';
23270         if (nodeName != 'BODY') {
23271              
23272             var i = 0;
23273             // Prints the node tagName, such as <A>, <IMG>, etc
23274             if (tagName) {
23275                 var attr = [];
23276                 for(i = 0; i < currentElement.attributes.length;i++) {
23277                     // quoting?
23278                     var aname = currentElement.attributes.item(i).name;
23279                     if (!currentElement.attributes.item(i).value.length) {
23280                         continue;
23281                     }
23282                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23283                 }
23284                 
23285                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23286             } 
23287             else {
23288                 
23289                 // eack
23290             }
23291         } else {
23292             tagName = false;
23293         }
23294         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23295             return ret;
23296         }
23297         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23298             nopadtext = true;
23299         }
23300         
23301         
23302         // Traverse the tree
23303         i = 0;
23304         var currentElementChild = currentElement.childNodes.item(i);
23305         var allText = true;
23306         var innerHTML  = '';
23307         lastnode = '';
23308         while (currentElementChild) {
23309             // Formatting code (indent the tree so it looks nice on the screen)
23310             var nopad = nopadtext;
23311             if (lastnode == 'SPAN') {
23312                 nopad  = true;
23313             }
23314             // text
23315             if  (currentElementChild.nodeName == '#text') {
23316                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23317                 toadd = nopadtext ? toadd : toadd.trim();
23318                 if (!nopad && toadd.length > 80) {
23319                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23320                 }
23321                 innerHTML  += toadd;
23322                 
23323                 i++;
23324                 currentElementChild = currentElement.childNodes.item(i);
23325                 lastNode = '';
23326                 continue;
23327             }
23328             allText = false;
23329             
23330             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23331                 
23332             // Recursively traverse the tree structure of the child node
23333             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23334             lastnode = currentElementChild.nodeName;
23335             i++;
23336             currentElementChild=currentElement.childNodes.item(i);
23337         }
23338         
23339         ret += innerHTML;
23340         
23341         if (!allText) {
23342                 // The remaining code is mostly for formatting the tree
23343             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23344         }
23345         
23346         
23347         if (tagName) {
23348             ret+= "</"+tagName+">";
23349         }
23350         return ret;
23351         
23352     },
23353         
23354     applyBlacklists : function()
23355     {
23356         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23357         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23358         
23359         this.white = [];
23360         this.black = [];
23361         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23362             if (b.indexOf(tag) > -1) {
23363                 return;
23364             }
23365             this.white.push(tag);
23366             
23367         }, this);
23368         
23369         Roo.each(w, function(tag) {
23370             if (b.indexOf(tag) > -1) {
23371                 return;
23372             }
23373             if (this.white.indexOf(tag) > -1) {
23374                 return;
23375             }
23376             this.white.push(tag);
23377             
23378         }, this);
23379         
23380         
23381         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23382             if (w.indexOf(tag) > -1) {
23383                 return;
23384             }
23385             this.black.push(tag);
23386             
23387         }, this);
23388         
23389         Roo.each(b, function(tag) {
23390             if (w.indexOf(tag) > -1) {
23391                 return;
23392             }
23393             if (this.black.indexOf(tag) > -1) {
23394                 return;
23395             }
23396             this.black.push(tag);
23397             
23398         }, this);
23399         
23400         
23401         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23402         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23403         
23404         this.cwhite = [];
23405         this.cblack = [];
23406         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23407             if (b.indexOf(tag) > -1) {
23408                 return;
23409             }
23410             this.cwhite.push(tag);
23411             
23412         }, this);
23413         
23414         Roo.each(w, function(tag) {
23415             if (b.indexOf(tag) > -1) {
23416                 return;
23417             }
23418             if (this.cwhite.indexOf(tag) > -1) {
23419                 return;
23420             }
23421             this.cwhite.push(tag);
23422             
23423         }, this);
23424         
23425         
23426         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23427             if (w.indexOf(tag) > -1) {
23428                 return;
23429             }
23430             this.cblack.push(tag);
23431             
23432         }, this);
23433         
23434         Roo.each(b, function(tag) {
23435             if (w.indexOf(tag) > -1) {
23436                 return;
23437             }
23438             if (this.cblack.indexOf(tag) > -1) {
23439                 return;
23440             }
23441             this.cblack.push(tag);
23442             
23443         }, this);
23444     },
23445     
23446     setStylesheets : function(stylesheets)
23447     {
23448         if(typeof(stylesheets) == 'string'){
23449             Roo.get(this.iframe.contentDocument.head).createChild({
23450                 tag : 'link',
23451                 rel : 'stylesheet',
23452                 type : 'text/css',
23453                 href : stylesheets
23454             });
23455             
23456             return;
23457         }
23458         var _this = this;
23459      
23460         Roo.each(stylesheets, function(s) {
23461             if(!s.length){
23462                 return;
23463             }
23464             
23465             Roo.get(_this.iframe.contentDocument.head).createChild({
23466                 tag : 'link',
23467                 rel : 'stylesheet',
23468                 type : 'text/css',
23469                 href : s
23470             });
23471         });
23472
23473         
23474     },
23475     
23476     removeStylesheets : function()
23477     {
23478         var _this = this;
23479         
23480         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23481             s.remove();
23482         });
23483     },
23484     
23485     setStyle : function(style)
23486     {
23487         Roo.get(this.iframe.contentDocument.head).createChild({
23488             tag : 'style',
23489             type : 'text/css',
23490             html : style
23491         });
23492
23493         return;
23494     }
23495     
23496     // hide stuff that is not compatible
23497     /**
23498      * @event blur
23499      * @hide
23500      */
23501     /**
23502      * @event change
23503      * @hide
23504      */
23505     /**
23506      * @event focus
23507      * @hide
23508      */
23509     /**
23510      * @event specialkey
23511      * @hide
23512      */
23513     /**
23514      * @cfg {String} fieldClass @hide
23515      */
23516     /**
23517      * @cfg {String} focusClass @hide
23518      */
23519     /**
23520      * @cfg {String} autoCreate @hide
23521      */
23522     /**
23523      * @cfg {String} inputType @hide
23524      */
23525     /**
23526      * @cfg {String} invalidClass @hide
23527      */
23528     /**
23529      * @cfg {String} invalidText @hide
23530      */
23531     /**
23532      * @cfg {String} msgFx @hide
23533      */
23534     /**
23535      * @cfg {String} validateOnBlur @hide
23536      */
23537 });
23538
23539 Roo.HtmlEditorCore.white = [
23540         'area', 'br', 'img', 'input', 'hr', 'wbr',
23541         
23542        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23543        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23544        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23545        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23546        'table',   'ul',         'xmp', 
23547        
23548        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23549       'thead',   'tr', 
23550      
23551       'dir', 'menu', 'ol', 'ul', 'dl',
23552        
23553       'embed',  'object'
23554 ];
23555
23556
23557 Roo.HtmlEditorCore.black = [
23558     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23559         'applet', // 
23560         'base',   'basefont', 'bgsound', 'blink',  'body', 
23561         'frame',  'frameset', 'head',    'html',   'ilayer', 
23562         'iframe', 'layer',  'link',     'meta',    'object',   
23563         'script', 'style' ,'title',  'xml' // clean later..
23564 ];
23565 Roo.HtmlEditorCore.clean = [
23566     'script', 'style', 'title', 'xml'
23567 ];
23568 Roo.HtmlEditorCore.remove = [
23569     'font'
23570 ];
23571 // attributes..
23572
23573 Roo.HtmlEditorCore.ablack = [
23574     'on'
23575 ];
23576     
23577 Roo.HtmlEditorCore.aclean = [ 
23578     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23579 ];
23580
23581 // protocols..
23582 Roo.HtmlEditorCore.pwhite= [
23583         'http',  'https',  'mailto'
23584 ];
23585
23586 // white listed style attributes.
23587 Roo.HtmlEditorCore.cwhite= [
23588       //  'text-align', /// default is to allow most things..
23589       
23590          
23591 //        'font-size'//??
23592 ];
23593
23594 // black listed style attributes.
23595 Roo.HtmlEditorCore.cblack= [
23596       //  'font-size' -- this can be set by the project 
23597 ];
23598
23599
23600 Roo.HtmlEditorCore.swapCodes   =[ 
23601     [    8211, "--" ], 
23602     [    8212, "--" ], 
23603     [    8216,  "'" ],  
23604     [    8217, "'" ],  
23605     [    8220, '"' ],  
23606     [    8221, '"' ],  
23607     [    8226, "*" ],  
23608     [    8230, "..." ]
23609 ]; 
23610
23611     /*
23612  * - LGPL
23613  *
23614  * HtmlEditor
23615  * 
23616  */
23617
23618 /**
23619  * @class Roo.bootstrap.HtmlEditor
23620  * @extends Roo.bootstrap.TextArea
23621  * Bootstrap HtmlEditor class
23622
23623  * @constructor
23624  * Create a new HtmlEditor
23625  * @param {Object} config The config object
23626  */
23627
23628 Roo.bootstrap.HtmlEditor = function(config){
23629     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23630     if (!this.toolbars) {
23631         this.toolbars = [];
23632     }
23633     
23634     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23635     this.addEvents({
23636             /**
23637              * @event initialize
23638              * Fires when the editor is fully initialized (including the iframe)
23639              * @param {HtmlEditor} this
23640              */
23641             initialize: true,
23642             /**
23643              * @event activate
23644              * Fires when the editor is first receives the focus. Any insertion must wait
23645              * until after this event.
23646              * @param {HtmlEditor} this
23647              */
23648             activate: true,
23649              /**
23650              * @event beforesync
23651              * Fires before the textarea is updated with content from the editor iframe. Return false
23652              * to cancel the sync.
23653              * @param {HtmlEditor} this
23654              * @param {String} html
23655              */
23656             beforesync: true,
23657              /**
23658              * @event beforepush
23659              * Fires before the iframe editor is updated with content from the textarea. Return false
23660              * to cancel the push.
23661              * @param {HtmlEditor} this
23662              * @param {String} html
23663              */
23664             beforepush: true,
23665              /**
23666              * @event sync
23667              * Fires when the textarea is updated with content from the editor iframe.
23668              * @param {HtmlEditor} this
23669              * @param {String} html
23670              */
23671             sync: true,
23672              /**
23673              * @event push
23674              * Fires when the iframe editor is updated with content from the textarea.
23675              * @param {HtmlEditor} this
23676              * @param {String} html
23677              */
23678             push: true,
23679              /**
23680              * @event editmodechange
23681              * Fires when the editor switches edit modes
23682              * @param {HtmlEditor} this
23683              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23684              */
23685             editmodechange: true,
23686             /**
23687              * @event editorevent
23688              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23689              * @param {HtmlEditor} this
23690              */
23691             editorevent: true,
23692             /**
23693              * @event firstfocus
23694              * Fires when on first focus - needed by toolbars..
23695              * @param {HtmlEditor} this
23696              */
23697             firstfocus: true,
23698             /**
23699              * @event autosave
23700              * Auto save the htmlEditor value as a file into Events
23701              * @param {HtmlEditor} this
23702              */
23703             autosave: true,
23704             /**
23705              * @event savedpreview
23706              * preview the saved version of htmlEditor
23707              * @param {HtmlEditor} this
23708              */
23709             savedpreview: true
23710         });
23711 };
23712
23713
23714 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23715     
23716     
23717       /**
23718      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23719      */
23720     toolbars : false,
23721     
23722      /**
23723     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23724     */
23725     btns : [],
23726    
23727      /**
23728      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23729      *                        Roo.resizable.
23730      */
23731     resizable : false,
23732      /**
23733      * @cfg {Number} height (in pixels)
23734      */   
23735     height: 300,
23736    /**
23737      * @cfg {Number} width (in pixels)
23738      */   
23739     width: false,
23740     
23741     /**
23742      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23743      * 
23744      */
23745     stylesheets: false,
23746     
23747     // id of frame..
23748     frameId: false,
23749     
23750     // private properties
23751     validationEvent : false,
23752     deferHeight: true,
23753     initialized : false,
23754     activated : false,
23755     
23756     onFocus : Roo.emptyFn,
23757     iframePad:3,
23758     hideMode:'offsets',
23759     
23760     tbContainer : false,
23761     
23762     bodyCls : '',
23763     
23764     toolbarContainer :function() {
23765         return this.wrap.select('.x-html-editor-tb',true).first();
23766     },
23767
23768     /**
23769      * Protected method that will not generally be called directly. It
23770      * is called when the editor creates its toolbar. Override this method if you need to
23771      * add custom toolbar buttons.
23772      * @param {HtmlEditor} editor
23773      */
23774     createToolbar : function(){
23775         Roo.log('renewing');
23776         Roo.log("create toolbars");
23777         
23778         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23779         this.toolbars[0].render(this.toolbarContainer());
23780         
23781         return;
23782         
23783 //        if (!editor.toolbars || !editor.toolbars.length) {
23784 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23785 //        }
23786 //        
23787 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23788 //            editor.toolbars[i] = Roo.factory(
23789 //                    typeof(editor.toolbars[i]) == 'string' ?
23790 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23791 //                Roo.bootstrap.HtmlEditor);
23792 //            editor.toolbars[i].init(editor);
23793 //        }
23794     },
23795
23796      
23797     // private
23798     onRender : function(ct, position)
23799     {
23800        // Roo.log("Call onRender: " + this.xtype);
23801         var _t = this;
23802         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23803       
23804         this.wrap = this.inputEl().wrap({
23805             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23806         });
23807         
23808         this.editorcore.onRender(ct, position);
23809          
23810         if (this.resizable) {
23811             this.resizeEl = new Roo.Resizable(this.wrap, {
23812                 pinned : true,
23813                 wrap: true,
23814                 dynamic : true,
23815                 minHeight : this.height,
23816                 height: this.height,
23817                 handles : this.resizable,
23818                 width: this.width,
23819                 listeners : {
23820                     resize : function(r, w, h) {
23821                         _t.onResize(w,h); // -something
23822                     }
23823                 }
23824             });
23825             
23826         }
23827         this.createToolbar(this);
23828        
23829         
23830         if(!this.width && this.resizable){
23831             this.setSize(this.wrap.getSize());
23832         }
23833         if (this.resizeEl) {
23834             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23835             // should trigger onReize..
23836         }
23837         
23838     },
23839
23840     // private
23841     onResize : function(w, h)
23842     {
23843         Roo.log('resize: ' +w + ',' + h );
23844         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23845         var ew = false;
23846         var eh = false;
23847         
23848         if(this.inputEl() ){
23849             if(typeof w == 'number'){
23850                 var aw = w - this.wrap.getFrameWidth('lr');
23851                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23852                 ew = aw;
23853             }
23854             if(typeof h == 'number'){
23855                  var tbh = -11;  // fixme it needs to tool bar size!
23856                 for (var i =0; i < this.toolbars.length;i++) {
23857                     // fixme - ask toolbars for heights?
23858                     tbh += this.toolbars[i].el.getHeight();
23859                     //if (this.toolbars[i].footer) {
23860                     //    tbh += this.toolbars[i].footer.el.getHeight();
23861                     //}
23862                 }
23863               
23864                 
23865                 
23866                 
23867                 
23868                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23869                 ah -= 5; // knock a few pixes off for look..
23870                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23871                 var eh = ah;
23872             }
23873         }
23874         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23875         this.editorcore.onResize(ew,eh);
23876         
23877     },
23878
23879     /**
23880      * Toggles the editor between standard and source edit mode.
23881      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23882      */
23883     toggleSourceEdit : function(sourceEditMode)
23884     {
23885         this.editorcore.toggleSourceEdit(sourceEditMode);
23886         
23887         if(this.editorcore.sourceEditMode){
23888             Roo.log('editor - showing textarea');
23889             
23890 //            Roo.log('in');
23891 //            Roo.log(this.syncValue());
23892             this.syncValue();
23893             this.inputEl().removeClass(['hide', 'x-hidden']);
23894             this.inputEl().dom.removeAttribute('tabIndex');
23895             this.inputEl().focus();
23896         }else{
23897             Roo.log('editor - hiding textarea');
23898 //            Roo.log('out')
23899 //            Roo.log(this.pushValue()); 
23900             this.pushValue();
23901             
23902             this.inputEl().addClass(['hide', 'x-hidden']);
23903             this.inputEl().dom.setAttribute('tabIndex', -1);
23904             //this.deferFocus();
23905         }
23906          
23907         if(this.resizable){
23908             this.setSize(this.wrap.getSize());
23909         }
23910         
23911         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23912     },
23913  
23914     // private (for BoxComponent)
23915     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23916
23917     // private (for BoxComponent)
23918     getResizeEl : function(){
23919         return this.wrap;
23920     },
23921
23922     // private (for BoxComponent)
23923     getPositionEl : function(){
23924         return this.wrap;
23925     },
23926
23927     // private
23928     initEvents : function(){
23929         this.originalValue = this.getValue();
23930     },
23931
23932 //    /**
23933 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23934 //     * @method
23935 //     */
23936 //    markInvalid : Roo.emptyFn,
23937 //    /**
23938 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23939 //     * @method
23940 //     */
23941 //    clearInvalid : Roo.emptyFn,
23942
23943     setValue : function(v){
23944         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23945         this.editorcore.pushValue();
23946     },
23947
23948      
23949     // private
23950     deferFocus : function(){
23951         this.focus.defer(10, this);
23952     },
23953
23954     // doc'ed in Field
23955     focus : function(){
23956         this.editorcore.focus();
23957         
23958     },
23959       
23960
23961     // private
23962     onDestroy : function(){
23963         
23964         
23965         
23966         if(this.rendered){
23967             
23968             for (var i =0; i < this.toolbars.length;i++) {
23969                 // fixme - ask toolbars for heights?
23970                 this.toolbars[i].onDestroy();
23971             }
23972             
23973             this.wrap.dom.innerHTML = '';
23974             this.wrap.remove();
23975         }
23976     },
23977
23978     // private
23979     onFirstFocus : function(){
23980         //Roo.log("onFirstFocus");
23981         this.editorcore.onFirstFocus();
23982          for (var i =0; i < this.toolbars.length;i++) {
23983             this.toolbars[i].onFirstFocus();
23984         }
23985         
23986     },
23987     
23988     // private
23989     syncValue : function()
23990     {   
23991         this.editorcore.syncValue();
23992     },
23993     
23994     pushValue : function()
23995     {   
23996         this.editorcore.pushValue();
23997     }
23998      
23999     
24000     // hide stuff that is not compatible
24001     /**
24002      * @event blur
24003      * @hide
24004      */
24005     /**
24006      * @event change
24007      * @hide
24008      */
24009     /**
24010      * @event focus
24011      * @hide
24012      */
24013     /**
24014      * @event specialkey
24015      * @hide
24016      */
24017     /**
24018      * @cfg {String} fieldClass @hide
24019      */
24020     /**
24021      * @cfg {String} focusClass @hide
24022      */
24023     /**
24024      * @cfg {String} autoCreate @hide
24025      */
24026     /**
24027      * @cfg {String} inputType @hide
24028      */
24029      
24030     /**
24031      * @cfg {String} invalidText @hide
24032      */
24033     /**
24034      * @cfg {String} msgFx @hide
24035      */
24036     /**
24037      * @cfg {String} validateOnBlur @hide
24038      */
24039 });
24040  
24041     
24042    
24043    
24044    
24045       
24046 Roo.namespace('Roo.bootstrap.htmleditor');
24047 /**
24048  * @class Roo.bootstrap.HtmlEditorToolbar1
24049  * Basic Toolbar
24050  * 
24051  * Usage:
24052  *
24053  new Roo.bootstrap.HtmlEditor({
24054     ....
24055     toolbars : [
24056         new Roo.bootstrap.HtmlEditorToolbar1({
24057             disable : { fonts: 1 , format: 1, ..., ... , ...],
24058             btns : [ .... ]
24059         })
24060     }
24061      
24062  * 
24063  * @cfg {Object} disable List of elements to disable..
24064  * @cfg {Array} btns List of additional buttons.
24065  * 
24066  * 
24067  * NEEDS Extra CSS? 
24068  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24069  */
24070  
24071 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24072 {
24073     
24074     Roo.apply(this, config);
24075     
24076     // default disabled, based on 'good practice'..
24077     this.disable = this.disable || {};
24078     Roo.applyIf(this.disable, {
24079         fontSize : true,
24080         colors : true,
24081         specialElements : true
24082     });
24083     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24084     
24085     this.editor = config.editor;
24086     this.editorcore = config.editor.editorcore;
24087     
24088     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24089     
24090     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24091     // dont call parent... till later.
24092 }
24093 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24094      
24095     bar : true,
24096     
24097     editor : false,
24098     editorcore : false,
24099     
24100     
24101     formats : [
24102         "p" ,  
24103         "h1","h2","h3","h4","h5","h6", 
24104         "pre", "code", 
24105         "abbr", "acronym", "address", "cite", "samp", "var",
24106         'div','span'
24107     ],
24108     
24109     onRender : function(ct, position)
24110     {
24111        // Roo.log("Call onRender: " + this.xtype);
24112         
24113        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24114        Roo.log(this.el);
24115        this.el.dom.style.marginBottom = '0';
24116        var _this = this;
24117        var editorcore = this.editorcore;
24118        var editor= this.editor;
24119        
24120        var children = [];
24121        var btn = function(id,cmd , toggle, handler, html){
24122        
24123             var  event = toggle ? 'toggle' : 'click';
24124        
24125             var a = {
24126                 size : 'sm',
24127                 xtype: 'Button',
24128                 xns: Roo.bootstrap,
24129                 //glyphicon : id,
24130                 fa: id,
24131                 cmd : id || cmd,
24132                 enableToggle:toggle !== false,
24133                 html : html || '',
24134                 pressed : toggle ? false : null,
24135                 listeners : {}
24136             };
24137             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24138                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24139             };
24140             children.push(a);
24141             return a;
24142        }
24143        
24144     //    var cb_box = function...
24145         
24146         var style = {
24147                 xtype: 'Button',
24148                 size : 'sm',
24149                 xns: Roo.bootstrap,
24150                 fa : 'font',
24151                 //html : 'submit'
24152                 menu : {
24153                     xtype: 'Menu',
24154                     xns: Roo.bootstrap,
24155                     items:  []
24156                 }
24157         };
24158         Roo.each(this.formats, function(f) {
24159             style.menu.items.push({
24160                 xtype :'MenuItem',
24161                 xns: Roo.bootstrap,
24162                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24163                 tagname : f,
24164                 listeners : {
24165                     click : function()
24166                     {
24167                         editorcore.insertTag(this.tagname);
24168                         editor.focus();
24169                     }
24170                 }
24171                 
24172             });
24173         });
24174         children.push(style);   
24175         
24176         btn('bold',false,true);
24177         btn('italic',false,true);
24178         btn('align-left', 'justifyleft',true);
24179         btn('align-center', 'justifycenter',true);
24180         btn('align-right' , 'justifyright',true);
24181         btn('link', false, false, function(btn) {
24182             //Roo.log("create link?");
24183             var url = prompt(this.createLinkText, this.defaultLinkValue);
24184             if(url && url != 'http:/'+'/'){
24185                 this.editorcore.relayCmd('createlink', url);
24186             }
24187         }),
24188         btn('list','insertunorderedlist',true);
24189         btn('pencil', false,true, function(btn){
24190                 Roo.log(this);
24191                 this.toggleSourceEdit(btn.pressed);
24192         });
24193         
24194         if (this.editor.btns.length > 0) {
24195             for (var i = 0; i<this.editor.btns.length; i++) {
24196                 children.push(this.editor.btns[i]);
24197             }
24198         }
24199         
24200         /*
24201         var cog = {
24202                 xtype: 'Button',
24203                 size : 'sm',
24204                 xns: Roo.bootstrap,
24205                 glyphicon : 'cog',
24206                 //html : 'submit'
24207                 menu : {
24208                     xtype: 'Menu',
24209                     xns: Roo.bootstrap,
24210                     items:  []
24211                 }
24212         };
24213         
24214         cog.menu.items.push({
24215             xtype :'MenuItem',
24216             xns: Roo.bootstrap,
24217             html : Clean styles,
24218             tagname : f,
24219             listeners : {
24220                 click : function()
24221                 {
24222                     editorcore.insertTag(this.tagname);
24223                     editor.focus();
24224                 }
24225             }
24226             
24227         });
24228        */
24229         
24230          
24231        this.xtype = 'NavSimplebar';
24232         
24233         for(var i=0;i< children.length;i++) {
24234             
24235             this.buttons.add(this.addxtypeChild(children[i]));
24236             
24237         }
24238         
24239         editor.on('editorevent', this.updateToolbar, this);
24240     },
24241     onBtnClick : function(id)
24242     {
24243        this.editorcore.relayCmd(id);
24244        this.editorcore.focus();
24245     },
24246     
24247     /**
24248      * Protected method that will not generally be called directly. It triggers
24249      * a toolbar update by reading the markup state of the current selection in the editor.
24250      */
24251     updateToolbar: function(){
24252
24253         if(!this.editorcore.activated){
24254             this.editor.onFirstFocus(); // is this neeed?
24255             return;
24256         }
24257
24258         var btns = this.buttons; 
24259         var doc = this.editorcore.doc;
24260         btns.get('bold').setActive(doc.queryCommandState('bold'));
24261         btns.get('italic').setActive(doc.queryCommandState('italic'));
24262         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24263         
24264         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24265         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24266         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24267         
24268         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24269         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24270          /*
24271         
24272         var ans = this.editorcore.getAllAncestors();
24273         if (this.formatCombo) {
24274             
24275             
24276             var store = this.formatCombo.store;
24277             this.formatCombo.setValue("");
24278             for (var i =0; i < ans.length;i++) {
24279                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24280                     // select it..
24281                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24282                     break;
24283                 }
24284             }
24285         }
24286         
24287         
24288         
24289         // hides menus... - so this cant be on a menu...
24290         Roo.bootstrap.MenuMgr.hideAll();
24291         */
24292         Roo.bootstrap.MenuMgr.hideAll();
24293         //this.editorsyncValue();
24294     },
24295     onFirstFocus: function() {
24296         this.buttons.each(function(item){
24297            item.enable();
24298         });
24299     },
24300     toggleSourceEdit : function(sourceEditMode){
24301         
24302           
24303         if(sourceEditMode){
24304             Roo.log("disabling buttons");
24305            this.buttons.each( function(item){
24306                 if(item.cmd != 'pencil'){
24307                     item.disable();
24308                 }
24309             });
24310           
24311         }else{
24312             Roo.log("enabling buttons");
24313             if(this.editorcore.initialized){
24314                 this.buttons.each( function(item){
24315                     item.enable();
24316                 });
24317             }
24318             
24319         }
24320         Roo.log("calling toggole on editor");
24321         // tell the editor that it's been pressed..
24322         this.editor.toggleSourceEdit(sourceEditMode);
24323        
24324     }
24325 });
24326
24327
24328
24329
24330
24331 /**
24332  * @class Roo.bootstrap.Table.AbstractSelectionModel
24333  * @extends Roo.util.Observable
24334  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24335  * implemented by descendant classes.  This class should not be directly instantiated.
24336  * @constructor
24337  */
24338 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24339     this.locked = false;
24340     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24341 };
24342
24343
24344 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24345     /** @ignore Called by the grid automatically. Do not call directly. */
24346     init : function(grid){
24347         this.grid = grid;
24348         this.initEvents();
24349     },
24350
24351     /**
24352      * Locks the selections.
24353      */
24354     lock : function(){
24355         this.locked = true;
24356     },
24357
24358     /**
24359      * Unlocks the selections.
24360      */
24361     unlock : function(){
24362         this.locked = false;
24363     },
24364
24365     /**
24366      * Returns true if the selections are locked.
24367      * @return {Boolean}
24368      */
24369     isLocked : function(){
24370         return this.locked;
24371     }
24372 });
24373 /**
24374  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24375  * @class Roo.bootstrap.Table.RowSelectionModel
24376  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24377  * It supports multiple selections and keyboard selection/navigation. 
24378  * @constructor
24379  * @param {Object} config
24380  */
24381
24382 Roo.bootstrap.Table.RowSelectionModel = function(config){
24383     Roo.apply(this, config);
24384     this.selections = new Roo.util.MixedCollection(false, function(o){
24385         return o.id;
24386     });
24387
24388     this.last = false;
24389     this.lastActive = false;
24390
24391     this.addEvents({
24392         /**
24393              * @event selectionchange
24394              * Fires when the selection changes
24395              * @param {SelectionModel} this
24396              */
24397             "selectionchange" : true,
24398         /**
24399              * @event afterselectionchange
24400              * Fires after the selection changes (eg. by key press or clicking)
24401              * @param {SelectionModel} this
24402              */
24403             "afterselectionchange" : true,
24404         /**
24405              * @event beforerowselect
24406              * Fires when a row is selected being selected, return false to cancel.
24407              * @param {SelectionModel} this
24408              * @param {Number} rowIndex The selected index
24409              * @param {Boolean} keepExisting False if other selections will be cleared
24410              */
24411             "beforerowselect" : true,
24412         /**
24413              * @event rowselect
24414              * Fires when a row is selected.
24415              * @param {SelectionModel} this
24416              * @param {Number} rowIndex The selected index
24417              * @param {Roo.data.Record} r The record
24418              */
24419             "rowselect" : true,
24420         /**
24421              * @event rowdeselect
24422              * Fires when a row is deselected.
24423              * @param {SelectionModel} this
24424              * @param {Number} rowIndex The selected index
24425              */
24426         "rowdeselect" : true
24427     });
24428     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24429     this.locked = false;
24430  };
24431
24432 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24433     /**
24434      * @cfg {Boolean} singleSelect
24435      * True to allow selection of only one row at a time (defaults to false)
24436      */
24437     singleSelect : false,
24438
24439     // private
24440     initEvents : function()
24441     {
24442
24443         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24444         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24445         //}else{ // allow click to work like normal
24446          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24447         //}
24448         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24449         this.grid.on("rowclick", this.handleMouseDown, this);
24450         
24451         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24452             "up" : function(e){
24453                 if(!e.shiftKey){
24454                     this.selectPrevious(e.shiftKey);
24455                 }else if(this.last !== false && this.lastActive !== false){
24456                     var last = this.last;
24457                     this.selectRange(this.last,  this.lastActive-1);
24458                     this.grid.getView().focusRow(this.lastActive);
24459                     if(last !== false){
24460                         this.last = last;
24461                     }
24462                 }else{
24463                     this.selectFirstRow();
24464                 }
24465                 this.fireEvent("afterselectionchange", this);
24466             },
24467             "down" : function(e){
24468                 if(!e.shiftKey){
24469                     this.selectNext(e.shiftKey);
24470                 }else if(this.last !== false && this.lastActive !== false){
24471                     var last = this.last;
24472                     this.selectRange(this.last,  this.lastActive+1);
24473                     this.grid.getView().focusRow(this.lastActive);
24474                     if(last !== false){
24475                         this.last = last;
24476                     }
24477                 }else{
24478                     this.selectFirstRow();
24479                 }
24480                 this.fireEvent("afterselectionchange", this);
24481             },
24482             scope: this
24483         });
24484         this.grid.store.on('load', function(){
24485             this.selections.clear();
24486         },this);
24487         /*
24488         var view = this.grid.view;
24489         view.on("refresh", this.onRefresh, this);
24490         view.on("rowupdated", this.onRowUpdated, this);
24491         view.on("rowremoved", this.onRemove, this);
24492         */
24493     },
24494
24495     // private
24496     onRefresh : function()
24497     {
24498         var ds = this.grid.store, i, v = this.grid.view;
24499         var s = this.selections;
24500         s.each(function(r){
24501             if((i = ds.indexOfId(r.id)) != -1){
24502                 v.onRowSelect(i);
24503             }else{
24504                 s.remove(r);
24505             }
24506         });
24507     },
24508
24509     // private
24510     onRemove : function(v, index, r){
24511         this.selections.remove(r);
24512     },
24513
24514     // private
24515     onRowUpdated : function(v, index, r){
24516         if(this.isSelected(r)){
24517             v.onRowSelect(index);
24518         }
24519     },
24520
24521     /**
24522      * Select records.
24523      * @param {Array} records The records to select
24524      * @param {Boolean} keepExisting (optional) True to keep existing selections
24525      */
24526     selectRecords : function(records, keepExisting)
24527     {
24528         if(!keepExisting){
24529             this.clearSelections();
24530         }
24531             var ds = this.grid.store;
24532         for(var i = 0, len = records.length; i < len; i++){
24533             this.selectRow(ds.indexOf(records[i]), true);
24534         }
24535     },
24536
24537     /**
24538      * Gets the number of selected rows.
24539      * @return {Number}
24540      */
24541     getCount : function(){
24542         return this.selections.length;
24543     },
24544
24545     /**
24546      * Selects the first row in the grid.
24547      */
24548     selectFirstRow : function(){
24549         this.selectRow(0);
24550     },
24551
24552     /**
24553      * Select the last row.
24554      * @param {Boolean} keepExisting (optional) True to keep existing selections
24555      */
24556     selectLastRow : function(keepExisting){
24557         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24558         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24559     },
24560
24561     /**
24562      * Selects the row immediately following the last selected row.
24563      * @param {Boolean} keepExisting (optional) True to keep existing selections
24564      */
24565     selectNext : function(keepExisting)
24566     {
24567             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24568             this.selectRow(this.last+1, keepExisting);
24569             this.grid.getView().focusRow(this.last);
24570         }
24571     },
24572
24573     /**
24574      * Selects the row that precedes the last selected row.
24575      * @param {Boolean} keepExisting (optional) True to keep existing selections
24576      */
24577     selectPrevious : function(keepExisting){
24578         if(this.last){
24579             this.selectRow(this.last-1, keepExisting);
24580             this.grid.getView().focusRow(this.last);
24581         }
24582     },
24583
24584     /**
24585      * Returns the selected records
24586      * @return {Array} Array of selected records
24587      */
24588     getSelections : function(){
24589         return [].concat(this.selections.items);
24590     },
24591
24592     /**
24593      * Returns the first selected record.
24594      * @return {Record}
24595      */
24596     getSelected : function(){
24597         return this.selections.itemAt(0);
24598     },
24599
24600
24601     /**
24602      * Clears all selections.
24603      */
24604     clearSelections : function(fast)
24605     {
24606         if(this.locked) {
24607             return;
24608         }
24609         if(fast !== true){
24610                 var ds = this.grid.store;
24611             var s = this.selections;
24612             s.each(function(r){
24613                 this.deselectRow(ds.indexOfId(r.id));
24614             }, this);
24615             s.clear();
24616         }else{
24617             this.selections.clear();
24618         }
24619         this.last = false;
24620     },
24621
24622
24623     /**
24624      * Selects all rows.
24625      */
24626     selectAll : function(){
24627         if(this.locked) {
24628             return;
24629         }
24630         this.selections.clear();
24631         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24632             this.selectRow(i, true);
24633         }
24634     },
24635
24636     /**
24637      * Returns True if there is a selection.
24638      * @return {Boolean}
24639      */
24640     hasSelection : function(){
24641         return this.selections.length > 0;
24642     },
24643
24644     /**
24645      * Returns True if the specified row is selected.
24646      * @param {Number/Record} record The record or index of the record to check
24647      * @return {Boolean}
24648      */
24649     isSelected : function(index){
24650             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24651         return (r && this.selections.key(r.id) ? true : false);
24652     },
24653
24654     /**
24655      * Returns True if the specified record id is selected.
24656      * @param {String} id The id of record to check
24657      * @return {Boolean}
24658      */
24659     isIdSelected : function(id){
24660         return (this.selections.key(id) ? true : false);
24661     },
24662
24663
24664     // private
24665     handleMouseDBClick : function(e, t){
24666         
24667     },
24668     // private
24669     handleMouseDown : function(e, t)
24670     {
24671             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24672         if(this.isLocked() || rowIndex < 0 ){
24673             return;
24674         };
24675         if(e.shiftKey && this.last !== false){
24676             var last = this.last;
24677             this.selectRange(last, rowIndex, e.ctrlKey);
24678             this.last = last; // reset the last
24679             t.focus();
24680     
24681         }else{
24682             var isSelected = this.isSelected(rowIndex);
24683             //Roo.log("select row:" + rowIndex);
24684             if(isSelected){
24685                 this.deselectRow(rowIndex);
24686             } else {
24687                         this.selectRow(rowIndex, true);
24688             }
24689     
24690             /*
24691                 if(e.button !== 0 && isSelected){
24692                 alert('rowIndex 2: ' + rowIndex);
24693                     view.focusRow(rowIndex);
24694                 }else if(e.ctrlKey && isSelected){
24695                     this.deselectRow(rowIndex);
24696                 }else if(!isSelected){
24697                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24698                     view.focusRow(rowIndex);
24699                 }
24700             */
24701         }
24702         this.fireEvent("afterselectionchange", this);
24703     },
24704     // private
24705     handleDragableRowClick :  function(grid, rowIndex, e) 
24706     {
24707         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24708             this.selectRow(rowIndex, false);
24709             grid.view.focusRow(rowIndex);
24710              this.fireEvent("afterselectionchange", this);
24711         }
24712     },
24713     
24714     /**
24715      * Selects multiple rows.
24716      * @param {Array} rows Array of the indexes of the row to select
24717      * @param {Boolean} keepExisting (optional) True to keep existing selections
24718      */
24719     selectRows : function(rows, keepExisting){
24720         if(!keepExisting){
24721             this.clearSelections();
24722         }
24723         for(var i = 0, len = rows.length; i < len; i++){
24724             this.selectRow(rows[i], true);
24725         }
24726     },
24727
24728     /**
24729      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24730      * @param {Number} startRow The index of the first row in the range
24731      * @param {Number} endRow The index of the last row in the range
24732      * @param {Boolean} keepExisting (optional) True to retain existing selections
24733      */
24734     selectRange : function(startRow, endRow, keepExisting){
24735         if(this.locked) {
24736             return;
24737         }
24738         if(!keepExisting){
24739             this.clearSelections();
24740         }
24741         if(startRow <= endRow){
24742             for(var i = startRow; i <= endRow; i++){
24743                 this.selectRow(i, true);
24744             }
24745         }else{
24746             for(var i = startRow; i >= endRow; i--){
24747                 this.selectRow(i, true);
24748             }
24749         }
24750     },
24751
24752     /**
24753      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24754      * @param {Number} startRow The index of the first row in the range
24755      * @param {Number} endRow The index of the last row in the range
24756      */
24757     deselectRange : function(startRow, endRow, preventViewNotify){
24758         if(this.locked) {
24759             return;
24760         }
24761         for(var i = startRow; i <= endRow; i++){
24762             this.deselectRow(i, preventViewNotify);
24763         }
24764     },
24765
24766     /**
24767      * Selects a row.
24768      * @param {Number} row The index of the row to select
24769      * @param {Boolean} keepExisting (optional) True to keep existing selections
24770      */
24771     selectRow : function(index, keepExisting, preventViewNotify)
24772     {
24773             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24774             return;
24775         }
24776         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24777             if(!keepExisting || this.singleSelect){
24778                 this.clearSelections();
24779             }
24780             
24781             var r = this.grid.store.getAt(index);
24782             //console.log('selectRow - record id :' + r.id);
24783             
24784             this.selections.add(r);
24785             this.last = this.lastActive = index;
24786             if(!preventViewNotify){
24787                 var proxy = new Roo.Element(
24788                                 this.grid.getRowDom(index)
24789                 );
24790                 proxy.addClass('bg-info info');
24791             }
24792             this.fireEvent("rowselect", this, index, r);
24793             this.fireEvent("selectionchange", this);
24794         }
24795     },
24796
24797     /**
24798      * Deselects a row.
24799      * @param {Number} row The index of the row to deselect
24800      */
24801     deselectRow : function(index, preventViewNotify)
24802     {
24803         if(this.locked) {
24804             return;
24805         }
24806         if(this.last == index){
24807             this.last = false;
24808         }
24809         if(this.lastActive == index){
24810             this.lastActive = false;
24811         }
24812         
24813         var r = this.grid.store.getAt(index);
24814         if (!r) {
24815             return;
24816         }
24817         
24818         this.selections.remove(r);
24819         //.console.log('deselectRow - record id :' + r.id);
24820         if(!preventViewNotify){
24821         
24822             var proxy = new Roo.Element(
24823                 this.grid.getRowDom(index)
24824             );
24825             proxy.removeClass('bg-info info');
24826         }
24827         this.fireEvent("rowdeselect", this, index);
24828         this.fireEvent("selectionchange", this);
24829     },
24830
24831     // private
24832     restoreLast : function(){
24833         if(this._last){
24834             this.last = this._last;
24835         }
24836     },
24837
24838     // private
24839     acceptsNav : function(row, col, cm){
24840         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24841     },
24842
24843     // private
24844     onEditorKey : function(field, e){
24845         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24846         if(k == e.TAB){
24847             e.stopEvent();
24848             ed.completeEdit();
24849             if(e.shiftKey){
24850                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24851             }else{
24852                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24853             }
24854         }else if(k == e.ENTER && !e.ctrlKey){
24855             e.stopEvent();
24856             ed.completeEdit();
24857             if(e.shiftKey){
24858                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24859             }else{
24860                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24861             }
24862         }else if(k == e.ESC){
24863             ed.cancelEdit();
24864         }
24865         if(newCell){
24866             g.startEditing(newCell[0], newCell[1]);
24867         }
24868     }
24869 });
24870 /*
24871  * Based on:
24872  * Ext JS Library 1.1.1
24873  * Copyright(c) 2006-2007, Ext JS, LLC.
24874  *
24875  * Originally Released Under LGPL - original licence link has changed is not relivant.
24876  *
24877  * Fork - LGPL
24878  * <script type="text/javascript">
24879  */
24880  
24881 /**
24882  * @class Roo.bootstrap.PagingToolbar
24883  * @extends Roo.bootstrap.NavSimplebar
24884  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24885  * @constructor
24886  * Create a new PagingToolbar
24887  * @param {Object} config The config object
24888  * @param {Roo.data.Store} store
24889  */
24890 Roo.bootstrap.PagingToolbar = function(config)
24891 {
24892     // old args format still supported... - xtype is prefered..
24893         // created from xtype...
24894     
24895     this.ds = config.dataSource;
24896     
24897     if (config.store && !this.ds) {
24898         this.store= Roo.factory(config.store, Roo.data);
24899         this.ds = this.store;
24900         this.ds.xmodule = this.xmodule || false;
24901     }
24902     
24903     this.toolbarItems = [];
24904     if (config.items) {
24905         this.toolbarItems = config.items;
24906     }
24907     
24908     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24909     
24910     this.cursor = 0;
24911     
24912     if (this.ds) { 
24913         this.bind(this.ds);
24914     }
24915     
24916     if (Roo.bootstrap.version == 4) {
24917         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24918     } else {
24919         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24920     }
24921     
24922 };
24923
24924 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24925     /**
24926      * @cfg {Roo.data.Store} dataSource
24927      * The underlying data store providing the paged data
24928      */
24929     /**
24930      * @cfg {String/HTMLElement/Element} container
24931      * container The id or element that will contain the toolbar
24932      */
24933     /**
24934      * @cfg {Boolean} displayInfo
24935      * True to display the displayMsg (defaults to false)
24936      */
24937     /**
24938      * @cfg {Number} pageSize
24939      * The number of records to display per page (defaults to 20)
24940      */
24941     pageSize: 20,
24942     /**
24943      * @cfg {String} displayMsg
24944      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24945      */
24946     displayMsg : 'Displaying {0} - {1} of {2}',
24947     /**
24948      * @cfg {String} emptyMsg
24949      * The message to display when no records are found (defaults to "No data to display")
24950      */
24951     emptyMsg : 'No data to display',
24952     /**
24953      * Customizable piece of the default paging text (defaults to "Page")
24954      * @type String
24955      */
24956     beforePageText : "Page",
24957     /**
24958      * Customizable piece of the default paging text (defaults to "of %0")
24959      * @type String
24960      */
24961     afterPageText : "of {0}",
24962     /**
24963      * Customizable piece of the default paging text (defaults to "First Page")
24964      * @type String
24965      */
24966     firstText : "First Page",
24967     /**
24968      * Customizable piece of the default paging text (defaults to "Previous Page")
24969      * @type String
24970      */
24971     prevText : "Previous Page",
24972     /**
24973      * Customizable piece of the default paging text (defaults to "Next Page")
24974      * @type String
24975      */
24976     nextText : "Next Page",
24977     /**
24978      * Customizable piece of the default paging text (defaults to "Last Page")
24979      * @type String
24980      */
24981     lastText : "Last Page",
24982     /**
24983      * Customizable piece of the default paging text (defaults to "Refresh")
24984      * @type String
24985      */
24986     refreshText : "Refresh",
24987
24988     buttons : false,
24989     // private
24990     onRender : function(ct, position) 
24991     {
24992         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24993         this.navgroup.parentId = this.id;
24994         this.navgroup.onRender(this.el, null);
24995         // add the buttons to the navgroup
24996         
24997         if(this.displayInfo){
24998             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24999             this.displayEl = this.el.select('.x-paging-info', true).first();
25000 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25001 //            this.displayEl = navel.el.select('span',true).first();
25002         }
25003         
25004         var _this = this;
25005         
25006         if(this.buttons){
25007             Roo.each(_this.buttons, function(e){ // this might need to use render????
25008                Roo.factory(e).render(_this.el);
25009             });
25010         }
25011             
25012         Roo.each(_this.toolbarItems, function(e) {
25013             _this.navgroup.addItem(e);
25014         });
25015         
25016         
25017         this.first = this.navgroup.addItem({
25018             tooltip: this.firstText,
25019             cls: "prev btn-outline-secondary",
25020             html : ' <i class="fa fa-step-backward"></i>',
25021             disabled: true,
25022             preventDefault: true,
25023             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25024         });
25025         
25026         this.prev =  this.navgroup.addItem({
25027             tooltip: this.prevText,
25028             cls: "prev btn-outline-secondary",
25029             html : ' <i class="fa fa-backward"></i>',
25030             disabled: true,
25031             preventDefault: true,
25032             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25033         });
25034     //this.addSeparator();
25035         
25036         
25037         var field = this.navgroup.addItem( {
25038             tagtype : 'span',
25039             cls : 'x-paging-position  btn-outline-secondary',
25040              disabled: true,
25041             html : this.beforePageText  +
25042                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25043                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25044          } ); //?? escaped?
25045         
25046         this.field = field.el.select('input', true).first();
25047         this.field.on("keydown", this.onPagingKeydown, this);
25048         this.field.on("focus", function(){this.dom.select();});
25049     
25050     
25051         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25052         //this.field.setHeight(18);
25053         //this.addSeparator();
25054         this.next = this.navgroup.addItem({
25055             tooltip: this.nextText,
25056             cls: "next btn-outline-secondary",
25057             html : ' <i class="fa fa-forward"></i>',
25058             disabled: true,
25059             preventDefault: true,
25060             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25061         });
25062         this.last = this.navgroup.addItem({
25063             tooltip: this.lastText,
25064             html : ' <i class="fa fa-step-forward"></i>',
25065             cls: "next btn-outline-secondary",
25066             disabled: true,
25067             preventDefault: true,
25068             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25069         });
25070     //this.addSeparator();
25071         this.loading = this.navgroup.addItem({
25072             tooltip: this.refreshText,
25073             cls: "btn-outline-secondary",
25074             html : ' <i class="fa fa-refresh"></i>',
25075             preventDefault: true,
25076             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25077         });
25078         
25079     },
25080
25081     // private
25082     updateInfo : function(){
25083         if(this.displayEl){
25084             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25085             var msg = count == 0 ?
25086                 this.emptyMsg :
25087                 String.format(
25088                     this.displayMsg,
25089                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25090                 );
25091             this.displayEl.update(msg);
25092         }
25093     },
25094
25095     // private
25096     onLoad : function(ds, r, o)
25097     {
25098         this.cursor = o.params.start ? o.params.start : 0;
25099         
25100         var d = this.getPageData(),
25101             ap = d.activePage,
25102             ps = d.pages;
25103         
25104         
25105         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25106         this.field.dom.value = ap;
25107         this.first.setDisabled(ap == 1);
25108         this.prev.setDisabled(ap == 1);
25109         this.next.setDisabled(ap == ps);
25110         this.last.setDisabled(ap == ps);
25111         this.loading.enable();
25112         this.updateInfo();
25113     },
25114
25115     // private
25116     getPageData : function(){
25117         var total = this.ds.getTotalCount();
25118         return {
25119             total : total,
25120             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25121             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25122         };
25123     },
25124
25125     // private
25126     onLoadError : function(){
25127         this.loading.enable();
25128     },
25129
25130     // private
25131     onPagingKeydown : function(e){
25132         var k = e.getKey();
25133         var d = this.getPageData();
25134         if(k == e.RETURN){
25135             var v = this.field.dom.value, pageNum;
25136             if(!v || isNaN(pageNum = parseInt(v, 10))){
25137                 this.field.dom.value = d.activePage;
25138                 return;
25139             }
25140             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25141             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25142             e.stopEvent();
25143         }
25144         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))
25145         {
25146           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25147           this.field.dom.value = pageNum;
25148           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25149           e.stopEvent();
25150         }
25151         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25152         {
25153           var v = this.field.dom.value, pageNum; 
25154           var increment = (e.shiftKey) ? 10 : 1;
25155           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25156                 increment *= -1;
25157           }
25158           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25159             this.field.dom.value = d.activePage;
25160             return;
25161           }
25162           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25163           {
25164             this.field.dom.value = parseInt(v, 10) + increment;
25165             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25166             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25167           }
25168           e.stopEvent();
25169         }
25170     },
25171
25172     // private
25173     beforeLoad : function(){
25174         if(this.loading){
25175             this.loading.disable();
25176         }
25177     },
25178
25179     // private
25180     onClick : function(which){
25181         
25182         var ds = this.ds;
25183         if (!ds) {
25184             return;
25185         }
25186         
25187         switch(which){
25188             case "first":
25189                 ds.load({params:{start: 0, limit: this.pageSize}});
25190             break;
25191             case "prev":
25192                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25193             break;
25194             case "next":
25195                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25196             break;
25197             case "last":
25198                 var total = ds.getTotalCount();
25199                 var extra = total % this.pageSize;
25200                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25201                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25202             break;
25203             case "refresh":
25204                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25205             break;
25206         }
25207     },
25208
25209     /**
25210      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25211      * @param {Roo.data.Store} store The data store to unbind
25212      */
25213     unbind : function(ds){
25214         ds.un("beforeload", this.beforeLoad, this);
25215         ds.un("load", this.onLoad, this);
25216         ds.un("loadexception", this.onLoadError, this);
25217         ds.un("remove", this.updateInfo, this);
25218         ds.un("add", this.updateInfo, this);
25219         this.ds = undefined;
25220     },
25221
25222     /**
25223      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25224      * @param {Roo.data.Store} store The data store to bind
25225      */
25226     bind : function(ds){
25227         ds.on("beforeload", this.beforeLoad, this);
25228         ds.on("load", this.onLoad, this);
25229         ds.on("loadexception", this.onLoadError, this);
25230         ds.on("remove", this.updateInfo, this);
25231         ds.on("add", this.updateInfo, this);
25232         this.ds = ds;
25233     }
25234 });/*
25235  * - LGPL
25236  *
25237  * element
25238  * 
25239  */
25240
25241 /**
25242  * @class Roo.bootstrap.MessageBar
25243  * @extends Roo.bootstrap.Component
25244  * Bootstrap MessageBar class
25245  * @cfg {String} html contents of the MessageBar
25246  * @cfg {String} weight (info | success | warning | danger) default info
25247  * @cfg {String} beforeClass insert the bar before the given class
25248  * @cfg {Boolean} closable (true | false) default false
25249  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25250  * 
25251  * @constructor
25252  * Create a new Element
25253  * @param {Object} config The config object
25254  */
25255
25256 Roo.bootstrap.MessageBar = function(config){
25257     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25258 };
25259
25260 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25261     
25262     html: '',
25263     weight: 'info',
25264     closable: false,
25265     fixed: false,
25266     beforeClass: 'bootstrap-sticky-wrap',
25267     
25268     getAutoCreate : function(){
25269         
25270         var cfg = {
25271             tag: 'div',
25272             cls: 'alert alert-dismissable alert-' + this.weight,
25273             cn: [
25274                 {
25275                     tag: 'span',
25276                     cls: 'message',
25277                     html: this.html || ''
25278                 }
25279             ]
25280         };
25281         
25282         if(this.fixed){
25283             cfg.cls += ' alert-messages-fixed';
25284         }
25285         
25286         if(this.closable){
25287             cfg.cn.push({
25288                 tag: 'button',
25289                 cls: 'close',
25290                 html: 'x'
25291             });
25292         }
25293         
25294         return cfg;
25295     },
25296     
25297     onRender : function(ct, position)
25298     {
25299         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25300         
25301         if(!this.el){
25302             var cfg = Roo.apply({},  this.getAutoCreate());
25303             cfg.id = Roo.id();
25304             
25305             if (this.cls) {
25306                 cfg.cls += ' ' + this.cls;
25307             }
25308             if (this.style) {
25309                 cfg.style = this.style;
25310             }
25311             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25312             
25313             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25314         }
25315         
25316         this.el.select('>button.close').on('click', this.hide, this);
25317         
25318     },
25319     
25320     show : function()
25321     {
25322         if (!this.rendered) {
25323             this.render();
25324         }
25325         
25326         this.el.show();
25327         
25328         this.fireEvent('show', this);
25329         
25330     },
25331     
25332     hide : function()
25333     {
25334         if (!this.rendered) {
25335             this.render();
25336         }
25337         
25338         this.el.hide();
25339         
25340         this.fireEvent('hide', this);
25341     },
25342     
25343     update : function()
25344     {
25345 //        var e = this.el.dom.firstChild;
25346 //        
25347 //        if(this.closable){
25348 //            e = e.nextSibling;
25349 //        }
25350 //        
25351 //        e.data = this.html || '';
25352
25353         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25354     }
25355    
25356 });
25357
25358  
25359
25360      /*
25361  * - LGPL
25362  *
25363  * Graph
25364  * 
25365  */
25366
25367
25368 /**
25369  * @class Roo.bootstrap.Graph
25370  * @extends Roo.bootstrap.Component
25371  * Bootstrap Graph class
25372 > Prameters
25373  -sm {number} sm 4
25374  -md {number} md 5
25375  @cfg {String} graphtype  bar | vbar | pie
25376  @cfg {number} g_x coodinator | centre x (pie)
25377  @cfg {number} g_y coodinator | centre y (pie)
25378  @cfg {number} g_r radius (pie)
25379  @cfg {number} g_height height of the chart (respected by all elements in the set)
25380  @cfg {number} g_width width of the chart (respected by all elements in the set)
25381  @cfg {Object} title The title of the chart
25382     
25383  -{Array}  values
25384  -opts (object) options for the chart 
25385      o {
25386      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25387      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25388      o vgutter (number)
25389      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.
25390      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25391      o to
25392      o stretch (boolean)
25393      o }
25394  -opts (object) options for the pie
25395      o{
25396      o cut
25397      o startAngle (number)
25398      o endAngle (number)
25399      } 
25400  *
25401  * @constructor
25402  * Create a new Input
25403  * @param {Object} config The config object
25404  */
25405
25406 Roo.bootstrap.Graph = function(config){
25407     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25408     
25409     this.addEvents({
25410         // img events
25411         /**
25412          * @event click
25413          * The img click event for the img.
25414          * @param {Roo.EventObject} e
25415          */
25416         "click" : true
25417     });
25418 };
25419
25420 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25421     
25422     sm: 4,
25423     md: 5,
25424     graphtype: 'bar',
25425     g_height: 250,
25426     g_width: 400,
25427     g_x: 50,
25428     g_y: 50,
25429     g_r: 30,
25430     opts:{
25431         //g_colors: this.colors,
25432         g_type: 'soft',
25433         g_gutter: '20%'
25434
25435     },
25436     title : false,
25437
25438     getAutoCreate : function(){
25439         
25440         var cfg = {
25441             tag: 'div',
25442             html : null
25443         };
25444         
25445         
25446         return  cfg;
25447     },
25448
25449     onRender : function(ct,position){
25450         
25451         
25452         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25453         
25454         if (typeof(Raphael) == 'undefined') {
25455             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25456             return;
25457         }
25458         
25459         this.raphael = Raphael(this.el.dom);
25460         
25461                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25462                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25463                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25464                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25465                 /*
25466                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25467                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25468                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25469                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25470                 
25471                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25472                 r.barchart(330, 10, 300, 220, data1);
25473                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25474                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25475                 */
25476                 
25477                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25478                 // r.barchart(30, 30, 560, 250,  xdata, {
25479                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25480                 //     axis : "0 0 1 1",
25481                 //     axisxlabels :  xdata
25482                 //     //yvalues : cols,
25483                    
25484                 // });
25485 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25486 //        
25487 //        this.load(null,xdata,{
25488 //                axis : "0 0 1 1",
25489 //                axisxlabels :  xdata
25490 //                });
25491
25492     },
25493
25494     load : function(graphtype,xdata,opts)
25495     {
25496         this.raphael.clear();
25497         if(!graphtype) {
25498             graphtype = this.graphtype;
25499         }
25500         if(!opts){
25501             opts = this.opts;
25502         }
25503         var r = this.raphael,
25504             fin = function () {
25505                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25506             },
25507             fout = function () {
25508                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25509             },
25510             pfin = function() {
25511                 this.sector.stop();
25512                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25513
25514                 if (this.label) {
25515                     this.label[0].stop();
25516                     this.label[0].attr({ r: 7.5 });
25517                     this.label[1].attr({ "font-weight": 800 });
25518                 }
25519             },
25520             pfout = function() {
25521                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25522
25523                 if (this.label) {
25524                     this.label[0].animate({ r: 5 }, 500, "bounce");
25525                     this.label[1].attr({ "font-weight": 400 });
25526                 }
25527             };
25528
25529         switch(graphtype){
25530             case 'bar':
25531                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25532                 break;
25533             case 'hbar':
25534                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25535                 break;
25536             case 'pie':
25537 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25538 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25539 //            
25540                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25541                 
25542                 break;
25543
25544         }
25545         
25546         if(this.title){
25547             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25548         }
25549         
25550     },
25551     
25552     setTitle: function(o)
25553     {
25554         this.title = o;
25555     },
25556     
25557     initEvents: function() {
25558         
25559         if(!this.href){
25560             this.el.on('click', this.onClick, this);
25561         }
25562     },
25563     
25564     onClick : function(e)
25565     {
25566         Roo.log('img onclick');
25567         this.fireEvent('click', this, e);
25568     }
25569    
25570 });
25571
25572  
25573 /*
25574  * - LGPL
25575  *
25576  * numberBox
25577  * 
25578  */
25579 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25580
25581 /**
25582  * @class Roo.bootstrap.dash.NumberBox
25583  * @extends Roo.bootstrap.Component
25584  * Bootstrap NumberBox class
25585  * @cfg {String} headline Box headline
25586  * @cfg {String} content Box content
25587  * @cfg {String} icon Box icon
25588  * @cfg {String} footer Footer text
25589  * @cfg {String} fhref Footer href
25590  * 
25591  * @constructor
25592  * Create a new NumberBox
25593  * @param {Object} config The config object
25594  */
25595
25596
25597 Roo.bootstrap.dash.NumberBox = function(config){
25598     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25599     
25600 };
25601
25602 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25603     
25604     headline : '',
25605     content : '',
25606     icon : '',
25607     footer : '',
25608     fhref : '',
25609     ficon : '',
25610     
25611     getAutoCreate : function(){
25612         
25613         var cfg = {
25614             tag : 'div',
25615             cls : 'small-box ',
25616             cn : [
25617                 {
25618                     tag : 'div',
25619                     cls : 'inner',
25620                     cn :[
25621                         {
25622                             tag : 'h3',
25623                             cls : 'roo-headline',
25624                             html : this.headline
25625                         },
25626                         {
25627                             tag : 'p',
25628                             cls : 'roo-content',
25629                             html : this.content
25630                         }
25631                     ]
25632                 }
25633             ]
25634         };
25635         
25636         if(this.icon){
25637             cfg.cn.push({
25638                 tag : 'div',
25639                 cls : 'icon',
25640                 cn :[
25641                     {
25642                         tag : 'i',
25643                         cls : 'ion ' + this.icon
25644                     }
25645                 ]
25646             });
25647         }
25648         
25649         if(this.footer){
25650             var footer = {
25651                 tag : 'a',
25652                 cls : 'small-box-footer',
25653                 href : this.fhref || '#',
25654                 html : this.footer
25655             };
25656             
25657             cfg.cn.push(footer);
25658             
25659         }
25660         
25661         return  cfg;
25662     },
25663
25664     onRender : function(ct,position){
25665         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25666
25667
25668        
25669                 
25670     },
25671
25672     setHeadline: function (value)
25673     {
25674         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25675     },
25676     
25677     setFooter: function (value, href)
25678     {
25679         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25680         
25681         if(href){
25682             this.el.select('a.small-box-footer',true).first().attr('href', href);
25683         }
25684         
25685     },
25686
25687     setContent: function (value)
25688     {
25689         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25690     },
25691
25692     initEvents: function() 
25693     {   
25694         
25695     }
25696     
25697 });
25698
25699  
25700 /*
25701  * - LGPL
25702  *
25703  * TabBox
25704  * 
25705  */
25706 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25707
25708 /**
25709  * @class Roo.bootstrap.dash.TabBox
25710  * @extends Roo.bootstrap.Component
25711  * Bootstrap TabBox class
25712  * @cfg {String} title Title of the TabBox
25713  * @cfg {String} icon Icon of the TabBox
25714  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25715  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25716  * 
25717  * @constructor
25718  * Create a new TabBox
25719  * @param {Object} config The config object
25720  */
25721
25722
25723 Roo.bootstrap.dash.TabBox = function(config){
25724     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25725     this.addEvents({
25726         // raw events
25727         /**
25728          * @event addpane
25729          * When a pane is added
25730          * @param {Roo.bootstrap.dash.TabPane} pane
25731          */
25732         "addpane" : true,
25733         /**
25734          * @event activatepane
25735          * When a pane is activated
25736          * @param {Roo.bootstrap.dash.TabPane} pane
25737          */
25738         "activatepane" : true
25739         
25740          
25741     });
25742     
25743     this.panes = [];
25744 };
25745
25746 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25747
25748     title : '',
25749     icon : false,
25750     showtabs : true,
25751     tabScrollable : false,
25752     
25753     getChildContainer : function()
25754     {
25755         return this.el.select('.tab-content', true).first();
25756     },
25757     
25758     getAutoCreate : function(){
25759         
25760         var header = {
25761             tag: 'li',
25762             cls: 'pull-left header',
25763             html: this.title,
25764             cn : []
25765         };
25766         
25767         if(this.icon){
25768             header.cn.push({
25769                 tag: 'i',
25770                 cls: 'fa ' + this.icon
25771             });
25772         }
25773         
25774         var h = {
25775             tag: 'ul',
25776             cls: 'nav nav-tabs pull-right',
25777             cn: [
25778                 header
25779             ]
25780         };
25781         
25782         if(this.tabScrollable){
25783             h = {
25784                 tag: 'div',
25785                 cls: 'tab-header',
25786                 cn: [
25787                     {
25788                         tag: 'ul',
25789                         cls: 'nav nav-tabs pull-right',
25790                         cn: [
25791                             header
25792                         ]
25793                     }
25794                 ]
25795             };
25796         }
25797         
25798         var cfg = {
25799             tag: 'div',
25800             cls: 'nav-tabs-custom',
25801             cn: [
25802                 h,
25803                 {
25804                     tag: 'div',
25805                     cls: 'tab-content no-padding',
25806                     cn: []
25807                 }
25808             ]
25809         };
25810
25811         return  cfg;
25812     },
25813     initEvents : function()
25814     {
25815         //Roo.log('add add pane handler');
25816         this.on('addpane', this.onAddPane, this);
25817     },
25818      /**
25819      * Updates the box title
25820      * @param {String} html to set the title to.
25821      */
25822     setTitle : function(value)
25823     {
25824         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25825     },
25826     onAddPane : function(pane)
25827     {
25828         this.panes.push(pane);
25829         //Roo.log('addpane');
25830         //Roo.log(pane);
25831         // tabs are rendere left to right..
25832         if(!this.showtabs){
25833             return;
25834         }
25835         
25836         var ctr = this.el.select('.nav-tabs', true).first();
25837          
25838          
25839         var existing = ctr.select('.nav-tab',true);
25840         var qty = existing.getCount();;
25841         
25842         
25843         var tab = ctr.createChild({
25844             tag : 'li',
25845             cls : 'nav-tab' + (qty ? '' : ' active'),
25846             cn : [
25847                 {
25848                     tag : 'a',
25849                     href:'#',
25850                     html : pane.title
25851                 }
25852             ]
25853         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25854         pane.tab = tab;
25855         
25856         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25857         if (!qty) {
25858             pane.el.addClass('active');
25859         }
25860         
25861                 
25862     },
25863     onTabClick : function(ev,un,ob,pane)
25864     {
25865         //Roo.log('tab - prev default');
25866         ev.preventDefault();
25867         
25868         
25869         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25870         pane.tab.addClass('active');
25871         //Roo.log(pane.title);
25872         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25873         // technically we should have a deactivate event.. but maybe add later.
25874         // and it should not de-activate the selected tab...
25875         this.fireEvent('activatepane', pane);
25876         pane.el.addClass('active');
25877         pane.fireEvent('activate');
25878         
25879         
25880     },
25881     
25882     getActivePane : function()
25883     {
25884         var r = false;
25885         Roo.each(this.panes, function(p) {
25886             if(p.el.hasClass('active')){
25887                 r = p;
25888                 return false;
25889             }
25890             
25891             return;
25892         });
25893         
25894         return r;
25895     }
25896     
25897     
25898 });
25899
25900  
25901 /*
25902  * - LGPL
25903  *
25904  * Tab pane
25905  * 
25906  */
25907 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25908 /**
25909  * @class Roo.bootstrap.TabPane
25910  * @extends Roo.bootstrap.Component
25911  * Bootstrap TabPane class
25912  * @cfg {Boolean} active (false | true) Default false
25913  * @cfg {String} title title of panel
25914
25915  * 
25916  * @constructor
25917  * Create a new TabPane
25918  * @param {Object} config The config object
25919  */
25920
25921 Roo.bootstrap.dash.TabPane = function(config){
25922     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25923     
25924     this.addEvents({
25925         // raw events
25926         /**
25927          * @event activate
25928          * When a pane is activated
25929          * @param {Roo.bootstrap.dash.TabPane} pane
25930          */
25931         "activate" : true
25932          
25933     });
25934 };
25935
25936 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25937     
25938     active : false,
25939     title : '',
25940     
25941     // the tabBox that this is attached to.
25942     tab : false,
25943      
25944     getAutoCreate : function() 
25945     {
25946         var cfg = {
25947             tag: 'div',
25948             cls: 'tab-pane'
25949         };
25950         
25951         if(this.active){
25952             cfg.cls += ' active';
25953         }
25954         
25955         return cfg;
25956     },
25957     initEvents  : function()
25958     {
25959         //Roo.log('trigger add pane handler');
25960         this.parent().fireEvent('addpane', this)
25961     },
25962     
25963      /**
25964      * Updates the tab title 
25965      * @param {String} html to set the title to.
25966      */
25967     setTitle: function(str)
25968     {
25969         if (!this.tab) {
25970             return;
25971         }
25972         this.title = str;
25973         this.tab.select('a', true).first().dom.innerHTML = str;
25974         
25975     }
25976     
25977     
25978     
25979 });
25980
25981  
25982
25983
25984  /*
25985  * - LGPL
25986  *
25987  * menu
25988  * 
25989  */
25990 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25991
25992 /**
25993  * @class Roo.bootstrap.menu.Menu
25994  * @extends Roo.bootstrap.Component
25995  * Bootstrap Menu class - container for Menu
25996  * @cfg {String} html Text of the menu
25997  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25998  * @cfg {String} icon Font awesome icon
25999  * @cfg {String} pos Menu align to (top | bottom) default bottom
26000  * 
26001  * 
26002  * @constructor
26003  * Create a new Menu
26004  * @param {Object} config The config object
26005  */
26006
26007
26008 Roo.bootstrap.menu.Menu = function(config){
26009     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26010     
26011     this.addEvents({
26012         /**
26013          * @event beforeshow
26014          * Fires before this menu is displayed
26015          * @param {Roo.bootstrap.menu.Menu} this
26016          */
26017         beforeshow : true,
26018         /**
26019          * @event beforehide
26020          * Fires before this menu is hidden
26021          * @param {Roo.bootstrap.menu.Menu} this
26022          */
26023         beforehide : true,
26024         /**
26025          * @event show
26026          * Fires after this menu is displayed
26027          * @param {Roo.bootstrap.menu.Menu} this
26028          */
26029         show : true,
26030         /**
26031          * @event hide
26032          * Fires after this menu is hidden
26033          * @param {Roo.bootstrap.menu.Menu} this
26034          */
26035         hide : true,
26036         /**
26037          * @event click
26038          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26039          * @param {Roo.bootstrap.menu.Menu} this
26040          * @param {Roo.EventObject} e
26041          */
26042         click : true
26043     });
26044     
26045 };
26046
26047 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26048     
26049     submenu : false,
26050     html : '',
26051     weight : 'default',
26052     icon : false,
26053     pos : 'bottom',
26054     
26055     
26056     getChildContainer : function() {
26057         if(this.isSubMenu){
26058             return this.el;
26059         }
26060         
26061         return this.el.select('ul.dropdown-menu', true).first();  
26062     },
26063     
26064     getAutoCreate : function()
26065     {
26066         var text = [
26067             {
26068                 tag : 'span',
26069                 cls : 'roo-menu-text',
26070                 html : this.html
26071             }
26072         ];
26073         
26074         if(this.icon){
26075             text.unshift({
26076                 tag : 'i',
26077                 cls : 'fa ' + this.icon
26078             })
26079         }
26080         
26081         
26082         var cfg = {
26083             tag : 'div',
26084             cls : 'btn-group',
26085             cn : [
26086                 {
26087                     tag : 'button',
26088                     cls : 'dropdown-button btn btn-' + this.weight,
26089                     cn : text
26090                 },
26091                 {
26092                     tag : 'button',
26093                     cls : 'dropdown-toggle btn btn-' + this.weight,
26094                     cn : [
26095                         {
26096                             tag : 'span',
26097                             cls : 'caret'
26098                         }
26099                     ]
26100                 },
26101                 {
26102                     tag : 'ul',
26103                     cls : 'dropdown-menu'
26104                 }
26105             ]
26106             
26107         };
26108         
26109         if(this.pos == 'top'){
26110             cfg.cls += ' dropup';
26111         }
26112         
26113         if(this.isSubMenu){
26114             cfg = {
26115                 tag : 'ul',
26116                 cls : 'dropdown-menu'
26117             }
26118         }
26119         
26120         return cfg;
26121     },
26122     
26123     onRender : function(ct, position)
26124     {
26125         this.isSubMenu = ct.hasClass('dropdown-submenu');
26126         
26127         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26128     },
26129     
26130     initEvents : function() 
26131     {
26132         if(this.isSubMenu){
26133             return;
26134         }
26135         
26136         this.hidden = true;
26137         
26138         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26139         this.triggerEl.on('click', this.onTriggerPress, this);
26140         
26141         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26142         this.buttonEl.on('click', this.onClick, this);
26143         
26144     },
26145     
26146     list : function()
26147     {
26148         if(this.isSubMenu){
26149             return this.el;
26150         }
26151         
26152         return this.el.select('ul.dropdown-menu', true).first();
26153     },
26154     
26155     onClick : function(e)
26156     {
26157         this.fireEvent("click", this, e);
26158     },
26159     
26160     onTriggerPress  : function(e)
26161     {   
26162         if (this.isVisible()) {
26163             this.hide();
26164         } else {
26165             this.show();
26166         }
26167     },
26168     
26169     isVisible : function(){
26170         return !this.hidden;
26171     },
26172     
26173     show : function()
26174     {
26175         this.fireEvent("beforeshow", this);
26176         
26177         this.hidden = false;
26178         this.el.addClass('open');
26179         
26180         Roo.get(document).on("mouseup", this.onMouseUp, this);
26181         
26182         this.fireEvent("show", this);
26183         
26184         
26185     },
26186     
26187     hide : function()
26188     {
26189         this.fireEvent("beforehide", this);
26190         
26191         this.hidden = true;
26192         this.el.removeClass('open');
26193         
26194         Roo.get(document).un("mouseup", this.onMouseUp);
26195         
26196         this.fireEvent("hide", this);
26197     },
26198     
26199     onMouseUp : function()
26200     {
26201         this.hide();
26202     }
26203     
26204 });
26205
26206  
26207  /*
26208  * - LGPL
26209  *
26210  * menu item
26211  * 
26212  */
26213 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26214
26215 /**
26216  * @class Roo.bootstrap.menu.Item
26217  * @extends Roo.bootstrap.Component
26218  * Bootstrap MenuItem class
26219  * @cfg {Boolean} submenu (true | false) default false
26220  * @cfg {String} html text of the item
26221  * @cfg {String} href the link
26222  * @cfg {Boolean} disable (true | false) default false
26223  * @cfg {Boolean} preventDefault (true | false) default true
26224  * @cfg {String} icon Font awesome icon
26225  * @cfg {String} pos Submenu align to (left | right) default right 
26226  * 
26227  * 
26228  * @constructor
26229  * Create a new Item
26230  * @param {Object} config The config object
26231  */
26232
26233
26234 Roo.bootstrap.menu.Item = function(config){
26235     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26236     this.addEvents({
26237         /**
26238          * @event mouseover
26239          * Fires when the mouse is hovering over this menu
26240          * @param {Roo.bootstrap.menu.Item} this
26241          * @param {Roo.EventObject} e
26242          */
26243         mouseover : true,
26244         /**
26245          * @event mouseout
26246          * Fires when the mouse exits this menu
26247          * @param {Roo.bootstrap.menu.Item} this
26248          * @param {Roo.EventObject} e
26249          */
26250         mouseout : true,
26251         // raw events
26252         /**
26253          * @event click
26254          * The raw click event for the entire grid.
26255          * @param {Roo.EventObject} e
26256          */
26257         click : true
26258     });
26259 };
26260
26261 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26262     
26263     submenu : false,
26264     href : '',
26265     html : '',
26266     preventDefault: true,
26267     disable : false,
26268     icon : false,
26269     pos : 'right',
26270     
26271     getAutoCreate : function()
26272     {
26273         var text = [
26274             {
26275                 tag : 'span',
26276                 cls : 'roo-menu-item-text',
26277                 html : this.html
26278             }
26279         ];
26280         
26281         if(this.icon){
26282             text.unshift({
26283                 tag : 'i',
26284                 cls : 'fa ' + this.icon
26285             })
26286         }
26287         
26288         var cfg = {
26289             tag : 'li',
26290             cn : [
26291                 {
26292                     tag : 'a',
26293                     href : this.href || '#',
26294                     cn : text
26295                 }
26296             ]
26297         };
26298         
26299         if(this.disable){
26300             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26301         }
26302         
26303         if(this.submenu){
26304             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26305             
26306             if(this.pos == 'left'){
26307                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26308             }
26309         }
26310         
26311         return cfg;
26312     },
26313     
26314     initEvents : function() 
26315     {
26316         this.el.on('mouseover', this.onMouseOver, this);
26317         this.el.on('mouseout', this.onMouseOut, this);
26318         
26319         this.el.select('a', true).first().on('click', this.onClick, this);
26320         
26321     },
26322     
26323     onClick : function(e)
26324     {
26325         if(this.preventDefault){
26326             e.preventDefault();
26327         }
26328         
26329         this.fireEvent("click", this, e);
26330     },
26331     
26332     onMouseOver : function(e)
26333     {
26334         if(this.submenu && this.pos == 'left'){
26335             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26336         }
26337         
26338         this.fireEvent("mouseover", this, e);
26339     },
26340     
26341     onMouseOut : function(e)
26342     {
26343         this.fireEvent("mouseout", this, e);
26344     }
26345 });
26346
26347  
26348
26349  /*
26350  * - LGPL
26351  *
26352  * menu separator
26353  * 
26354  */
26355 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26356
26357 /**
26358  * @class Roo.bootstrap.menu.Separator
26359  * @extends Roo.bootstrap.Component
26360  * Bootstrap Separator class
26361  * 
26362  * @constructor
26363  * Create a new Separator
26364  * @param {Object} config The config object
26365  */
26366
26367
26368 Roo.bootstrap.menu.Separator = function(config){
26369     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26370 };
26371
26372 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26373     
26374     getAutoCreate : function(){
26375         var cfg = {
26376             tag : 'li',
26377             cls: 'divider'
26378         };
26379         
26380         return cfg;
26381     }
26382    
26383 });
26384
26385  
26386
26387  /*
26388  * - LGPL
26389  *
26390  * Tooltip
26391  * 
26392  */
26393
26394 /**
26395  * @class Roo.bootstrap.Tooltip
26396  * Bootstrap Tooltip class
26397  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26398  * to determine which dom element triggers the tooltip.
26399  * 
26400  * It needs to add support for additional attributes like tooltip-position
26401  * 
26402  * @constructor
26403  * Create a new Toolti
26404  * @param {Object} config The config object
26405  */
26406
26407 Roo.bootstrap.Tooltip = function(config){
26408     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26409     
26410     this.alignment = Roo.bootstrap.Tooltip.alignment;
26411     
26412     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26413         this.alignment = config.alignment;
26414     }
26415     
26416 };
26417
26418 Roo.apply(Roo.bootstrap.Tooltip, {
26419     /**
26420      * @function init initialize tooltip monitoring.
26421      * @static
26422      */
26423     currentEl : false,
26424     currentTip : false,
26425     currentRegion : false,
26426     
26427     //  init : delay?
26428     
26429     init : function()
26430     {
26431         Roo.get(document).on('mouseover', this.enter ,this);
26432         Roo.get(document).on('mouseout', this.leave, this);
26433          
26434         
26435         this.currentTip = new Roo.bootstrap.Tooltip();
26436     },
26437     
26438     enter : function(ev)
26439     {
26440         var dom = ev.getTarget();
26441         
26442         //Roo.log(['enter',dom]);
26443         var el = Roo.fly(dom);
26444         if (this.currentEl) {
26445             //Roo.log(dom);
26446             //Roo.log(this.currentEl);
26447             //Roo.log(this.currentEl.contains(dom));
26448             if (this.currentEl == el) {
26449                 return;
26450             }
26451             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26452                 return;
26453             }
26454
26455         }
26456         
26457         if (this.currentTip.el) {
26458             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26459         }    
26460         //Roo.log(ev);
26461         
26462         if(!el || el.dom == document){
26463             return;
26464         }
26465         
26466         var bindEl = el;
26467         
26468         // you can not look for children, as if el is the body.. then everythign is the child..
26469         if (!el.attr('tooltip')) { //
26470             if (!el.select("[tooltip]").elements.length) {
26471                 return;
26472             }
26473             // is the mouse over this child...?
26474             bindEl = el.select("[tooltip]").first();
26475             var xy = ev.getXY();
26476             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26477                 //Roo.log("not in region.");
26478                 return;
26479             }
26480             //Roo.log("child element over..");
26481             
26482         }
26483         this.currentEl = bindEl;
26484         this.currentTip.bind(bindEl);
26485         this.currentRegion = Roo.lib.Region.getRegion(dom);
26486         this.currentTip.enter();
26487         
26488     },
26489     leave : function(ev)
26490     {
26491         var dom = ev.getTarget();
26492         //Roo.log(['leave',dom]);
26493         if (!this.currentEl) {
26494             return;
26495         }
26496         
26497         
26498         if (dom != this.currentEl.dom) {
26499             return;
26500         }
26501         var xy = ev.getXY();
26502         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26503             return;
26504         }
26505         // only activate leave if mouse cursor is outside... bounding box..
26506         
26507         
26508         
26509         
26510         if (this.currentTip) {
26511             this.currentTip.leave();
26512         }
26513         //Roo.log('clear currentEl');
26514         this.currentEl = false;
26515         
26516         
26517     },
26518     alignment : {
26519         'left' : ['r-l', [-2,0], 'right'],
26520         'right' : ['l-r', [2,0], 'left'],
26521         'bottom' : ['t-b', [0,2], 'top'],
26522         'top' : [ 'b-t', [0,-2], 'bottom']
26523     }
26524     
26525 });
26526
26527
26528 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26529     
26530     
26531     bindEl : false,
26532     
26533     delay : null, // can be { show : 300 , hide: 500}
26534     
26535     timeout : null,
26536     
26537     hoverState : null, //???
26538     
26539     placement : 'bottom', 
26540     
26541     alignment : false,
26542     
26543     getAutoCreate : function(){
26544     
26545         var cfg = {
26546            cls : 'tooltip',
26547            role : 'tooltip',
26548            cn : [
26549                 {
26550                     cls : 'tooltip-arrow'
26551                 },
26552                 {
26553                     cls : 'tooltip-inner'
26554                 }
26555            ]
26556         };
26557         
26558         return cfg;
26559     },
26560     bind : function(el)
26561     {
26562         this.bindEl = el;
26563     },
26564       
26565     
26566     enter : function () {
26567        
26568         if (this.timeout != null) {
26569             clearTimeout(this.timeout);
26570         }
26571         
26572         this.hoverState = 'in';
26573          //Roo.log("enter - show");
26574         if (!this.delay || !this.delay.show) {
26575             this.show();
26576             return;
26577         }
26578         var _t = this;
26579         this.timeout = setTimeout(function () {
26580             if (_t.hoverState == 'in') {
26581                 _t.show();
26582             }
26583         }, this.delay.show);
26584     },
26585     leave : function()
26586     {
26587         clearTimeout(this.timeout);
26588     
26589         this.hoverState = 'out';
26590          if (!this.delay || !this.delay.hide) {
26591             this.hide();
26592             return;
26593         }
26594        
26595         var _t = this;
26596         this.timeout = setTimeout(function () {
26597             //Roo.log("leave - timeout");
26598             
26599             if (_t.hoverState == 'out') {
26600                 _t.hide();
26601                 Roo.bootstrap.Tooltip.currentEl = false;
26602             }
26603         }, delay);
26604     },
26605     
26606     show : function (msg)
26607     {
26608         if (!this.el) {
26609             this.render(document.body);
26610         }
26611         // set content.
26612         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26613         
26614         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26615         
26616         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26617         
26618         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26619         
26620         var placement = typeof this.placement == 'function' ?
26621             this.placement.call(this, this.el, on_el) :
26622             this.placement;
26623             
26624         var autoToken = /\s?auto?\s?/i;
26625         var autoPlace = autoToken.test(placement);
26626         if (autoPlace) {
26627             placement = placement.replace(autoToken, '') || 'top';
26628         }
26629         
26630         //this.el.detach()
26631         //this.el.setXY([0,0]);
26632         this.el.show();
26633         //this.el.dom.style.display='block';
26634         
26635         //this.el.appendTo(on_el);
26636         
26637         var p = this.getPosition();
26638         var box = this.el.getBox();
26639         
26640         if (autoPlace) {
26641             // fixme..
26642         }
26643         
26644         var align = this.alignment[placement];
26645         
26646         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26647         
26648         if(placement == 'top' || placement == 'bottom'){
26649             if(xy[0] < 0){
26650                 placement = 'right';
26651             }
26652             
26653             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26654                 placement = 'left';
26655             }
26656             
26657             var scroll = Roo.select('body', true).first().getScroll();
26658             
26659             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26660                 placement = 'top';
26661             }
26662             
26663             align = this.alignment[placement];
26664         }
26665         
26666         this.el.alignTo(this.bindEl, align[0],align[1]);
26667         //var arrow = this.el.select('.arrow',true).first();
26668         //arrow.set(align[2], 
26669         
26670         this.el.addClass(placement);
26671         
26672         this.el.addClass('in fade');
26673         
26674         this.hoverState = null;
26675         
26676         if (this.el.hasClass('fade')) {
26677             // fade it?
26678         }
26679         
26680     },
26681     hide : function()
26682     {
26683          
26684         if (!this.el) {
26685             return;
26686         }
26687         //this.el.setXY([0,0]);
26688         this.el.removeClass('in');
26689         //this.el.hide();
26690         
26691     }
26692     
26693 });
26694  
26695
26696  /*
26697  * - LGPL
26698  *
26699  * Location Picker
26700  * 
26701  */
26702
26703 /**
26704  * @class Roo.bootstrap.LocationPicker
26705  * @extends Roo.bootstrap.Component
26706  * Bootstrap LocationPicker class
26707  * @cfg {Number} latitude Position when init default 0
26708  * @cfg {Number} longitude Position when init default 0
26709  * @cfg {Number} zoom default 15
26710  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26711  * @cfg {Boolean} mapTypeControl default false
26712  * @cfg {Boolean} disableDoubleClickZoom default false
26713  * @cfg {Boolean} scrollwheel default true
26714  * @cfg {Boolean} streetViewControl default false
26715  * @cfg {Number} radius default 0
26716  * @cfg {String} locationName
26717  * @cfg {Boolean} draggable default true
26718  * @cfg {Boolean} enableAutocomplete default false
26719  * @cfg {Boolean} enableReverseGeocode default true
26720  * @cfg {String} markerTitle
26721  * 
26722  * @constructor
26723  * Create a new LocationPicker
26724  * @param {Object} config The config object
26725  */
26726
26727
26728 Roo.bootstrap.LocationPicker = function(config){
26729     
26730     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26731     
26732     this.addEvents({
26733         /**
26734          * @event initial
26735          * Fires when the picker initialized.
26736          * @param {Roo.bootstrap.LocationPicker} this
26737          * @param {Google Location} location
26738          */
26739         initial : true,
26740         /**
26741          * @event positionchanged
26742          * Fires when the picker position changed.
26743          * @param {Roo.bootstrap.LocationPicker} this
26744          * @param {Google Location} location
26745          */
26746         positionchanged : true,
26747         /**
26748          * @event resize
26749          * Fires when the map resize.
26750          * @param {Roo.bootstrap.LocationPicker} this
26751          */
26752         resize : true,
26753         /**
26754          * @event show
26755          * Fires when the map show.
26756          * @param {Roo.bootstrap.LocationPicker} this
26757          */
26758         show : true,
26759         /**
26760          * @event hide
26761          * Fires when the map hide.
26762          * @param {Roo.bootstrap.LocationPicker} this
26763          */
26764         hide : true,
26765         /**
26766          * @event mapClick
26767          * Fires when click the map.
26768          * @param {Roo.bootstrap.LocationPicker} this
26769          * @param {Map event} e
26770          */
26771         mapClick : true,
26772         /**
26773          * @event mapRightClick
26774          * Fires when right click the map.
26775          * @param {Roo.bootstrap.LocationPicker} this
26776          * @param {Map event} e
26777          */
26778         mapRightClick : true,
26779         /**
26780          * @event markerClick
26781          * Fires when click the marker.
26782          * @param {Roo.bootstrap.LocationPicker} this
26783          * @param {Map event} e
26784          */
26785         markerClick : true,
26786         /**
26787          * @event markerRightClick
26788          * Fires when right click the marker.
26789          * @param {Roo.bootstrap.LocationPicker} this
26790          * @param {Map event} e
26791          */
26792         markerRightClick : true,
26793         /**
26794          * @event OverlayViewDraw
26795          * Fires when OverlayView Draw
26796          * @param {Roo.bootstrap.LocationPicker} this
26797          */
26798         OverlayViewDraw : true,
26799         /**
26800          * @event OverlayViewOnAdd
26801          * Fires when OverlayView Draw
26802          * @param {Roo.bootstrap.LocationPicker} this
26803          */
26804         OverlayViewOnAdd : true,
26805         /**
26806          * @event OverlayViewOnRemove
26807          * Fires when OverlayView Draw
26808          * @param {Roo.bootstrap.LocationPicker} this
26809          */
26810         OverlayViewOnRemove : true,
26811         /**
26812          * @event OverlayViewShow
26813          * Fires when OverlayView Draw
26814          * @param {Roo.bootstrap.LocationPicker} this
26815          * @param {Pixel} cpx
26816          */
26817         OverlayViewShow : true,
26818         /**
26819          * @event OverlayViewHide
26820          * Fires when OverlayView Draw
26821          * @param {Roo.bootstrap.LocationPicker} this
26822          */
26823         OverlayViewHide : true,
26824         /**
26825          * @event loadexception
26826          * Fires when load google lib failed.
26827          * @param {Roo.bootstrap.LocationPicker} this
26828          */
26829         loadexception : true
26830     });
26831         
26832 };
26833
26834 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26835     
26836     gMapContext: false,
26837     
26838     latitude: 0,
26839     longitude: 0,
26840     zoom: 15,
26841     mapTypeId: false,
26842     mapTypeControl: false,
26843     disableDoubleClickZoom: false,
26844     scrollwheel: true,
26845     streetViewControl: false,
26846     radius: 0,
26847     locationName: '',
26848     draggable: true,
26849     enableAutocomplete: false,
26850     enableReverseGeocode: true,
26851     markerTitle: '',
26852     
26853     getAutoCreate: function()
26854     {
26855
26856         var cfg = {
26857             tag: 'div',
26858             cls: 'roo-location-picker'
26859         };
26860         
26861         return cfg
26862     },
26863     
26864     initEvents: function(ct, position)
26865     {       
26866         if(!this.el.getWidth() || this.isApplied()){
26867             return;
26868         }
26869         
26870         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26871         
26872         this.initial();
26873     },
26874     
26875     initial: function()
26876     {
26877         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26878             this.fireEvent('loadexception', this);
26879             return;
26880         }
26881         
26882         if(!this.mapTypeId){
26883             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26884         }
26885         
26886         this.gMapContext = this.GMapContext();
26887         
26888         this.initOverlayView();
26889         
26890         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26891         
26892         var _this = this;
26893                 
26894         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26895             _this.setPosition(_this.gMapContext.marker.position);
26896         });
26897         
26898         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26899             _this.fireEvent('mapClick', this, event);
26900             
26901         });
26902
26903         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26904             _this.fireEvent('mapRightClick', this, event);
26905             
26906         });
26907         
26908         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26909             _this.fireEvent('markerClick', this, event);
26910             
26911         });
26912
26913         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26914             _this.fireEvent('markerRightClick', this, event);
26915             
26916         });
26917         
26918         this.setPosition(this.gMapContext.location);
26919         
26920         this.fireEvent('initial', this, this.gMapContext.location);
26921     },
26922     
26923     initOverlayView: function()
26924     {
26925         var _this = this;
26926         
26927         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26928             
26929             draw: function()
26930             {
26931                 _this.fireEvent('OverlayViewDraw', _this);
26932             },
26933             
26934             onAdd: function()
26935             {
26936                 _this.fireEvent('OverlayViewOnAdd', _this);
26937             },
26938             
26939             onRemove: function()
26940             {
26941                 _this.fireEvent('OverlayViewOnRemove', _this);
26942             },
26943             
26944             show: function(cpx)
26945             {
26946                 _this.fireEvent('OverlayViewShow', _this, cpx);
26947             },
26948             
26949             hide: function()
26950             {
26951                 _this.fireEvent('OverlayViewHide', _this);
26952             }
26953             
26954         });
26955     },
26956     
26957     fromLatLngToContainerPixel: function(event)
26958     {
26959         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26960     },
26961     
26962     isApplied: function() 
26963     {
26964         return this.getGmapContext() == false ? false : true;
26965     },
26966     
26967     getGmapContext: function() 
26968     {
26969         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26970     },
26971     
26972     GMapContext: function() 
26973     {
26974         var position = new google.maps.LatLng(this.latitude, this.longitude);
26975         
26976         var _map = new google.maps.Map(this.el.dom, {
26977             center: position,
26978             zoom: this.zoom,
26979             mapTypeId: this.mapTypeId,
26980             mapTypeControl: this.mapTypeControl,
26981             disableDoubleClickZoom: this.disableDoubleClickZoom,
26982             scrollwheel: this.scrollwheel,
26983             streetViewControl: this.streetViewControl,
26984             locationName: this.locationName,
26985             draggable: this.draggable,
26986             enableAutocomplete: this.enableAutocomplete,
26987             enableReverseGeocode: this.enableReverseGeocode
26988         });
26989         
26990         var _marker = new google.maps.Marker({
26991             position: position,
26992             map: _map,
26993             title: this.markerTitle,
26994             draggable: this.draggable
26995         });
26996         
26997         return {
26998             map: _map,
26999             marker: _marker,
27000             circle: null,
27001             location: position,
27002             radius: this.radius,
27003             locationName: this.locationName,
27004             addressComponents: {
27005                 formatted_address: null,
27006                 addressLine1: null,
27007                 addressLine2: null,
27008                 streetName: null,
27009                 streetNumber: null,
27010                 city: null,
27011                 district: null,
27012                 state: null,
27013                 stateOrProvince: null
27014             },
27015             settings: this,
27016             domContainer: this.el.dom,
27017             geodecoder: new google.maps.Geocoder()
27018         };
27019     },
27020     
27021     drawCircle: function(center, radius, options) 
27022     {
27023         if (this.gMapContext.circle != null) {
27024             this.gMapContext.circle.setMap(null);
27025         }
27026         if (radius > 0) {
27027             radius *= 1;
27028             options = Roo.apply({}, options, {
27029                 strokeColor: "#0000FF",
27030                 strokeOpacity: .35,
27031                 strokeWeight: 2,
27032                 fillColor: "#0000FF",
27033                 fillOpacity: .2
27034             });
27035             
27036             options.map = this.gMapContext.map;
27037             options.radius = radius;
27038             options.center = center;
27039             this.gMapContext.circle = new google.maps.Circle(options);
27040             return this.gMapContext.circle;
27041         }
27042         
27043         return null;
27044     },
27045     
27046     setPosition: function(location) 
27047     {
27048         this.gMapContext.location = location;
27049         this.gMapContext.marker.setPosition(location);
27050         this.gMapContext.map.panTo(location);
27051         this.drawCircle(location, this.gMapContext.radius, {});
27052         
27053         var _this = this;
27054         
27055         if (this.gMapContext.settings.enableReverseGeocode) {
27056             this.gMapContext.geodecoder.geocode({
27057                 latLng: this.gMapContext.location
27058             }, function(results, status) {
27059                 
27060                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27061                     _this.gMapContext.locationName = results[0].formatted_address;
27062                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27063                     
27064                     _this.fireEvent('positionchanged', this, location);
27065                 }
27066             });
27067             
27068             return;
27069         }
27070         
27071         this.fireEvent('positionchanged', this, location);
27072     },
27073     
27074     resize: function()
27075     {
27076         google.maps.event.trigger(this.gMapContext.map, "resize");
27077         
27078         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27079         
27080         this.fireEvent('resize', this);
27081     },
27082     
27083     setPositionByLatLng: function(latitude, longitude)
27084     {
27085         this.setPosition(new google.maps.LatLng(latitude, longitude));
27086     },
27087     
27088     getCurrentPosition: function() 
27089     {
27090         return {
27091             latitude: this.gMapContext.location.lat(),
27092             longitude: this.gMapContext.location.lng()
27093         };
27094     },
27095     
27096     getAddressName: function() 
27097     {
27098         return this.gMapContext.locationName;
27099     },
27100     
27101     getAddressComponents: function() 
27102     {
27103         return this.gMapContext.addressComponents;
27104     },
27105     
27106     address_component_from_google_geocode: function(address_components) 
27107     {
27108         var result = {};
27109         
27110         for (var i = 0; i < address_components.length; i++) {
27111             var component = address_components[i];
27112             if (component.types.indexOf("postal_code") >= 0) {
27113                 result.postalCode = component.short_name;
27114             } else if (component.types.indexOf("street_number") >= 0) {
27115                 result.streetNumber = component.short_name;
27116             } else if (component.types.indexOf("route") >= 0) {
27117                 result.streetName = component.short_name;
27118             } else if (component.types.indexOf("neighborhood") >= 0) {
27119                 result.city = component.short_name;
27120             } else if (component.types.indexOf("locality") >= 0) {
27121                 result.city = component.short_name;
27122             } else if (component.types.indexOf("sublocality") >= 0) {
27123                 result.district = component.short_name;
27124             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27125                 result.stateOrProvince = component.short_name;
27126             } else if (component.types.indexOf("country") >= 0) {
27127                 result.country = component.short_name;
27128             }
27129         }
27130         
27131         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27132         result.addressLine2 = "";
27133         return result;
27134     },
27135     
27136     setZoomLevel: function(zoom)
27137     {
27138         this.gMapContext.map.setZoom(zoom);
27139     },
27140     
27141     show: function()
27142     {
27143         if(!this.el){
27144             return;
27145         }
27146         
27147         this.el.show();
27148         
27149         this.resize();
27150         
27151         this.fireEvent('show', this);
27152     },
27153     
27154     hide: function()
27155     {
27156         if(!this.el){
27157             return;
27158         }
27159         
27160         this.el.hide();
27161         
27162         this.fireEvent('hide', this);
27163     }
27164     
27165 });
27166
27167 Roo.apply(Roo.bootstrap.LocationPicker, {
27168     
27169     OverlayView : function(map, options)
27170     {
27171         options = options || {};
27172         
27173         this.setMap(map);
27174     }
27175     
27176     
27177 });/*
27178  * - LGPL
27179  *
27180  * Alert
27181  * 
27182  */
27183
27184 /**
27185  * @class Roo.bootstrap.Alert
27186  * @extends Roo.bootstrap.Component
27187  * Bootstrap Alert class
27188  * @cfg {String} title The title of alert
27189  * @cfg {String} html The content of alert
27190  * @cfg {String} weight (  success | info | warning | danger )
27191  * @cfg {String} faicon font-awesomeicon
27192  * 
27193  * @constructor
27194  * Create a new alert
27195  * @param {Object} config The config object
27196  */
27197
27198
27199 Roo.bootstrap.Alert = function(config){
27200     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27201     
27202 };
27203
27204 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27205     
27206     title: '',
27207     html: '',
27208     weight: false,
27209     faicon: false,
27210     
27211     getAutoCreate : function()
27212     {
27213         
27214         var cfg = {
27215             tag : 'div',
27216             cls : 'alert',
27217             cn : [
27218                 {
27219                     tag : 'i',
27220                     cls : 'roo-alert-icon'
27221                     
27222                 },
27223                 {
27224                     tag : 'b',
27225                     cls : 'roo-alert-title',
27226                     html : this.title
27227                 },
27228                 {
27229                     tag : 'span',
27230                     cls : 'roo-alert-text',
27231                     html : this.html
27232                 }
27233             ]
27234         };
27235         
27236         if(this.faicon){
27237             cfg.cn[0].cls += ' fa ' + this.faicon;
27238         }
27239         
27240         if(this.weight){
27241             cfg.cls += ' alert-' + this.weight;
27242         }
27243         
27244         return cfg;
27245     },
27246     
27247     initEvents: function() 
27248     {
27249         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27250     },
27251     
27252     setTitle : function(str)
27253     {
27254         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27255     },
27256     
27257     setText : function(str)
27258     {
27259         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27260     },
27261     
27262     setWeight : function(weight)
27263     {
27264         if(this.weight){
27265             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27266         }
27267         
27268         this.weight = weight;
27269         
27270         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27271     },
27272     
27273     setIcon : function(icon)
27274     {
27275         if(this.faicon){
27276             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27277         }
27278         
27279         this.faicon = icon;
27280         
27281         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27282     },
27283     
27284     hide: function() 
27285     {
27286         this.el.hide();   
27287     },
27288     
27289     show: function() 
27290     {  
27291         this.el.show();   
27292     }
27293     
27294 });
27295
27296  
27297 /*
27298 * Licence: LGPL
27299 */
27300
27301 /**
27302  * @class Roo.bootstrap.UploadCropbox
27303  * @extends Roo.bootstrap.Component
27304  * Bootstrap UploadCropbox class
27305  * @cfg {String} emptyText show when image has been loaded
27306  * @cfg {String} rotateNotify show when image too small to rotate
27307  * @cfg {Number} errorTimeout default 3000
27308  * @cfg {Number} minWidth default 300
27309  * @cfg {Number} minHeight default 300
27310  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27311  * @cfg {Boolean} isDocument (true|false) default false
27312  * @cfg {String} url action url
27313  * @cfg {String} paramName default 'imageUpload'
27314  * @cfg {String} method default POST
27315  * @cfg {Boolean} loadMask (true|false) default true
27316  * @cfg {Boolean} loadingText default 'Loading...'
27317  * 
27318  * @constructor
27319  * Create a new UploadCropbox
27320  * @param {Object} config The config object
27321  */
27322
27323 Roo.bootstrap.UploadCropbox = function(config){
27324     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27325     
27326     this.addEvents({
27327         /**
27328          * @event beforeselectfile
27329          * Fire before select file
27330          * @param {Roo.bootstrap.UploadCropbox} this
27331          */
27332         "beforeselectfile" : true,
27333         /**
27334          * @event initial
27335          * Fire after initEvent
27336          * @param {Roo.bootstrap.UploadCropbox} this
27337          */
27338         "initial" : true,
27339         /**
27340          * @event crop
27341          * Fire after initEvent
27342          * @param {Roo.bootstrap.UploadCropbox} this
27343          * @param {String} data
27344          */
27345         "crop" : true,
27346         /**
27347          * @event prepare
27348          * Fire when preparing the file data
27349          * @param {Roo.bootstrap.UploadCropbox} this
27350          * @param {Object} file
27351          */
27352         "prepare" : true,
27353         /**
27354          * @event exception
27355          * Fire when get exception
27356          * @param {Roo.bootstrap.UploadCropbox} this
27357          * @param {XMLHttpRequest} xhr
27358          */
27359         "exception" : true,
27360         /**
27361          * @event beforeloadcanvas
27362          * Fire before load the canvas
27363          * @param {Roo.bootstrap.UploadCropbox} this
27364          * @param {String} src
27365          */
27366         "beforeloadcanvas" : true,
27367         /**
27368          * @event trash
27369          * Fire when trash image
27370          * @param {Roo.bootstrap.UploadCropbox} this
27371          */
27372         "trash" : true,
27373         /**
27374          * @event download
27375          * Fire when download the image
27376          * @param {Roo.bootstrap.UploadCropbox} this
27377          */
27378         "download" : true,
27379         /**
27380          * @event footerbuttonclick
27381          * Fire when footerbuttonclick
27382          * @param {Roo.bootstrap.UploadCropbox} this
27383          * @param {String} type
27384          */
27385         "footerbuttonclick" : true,
27386         /**
27387          * @event resize
27388          * Fire when resize
27389          * @param {Roo.bootstrap.UploadCropbox} this
27390          */
27391         "resize" : true,
27392         /**
27393          * @event rotate
27394          * Fire when rotate the image
27395          * @param {Roo.bootstrap.UploadCropbox} this
27396          * @param {String} pos
27397          */
27398         "rotate" : true,
27399         /**
27400          * @event inspect
27401          * Fire when inspect the file
27402          * @param {Roo.bootstrap.UploadCropbox} this
27403          * @param {Object} file
27404          */
27405         "inspect" : true,
27406         /**
27407          * @event upload
27408          * Fire when xhr upload the file
27409          * @param {Roo.bootstrap.UploadCropbox} this
27410          * @param {Object} data
27411          */
27412         "upload" : true,
27413         /**
27414          * @event arrange
27415          * Fire when arrange the file data
27416          * @param {Roo.bootstrap.UploadCropbox} this
27417          * @param {Object} formData
27418          */
27419         "arrange" : true
27420     });
27421     
27422     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27423 };
27424
27425 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27426     
27427     emptyText : 'Click to upload image',
27428     rotateNotify : 'Image is too small to rotate',
27429     errorTimeout : 3000,
27430     scale : 0,
27431     baseScale : 1,
27432     rotate : 0,
27433     dragable : false,
27434     pinching : false,
27435     mouseX : 0,
27436     mouseY : 0,
27437     cropData : false,
27438     minWidth : 300,
27439     minHeight : 300,
27440     file : false,
27441     exif : {},
27442     baseRotate : 1,
27443     cropType : 'image/jpeg',
27444     buttons : false,
27445     canvasLoaded : false,
27446     isDocument : false,
27447     method : 'POST',
27448     paramName : 'imageUpload',
27449     loadMask : true,
27450     loadingText : 'Loading...',
27451     maskEl : false,
27452     
27453     getAutoCreate : function()
27454     {
27455         var cfg = {
27456             tag : 'div',
27457             cls : 'roo-upload-cropbox',
27458             cn : [
27459                 {
27460                     tag : 'input',
27461                     cls : 'roo-upload-cropbox-selector',
27462                     type : 'file'
27463                 },
27464                 {
27465                     tag : 'div',
27466                     cls : 'roo-upload-cropbox-body',
27467                     style : 'cursor:pointer',
27468                     cn : [
27469                         {
27470                             tag : 'div',
27471                             cls : 'roo-upload-cropbox-preview'
27472                         },
27473                         {
27474                             tag : 'div',
27475                             cls : 'roo-upload-cropbox-thumb'
27476                         },
27477                         {
27478                             tag : 'div',
27479                             cls : 'roo-upload-cropbox-empty-notify',
27480                             html : this.emptyText
27481                         },
27482                         {
27483                             tag : 'div',
27484                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27485                             html : this.rotateNotify
27486                         }
27487                     ]
27488                 },
27489                 {
27490                     tag : 'div',
27491                     cls : 'roo-upload-cropbox-footer',
27492                     cn : {
27493                         tag : 'div',
27494                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27495                         cn : []
27496                     }
27497                 }
27498             ]
27499         };
27500         
27501         return cfg;
27502     },
27503     
27504     onRender : function(ct, position)
27505     {
27506         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27507         
27508         if (this.buttons.length) {
27509             
27510             Roo.each(this.buttons, function(bb) {
27511                 
27512                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27513                 
27514                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27515                 
27516             }, this);
27517         }
27518         
27519         if(this.loadMask){
27520             this.maskEl = this.el;
27521         }
27522     },
27523     
27524     initEvents : function()
27525     {
27526         this.urlAPI = (window.createObjectURL && window) || 
27527                                 (window.URL && URL.revokeObjectURL && URL) || 
27528                                 (window.webkitURL && webkitURL);
27529                         
27530         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27531         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27532         
27533         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27534         this.selectorEl.hide();
27535         
27536         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27537         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27538         
27539         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27540         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27541         this.thumbEl.hide();
27542         
27543         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27544         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27545         
27546         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27547         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27548         this.errorEl.hide();
27549         
27550         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27551         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27552         this.footerEl.hide();
27553         
27554         this.setThumbBoxSize();
27555         
27556         this.bind();
27557         
27558         this.resize();
27559         
27560         this.fireEvent('initial', this);
27561     },
27562
27563     bind : function()
27564     {
27565         var _this = this;
27566         
27567         window.addEventListener("resize", function() { _this.resize(); } );
27568         
27569         this.bodyEl.on('click', this.beforeSelectFile, this);
27570         
27571         if(Roo.isTouch){
27572             this.bodyEl.on('touchstart', this.onTouchStart, this);
27573             this.bodyEl.on('touchmove', this.onTouchMove, this);
27574             this.bodyEl.on('touchend', this.onTouchEnd, this);
27575         }
27576         
27577         if(!Roo.isTouch){
27578             this.bodyEl.on('mousedown', this.onMouseDown, this);
27579             this.bodyEl.on('mousemove', this.onMouseMove, this);
27580             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27581             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27582             Roo.get(document).on('mouseup', this.onMouseUp, this);
27583         }
27584         
27585         this.selectorEl.on('change', this.onFileSelected, this);
27586     },
27587     
27588     reset : function()
27589     {    
27590         this.scale = 0;
27591         this.baseScale = 1;
27592         this.rotate = 0;
27593         this.baseRotate = 1;
27594         this.dragable = false;
27595         this.pinching = false;
27596         this.mouseX = 0;
27597         this.mouseY = 0;
27598         this.cropData = false;
27599         this.notifyEl.dom.innerHTML = this.emptyText;
27600         
27601         this.selectorEl.dom.value = '';
27602         
27603     },
27604     
27605     resize : function()
27606     {
27607         if(this.fireEvent('resize', this) != false){
27608             this.setThumbBoxPosition();
27609             this.setCanvasPosition();
27610         }
27611     },
27612     
27613     onFooterButtonClick : function(e, el, o, type)
27614     {
27615         switch (type) {
27616             case 'rotate-left' :
27617                 this.onRotateLeft(e);
27618                 break;
27619             case 'rotate-right' :
27620                 this.onRotateRight(e);
27621                 break;
27622             case 'picture' :
27623                 this.beforeSelectFile(e);
27624                 break;
27625             case 'trash' :
27626                 this.trash(e);
27627                 break;
27628             case 'crop' :
27629                 this.crop(e);
27630                 break;
27631             case 'download' :
27632                 this.download(e);
27633                 break;
27634             default :
27635                 break;
27636         }
27637         
27638         this.fireEvent('footerbuttonclick', this, type);
27639     },
27640     
27641     beforeSelectFile : function(e)
27642     {
27643         e.preventDefault();
27644         
27645         if(this.fireEvent('beforeselectfile', this) != false){
27646             this.selectorEl.dom.click();
27647         }
27648     },
27649     
27650     onFileSelected : function(e)
27651     {
27652         e.preventDefault();
27653         
27654         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27655             return;
27656         }
27657         
27658         var file = this.selectorEl.dom.files[0];
27659         
27660         if(this.fireEvent('inspect', this, file) != false){
27661             this.prepare(file);
27662         }
27663         
27664     },
27665     
27666     trash : function(e)
27667     {
27668         this.fireEvent('trash', this);
27669     },
27670     
27671     download : function(e)
27672     {
27673         this.fireEvent('download', this);
27674     },
27675     
27676     loadCanvas : function(src)
27677     {   
27678         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27679             
27680             this.reset();
27681             
27682             this.imageEl = document.createElement('img');
27683             
27684             var _this = this;
27685             
27686             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27687             
27688             this.imageEl.src = src;
27689         }
27690     },
27691     
27692     onLoadCanvas : function()
27693     {   
27694         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27695         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27696         
27697         this.bodyEl.un('click', this.beforeSelectFile, this);
27698         
27699         this.notifyEl.hide();
27700         this.thumbEl.show();
27701         this.footerEl.show();
27702         
27703         this.baseRotateLevel();
27704         
27705         if(this.isDocument){
27706             this.setThumbBoxSize();
27707         }
27708         
27709         this.setThumbBoxPosition();
27710         
27711         this.baseScaleLevel();
27712         
27713         this.draw();
27714         
27715         this.resize();
27716         
27717         this.canvasLoaded = true;
27718         
27719         if(this.loadMask){
27720             this.maskEl.unmask();
27721         }
27722         
27723     },
27724     
27725     setCanvasPosition : function()
27726     {   
27727         if(!this.canvasEl){
27728             return;
27729         }
27730         
27731         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27732         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27733         
27734         this.previewEl.setLeft(pw);
27735         this.previewEl.setTop(ph);
27736         
27737     },
27738     
27739     onMouseDown : function(e)
27740     {   
27741         e.stopEvent();
27742         
27743         this.dragable = true;
27744         this.pinching = false;
27745         
27746         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27747             this.dragable = false;
27748             return;
27749         }
27750         
27751         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27752         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27753         
27754     },
27755     
27756     onMouseMove : function(e)
27757     {   
27758         e.stopEvent();
27759         
27760         if(!this.canvasLoaded){
27761             return;
27762         }
27763         
27764         if (!this.dragable){
27765             return;
27766         }
27767         
27768         var minX = Math.ceil(this.thumbEl.getLeft(true));
27769         var minY = Math.ceil(this.thumbEl.getTop(true));
27770         
27771         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27772         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27773         
27774         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27775         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27776         
27777         x = x - this.mouseX;
27778         y = y - this.mouseY;
27779         
27780         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27781         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27782         
27783         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27784         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27785         
27786         this.previewEl.setLeft(bgX);
27787         this.previewEl.setTop(bgY);
27788         
27789         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27790         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27791     },
27792     
27793     onMouseUp : function(e)
27794     {   
27795         e.stopEvent();
27796         
27797         this.dragable = false;
27798     },
27799     
27800     onMouseWheel : function(e)
27801     {   
27802         e.stopEvent();
27803         
27804         this.startScale = this.scale;
27805         
27806         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27807         
27808         if(!this.zoomable()){
27809             this.scale = this.startScale;
27810             return;
27811         }
27812         
27813         this.draw();
27814         
27815         return;
27816     },
27817     
27818     zoomable : function()
27819     {
27820         var minScale = this.thumbEl.getWidth() / this.minWidth;
27821         
27822         if(this.minWidth < this.minHeight){
27823             minScale = this.thumbEl.getHeight() / this.minHeight;
27824         }
27825         
27826         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27827         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27828         
27829         if(
27830                 this.isDocument &&
27831                 (this.rotate == 0 || this.rotate == 180) && 
27832                 (
27833                     width > this.imageEl.OriginWidth || 
27834                     height > this.imageEl.OriginHeight ||
27835                     (width < this.minWidth && height < this.minHeight)
27836                 )
27837         ){
27838             return false;
27839         }
27840         
27841         if(
27842                 this.isDocument &&
27843                 (this.rotate == 90 || this.rotate == 270) && 
27844                 (
27845                     width > this.imageEl.OriginWidth || 
27846                     height > this.imageEl.OriginHeight ||
27847                     (width < this.minHeight && height < this.minWidth)
27848                 )
27849         ){
27850             return false;
27851         }
27852         
27853         if(
27854                 !this.isDocument &&
27855                 (this.rotate == 0 || this.rotate == 180) && 
27856                 (
27857                     width < this.minWidth || 
27858                     width > this.imageEl.OriginWidth || 
27859                     height < this.minHeight || 
27860                     height > this.imageEl.OriginHeight
27861                 )
27862         ){
27863             return false;
27864         }
27865         
27866         if(
27867                 !this.isDocument &&
27868                 (this.rotate == 90 || this.rotate == 270) && 
27869                 (
27870                     width < this.minHeight || 
27871                     width > this.imageEl.OriginWidth || 
27872                     height < this.minWidth || 
27873                     height > this.imageEl.OriginHeight
27874                 )
27875         ){
27876             return false;
27877         }
27878         
27879         return true;
27880         
27881     },
27882     
27883     onRotateLeft : function(e)
27884     {   
27885         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27886             
27887             var minScale = this.thumbEl.getWidth() / this.minWidth;
27888             
27889             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27890             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27891             
27892             this.startScale = this.scale;
27893             
27894             while (this.getScaleLevel() < minScale){
27895             
27896                 this.scale = this.scale + 1;
27897                 
27898                 if(!this.zoomable()){
27899                     break;
27900                 }
27901                 
27902                 if(
27903                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27904                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27905                 ){
27906                     continue;
27907                 }
27908                 
27909                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27910
27911                 this.draw();
27912                 
27913                 return;
27914             }
27915             
27916             this.scale = this.startScale;
27917             
27918             this.onRotateFail();
27919             
27920             return false;
27921         }
27922         
27923         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27924
27925         if(this.isDocument){
27926             this.setThumbBoxSize();
27927             this.setThumbBoxPosition();
27928             this.setCanvasPosition();
27929         }
27930         
27931         this.draw();
27932         
27933         this.fireEvent('rotate', this, 'left');
27934         
27935     },
27936     
27937     onRotateRight : function(e)
27938     {
27939         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27940             
27941             var minScale = this.thumbEl.getWidth() / this.minWidth;
27942         
27943             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27944             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27945             
27946             this.startScale = this.scale;
27947             
27948             while (this.getScaleLevel() < minScale){
27949             
27950                 this.scale = this.scale + 1;
27951                 
27952                 if(!this.zoomable()){
27953                     break;
27954                 }
27955                 
27956                 if(
27957                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27958                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27959                 ){
27960                     continue;
27961                 }
27962                 
27963                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27964
27965                 this.draw();
27966                 
27967                 return;
27968             }
27969             
27970             this.scale = this.startScale;
27971             
27972             this.onRotateFail();
27973             
27974             return false;
27975         }
27976         
27977         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27978
27979         if(this.isDocument){
27980             this.setThumbBoxSize();
27981             this.setThumbBoxPosition();
27982             this.setCanvasPosition();
27983         }
27984         
27985         this.draw();
27986         
27987         this.fireEvent('rotate', this, 'right');
27988     },
27989     
27990     onRotateFail : function()
27991     {
27992         this.errorEl.show(true);
27993         
27994         var _this = this;
27995         
27996         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27997     },
27998     
27999     draw : function()
28000     {
28001         this.previewEl.dom.innerHTML = '';
28002         
28003         var canvasEl = document.createElement("canvas");
28004         
28005         var contextEl = canvasEl.getContext("2d");
28006         
28007         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28008         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28009         var center = this.imageEl.OriginWidth / 2;
28010         
28011         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28012             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28013             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28014             center = this.imageEl.OriginHeight / 2;
28015         }
28016         
28017         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28018         
28019         contextEl.translate(center, center);
28020         contextEl.rotate(this.rotate * Math.PI / 180);
28021
28022         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28023         
28024         this.canvasEl = document.createElement("canvas");
28025         
28026         this.contextEl = this.canvasEl.getContext("2d");
28027         
28028         switch (this.rotate) {
28029             case 0 :
28030                 
28031                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28032                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28033                 
28034                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28035                 
28036                 break;
28037             case 90 : 
28038                 
28039                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28040                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28041                 
28042                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28043                     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);
28044                     break;
28045                 }
28046                 
28047                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28048                 
28049                 break;
28050             case 180 :
28051                 
28052                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28053                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28054                 
28055                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28056                     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);
28057                     break;
28058                 }
28059                 
28060                 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);
28061                 
28062                 break;
28063             case 270 :
28064                 
28065                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28066                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28067         
28068                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28069                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28070                     break;
28071                 }
28072                 
28073                 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);
28074                 
28075                 break;
28076             default : 
28077                 break;
28078         }
28079         
28080         this.previewEl.appendChild(this.canvasEl);
28081         
28082         this.setCanvasPosition();
28083     },
28084     
28085     crop : function()
28086     {
28087         if(!this.canvasLoaded){
28088             return;
28089         }
28090         
28091         var imageCanvas = document.createElement("canvas");
28092         
28093         var imageContext = imageCanvas.getContext("2d");
28094         
28095         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28096         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28097         
28098         var center = imageCanvas.width / 2;
28099         
28100         imageContext.translate(center, center);
28101         
28102         imageContext.rotate(this.rotate * Math.PI / 180);
28103         
28104         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28105         
28106         var canvas = document.createElement("canvas");
28107         
28108         var context = canvas.getContext("2d");
28109                 
28110         canvas.width = this.minWidth;
28111         canvas.height = this.minHeight;
28112
28113         switch (this.rotate) {
28114             case 0 :
28115                 
28116                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28117                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28118                 
28119                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28120                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28121                 
28122                 var targetWidth = this.minWidth - 2 * x;
28123                 var targetHeight = this.minHeight - 2 * y;
28124                 
28125                 var scale = 1;
28126                 
28127                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28128                     scale = targetWidth / width;
28129                 }
28130                 
28131                 if(x > 0 && y == 0){
28132                     scale = targetHeight / height;
28133                 }
28134                 
28135                 if(x > 0 && y > 0){
28136                     scale = targetWidth / width;
28137                     
28138                     if(width < height){
28139                         scale = targetHeight / height;
28140                     }
28141                 }
28142                 
28143                 context.scale(scale, scale);
28144                 
28145                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28146                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28147
28148                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28149                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28150
28151                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28152                 
28153                 break;
28154             case 90 : 
28155                 
28156                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28157                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28158                 
28159                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28160                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28161                 
28162                 var targetWidth = this.minWidth - 2 * x;
28163                 var targetHeight = this.minHeight - 2 * y;
28164                 
28165                 var scale = 1;
28166                 
28167                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28168                     scale = targetWidth / width;
28169                 }
28170                 
28171                 if(x > 0 && y == 0){
28172                     scale = targetHeight / height;
28173                 }
28174                 
28175                 if(x > 0 && y > 0){
28176                     scale = targetWidth / width;
28177                     
28178                     if(width < height){
28179                         scale = targetHeight / height;
28180                     }
28181                 }
28182                 
28183                 context.scale(scale, scale);
28184                 
28185                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28186                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28187
28188                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28189                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28190                 
28191                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28192                 
28193                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28194                 
28195                 break;
28196             case 180 :
28197                 
28198                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28199                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28200                 
28201                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28202                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28203                 
28204                 var targetWidth = this.minWidth - 2 * x;
28205                 var targetHeight = this.minHeight - 2 * y;
28206                 
28207                 var scale = 1;
28208                 
28209                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28210                     scale = targetWidth / width;
28211                 }
28212                 
28213                 if(x > 0 && y == 0){
28214                     scale = targetHeight / height;
28215                 }
28216                 
28217                 if(x > 0 && y > 0){
28218                     scale = targetWidth / width;
28219                     
28220                     if(width < height){
28221                         scale = targetHeight / height;
28222                     }
28223                 }
28224                 
28225                 context.scale(scale, scale);
28226                 
28227                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28228                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28229
28230                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28231                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28232
28233                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28234                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28235                 
28236                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28237                 
28238                 break;
28239             case 270 :
28240                 
28241                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28242                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28243                 
28244                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28245                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28246                 
28247                 var targetWidth = this.minWidth - 2 * x;
28248                 var targetHeight = this.minHeight - 2 * y;
28249                 
28250                 var scale = 1;
28251                 
28252                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28253                     scale = targetWidth / width;
28254                 }
28255                 
28256                 if(x > 0 && y == 0){
28257                     scale = targetHeight / height;
28258                 }
28259                 
28260                 if(x > 0 && y > 0){
28261                     scale = targetWidth / width;
28262                     
28263                     if(width < height){
28264                         scale = targetHeight / height;
28265                     }
28266                 }
28267                 
28268                 context.scale(scale, scale);
28269                 
28270                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28271                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28272
28273                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28274                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28275                 
28276                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28277                 
28278                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28279                 
28280                 break;
28281             default : 
28282                 break;
28283         }
28284         
28285         this.cropData = canvas.toDataURL(this.cropType);
28286         
28287         if(this.fireEvent('crop', this, this.cropData) !== false){
28288             this.process(this.file, this.cropData);
28289         }
28290         
28291         return;
28292         
28293     },
28294     
28295     setThumbBoxSize : function()
28296     {
28297         var width, height;
28298         
28299         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28300             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28301             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28302             
28303             this.minWidth = width;
28304             this.minHeight = height;
28305             
28306             if(this.rotate == 90 || this.rotate == 270){
28307                 this.minWidth = height;
28308                 this.minHeight = width;
28309             }
28310         }
28311         
28312         height = 300;
28313         width = Math.ceil(this.minWidth * height / this.minHeight);
28314         
28315         if(this.minWidth > this.minHeight){
28316             width = 300;
28317             height = Math.ceil(this.minHeight * width / this.minWidth);
28318         }
28319         
28320         this.thumbEl.setStyle({
28321             width : width + 'px',
28322             height : height + 'px'
28323         });
28324
28325         return;
28326             
28327     },
28328     
28329     setThumbBoxPosition : function()
28330     {
28331         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28332         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28333         
28334         this.thumbEl.setLeft(x);
28335         this.thumbEl.setTop(y);
28336         
28337     },
28338     
28339     baseRotateLevel : function()
28340     {
28341         this.baseRotate = 1;
28342         
28343         if(
28344                 typeof(this.exif) != 'undefined' &&
28345                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28346                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28347         ){
28348             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28349         }
28350         
28351         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28352         
28353     },
28354     
28355     baseScaleLevel : function()
28356     {
28357         var width, height;
28358         
28359         if(this.isDocument){
28360             
28361             if(this.baseRotate == 6 || this.baseRotate == 8){
28362             
28363                 height = this.thumbEl.getHeight();
28364                 this.baseScale = height / this.imageEl.OriginWidth;
28365
28366                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28367                     width = this.thumbEl.getWidth();
28368                     this.baseScale = width / this.imageEl.OriginHeight;
28369                 }
28370
28371                 return;
28372             }
28373
28374             height = this.thumbEl.getHeight();
28375             this.baseScale = height / this.imageEl.OriginHeight;
28376
28377             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28378                 width = this.thumbEl.getWidth();
28379                 this.baseScale = width / this.imageEl.OriginWidth;
28380             }
28381
28382             return;
28383         }
28384         
28385         if(this.baseRotate == 6 || this.baseRotate == 8){
28386             
28387             width = this.thumbEl.getHeight();
28388             this.baseScale = width / this.imageEl.OriginHeight;
28389             
28390             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28391                 height = this.thumbEl.getWidth();
28392                 this.baseScale = height / this.imageEl.OriginHeight;
28393             }
28394             
28395             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28396                 height = this.thumbEl.getWidth();
28397                 this.baseScale = height / this.imageEl.OriginHeight;
28398                 
28399                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28400                     width = this.thumbEl.getHeight();
28401                     this.baseScale = width / this.imageEl.OriginWidth;
28402                 }
28403             }
28404             
28405             return;
28406         }
28407         
28408         width = this.thumbEl.getWidth();
28409         this.baseScale = width / this.imageEl.OriginWidth;
28410         
28411         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28412             height = this.thumbEl.getHeight();
28413             this.baseScale = height / this.imageEl.OriginHeight;
28414         }
28415         
28416         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28417             
28418             height = this.thumbEl.getHeight();
28419             this.baseScale = height / this.imageEl.OriginHeight;
28420             
28421             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28422                 width = this.thumbEl.getWidth();
28423                 this.baseScale = width / this.imageEl.OriginWidth;
28424             }
28425             
28426         }
28427         
28428         return;
28429     },
28430     
28431     getScaleLevel : function()
28432     {
28433         return this.baseScale * Math.pow(1.1, this.scale);
28434     },
28435     
28436     onTouchStart : function(e)
28437     {
28438         if(!this.canvasLoaded){
28439             this.beforeSelectFile(e);
28440             return;
28441         }
28442         
28443         var touches = e.browserEvent.touches;
28444         
28445         if(!touches){
28446             return;
28447         }
28448         
28449         if(touches.length == 1){
28450             this.onMouseDown(e);
28451             return;
28452         }
28453         
28454         if(touches.length != 2){
28455             return;
28456         }
28457         
28458         var coords = [];
28459         
28460         for(var i = 0, finger; finger = touches[i]; i++){
28461             coords.push(finger.pageX, finger.pageY);
28462         }
28463         
28464         var x = Math.pow(coords[0] - coords[2], 2);
28465         var y = Math.pow(coords[1] - coords[3], 2);
28466         
28467         this.startDistance = Math.sqrt(x + y);
28468         
28469         this.startScale = this.scale;
28470         
28471         this.pinching = true;
28472         this.dragable = false;
28473         
28474     },
28475     
28476     onTouchMove : function(e)
28477     {
28478         if(!this.pinching && !this.dragable){
28479             return;
28480         }
28481         
28482         var touches = e.browserEvent.touches;
28483         
28484         if(!touches){
28485             return;
28486         }
28487         
28488         if(this.dragable){
28489             this.onMouseMove(e);
28490             return;
28491         }
28492         
28493         var coords = [];
28494         
28495         for(var i = 0, finger; finger = touches[i]; i++){
28496             coords.push(finger.pageX, finger.pageY);
28497         }
28498         
28499         var x = Math.pow(coords[0] - coords[2], 2);
28500         var y = Math.pow(coords[1] - coords[3], 2);
28501         
28502         this.endDistance = Math.sqrt(x + y);
28503         
28504         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28505         
28506         if(!this.zoomable()){
28507             this.scale = this.startScale;
28508             return;
28509         }
28510         
28511         this.draw();
28512         
28513     },
28514     
28515     onTouchEnd : function(e)
28516     {
28517         this.pinching = false;
28518         this.dragable = false;
28519         
28520     },
28521     
28522     process : function(file, crop)
28523     {
28524         if(this.loadMask){
28525             this.maskEl.mask(this.loadingText);
28526         }
28527         
28528         this.xhr = new XMLHttpRequest();
28529         
28530         file.xhr = this.xhr;
28531
28532         this.xhr.open(this.method, this.url, true);
28533         
28534         var headers = {
28535             "Accept": "application/json",
28536             "Cache-Control": "no-cache",
28537             "X-Requested-With": "XMLHttpRequest"
28538         };
28539         
28540         for (var headerName in headers) {
28541             var headerValue = headers[headerName];
28542             if (headerValue) {
28543                 this.xhr.setRequestHeader(headerName, headerValue);
28544             }
28545         }
28546         
28547         var _this = this;
28548         
28549         this.xhr.onload = function()
28550         {
28551             _this.xhrOnLoad(_this.xhr);
28552         }
28553         
28554         this.xhr.onerror = function()
28555         {
28556             _this.xhrOnError(_this.xhr);
28557         }
28558         
28559         var formData = new FormData();
28560
28561         formData.append('returnHTML', 'NO');
28562         
28563         if(crop){
28564             formData.append('crop', crop);
28565         }
28566         
28567         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28568             formData.append(this.paramName, file, file.name);
28569         }
28570         
28571         if(typeof(file.filename) != 'undefined'){
28572             formData.append('filename', file.filename);
28573         }
28574         
28575         if(typeof(file.mimetype) != 'undefined'){
28576             formData.append('mimetype', file.mimetype);
28577         }
28578         
28579         if(this.fireEvent('arrange', this, formData) != false){
28580             this.xhr.send(formData);
28581         };
28582     },
28583     
28584     xhrOnLoad : function(xhr)
28585     {
28586         if(this.loadMask){
28587             this.maskEl.unmask();
28588         }
28589         
28590         if (xhr.readyState !== 4) {
28591             this.fireEvent('exception', this, xhr);
28592             return;
28593         }
28594
28595         var response = Roo.decode(xhr.responseText);
28596         
28597         if(!response.success){
28598             this.fireEvent('exception', this, xhr);
28599             return;
28600         }
28601         
28602         var response = Roo.decode(xhr.responseText);
28603         
28604         this.fireEvent('upload', this, response);
28605         
28606     },
28607     
28608     xhrOnError : function()
28609     {
28610         if(this.loadMask){
28611             this.maskEl.unmask();
28612         }
28613         
28614         Roo.log('xhr on error');
28615         
28616         var response = Roo.decode(xhr.responseText);
28617           
28618         Roo.log(response);
28619         
28620     },
28621     
28622     prepare : function(file)
28623     {   
28624         if(this.loadMask){
28625             this.maskEl.mask(this.loadingText);
28626         }
28627         
28628         this.file = false;
28629         this.exif = {};
28630         
28631         if(typeof(file) === 'string'){
28632             this.loadCanvas(file);
28633             return;
28634         }
28635         
28636         if(!file || !this.urlAPI){
28637             return;
28638         }
28639         
28640         this.file = file;
28641         this.cropType = file.type;
28642         
28643         var _this = this;
28644         
28645         if(this.fireEvent('prepare', this, this.file) != false){
28646             
28647             var reader = new FileReader();
28648             
28649             reader.onload = function (e) {
28650                 if (e.target.error) {
28651                     Roo.log(e.target.error);
28652                     return;
28653                 }
28654                 
28655                 var buffer = e.target.result,
28656                     dataView = new DataView(buffer),
28657                     offset = 2,
28658                     maxOffset = dataView.byteLength - 4,
28659                     markerBytes,
28660                     markerLength;
28661                 
28662                 if (dataView.getUint16(0) === 0xffd8) {
28663                     while (offset < maxOffset) {
28664                         markerBytes = dataView.getUint16(offset);
28665                         
28666                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28667                             markerLength = dataView.getUint16(offset + 2) + 2;
28668                             if (offset + markerLength > dataView.byteLength) {
28669                                 Roo.log('Invalid meta data: Invalid segment size.');
28670                                 break;
28671                             }
28672                             
28673                             if(markerBytes == 0xffe1){
28674                                 _this.parseExifData(
28675                                     dataView,
28676                                     offset,
28677                                     markerLength
28678                                 );
28679                             }
28680                             
28681                             offset += markerLength;
28682                             
28683                             continue;
28684                         }
28685                         
28686                         break;
28687                     }
28688                     
28689                 }
28690                 
28691                 var url = _this.urlAPI.createObjectURL(_this.file);
28692                 
28693                 _this.loadCanvas(url);
28694                 
28695                 return;
28696             }
28697             
28698             reader.readAsArrayBuffer(this.file);
28699             
28700         }
28701         
28702     },
28703     
28704     parseExifData : function(dataView, offset, length)
28705     {
28706         var tiffOffset = offset + 10,
28707             littleEndian,
28708             dirOffset;
28709     
28710         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28711             // No Exif data, might be XMP data instead
28712             return;
28713         }
28714         
28715         // Check for the ASCII code for "Exif" (0x45786966):
28716         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28717             // No Exif data, might be XMP data instead
28718             return;
28719         }
28720         if (tiffOffset + 8 > dataView.byteLength) {
28721             Roo.log('Invalid Exif data: Invalid segment size.');
28722             return;
28723         }
28724         // Check for the two null bytes:
28725         if (dataView.getUint16(offset + 8) !== 0x0000) {
28726             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28727             return;
28728         }
28729         // Check the byte alignment:
28730         switch (dataView.getUint16(tiffOffset)) {
28731         case 0x4949:
28732             littleEndian = true;
28733             break;
28734         case 0x4D4D:
28735             littleEndian = false;
28736             break;
28737         default:
28738             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28739             return;
28740         }
28741         // Check for the TIFF tag marker (0x002A):
28742         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28743             Roo.log('Invalid Exif data: Missing TIFF marker.');
28744             return;
28745         }
28746         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28747         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28748         
28749         this.parseExifTags(
28750             dataView,
28751             tiffOffset,
28752             tiffOffset + dirOffset,
28753             littleEndian
28754         );
28755     },
28756     
28757     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28758     {
28759         var tagsNumber,
28760             dirEndOffset,
28761             i;
28762         if (dirOffset + 6 > dataView.byteLength) {
28763             Roo.log('Invalid Exif data: Invalid directory offset.');
28764             return;
28765         }
28766         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28767         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28768         if (dirEndOffset + 4 > dataView.byteLength) {
28769             Roo.log('Invalid Exif data: Invalid directory size.');
28770             return;
28771         }
28772         for (i = 0; i < tagsNumber; i += 1) {
28773             this.parseExifTag(
28774                 dataView,
28775                 tiffOffset,
28776                 dirOffset + 2 + 12 * i, // tag offset
28777                 littleEndian
28778             );
28779         }
28780         // Return the offset to the next directory:
28781         return dataView.getUint32(dirEndOffset, littleEndian);
28782     },
28783     
28784     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28785     {
28786         var tag = dataView.getUint16(offset, littleEndian);
28787         
28788         this.exif[tag] = this.getExifValue(
28789             dataView,
28790             tiffOffset,
28791             offset,
28792             dataView.getUint16(offset + 2, littleEndian), // tag type
28793             dataView.getUint32(offset + 4, littleEndian), // tag length
28794             littleEndian
28795         );
28796     },
28797     
28798     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28799     {
28800         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28801             tagSize,
28802             dataOffset,
28803             values,
28804             i,
28805             str,
28806             c;
28807     
28808         if (!tagType) {
28809             Roo.log('Invalid Exif data: Invalid tag type.');
28810             return;
28811         }
28812         
28813         tagSize = tagType.size * length;
28814         // Determine if the value is contained in the dataOffset bytes,
28815         // or if the value at the dataOffset is a pointer to the actual data:
28816         dataOffset = tagSize > 4 ?
28817                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28818         if (dataOffset + tagSize > dataView.byteLength) {
28819             Roo.log('Invalid Exif data: Invalid data offset.');
28820             return;
28821         }
28822         if (length === 1) {
28823             return tagType.getValue(dataView, dataOffset, littleEndian);
28824         }
28825         values = [];
28826         for (i = 0; i < length; i += 1) {
28827             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28828         }
28829         
28830         if (tagType.ascii) {
28831             str = '';
28832             // Concatenate the chars:
28833             for (i = 0; i < values.length; i += 1) {
28834                 c = values[i];
28835                 // Ignore the terminating NULL byte(s):
28836                 if (c === '\u0000') {
28837                     break;
28838                 }
28839                 str += c;
28840             }
28841             return str;
28842         }
28843         return values;
28844     }
28845     
28846 });
28847
28848 Roo.apply(Roo.bootstrap.UploadCropbox, {
28849     tags : {
28850         'Orientation': 0x0112
28851     },
28852     
28853     Orientation: {
28854             1: 0, //'top-left',
28855 //            2: 'top-right',
28856             3: 180, //'bottom-right',
28857 //            4: 'bottom-left',
28858 //            5: 'left-top',
28859             6: 90, //'right-top',
28860 //            7: 'right-bottom',
28861             8: 270 //'left-bottom'
28862     },
28863     
28864     exifTagTypes : {
28865         // byte, 8-bit unsigned int:
28866         1: {
28867             getValue: function (dataView, dataOffset) {
28868                 return dataView.getUint8(dataOffset);
28869             },
28870             size: 1
28871         },
28872         // ascii, 8-bit byte:
28873         2: {
28874             getValue: function (dataView, dataOffset) {
28875                 return String.fromCharCode(dataView.getUint8(dataOffset));
28876             },
28877             size: 1,
28878             ascii: true
28879         },
28880         // short, 16 bit int:
28881         3: {
28882             getValue: function (dataView, dataOffset, littleEndian) {
28883                 return dataView.getUint16(dataOffset, littleEndian);
28884             },
28885             size: 2
28886         },
28887         // long, 32 bit int:
28888         4: {
28889             getValue: function (dataView, dataOffset, littleEndian) {
28890                 return dataView.getUint32(dataOffset, littleEndian);
28891             },
28892             size: 4
28893         },
28894         // rational = two long values, first is numerator, second is denominator:
28895         5: {
28896             getValue: function (dataView, dataOffset, littleEndian) {
28897                 return dataView.getUint32(dataOffset, littleEndian) /
28898                     dataView.getUint32(dataOffset + 4, littleEndian);
28899             },
28900             size: 8
28901         },
28902         // slong, 32 bit signed int:
28903         9: {
28904             getValue: function (dataView, dataOffset, littleEndian) {
28905                 return dataView.getInt32(dataOffset, littleEndian);
28906             },
28907             size: 4
28908         },
28909         // srational, two slongs, first is numerator, second is denominator:
28910         10: {
28911             getValue: function (dataView, dataOffset, littleEndian) {
28912                 return dataView.getInt32(dataOffset, littleEndian) /
28913                     dataView.getInt32(dataOffset + 4, littleEndian);
28914             },
28915             size: 8
28916         }
28917     },
28918     
28919     footer : {
28920         STANDARD : [
28921             {
28922                 tag : 'div',
28923                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28924                 action : 'rotate-left',
28925                 cn : [
28926                     {
28927                         tag : 'button',
28928                         cls : 'btn btn-default',
28929                         html : '<i class="fa fa-undo"></i>'
28930                     }
28931                 ]
28932             },
28933             {
28934                 tag : 'div',
28935                 cls : 'btn-group roo-upload-cropbox-picture',
28936                 action : 'picture',
28937                 cn : [
28938                     {
28939                         tag : 'button',
28940                         cls : 'btn btn-default',
28941                         html : '<i class="fa fa-picture-o"></i>'
28942                     }
28943                 ]
28944             },
28945             {
28946                 tag : 'div',
28947                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28948                 action : 'rotate-right',
28949                 cn : [
28950                     {
28951                         tag : 'button',
28952                         cls : 'btn btn-default',
28953                         html : '<i class="fa fa-repeat"></i>'
28954                     }
28955                 ]
28956             }
28957         ],
28958         DOCUMENT : [
28959             {
28960                 tag : 'div',
28961                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28962                 action : 'rotate-left',
28963                 cn : [
28964                     {
28965                         tag : 'button',
28966                         cls : 'btn btn-default',
28967                         html : '<i class="fa fa-undo"></i>'
28968                     }
28969                 ]
28970             },
28971             {
28972                 tag : 'div',
28973                 cls : 'btn-group roo-upload-cropbox-download',
28974                 action : 'download',
28975                 cn : [
28976                     {
28977                         tag : 'button',
28978                         cls : 'btn btn-default',
28979                         html : '<i class="fa fa-download"></i>'
28980                     }
28981                 ]
28982             },
28983             {
28984                 tag : 'div',
28985                 cls : 'btn-group roo-upload-cropbox-crop',
28986                 action : 'crop',
28987                 cn : [
28988                     {
28989                         tag : 'button',
28990                         cls : 'btn btn-default',
28991                         html : '<i class="fa fa-crop"></i>'
28992                     }
28993                 ]
28994             },
28995             {
28996                 tag : 'div',
28997                 cls : 'btn-group roo-upload-cropbox-trash',
28998                 action : 'trash',
28999                 cn : [
29000                     {
29001                         tag : 'button',
29002                         cls : 'btn btn-default',
29003                         html : '<i class="fa fa-trash"></i>'
29004                     }
29005                 ]
29006             },
29007             {
29008                 tag : 'div',
29009                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29010                 action : 'rotate-right',
29011                 cn : [
29012                     {
29013                         tag : 'button',
29014                         cls : 'btn btn-default',
29015                         html : '<i class="fa fa-repeat"></i>'
29016                     }
29017                 ]
29018             }
29019         ],
29020         ROTATOR : [
29021             {
29022                 tag : 'div',
29023                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29024                 action : 'rotate-left',
29025                 cn : [
29026                     {
29027                         tag : 'button',
29028                         cls : 'btn btn-default',
29029                         html : '<i class="fa fa-undo"></i>'
29030                     }
29031                 ]
29032             },
29033             {
29034                 tag : 'div',
29035                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29036                 action : 'rotate-right',
29037                 cn : [
29038                     {
29039                         tag : 'button',
29040                         cls : 'btn btn-default',
29041                         html : '<i class="fa fa-repeat"></i>'
29042                     }
29043                 ]
29044             }
29045         ]
29046     }
29047 });
29048
29049 /*
29050 * Licence: LGPL
29051 */
29052
29053 /**
29054  * @class Roo.bootstrap.DocumentManager
29055  * @extends Roo.bootstrap.Component
29056  * Bootstrap DocumentManager class
29057  * @cfg {String} paramName default 'imageUpload'
29058  * @cfg {String} toolTipName default 'filename'
29059  * @cfg {String} method default POST
29060  * @cfg {String} url action url
29061  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29062  * @cfg {Boolean} multiple multiple upload default true
29063  * @cfg {Number} thumbSize default 300
29064  * @cfg {String} fieldLabel
29065  * @cfg {Number} labelWidth default 4
29066  * @cfg {String} labelAlign (left|top) default left
29067  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29068 * @cfg {Number} labellg set the width of label (1-12)
29069  * @cfg {Number} labelmd set the width of label (1-12)
29070  * @cfg {Number} labelsm set the width of label (1-12)
29071  * @cfg {Number} labelxs set the width of label (1-12)
29072  * 
29073  * @constructor
29074  * Create a new DocumentManager
29075  * @param {Object} config The config object
29076  */
29077
29078 Roo.bootstrap.DocumentManager = function(config){
29079     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29080     
29081     this.files = [];
29082     this.delegates = [];
29083     
29084     this.addEvents({
29085         /**
29086          * @event initial
29087          * Fire when initial the DocumentManager
29088          * @param {Roo.bootstrap.DocumentManager} this
29089          */
29090         "initial" : true,
29091         /**
29092          * @event inspect
29093          * inspect selected file
29094          * @param {Roo.bootstrap.DocumentManager} this
29095          * @param {File} file
29096          */
29097         "inspect" : true,
29098         /**
29099          * @event exception
29100          * Fire when xhr load exception
29101          * @param {Roo.bootstrap.DocumentManager} this
29102          * @param {XMLHttpRequest} xhr
29103          */
29104         "exception" : true,
29105         /**
29106          * @event afterupload
29107          * Fire when xhr load exception
29108          * @param {Roo.bootstrap.DocumentManager} this
29109          * @param {XMLHttpRequest} xhr
29110          */
29111         "afterupload" : true,
29112         /**
29113          * @event prepare
29114          * prepare the form data
29115          * @param {Roo.bootstrap.DocumentManager} this
29116          * @param {Object} formData
29117          */
29118         "prepare" : true,
29119         /**
29120          * @event remove
29121          * Fire when remove the file
29122          * @param {Roo.bootstrap.DocumentManager} this
29123          * @param {Object} file
29124          */
29125         "remove" : true,
29126         /**
29127          * @event refresh
29128          * Fire after refresh the file
29129          * @param {Roo.bootstrap.DocumentManager} this
29130          */
29131         "refresh" : true,
29132         /**
29133          * @event click
29134          * Fire after click the image
29135          * @param {Roo.bootstrap.DocumentManager} this
29136          * @param {Object} file
29137          */
29138         "click" : true,
29139         /**
29140          * @event edit
29141          * Fire when upload a image and editable set to true
29142          * @param {Roo.bootstrap.DocumentManager} this
29143          * @param {Object} file
29144          */
29145         "edit" : true,
29146         /**
29147          * @event beforeselectfile
29148          * Fire before select file
29149          * @param {Roo.bootstrap.DocumentManager} this
29150          */
29151         "beforeselectfile" : true,
29152         /**
29153          * @event process
29154          * Fire before process file
29155          * @param {Roo.bootstrap.DocumentManager} this
29156          * @param {Object} file
29157          */
29158         "process" : true,
29159         /**
29160          * @event previewrendered
29161          * Fire when preview rendered
29162          * @param {Roo.bootstrap.DocumentManager} this
29163          * @param {Object} file
29164          */
29165         "previewrendered" : true,
29166         /**
29167          */
29168         "previewResize" : true
29169         
29170     });
29171 };
29172
29173 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29174     
29175     boxes : 0,
29176     inputName : '',
29177     thumbSize : 300,
29178     multiple : true,
29179     files : false,
29180     method : 'POST',
29181     url : '',
29182     paramName : 'imageUpload',
29183     toolTipName : 'filename',
29184     fieldLabel : '',
29185     labelWidth : 4,
29186     labelAlign : 'left',
29187     editable : true,
29188     delegates : false,
29189     xhr : false, 
29190     
29191     labellg : 0,
29192     labelmd : 0,
29193     labelsm : 0,
29194     labelxs : 0,
29195     
29196     getAutoCreate : function()
29197     {   
29198         var managerWidget = {
29199             tag : 'div',
29200             cls : 'roo-document-manager',
29201             cn : [
29202                 {
29203                     tag : 'input',
29204                     cls : 'roo-document-manager-selector',
29205                     type : 'file'
29206                 },
29207                 {
29208                     tag : 'div',
29209                     cls : 'roo-document-manager-uploader',
29210                     cn : [
29211                         {
29212                             tag : 'div',
29213                             cls : 'roo-document-manager-upload-btn',
29214                             html : '<i class="fa fa-plus"></i>'
29215                         }
29216                     ]
29217                     
29218                 }
29219             ]
29220         };
29221         
29222         var content = [
29223             {
29224                 tag : 'div',
29225                 cls : 'column col-md-12',
29226                 cn : managerWidget
29227             }
29228         ];
29229         
29230         if(this.fieldLabel.length){
29231             
29232             content = [
29233                 {
29234                     tag : 'div',
29235                     cls : 'column col-md-12',
29236                     html : this.fieldLabel
29237                 },
29238                 {
29239                     tag : 'div',
29240                     cls : 'column col-md-12',
29241                     cn : managerWidget
29242                 }
29243             ];
29244
29245             if(this.labelAlign == 'left'){
29246                 content = [
29247                     {
29248                         tag : 'div',
29249                         cls : 'column',
29250                         html : this.fieldLabel
29251                     },
29252                     {
29253                         tag : 'div',
29254                         cls : 'column',
29255                         cn : managerWidget
29256                     }
29257                 ];
29258                 
29259                 if(this.labelWidth > 12){
29260                     content[0].style = "width: " + this.labelWidth + 'px';
29261                 }
29262
29263                 if(this.labelWidth < 13 && this.labelmd == 0){
29264                     this.labelmd = this.labelWidth;
29265                 }
29266
29267                 if(this.labellg > 0){
29268                     content[0].cls += ' col-lg-' + this.labellg;
29269                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29270                 }
29271
29272                 if(this.labelmd > 0){
29273                     content[0].cls += ' col-md-' + this.labelmd;
29274                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29275                 }
29276
29277                 if(this.labelsm > 0){
29278                     content[0].cls += ' col-sm-' + this.labelsm;
29279                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29280                 }
29281
29282                 if(this.labelxs > 0){
29283                     content[0].cls += ' col-xs-' + this.labelxs;
29284                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29285                 }
29286                 
29287             }
29288         }
29289         
29290         var cfg = {
29291             tag : 'div',
29292             cls : 'row clearfix',
29293             cn : content
29294         };
29295         
29296         return cfg;
29297         
29298     },
29299     
29300     initEvents : function()
29301     {
29302         this.managerEl = this.el.select('.roo-document-manager', true).first();
29303         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29304         
29305         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29306         this.selectorEl.hide();
29307         
29308         if(this.multiple){
29309             this.selectorEl.attr('multiple', 'multiple');
29310         }
29311         
29312         this.selectorEl.on('change', this.onFileSelected, this);
29313         
29314         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29315         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29316         
29317         this.uploader.on('click', this.onUploaderClick, this);
29318         
29319         this.renderProgressDialog();
29320         
29321         var _this = this;
29322         
29323         window.addEventListener("resize", function() { _this.refresh(); } );
29324         
29325         this.fireEvent('initial', this);
29326     },
29327     
29328     renderProgressDialog : function()
29329     {
29330         var _this = this;
29331         
29332         this.progressDialog = new Roo.bootstrap.Modal({
29333             cls : 'roo-document-manager-progress-dialog',
29334             allow_close : false,
29335             animate : false,
29336             title : '',
29337             buttons : [
29338                 {
29339                     name  :'cancel',
29340                     weight : 'danger',
29341                     html : 'Cancel'
29342                 }
29343             ], 
29344             listeners : { 
29345                 btnclick : function() {
29346                     _this.uploadCancel();
29347                     this.hide();
29348                 }
29349             }
29350         });
29351          
29352         this.progressDialog.render(Roo.get(document.body));
29353          
29354         this.progress = new Roo.bootstrap.Progress({
29355             cls : 'roo-document-manager-progress',
29356             active : true,
29357             striped : true
29358         });
29359         
29360         this.progress.render(this.progressDialog.getChildContainer());
29361         
29362         this.progressBar = new Roo.bootstrap.ProgressBar({
29363             cls : 'roo-document-manager-progress-bar',
29364             aria_valuenow : 0,
29365             aria_valuemin : 0,
29366             aria_valuemax : 12,
29367             panel : 'success'
29368         });
29369         
29370         this.progressBar.render(this.progress.getChildContainer());
29371     },
29372     
29373     onUploaderClick : function(e)
29374     {
29375         e.preventDefault();
29376      
29377         if(this.fireEvent('beforeselectfile', this) != false){
29378             this.selectorEl.dom.click();
29379         }
29380         
29381     },
29382     
29383     onFileSelected : function(e)
29384     {
29385         e.preventDefault();
29386         
29387         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29388             return;
29389         }
29390         
29391         Roo.each(this.selectorEl.dom.files, function(file){
29392             if(this.fireEvent('inspect', this, file) != false){
29393                 this.files.push(file);
29394             }
29395         }, this);
29396         
29397         this.queue();
29398         
29399     },
29400     
29401     queue : function()
29402     {
29403         this.selectorEl.dom.value = '';
29404         
29405         if(!this.files || !this.files.length){
29406             return;
29407         }
29408         
29409         if(this.boxes > 0 && this.files.length > this.boxes){
29410             this.files = this.files.slice(0, this.boxes);
29411         }
29412         
29413         this.uploader.show();
29414         
29415         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29416             this.uploader.hide();
29417         }
29418         
29419         var _this = this;
29420         
29421         var files = [];
29422         
29423         var docs = [];
29424         
29425         Roo.each(this.files, function(file){
29426             
29427             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29428                 var f = this.renderPreview(file);
29429                 files.push(f);
29430                 return;
29431             }
29432             
29433             if(file.type.indexOf('image') != -1){
29434                 this.delegates.push(
29435                     (function(){
29436                         _this.process(file);
29437                     }).createDelegate(this)
29438                 );
29439         
29440                 return;
29441             }
29442             
29443             docs.push(
29444                 (function(){
29445                     _this.process(file);
29446                 }).createDelegate(this)
29447             );
29448             
29449         }, this);
29450         
29451         this.files = files;
29452         
29453         this.delegates = this.delegates.concat(docs);
29454         
29455         if(!this.delegates.length){
29456             this.refresh();
29457             return;
29458         }
29459         
29460         this.progressBar.aria_valuemax = this.delegates.length;
29461         
29462         this.arrange();
29463         
29464         return;
29465     },
29466     
29467     arrange : function()
29468     {
29469         if(!this.delegates.length){
29470             this.progressDialog.hide();
29471             this.refresh();
29472             return;
29473         }
29474         
29475         var delegate = this.delegates.shift();
29476         
29477         this.progressDialog.show();
29478         
29479         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29480         
29481         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29482         
29483         delegate();
29484     },
29485     
29486     refresh : function()
29487     {
29488         this.uploader.show();
29489         
29490         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29491             this.uploader.hide();
29492         }
29493         
29494         Roo.isTouch ? this.closable(false) : this.closable(true);
29495         
29496         this.fireEvent('refresh', this);
29497     },
29498     
29499     onRemove : function(e, el, o)
29500     {
29501         e.preventDefault();
29502         
29503         this.fireEvent('remove', this, o);
29504         
29505     },
29506     
29507     remove : function(o)
29508     {
29509         var files = [];
29510         
29511         Roo.each(this.files, function(file){
29512             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29513                 files.push(file);
29514                 return;
29515             }
29516
29517             o.target.remove();
29518
29519         }, this);
29520         
29521         this.files = files;
29522         
29523         this.refresh();
29524     },
29525     
29526     clear : function()
29527     {
29528         Roo.each(this.files, function(file){
29529             if(!file.target){
29530                 return;
29531             }
29532             
29533             file.target.remove();
29534
29535         }, this);
29536         
29537         this.files = [];
29538         
29539         this.refresh();
29540     },
29541     
29542     onClick : function(e, el, o)
29543     {
29544         e.preventDefault();
29545         
29546         this.fireEvent('click', this, o);
29547         
29548     },
29549     
29550     closable : function(closable)
29551     {
29552         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29553             
29554             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29555             
29556             if(closable){
29557                 el.show();
29558                 return;
29559             }
29560             
29561             el.hide();
29562             
29563         }, this);
29564     },
29565     
29566     xhrOnLoad : function(xhr)
29567     {
29568         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29569             el.remove();
29570         }, this);
29571         
29572         if (xhr.readyState !== 4) {
29573             this.arrange();
29574             this.fireEvent('exception', this, xhr);
29575             return;
29576         }
29577
29578         var response = Roo.decode(xhr.responseText);
29579         
29580         if(!response.success){
29581             this.arrange();
29582             this.fireEvent('exception', this, xhr);
29583             return;
29584         }
29585         
29586         var file = this.renderPreview(response.data);
29587         
29588         this.files.push(file);
29589         
29590         this.arrange();
29591         
29592         this.fireEvent('afterupload', this, xhr);
29593         
29594     },
29595     
29596     xhrOnError : function(xhr)
29597     {
29598         Roo.log('xhr on error');
29599         
29600         var response = Roo.decode(xhr.responseText);
29601           
29602         Roo.log(response);
29603         
29604         this.arrange();
29605     },
29606     
29607     process : function(file)
29608     {
29609         if(this.fireEvent('process', this, file) !== false){
29610             if(this.editable && file.type.indexOf('image') != -1){
29611                 this.fireEvent('edit', this, file);
29612                 return;
29613             }
29614
29615             this.uploadStart(file, false);
29616
29617             return;
29618         }
29619         
29620     },
29621     
29622     uploadStart : function(file, crop)
29623     {
29624         this.xhr = new XMLHttpRequest();
29625         
29626         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29627             this.arrange();
29628             return;
29629         }
29630         
29631         file.xhr = this.xhr;
29632             
29633         this.managerEl.createChild({
29634             tag : 'div',
29635             cls : 'roo-document-manager-loading',
29636             cn : [
29637                 {
29638                     tag : 'div',
29639                     tooltip : file.name,
29640                     cls : 'roo-document-manager-thumb',
29641                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29642                 }
29643             ]
29644
29645         });
29646
29647         this.xhr.open(this.method, this.url, true);
29648         
29649         var headers = {
29650             "Accept": "application/json",
29651             "Cache-Control": "no-cache",
29652             "X-Requested-With": "XMLHttpRequest"
29653         };
29654         
29655         for (var headerName in headers) {
29656             var headerValue = headers[headerName];
29657             if (headerValue) {
29658                 this.xhr.setRequestHeader(headerName, headerValue);
29659             }
29660         }
29661         
29662         var _this = this;
29663         
29664         this.xhr.onload = function()
29665         {
29666             _this.xhrOnLoad(_this.xhr);
29667         }
29668         
29669         this.xhr.onerror = function()
29670         {
29671             _this.xhrOnError(_this.xhr);
29672         }
29673         
29674         var formData = new FormData();
29675
29676         formData.append('returnHTML', 'NO');
29677         
29678         if(crop){
29679             formData.append('crop', crop);
29680         }
29681         
29682         formData.append(this.paramName, file, file.name);
29683         
29684         var options = {
29685             file : file, 
29686             manually : false
29687         };
29688         
29689         if(this.fireEvent('prepare', this, formData, options) != false){
29690             
29691             if(options.manually){
29692                 return;
29693             }
29694             
29695             this.xhr.send(formData);
29696             return;
29697         };
29698         
29699         this.uploadCancel();
29700     },
29701     
29702     uploadCancel : function()
29703     {
29704         if (this.xhr) {
29705             this.xhr.abort();
29706         }
29707         
29708         this.delegates = [];
29709         
29710         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29711             el.remove();
29712         }, this);
29713         
29714         this.arrange();
29715     },
29716     
29717     renderPreview : function(file)
29718     {
29719         if(typeof(file.target) != 'undefined' && file.target){
29720             return file;
29721         }
29722         
29723         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29724         
29725         var previewEl = this.managerEl.createChild({
29726             tag : 'div',
29727             cls : 'roo-document-manager-preview',
29728             cn : [
29729                 {
29730                     tag : 'div',
29731                     tooltip : file[this.toolTipName],
29732                     cls : 'roo-document-manager-thumb',
29733                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29734                 },
29735                 {
29736                     tag : 'button',
29737                     cls : 'close',
29738                     html : '<i class="fa fa-times-circle"></i>'
29739                 }
29740             ]
29741         });
29742
29743         var close = previewEl.select('button.close', true).first();
29744
29745         close.on('click', this.onRemove, this, file);
29746
29747         file.target = previewEl;
29748
29749         var image = previewEl.select('img', true).first();
29750         
29751         var _this = this;
29752         
29753         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29754         
29755         image.on('click', this.onClick, this, file);
29756         
29757         this.fireEvent('previewrendered', this, file);
29758         
29759         return file;
29760         
29761     },
29762     
29763     onPreviewLoad : function(file, image)
29764     {
29765         if(typeof(file.target) == 'undefined' || !file.target){
29766             return;
29767         }
29768         
29769         var width = image.dom.naturalWidth || image.dom.width;
29770         var height = image.dom.naturalHeight || image.dom.height;
29771         
29772         if(!this.previewResize) {
29773             return;
29774         }
29775         
29776         if(width > height){
29777             file.target.addClass('wide');
29778             return;
29779         }
29780         
29781         file.target.addClass('tall');
29782         return;
29783         
29784     },
29785     
29786     uploadFromSource : function(file, crop)
29787     {
29788         this.xhr = new XMLHttpRequest();
29789         
29790         this.managerEl.createChild({
29791             tag : 'div',
29792             cls : 'roo-document-manager-loading',
29793             cn : [
29794                 {
29795                     tag : 'div',
29796                     tooltip : file.name,
29797                     cls : 'roo-document-manager-thumb',
29798                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29799                 }
29800             ]
29801
29802         });
29803
29804         this.xhr.open(this.method, this.url, true);
29805         
29806         var headers = {
29807             "Accept": "application/json",
29808             "Cache-Control": "no-cache",
29809             "X-Requested-With": "XMLHttpRequest"
29810         };
29811         
29812         for (var headerName in headers) {
29813             var headerValue = headers[headerName];
29814             if (headerValue) {
29815                 this.xhr.setRequestHeader(headerName, headerValue);
29816             }
29817         }
29818         
29819         var _this = this;
29820         
29821         this.xhr.onload = function()
29822         {
29823             _this.xhrOnLoad(_this.xhr);
29824         }
29825         
29826         this.xhr.onerror = function()
29827         {
29828             _this.xhrOnError(_this.xhr);
29829         }
29830         
29831         var formData = new FormData();
29832
29833         formData.append('returnHTML', 'NO');
29834         
29835         formData.append('crop', crop);
29836         
29837         if(typeof(file.filename) != 'undefined'){
29838             formData.append('filename', file.filename);
29839         }
29840         
29841         if(typeof(file.mimetype) != 'undefined'){
29842             formData.append('mimetype', file.mimetype);
29843         }
29844         
29845         Roo.log(formData);
29846         
29847         if(this.fireEvent('prepare', this, formData) != false){
29848             this.xhr.send(formData);
29849         };
29850     }
29851 });
29852
29853 /*
29854 * Licence: LGPL
29855 */
29856
29857 /**
29858  * @class Roo.bootstrap.DocumentViewer
29859  * @extends Roo.bootstrap.Component
29860  * Bootstrap DocumentViewer class
29861  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29862  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29863  * 
29864  * @constructor
29865  * Create a new DocumentViewer
29866  * @param {Object} config The config object
29867  */
29868
29869 Roo.bootstrap.DocumentViewer = function(config){
29870     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29871     
29872     this.addEvents({
29873         /**
29874          * @event initial
29875          * Fire after initEvent
29876          * @param {Roo.bootstrap.DocumentViewer} this
29877          */
29878         "initial" : true,
29879         /**
29880          * @event click
29881          * Fire after click
29882          * @param {Roo.bootstrap.DocumentViewer} this
29883          */
29884         "click" : true,
29885         /**
29886          * @event download
29887          * Fire after download button
29888          * @param {Roo.bootstrap.DocumentViewer} this
29889          */
29890         "download" : true,
29891         /**
29892          * @event trash
29893          * Fire after trash button
29894          * @param {Roo.bootstrap.DocumentViewer} this
29895          */
29896         "trash" : true
29897         
29898     });
29899 };
29900
29901 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29902     
29903     showDownload : true,
29904     
29905     showTrash : true,
29906     
29907     getAutoCreate : function()
29908     {
29909         var cfg = {
29910             tag : 'div',
29911             cls : 'roo-document-viewer',
29912             cn : [
29913                 {
29914                     tag : 'div',
29915                     cls : 'roo-document-viewer-body',
29916                     cn : [
29917                         {
29918                             tag : 'div',
29919                             cls : 'roo-document-viewer-thumb',
29920                             cn : [
29921                                 {
29922                                     tag : 'img',
29923                                     cls : 'roo-document-viewer-image'
29924                                 }
29925                             ]
29926                         }
29927                     ]
29928                 },
29929                 {
29930                     tag : 'div',
29931                     cls : 'roo-document-viewer-footer',
29932                     cn : {
29933                         tag : 'div',
29934                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29935                         cn : [
29936                             {
29937                                 tag : 'div',
29938                                 cls : 'btn-group roo-document-viewer-download',
29939                                 cn : [
29940                                     {
29941                                         tag : 'button',
29942                                         cls : 'btn btn-default',
29943                                         html : '<i class="fa fa-download"></i>'
29944                                     }
29945                                 ]
29946                             },
29947                             {
29948                                 tag : 'div',
29949                                 cls : 'btn-group roo-document-viewer-trash',
29950                                 cn : [
29951                                     {
29952                                         tag : 'button',
29953                                         cls : 'btn btn-default',
29954                                         html : '<i class="fa fa-trash"></i>'
29955                                     }
29956                                 ]
29957                             }
29958                         ]
29959                     }
29960                 }
29961             ]
29962         };
29963         
29964         return cfg;
29965     },
29966     
29967     initEvents : function()
29968     {
29969         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29970         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29971         
29972         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29973         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29974         
29975         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29976         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29977         
29978         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29979         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29980         
29981         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29982         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29983         
29984         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29985         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29986         
29987         this.bodyEl.on('click', this.onClick, this);
29988         this.downloadBtn.on('click', this.onDownload, this);
29989         this.trashBtn.on('click', this.onTrash, this);
29990         
29991         this.downloadBtn.hide();
29992         this.trashBtn.hide();
29993         
29994         if(this.showDownload){
29995             this.downloadBtn.show();
29996         }
29997         
29998         if(this.showTrash){
29999             this.trashBtn.show();
30000         }
30001         
30002         if(!this.showDownload && !this.showTrash) {
30003             this.footerEl.hide();
30004         }
30005         
30006     },
30007     
30008     initial : function()
30009     {
30010         this.fireEvent('initial', this);
30011         
30012     },
30013     
30014     onClick : function(e)
30015     {
30016         e.preventDefault();
30017         
30018         this.fireEvent('click', this);
30019     },
30020     
30021     onDownload : function(e)
30022     {
30023         e.preventDefault();
30024         
30025         this.fireEvent('download', this);
30026     },
30027     
30028     onTrash : function(e)
30029     {
30030         e.preventDefault();
30031         
30032         this.fireEvent('trash', this);
30033     }
30034     
30035 });
30036 /*
30037  * - LGPL
30038  *
30039  * nav progress bar
30040  * 
30041  */
30042
30043 /**
30044  * @class Roo.bootstrap.NavProgressBar
30045  * @extends Roo.bootstrap.Component
30046  * Bootstrap NavProgressBar class
30047  * 
30048  * @constructor
30049  * Create a new nav progress bar
30050  * @param {Object} config The config object
30051  */
30052
30053 Roo.bootstrap.NavProgressBar = function(config){
30054     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30055
30056     this.bullets = this.bullets || [];
30057    
30058 //    Roo.bootstrap.NavProgressBar.register(this);
30059      this.addEvents({
30060         /**
30061              * @event changed
30062              * Fires when the active item changes
30063              * @param {Roo.bootstrap.NavProgressBar} this
30064              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30065              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30066          */
30067         'changed': true
30068      });
30069     
30070 };
30071
30072 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30073     
30074     bullets : [],
30075     barItems : [],
30076     
30077     getAutoCreate : function()
30078     {
30079         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30080         
30081         cfg = {
30082             tag : 'div',
30083             cls : 'roo-navigation-bar-group',
30084             cn : [
30085                 {
30086                     tag : 'div',
30087                     cls : 'roo-navigation-top-bar'
30088                 },
30089                 {
30090                     tag : 'div',
30091                     cls : 'roo-navigation-bullets-bar',
30092                     cn : [
30093                         {
30094                             tag : 'ul',
30095                             cls : 'roo-navigation-bar'
30096                         }
30097                     ]
30098                 },
30099                 
30100                 {
30101                     tag : 'div',
30102                     cls : 'roo-navigation-bottom-bar'
30103                 }
30104             ]
30105             
30106         };
30107         
30108         return cfg;
30109         
30110     },
30111     
30112     initEvents: function() 
30113     {
30114         
30115     },
30116     
30117     onRender : function(ct, position) 
30118     {
30119         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30120         
30121         if(this.bullets.length){
30122             Roo.each(this.bullets, function(b){
30123                this.addItem(b);
30124             }, this);
30125         }
30126         
30127         this.format();
30128         
30129     },
30130     
30131     addItem : function(cfg)
30132     {
30133         var item = new Roo.bootstrap.NavProgressItem(cfg);
30134         
30135         item.parentId = this.id;
30136         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30137         
30138         if(cfg.html){
30139             var top = new Roo.bootstrap.Element({
30140                 tag : 'div',
30141                 cls : 'roo-navigation-bar-text'
30142             });
30143             
30144             var bottom = new Roo.bootstrap.Element({
30145                 tag : 'div',
30146                 cls : 'roo-navigation-bar-text'
30147             });
30148             
30149             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30150             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30151             
30152             var topText = new Roo.bootstrap.Element({
30153                 tag : 'span',
30154                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30155             });
30156             
30157             var bottomText = new Roo.bootstrap.Element({
30158                 tag : 'span',
30159                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30160             });
30161             
30162             topText.onRender(top.el, null);
30163             bottomText.onRender(bottom.el, null);
30164             
30165             item.topEl = top;
30166             item.bottomEl = bottom;
30167         }
30168         
30169         this.barItems.push(item);
30170         
30171         return item;
30172     },
30173     
30174     getActive : function()
30175     {
30176         var active = false;
30177         
30178         Roo.each(this.barItems, function(v){
30179             
30180             if (!v.isActive()) {
30181                 return;
30182             }
30183             
30184             active = v;
30185             return false;
30186             
30187         });
30188         
30189         return active;
30190     },
30191     
30192     setActiveItem : function(item)
30193     {
30194         var prev = false;
30195         
30196         Roo.each(this.barItems, function(v){
30197             if (v.rid == item.rid) {
30198                 return ;
30199             }
30200             
30201             if (v.isActive()) {
30202                 v.setActive(false);
30203                 prev = v;
30204             }
30205         });
30206
30207         item.setActive(true);
30208         
30209         this.fireEvent('changed', this, item, prev);
30210     },
30211     
30212     getBarItem: function(rid)
30213     {
30214         var ret = false;
30215         
30216         Roo.each(this.barItems, function(e) {
30217             if (e.rid != rid) {
30218                 return;
30219             }
30220             
30221             ret =  e;
30222             return false;
30223         });
30224         
30225         return ret;
30226     },
30227     
30228     indexOfItem : function(item)
30229     {
30230         var index = false;
30231         
30232         Roo.each(this.barItems, function(v, i){
30233             
30234             if (v.rid != item.rid) {
30235                 return;
30236             }
30237             
30238             index = i;
30239             return false
30240         });
30241         
30242         return index;
30243     },
30244     
30245     setActiveNext : function()
30246     {
30247         var i = this.indexOfItem(this.getActive());
30248         
30249         if (i > this.barItems.length) {
30250             return;
30251         }
30252         
30253         this.setActiveItem(this.barItems[i+1]);
30254     },
30255     
30256     setActivePrev : function()
30257     {
30258         var i = this.indexOfItem(this.getActive());
30259         
30260         if (i  < 1) {
30261             return;
30262         }
30263         
30264         this.setActiveItem(this.barItems[i-1]);
30265     },
30266     
30267     format : function()
30268     {
30269         if(!this.barItems.length){
30270             return;
30271         }
30272      
30273         var width = 100 / this.barItems.length;
30274         
30275         Roo.each(this.barItems, function(i){
30276             i.el.setStyle('width', width + '%');
30277             i.topEl.el.setStyle('width', width + '%');
30278             i.bottomEl.el.setStyle('width', width + '%');
30279         }, this);
30280         
30281     }
30282     
30283 });
30284 /*
30285  * - LGPL
30286  *
30287  * Nav Progress Item
30288  * 
30289  */
30290
30291 /**
30292  * @class Roo.bootstrap.NavProgressItem
30293  * @extends Roo.bootstrap.Component
30294  * Bootstrap NavProgressItem class
30295  * @cfg {String} rid the reference id
30296  * @cfg {Boolean} active (true|false) Is item active default false
30297  * @cfg {Boolean} disabled (true|false) Is item active default false
30298  * @cfg {String} html
30299  * @cfg {String} position (top|bottom) text position default bottom
30300  * @cfg {String} icon show icon instead of number
30301  * 
30302  * @constructor
30303  * Create a new NavProgressItem
30304  * @param {Object} config The config object
30305  */
30306 Roo.bootstrap.NavProgressItem = function(config){
30307     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30308     this.addEvents({
30309         // raw events
30310         /**
30311          * @event click
30312          * The raw click event for the entire grid.
30313          * @param {Roo.bootstrap.NavProgressItem} this
30314          * @param {Roo.EventObject} e
30315          */
30316         "click" : true
30317     });
30318    
30319 };
30320
30321 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30322     
30323     rid : '',
30324     active : false,
30325     disabled : false,
30326     html : '',
30327     position : 'bottom',
30328     icon : false,
30329     
30330     getAutoCreate : function()
30331     {
30332         var iconCls = 'roo-navigation-bar-item-icon';
30333         
30334         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30335         
30336         var cfg = {
30337             tag: 'li',
30338             cls: 'roo-navigation-bar-item',
30339             cn : [
30340                 {
30341                     tag : 'i',
30342                     cls : iconCls
30343                 }
30344             ]
30345         };
30346         
30347         if(this.active){
30348             cfg.cls += ' active';
30349         }
30350         if(this.disabled){
30351             cfg.cls += ' disabled';
30352         }
30353         
30354         return cfg;
30355     },
30356     
30357     disable : function()
30358     {
30359         this.setDisabled(true);
30360     },
30361     
30362     enable : function()
30363     {
30364         this.setDisabled(false);
30365     },
30366     
30367     initEvents: function() 
30368     {
30369         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30370         
30371         this.iconEl.on('click', this.onClick, this);
30372     },
30373     
30374     onClick : function(e)
30375     {
30376         e.preventDefault();
30377         
30378         if(this.disabled){
30379             return;
30380         }
30381         
30382         if(this.fireEvent('click', this, e) === false){
30383             return;
30384         };
30385         
30386         this.parent().setActiveItem(this);
30387     },
30388     
30389     isActive: function () 
30390     {
30391         return this.active;
30392     },
30393     
30394     setActive : function(state)
30395     {
30396         if(this.active == state){
30397             return;
30398         }
30399         
30400         this.active = state;
30401         
30402         if (state) {
30403             this.el.addClass('active');
30404             return;
30405         }
30406         
30407         this.el.removeClass('active');
30408         
30409         return;
30410     },
30411     
30412     setDisabled : function(state)
30413     {
30414         if(this.disabled == state){
30415             return;
30416         }
30417         
30418         this.disabled = state;
30419         
30420         if (state) {
30421             this.el.addClass('disabled');
30422             return;
30423         }
30424         
30425         this.el.removeClass('disabled');
30426     },
30427     
30428     tooltipEl : function()
30429     {
30430         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30431     }
30432 });
30433  
30434
30435  /*
30436  * - LGPL
30437  *
30438  * FieldLabel
30439  * 
30440  */
30441
30442 /**
30443  * @class Roo.bootstrap.FieldLabel
30444  * @extends Roo.bootstrap.Component
30445  * Bootstrap FieldLabel class
30446  * @cfg {String} html contents of the element
30447  * @cfg {String} tag tag of the element default label
30448  * @cfg {String} cls class of the element
30449  * @cfg {String} target label target 
30450  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30451  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30452  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30453  * @cfg {String} iconTooltip default "This field is required"
30454  * @cfg {String} indicatorpos (left|right) default left
30455  * 
30456  * @constructor
30457  * Create a new FieldLabel
30458  * @param {Object} config The config object
30459  */
30460
30461 Roo.bootstrap.FieldLabel = function(config){
30462     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30463     
30464     this.addEvents({
30465             /**
30466              * @event invalid
30467              * Fires after the field has been marked as invalid.
30468              * @param {Roo.form.FieldLabel} this
30469              * @param {String} msg The validation message
30470              */
30471             invalid : true,
30472             /**
30473              * @event valid
30474              * Fires after the field has been validated with no errors.
30475              * @param {Roo.form.FieldLabel} this
30476              */
30477             valid : true
30478         });
30479 };
30480
30481 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30482     
30483     tag: 'label',
30484     cls: '',
30485     html: '',
30486     target: '',
30487     allowBlank : true,
30488     invalidClass : 'has-warning',
30489     validClass : 'has-success',
30490     iconTooltip : 'This field is required',
30491     indicatorpos : 'left',
30492     
30493     getAutoCreate : function(){
30494         
30495         var cls = "";
30496         if (!this.allowBlank) {
30497             cls  = "visible";
30498         }
30499         
30500         var cfg = {
30501             tag : this.tag,
30502             cls : 'roo-bootstrap-field-label ' + this.cls,
30503             for : this.target,
30504             cn : [
30505                 {
30506                     tag : 'i',
30507                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30508                     tooltip : this.iconTooltip
30509                 },
30510                 {
30511                     tag : 'span',
30512                     html : this.html
30513                 }
30514             ] 
30515         };
30516         
30517         if(this.indicatorpos == 'right'){
30518             var cfg = {
30519                 tag : this.tag,
30520                 cls : 'roo-bootstrap-field-label ' + this.cls,
30521                 for : this.target,
30522                 cn : [
30523                     {
30524                         tag : 'span',
30525                         html : this.html
30526                     },
30527                     {
30528                         tag : 'i',
30529                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30530                         tooltip : this.iconTooltip
30531                     }
30532                 ] 
30533             };
30534         }
30535         
30536         return cfg;
30537     },
30538     
30539     initEvents: function() 
30540     {
30541         Roo.bootstrap.Element.superclass.initEvents.call(this);
30542         
30543         this.indicator = this.indicatorEl();
30544         
30545         if(this.indicator){
30546             this.indicator.removeClass('visible');
30547             this.indicator.addClass('invisible');
30548         }
30549         
30550         Roo.bootstrap.FieldLabel.register(this);
30551     },
30552     
30553     indicatorEl : function()
30554     {
30555         var indicator = this.el.select('i.roo-required-indicator',true).first();
30556         
30557         if(!indicator){
30558             return false;
30559         }
30560         
30561         return indicator;
30562         
30563     },
30564     
30565     /**
30566      * Mark this field as valid
30567      */
30568     markValid : function()
30569     {
30570         if(this.indicator){
30571             this.indicator.removeClass('visible');
30572             this.indicator.addClass('invisible');
30573         }
30574         if (Roo.bootstrap.version == 3) {
30575             this.el.removeClass(this.invalidClass);
30576             this.el.addClass(this.validClass);
30577         } else {
30578             this.el.removeClass('is-invalid');
30579             this.el.addClass('is-valid');
30580         }
30581         
30582         
30583         this.fireEvent('valid', this);
30584     },
30585     
30586     /**
30587      * Mark this field as invalid
30588      * @param {String} msg The validation message
30589      */
30590     markInvalid : function(msg)
30591     {
30592         if(this.indicator){
30593             this.indicator.removeClass('invisible');
30594             this.indicator.addClass('visible');
30595         }
30596           if (Roo.bootstrap.version == 3) {
30597             this.el.removeClass(this.validClass);
30598             this.el.addClass(this.invalidClass);
30599         } else {
30600             this.el.removeClass('is-valid');
30601             this.el.addClass('is-invalid');
30602         }
30603         
30604         
30605         this.fireEvent('invalid', this, msg);
30606     }
30607     
30608    
30609 });
30610
30611 Roo.apply(Roo.bootstrap.FieldLabel, {
30612     
30613     groups: {},
30614     
30615      /**
30616     * register a FieldLabel Group
30617     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30618     */
30619     register : function(label)
30620     {
30621         if(this.groups.hasOwnProperty(label.target)){
30622             return;
30623         }
30624      
30625         this.groups[label.target] = label;
30626         
30627     },
30628     /**
30629     * fetch a FieldLabel Group based on the target
30630     * @param {string} target
30631     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30632     */
30633     get: function(target) {
30634         if (typeof(this.groups[target]) == 'undefined') {
30635             return false;
30636         }
30637         
30638         return this.groups[target] ;
30639     }
30640 });
30641
30642  
30643
30644  /*
30645  * - LGPL
30646  *
30647  * page DateSplitField.
30648  * 
30649  */
30650
30651
30652 /**
30653  * @class Roo.bootstrap.DateSplitField
30654  * @extends Roo.bootstrap.Component
30655  * Bootstrap DateSplitField class
30656  * @cfg {string} fieldLabel - the label associated
30657  * @cfg {Number} labelWidth set the width of label (0-12)
30658  * @cfg {String} labelAlign (top|left)
30659  * @cfg {Boolean} dayAllowBlank (true|false) default false
30660  * @cfg {Boolean} monthAllowBlank (true|false) default false
30661  * @cfg {Boolean} yearAllowBlank (true|false) default false
30662  * @cfg {string} dayPlaceholder 
30663  * @cfg {string} monthPlaceholder
30664  * @cfg {string} yearPlaceholder
30665  * @cfg {string} dayFormat default 'd'
30666  * @cfg {string} monthFormat default 'm'
30667  * @cfg {string} yearFormat default 'Y'
30668  * @cfg {Number} labellg set the width of label (1-12)
30669  * @cfg {Number} labelmd set the width of label (1-12)
30670  * @cfg {Number} labelsm set the width of label (1-12)
30671  * @cfg {Number} labelxs set the width of label (1-12)
30672
30673  *     
30674  * @constructor
30675  * Create a new DateSplitField
30676  * @param {Object} config The config object
30677  */
30678
30679 Roo.bootstrap.DateSplitField = function(config){
30680     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30681     
30682     this.addEvents({
30683         // raw events
30684          /**
30685          * @event years
30686          * getting the data of years
30687          * @param {Roo.bootstrap.DateSplitField} this
30688          * @param {Object} years
30689          */
30690         "years" : true,
30691         /**
30692          * @event days
30693          * getting the data of days
30694          * @param {Roo.bootstrap.DateSplitField} this
30695          * @param {Object} days
30696          */
30697         "days" : true,
30698         /**
30699          * @event invalid
30700          * Fires after the field has been marked as invalid.
30701          * @param {Roo.form.Field} this
30702          * @param {String} msg The validation message
30703          */
30704         invalid : true,
30705        /**
30706          * @event valid
30707          * Fires after the field has been validated with no errors.
30708          * @param {Roo.form.Field} this
30709          */
30710         valid : true
30711     });
30712 };
30713
30714 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30715     
30716     fieldLabel : '',
30717     labelAlign : 'top',
30718     labelWidth : 3,
30719     dayAllowBlank : false,
30720     monthAllowBlank : false,
30721     yearAllowBlank : false,
30722     dayPlaceholder : '',
30723     monthPlaceholder : '',
30724     yearPlaceholder : '',
30725     dayFormat : 'd',
30726     monthFormat : 'm',
30727     yearFormat : 'Y',
30728     isFormField : true,
30729     labellg : 0,
30730     labelmd : 0,
30731     labelsm : 0,
30732     labelxs : 0,
30733     
30734     getAutoCreate : function()
30735     {
30736         var cfg = {
30737             tag : 'div',
30738             cls : 'row roo-date-split-field-group',
30739             cn : [
30740                 {
30741                     tag : 'input',
30742                     type : 'hidden',
30743                     cls : 'form-hidden-field roo-date-split-field-group-value',
30744                     name : this.name
30745                 }
30746             ]
30747         };
30748         
30749         var labelCls = 'col-md-12';
30750         var contentCls = 'col-md-4';
30751         
30752         if(this.fieldLabel){
30753             
30754             var label = {
30755                 tag : 'div',
30756                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30757                 cn : [
30758                     {
30759                         tag : 'label',
30760                         html : this.fieldLabel
30761                     }
30762                 ]
30763             };
30764             
30765             if(this.labelAlign == 'left'){
30766             
30767                 if(this.labelWidth > 12){
30768                     label.style = "width: " + this.labelWidth + 'px';
30769                 }
30770
30771                 if(this.labelWidth < 13 && this.labelmd == 0){
30772                     this.labelmd = this.labelWidth;
30773                 }
30774
30775                 if(this.labellg > 0){
30776                     labelCls = ' col-lg-' + this.labellg;
30777                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30778                 }
30779
30780                 if(this.labelmd > 0){
30781                     labelCls = ' col-md-' + this.labelmd;
30782                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30783                 }
30784
30785                 if(this.labelsm > 0){
30786                     labelCls = ' col-sm-' + this.labelsm;
30787                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30788                 }
30789
30790                 if(this.labelxs > 0){
30791                     labelCls = ' col-xs-' + this.labelxs;
30792                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30793                 }
30794             }
30795             
30796             label.cls += ' ' + labelCls;
30797             
30798             cfg.cn.push(label);
30799         }
30800         
30801         Roo.each(['day', 'month', 'year'], function(t){
30802             cfg.cn.push({
30803                 tag : 'div',
30804                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30805             });
30806         }, this);
30807         
30808         return cfg;
30809     },
30810     
30811     inputEl: function ()
30812     {
30813         return this.el.select('.roo-date-split-field-group-value', true).first();
30814     },
30815     
30816     onRender : function(ct, position) 
30817     {
30818         var _this = this;
30819         
30820         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30821         
30822         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30823         
30824         this.dayField = new Roo.bootstrap.ComboBox({
30825             allowBlank : this.dayAllowBlank,
30826             alwaysQuery : true,
30827             displayField : 'value',
30828             editable : false,
30829             fieldLabel : '',
30830             forceSelection : true,
30831             mode : 'local',
30832             placeholder : this.dayPlaceholder,
30833             selectOnFocus : true,
30834             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30835             triggerAction : 'all',
30836             typeAhead : true,
30837             valueField : 'value',
30838             store : new Roo.data.SimpleStore({
30839                 data : (function() {    
30840                     var days = [];
30841                     _this.fireEvent('days', _this, days);
30842                     return days;
30843                 })(),
30844                 fields : [ 'value' ]
30845             }),
30846             listeners : {
30847                 select : function (_self, record, index)
30848                 {
30849                     _this.setValue(_this.getValue());
30850                 }
30851             }
30852         });
30853
30854         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30855         
30856         this.monthField = new Roo.bootstrap.MonthField({
30857             after : '<i class=\"fa fa-calendar\"></i>',
30858             allowBlank : this.monthAllowBlank,
30859             placeholder : this.monthPlaceholder,
30860             readOnly : true,
30861             listeners : {
30862                 render : function (_self)
30863                 {
30864                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30865                         e.preventDefault();
30866                         _self.focus();
30867                     });
30868                 },
30869                 select : function (_self, oldvalue, newvalue)
30870                 {
30871                     _this.setValue(_this.getValue());
30872                 }
30873             }
30874         });
30875         
30876         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30877         
30878         this.yearField = new Roo.bootstrap.ComboBox({
30879             allowBlank : this.yearAllowBlank,
30880             alwaysQuery : true,
30881             displayField : 'value',
30882             editable : false,
30883             fieldLabel : '',
30884             forceSelection : true,
30885             mode : 'local',
30886             placeholder : this.yearPlaceholder,
30887             selectOnFocus : true,
30888             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30889             triggerAction : 'all',
30890             typeAhead : true,
30891             valueField : 'value',
30892             store : new Roo.data.SimpleStore({
30893                 data : (function() {
30894                     var years = [];
30895                     _this.fireEvent('years', _this, years);
30896                     return years;
30897                 })(),
30898                 fields : [ 'value' ]
30899             }),
30900             listeners : {
30901                 select : function (_self, record, index)
30902                 {
30903                     _this.setValue(_this.getValue());
30904                 }
30905             }
30906         });
30907
30908         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30909     },
30910     
30911     setValue : function(v, format)
30912     {
30913         this.inputEl.dom.value = v;
30914         
30915         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30916         
30917         var d = Date.parseDate(v, f);
30918         
30919         if(!d){
30920             this.validate();
30921             return;
30922         }
30923         
30924         this.setDay(d.format(this.dayFormat));
30925         this.setMonth(d.format(this.monthFormat));
30926         this.setYear(d.format(this.yearFormat));
30927         
30928         this.validate();
30929         
30930         return;
30931     },
30932     
30933     setDay : function(v)
30934     {
30935         this.dayField.setValue(v);
30936         this.inputEl.dom.value = this.getValue();
30937         this.validate();
30938         return;
30939     },
30940     
30941     setMonth : function(v)
30942     {
30943         this.monthField.setValue(v, true);
30944         this.inputEl.dom.value = this.getValue();
30945         this.validate();
30946         return;
30947     },
30948     
30949     setYear : function(v)
30950     {
30951         this.yearField.setValue(v);
30952         this.inputEl.dom.value = this.getValue();
30953         this.validate();
30954         return;
30955     },
30956     
30957     getDay : function()
30958     {
30959         return this.dayField.getValue();
30960     },
30961     
30962     getMonth : function()
30963     {
30964         return this.monthField.getValue();
30965     },
30966     
30967     getYear : function()
30968     {
30969         return this.yearField.getValue();
30970     },
30971     
30972     getValue : function()
30973     {
30974         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30975         
30976         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30977         
30978         return date;
30979     },
30980     
30981     reset : function()
30982     {
30983         this.setDay('');
30984         this.setMonth('');
30985         this.setYear('');
30986         this.inputEl.dom.value = '';
30987         this.validate();
30988         return;
30989     },
30990     
30991     validate : function()
30992     {
30993         var d = this.dayField.validate();
30994         var m = this.monthField.validate();
30995         var y = this.yearField.validate();
30996         
30997         var valid = true;
30998         
30999         if(
31000                 (!this.dayAllowBlank && !d) ||
31001                 (!this.monthAllowBlank && !m) ||
31002                 (!this.yearAllowBlank && !y)
31003         ){
31004             valid = false;
31005         }
31006         
31007         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31008             return valid;
31009         }
31010         
31011         if(valid){
31012             this.markValid();
31013             return valid;
31014         }
31015         
31016         this.markInvalid();
31017         
31018         return valid;
31019     },
31020     
31021     markValid : function()
31022     {
31023         
31024         var label = this.el.select('label', true).first();
31025         var icon = this.el.select('i.fa-star', true).first();
31026
31027         if(label && icon){
31028             icon.remove();
31029         }
31030         
31031         this.fireEvent('valid', this);
31032     },
31033     
31034      /**
31035      * Mark this field as invalid
31036      * @param {String} msg The validation message
31037      */
31038     markInvalid : function(msg)
31039     {
31040         
31041         var label = this.el.select('label', true).first();
31042         var icon = this.el.select('i.fa-star', true).first();
31043
31044         if(label && !icon){
31045             this.el.select('.roo-date-split-field-label', true).createChild({
31046                 tag : 'i',
31047                 cls : 'text-danger fa fa-lg fa-star',
31048                 tooltip : 'This field is required',
31049                 style : 'margin-right:5px;'
31050             }, label, true);
31051         }
31052         
31053         this.fireEvent('invalid', this, msg);
31054     },
31055     
31056     clearInvalid : function()
31057     {
31058         var label = this.el.select('label', true).first();
31059         var icon = this.el.select('i.fa-star', true).first();
31060
31061         if(label && icon){
31062             icon.remove();
31063         }
31064         
31065         this.fireEvent('valid', this);
31066     },
31067     
31068     getName: function()
31069     {
31070         return this.name;
31071     }
31072     
31073 });
31074
31075  /**
31076  *
31077  * This is based on 
31078  * http://masonry.desandro.com
31079  *
31080  * The idea is to render all the bricks based on vertical width...
31081  *
31082  * The original code extends 'outlayer' - we might need to use that....
31083  * 
31084  */
31085
31086
31087 /**
31088  * @class Roo.bootstrap.LayoutMasonry
31089  * @extends Roo.bootstrap.Component
31090  * Bootstrap Layout Masonry class
31091  * 
31092  * @constructor
31093  * Create a new Element
31094  * @param {Object} config The config object
31095  */
31096
31097 Roo.bootstrap.LayoutMasonry = function(config){
31098     
31099     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31100     
31101     this.bricks = [];
31102     
31103     Roo.bootstrap.LayoutMasonry.register(this);
31104     
31105     this.addEvents({
31106         // raw events
31107         /**
31108          * @event layout
31109          * Fire after layout the items
31110          * @param {Roo.bootstrap.LayoutMasonry} this
31111          * @param {Roo.EventObject} e
31112          */
31113         "layout" : true
31114     });
31115     
31116 };
31117
31118 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31119     
31120     /**
31121      * @cfg {Boolean} isLayoutInstant = no animation?
31122      */   
31123     isLayoutInstant : false, // needed?
31124    
31125     /**
31126      * @cfg {Number} boxWidth  width of the columns
31127      */   
31128     boxWidth : 450,
31129     
31130       /**
31131      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31132      */   
31133     boxHeight : 0,
31134     
31135     /**
31136      * @cfg {Number} padWidth padding below box..
31137      */   
31138     padWidth : 10, 
31139     
31140     /**
31141      * @cfg {Number} gutter gutter width..
31142      */   
31143     gutter : 10,
31144     
31145      /**
31146      * @cfg {Number} maxCols maximum number of columns
31147      */   
31148     
31149     maxCols: 0,
31150     
31151     /**
31152      * @cfg {Boolean} isAutoInitial defalut true
31153      */   
31154     isAutoInitial : true, 
31155     
31156     containerWidth: 0,
31157     
31158     /**
31159      * @cfg {Boolean} isHorizontal defalut false
31160      */   
31161     isHorizontal : false, 
31162
31163     currentSize : null,
31164     
31165     tag: 'div',
31166     
31167     cls: '',
31168     
31169     bricks: null, //CompositeElement
31170     
31171     cols : 1,
31172     
31173     _isLayoutInited : false,
31174     
31175 //    isAlternative : false, // only use for vertical layout...
31176     
31177     /**
31178      * @cfg {Number} alternativePadWidth padding below box..
31179      */   
31180     alternativePadWidth : 50,
31181     
31182     selectedBrick : [],
31183     
31184     getAutoCreate : function(){
31185         
31186         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31187         
31188         var cfg = {
31189             tag: this.tag,
31190             cls: 'blog-masonary-wrapper ' + this.cls,
31191             cn : {
31192                 cls : 'mas-boxes masonary'
31193             }
31194         };
31195         
31196         return cfg;
31197     },
31198     
31199     getChildContainer: function( )
31200     {
31201         if (this.boxesEl) {
31202             return this.boxesEl;
31203         }
31204         
31205         this.boxesEl = this.el.select('.mas-boxes').first();
31206         
31207         return this.boxesEl;
31208     },
31209     
31210     
31211     initEvents : function()
31212     {
31213         var _this = this;
31214         
31215         if(this.isAutoInitial){
31216             Roo.log('hook children rendered');
31217             this.on('childrenrendered', function() {
31218                 Roo.log('children rendered');
31219                 _this.initial();
31220             } ,this);
31221         }
31222     },
31223     
31224     initial : function()
31225     {
31226         this.selectedBrick = [];
31227         
31228         this.currentSize = this.el.getBox(true);
31229         
31230         Roo.EventManager.onWindowResize(this.resize, this); 
31231
31232         if(!this.isAutoInitial){
31233             this.layout();
31234             return;
31235         }
31236         
31237         this.layout();
31238         
31239         return;
31240         //this.layout.defer(500,this);
31241         
31242     },
31243     
31244     resize : function()
31245     {
31246         var cs = this.el.getBox(true);
31247         
31248         if (
31249                 this.currentSize.width == cs.width && 
31250                 this.currentSize.x == cs.x && 
31251                 this.currentSize.height == cs.height && 
31252                 this.currentSize.y == cs.y 
31253         ) {
31254             Roo.log("no change in with or X or Y");
31255             return;
31256         }
31257         
31258         this.currentSize = cs;
31259         
31260         this.layout();
31261         
31262     },
31263     
31264     layout : function()
31265     {   
31266         this._resetLayout();
31267         
31268         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31269         
31270         this.layoutItems( isInstant );
31271       
31272         this._isLayoutInited = true;
31273         
31274         this.fireEvent('layout', this);
31275         
31276     },
31277     
31278     _resetLayout : function()
31279     {
31280         if(this.isHorizontal){
31281             this.horizontalMeasureColumns();
31282             return;
31283         }
31284         
31285         this.verticalMeasureColumns();
31286         
31287     },
31288     
31289     verticalMeasureColumns : function()
31290     {
31291         this.getContainerWidth();
31292         
31293 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31294 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31295 //            return;
31296 //        }
31297         
31298         var boxWidth = this.boxWidth + this.padWidth;
31299         
31300         if(this.containerWidth < this.boxWidth){
31301             boxWidth = this.containerWidth
31302         }
31303         
31304         var containerWidth = this.containerWidth;
31305         
31306         var cols = Math.floor(containerWidth / boxWidth);
31307         
31308         this.cols = Math.max( cols, 1 );
31309         
31310         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31311         
31312         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31313         
31314         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31315         
31316         this.colWidth = boxWidth + avail - this.padWidth;
31317         
31318         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31319         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31320     },
31321     
31322     horizontalMeasureColumns : function()
31323     {
31324         this.getContainerWidth();
31325         
31326         var boxWidth = this.boxWidth;
31327         
31328         if(this.containerWidth < boxWidth){
31329             boxWidth = this.containerWidth;
31330         }
31331         
31332         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31333         
31334         this.el.setHeight(boxWidth);
31335         
31336     },
31337     
31338     getContainerWidth : function()
31339     {
31340         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31341     },
31342     
31343     layoutItems : function( isInstant )
31344     {
31345         Roo.log(this.bricks);
31346         
31347         var items = Roo.apply([], this.bricks);
31348         
31349         if(this.isHorizontal){
31350             this._horizontalLayoutItems( items , isInstant );
31351             return;
31352         }
31353         
31354 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31355 //            this._verticalAlternativeLayoutItems( items , isInstant );
31356 //            return;
31357 //        }
31358         
31359         this._verticalLayoutItems( items , isInstant );
31360         
31361     },
31362     
31363     _verticalLayoutItems : function ( items , isInstant)
31364     {
31365         if ( !items || !items.length ) {
31366             return;
31367         }
31368         
31369         var standard = [
31370             ['xs', 'xs', 'xs', 'tall'],
31371             ['xs', 'xs', 'tall'],
31372             ['xs', 'xs', 'sm'],
31373             ['xs', 'xs', 'xs'],
31374             ['xs', 'tall'],
31375             ['xs', 'sm'],
31376             ['xs', 'xs'],
31377             ['xs'],
31378             
31379             ['sm', 'xs', 'xs'],
31380             ['sm', 'xs'],
31381             ['sm'],
31382             
31383             ['tall', 'xs', 'xs', 'xs'],
31384             ['tall', 'xs', 'xs'],
31385             ['tall', 'xs'],
31386             ['tall']
31387             
31388         ];
31389         
31390         var queue = [];
31391         
31392         var boxes = [];
31393         
31394         var box = [];
31395         
31396         Roo.each(items, function(item, k){
31397             
31398             switch (item.size) {
31399                 // these layouts take up a full box,
31400                 case 'md' :
31401                 case 'md-left' :
31402                 case 'md-right' :
31403                 case 'wide' :
31404                     
31405                     if(box.length){
31406                         boxes.push(box);
31407                         box = [];
31408                     }
31409                     
31410                     boxes.push([item]);
31411                     
31412                     break;
31413                     
31414                 case 'xs' :
31415                 case 'sm' :
31416                 case 'tall' :
31417                     
31418                     box.push(item);
31419                     
31420                     break;
31421                 default :
31422                     break;
31423                     
31424             }
31425             
31426         }, this);
31427         
31428         if(box.length){
31429             boxes.push(box);
31430             box = [];
31431         }
31432         
31433         var filterPattern = function(box, length)
31434         {
31435             if(!box.length){
31436                 return;
31437             }
31438             
31439             var match = false;
31440             
31441             var pattern = box.slice(0, length);
31442             
31443             var format = [];
31444             
31445             Roo.each(pattern, function(i){
31446                 format.push(i.size);
31447             }, this);
31448             
31449             Roo.each(standard, function(s){
31450                 
31451                 if(String(s) != String(format)){
31452                     return;
31453                 }
31454                 
31455                 match = true;
31456                 return false;
31457                 
31458             }, this);
31459             
31460             if(!match && length == 1){
31461                 return;
31462             }
31463             
31464             if(!match){
31465                 filterPattern(box, length - 1);
31466                 return;
31467             }
31468                 
31469             queue.push(pattern);
31470
31471             box = box.slice(length, box.length);
31472
31473             filterPattern(box, 4);
31474
31475             return;
31476             
31477         }
31478         
31479         Roo.each(boxes, function(box, k){
31480             
31481             if(!box.length){
31482                 return;
31483             }
31484             
31485             if(box.length == 1){
31486                 queue.push(box);
31487                 return;
31488             }
31489             
31490             filterPattern(box, 4);
31491             
31492         }, this);
31493         
31494         this._processVerticalLayoutQueue( queue, isInstant );
31495         
31496     },
31497     
31498 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31499 //    {
31500 //        if ( !items || !items.length ) {
31501 //            return;
31502 //        }
31503 //
31504 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31505 //        
31506 //    },
31507     
31508     _horizontalLayoutItems : function ( items , isInstant)
31509     {
31510         if ( !items || !items.length || items.length < 3) {
31511             return;
31512         }
31513         
31514         items.reverse();
31515         
31516         var eItems = items.slice(0, 3);
31517         
31518         items = items.slice(3, items.length);
31519         
31520         var standard = [
31521             ['xs', 'xs', 'xs', 'wide'],
31522             ['xs', 'xs', 'wide'],
31523             ['xs', 'xs', 'sm'],
31524             ['xs', 'xs', 'xs'],
31525             ['xs', 'wide'],
31526             ['xs', 'sm'],
31527             ['xs', 'xs'],
31528             ['xs'],
31529             
31530             ['sm', 'xs', 'xs'],
31531             ['sm', 'xs'],
31532             ['sm'],
31533             
31534             ['wide', 'xs', 'xs', 'xs'],
31535             ['wide', 'xs', 'xs'],
31536             ['wide', 'xs'],
31537             ['wide'],
31538             
31539             ['wide-thin']
31540         ];
31541         
31542         var queue = [];
31543         
31544         var boxes = [];
31545         
31546         var box = [];
31547         
31548         Roo.each(items, function(item, k){
31549             
31550             switch (item.size) {
31551                 case 'md' :
31552                 case 'md-left' :
31553                 case 'md-right' :
31554                 case 'tall' :
31555                     
31556                     if(box.length){
31557                         boxes.push(box);
31558                         box = [];
31559                     }
31560                     
31561                     boxes.push([item]);
31562                     
31563                     break;
31564                     
31565                 case 'xs' :
31566                 case 'sm' :
31567                 case 'wide' :
31568                 case 'wide-thin' :
31569                     
31570                     box.push(item);
31571                     
31572                     break;
31573                 default :
31574                     break;
31575                     
31576             }
31577             
31578         }, this);
31579         
31580         if(box.length){
31581             boxes.push(box);
31582             box = [];
31583         }
31584         
31585         var filterPattern = function(box, length)
31586         {
31587             if(!box.length){
31588                 return;
31589             }
31590             
31591             var match = false;
31592             
31593             var pattern = box.slice(0, length);
31594             
31595             var format = [];
31596             
31597             Roo.each(pattern, function(i){
31598                 format.push(i.size);
31599             }, this);
31600             
31601             Roo.each(standard, function(s){
31602                 
31603                 if(String(s) != String(format)){
31604                     return;
31605                 }
31606                 
31607                 match = true;
31608                 return false;
31609                 
31610             }, this);
31611             
31612             if(!match && length == 1){
31613                 return;
31614             }
31615             
31616             if(!match){
31617                 filterPattern(box, length - 1);
31618                 return;
31619             }
31620                 
31621             queue.push(pattern);
31622
31623             box = box.slice(length, box.length);
31624
31625             filterPattern(box, 4);
31626
31627             return;
31628             
31629         }
31630         
31631         Roo.each(boxes, function(box, k){
31632             
31633             if(!box.length){
31634                 return;
31635             }
31636             
31637             if(box.length == 1){
31638                 queue.push(box);
31639                 return;
31640             }
31641             
31642             filterPattern(box, 4);
31643             
31644         }, this);
31645         
31646         
31647         var prune = [];
31648         
31649         var pos = this.el.getBox(true);
31650         
31651         var minX = pos.x;
31652         
31653         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31654         
31655         var hit_end = false;
31656         
31657         Roo.each(queue, function(box){
31658             
31659             if(hit_end){
31660                 
31661                 Roo.each(box, function(b){
31662                 
31663                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31664                     b.el.hide();
31665
31666                 }, this);
31667
31668                 return;
31669             }
31670             
31671             var mx = 0;
31672             
31673             Roo.each(box, function(b){
31674                 
31675                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31676                 b.el.show();
31677
31678                 mx = Math.max(mx, b.x);
31679                 
31680             }, this);
31681             
31682             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31683             
31684             if(maxX < minX){
31685                 
31686                 Roo.each(box, function(b){
31687                 
31688                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31689                     b.el.hide();
31690                     
31691                 }, this);
31692                 
31693                 hit_end = true;
31694                 
31695                 return;
31696             }
31697             
31698             prune.push(box);
31699             
31700         }, this);
31701         
31702         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31703     },
31704     
31705     /** Sets position of item in DOM
31706     * @param {Element} item
31707     * @param {Number} x - horizontal position
31708     * @param {Number} y - vertical position
31709     * @param {Boolean} isInstant - disables transitions
31710     */
31711     _processVerticalLayoutQueue : function( queue, isInstant )
31712     {
31713         var pos = this.el.getBox(true);
31714         var x = pos.x;
31715         var y = pos.y;
31716         var maxY = [];
31717         
31718         for (var i = 0; i < this.cols; i++){
31719             maxY[i] = pos.y;
31720         }
31721         
31722         Roo.each(queue, function(box, k){
31723             
31724             var col = k % this.cols;
31725             
31726             Roo.each(box, function(b,kk){
31727                 
31728                 b.el.position('absolute');
31729                 
31730                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31731                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31732                 
31733                 if(b.size == 'md-left' || b.size == 'md-right'){
31734                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31735                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31736                 }
31737                 
31738                 b.el.setWidth(width);
31739                 b.el.setHeight(height);
31740                 // iframe?
31741                 b.el.select('iframe',true).setSize(width,height);
31742                 
31743             }, this);
31744             
31745             for (var i = 0; i < this.cols; i++){
31746                 
31747                 if(maxY[i] < maxY[col]){
31748                     col = i;
31749                     continue;
31750                 }
31751                 
31752                 col = Math.min(col, i);
31753                 
31754             }
31755             
31756             x = pos.x + col * (this.colWidth + this.padWidth);
31757             
31758             y = maxY[col];
31759             
31760             var positions = [];
31761             
31762             switch (box.length){
31763                 case 1 :
31764                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31765                     break;
31766                 case 2 :
31767                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31768                     break;
31769                 case 3 :
31770                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31771                     break;
31772                 case 4 :
31773                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31774                     break;
31775                 default :
31776                     break;
31777             }
31778             
31779             Roo.each(box, function(b,kk){
31780                 
31781                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31782                 
31783                 var sz = b.el.getSize();
31784                 
31785                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31786                 
31787             }, this);
31788             
31789         }, this);
31790         
31791         var mY = 0;
31792         
31793         for (var i = 0; i < this.cols; i++){
31794             mY = Math.max(mY, maxY[i]);
31795         }
31796         
31797         this.el.setHeight(mY - pos.y);
31798         
31799     },
31800     
31801 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31802 //    {
31803 //        var pos = this.el.getBox(true);
31804 //        var x = pos.x;
31805 //        var y = pos.y;
31806 //        var maxX = pos.right;
31807 //        
31808 //        var maxHeight = 0;
31809 //        
31810 //        Roo.each(items, function(item, k){
31811 //            
31812 //            var c = k % 2;
31813 //            
31814 //            item.el.position('absolute');
31815 //                
31816 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31817 //
31818 //            item.el.setWidth(width);
31819 //
31820 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31821 //
31822 //            item.el.setHeight(height);
31823 //            
31824 //            if(c == 0){
31825 //                item.el.setXY([x, y], isInstant ? false : true);
31826 //            } else {
31827 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31828 //            }
31829 //            
31830 //            y = y + height + this.alternativePadWidth;
31831 //            
31832 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31833 //            
31834 //        }, this);
31835 //        
31836 //        this.el.setHeight(maxHeight);
31837 //        
31838 //    },
31839     
31840     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31841     {
31842         var pos = this.el.getBox(true);
31843         
31844         var minX = pos.x;
31845         var minY = pos.y;
31846         
31847         var maxX = pos.right;
31848         
31849         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31850         
31851         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31852         
31853         Roo.each(queue, function(box, k){
31854             
31855             Roo.each(box, function(b, kk){
31856                 
31857                 b.el.position('absolute');
31858                 
31859                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31860                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31861                 
31862                 if(b.size == 'md-left' || b.size == 'md-right'){
31863                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31864                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31865                 }
31866                 
31867                 b.el.setWidth(width);
31868                 b.el.setHeight(height);
31869                 
31870             }, this);
31871             
31872             if(!box.length){
31873                 return;
31874             }
31875             
31876             var positions = [];
31877             
31878             switch (box.length){
31879                 case 1 :
31880                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31881                     break;
31882                 case 2 :
31883                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31884                     break;
31885                 case 3 :
31886                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31887                     break;
31888                 case 4 :
31889                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31890                     break;
31891                 default :
31892                     break;
31893             }
31894             
31895             Roo.each(box, function(b,kk){
31896                 
31897                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31898                 
31899                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31900                 
31901             }, this);
31902             
31903         }, this);
31904         
31905     },
31906     
31907     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31908     {
31909         Roo.each(eItems, function(b,k){
31910             
31911             b.size = (k == 0) ? 'sm' : 'xs';
31912             b.x = (k == 0) ? 2 : 1;
31913             b.y = (k == 0) ? 2 : 1;
31914             
31915             b.el.position('absolute');
31916             
31917             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31918                 
31919             b.el.setWidth(width);
31920             
31921             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31922             
31923             b.el.setHeight(height);
31924             
31925         }, this);
31926
31927         var positions = [];
31928         
31929         positions.push({
31930             x : maxX - this.unitWidth * 2 - this.gutter,
31931             y : minY
31932         });
31933         
31934         positions.push({
31935             x : maxX - this.unitWidth,
31936             y : minY + (this.unitWidth + this.gutter) * 2
31937         });
31938         
31939         positions.push({
31940             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31941             y : minY
31942         });
31943         
31944         Roo.each(eItems, function(b,k){
31945             
31946             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31947
31948         }, this);
31949         
31950     },
31951     
31952     getVerticalOneBoxColPositions : function(x, y, box)
31953     {
31954         var pos = [];
31955         
31956         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31957         
31958         if(box[0].size == 'md-left'){
31959             rand = 0;
31960         }
31961         
31962         if(box[0].size == 'md-right'){
31963             rand = 1;
31964         }
31965         
31966         pos.push({
31967             x : x + (this.unitWidth + this.gutter) * rand,
31968             y : y
31969         });
31970         
31971         return pos;
31972     },
31973     
31974     getVerticalTwoBoxColPositions : function(x, y, box)
31975     {
31976         var pos = [];
31977         
31978         if(box[0].size == 'xs'){
31979             
31980             pos.push({
31981                 x : x,
31982                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31983             });
31984
31985             pos.push({
31986                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31987                 y : y
31988             });
31989             
31990             return pos;
31991             
31992         }
31993         
31994         pos.push({
31995             x : x,
31996             y : y
31997         });
31998
31999         pos.push({
32000             x : x + (this.unitWidth + this.gutter) * 2,
32001             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32002         });
32003         
32004         return pos;
32005         
32006     },
32007     
32008     getVerticalThreeBoxColPositions : function(x, y, box)
32009     {
32010         var pos = [];
32011         
32012         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32013             
32014             pos.push({
32015                 x : x,
32016                 y : y
32017             });
32018
32019             pos.push({
32020                 x : x + (this.unitWidth + this.gutter) * 1,
32021                 y : y
32022             });
32023             
32024             pos.push({
32025                 x : x + (this.unitWidth + this.gutter) * 2,
32026                 y : y
32027             });
32028             
32029             return pos;
32030             
32031         }
32032         
32033         if(box[0].size == 'xs' && box[1].size == 'xs'){
32034             
32035             pos.push({
32036                 x : x,
32037                 y : y
32038             });
32039
32040             pos.push({
32041                 x : x,
32042                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32043             });
32044             
32045             pos.push({
32046                 x : x + (this.unitWidth + this.gutter) * 1,
32047                 y : y
32048             });
32049             
32050             return pos;
32051             
32052         }
32053         
32054         pos.push({
32055             x : x,
32056             y : y
32057         });
32058
32059         pos.push({
32060             x : x + (this.unitWidth + this.gutter) * 2,
32061             y : y
32062         });
32063
32064         pos.push({
32065             x : x + (this.unitWidth + this.gutter) * 2,
32066             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32067         });
32068             
32069         return pos;
32070         
32071     },
32072     
32073     getVerticalFourBoxColPositions : function(x, y, box)
32074     {
32075         var pos = [];
32076         
32077         if(box[0].size == 'xs'){
32078             
32079             pos.push({
32080                 x : x,
32081                 y : y
32082             });
32083
32084             pos.push({
32085                 x : x,
32086                 y : y + (this.unitHeight + this.gutter) * 1
32087             });
32088             
32089             pos.push({
32090                 x : x,
32091                 y : y + (this.unitHeight + this.gutter) * 2
32092             });
32093             
32094             pos.push({
32095                 x : x + (this.unitWidth + this.gutter) * 1,
32096                 y : y
32097             });
32098             
32099             return pos;
32100             
32101         }
32102         
32103         pos.push({
32104             x : x,
32105             y : y
32106         });
32107
32108         pos.push({
32109             x : x + (this.unitWidth + this.gutter) * 2,
32110             y : y
32111         });
32112
32113         pos.push({
32114             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32115             y : y + (this.unitHeight + this.gutter) * 1
32116         });
32117
32118         pos.push({
32119             x : x + (this.unitWidth + this.gutter) * 2,
32120             y : y + (this.unitWidth + this.gutter) * 2
32121         });
32122
32123         return pos;
32124         
32125     },
32126     
32127     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32128     {
32129         var pos = [];
32130         
32131         if(box[0].size == 'md-left'){
32132             pos.push({
32133                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32134                 y : minY
32135             });
32136             
32137             return pos;
32138         }
32139         
32140         if(box[0].size == 'md-right'){
32141             pos.push({
32142                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32143                 y : minY + (this.unitWidth + this.gutter) * 1
32144             });
32145             
32146             return pos;
32147         }
32148         
32149         var rand = Math.floor(Math.random() * (4 - box[0].y));
32150         
32151         pos.push({
32152             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32153             y : minY + (this.unitWidth + this.gutter) * rand
32154         });
32155         
32156         return pos;
32157         
32158     },
32159     
32160     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32161     {
32162         var pos = [];
32163         
32164         if(box[0].size == 'xs'){
32165             
32166             pos.push({
32167                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32168                 y : minY
32169             });
32170
32171             pos.push({
32172                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32173                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32174             });
32175             
32176             return pos;
32177             
32178         }
32179         
32180         pos.push({
32181             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32182             y : minY
32183         });
32184
32185         pos.push({
32186             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32187             y : minY + (this.unitWidth + this.gutter) * 2
32188         });
32189         
32190         return pos;
32191         
32192     },
32193     
32194     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32195     {
32196         var pos = [];
32197         
32198         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32199             
32200             pos.push({
32201                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32202                 y : minY
32203             });
32204
32205             pos.push({
32206                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32207                 y : minY + (this.unitWidth + this.gutter) * 1
32208             });
32209             
32210             pos.push({
32211                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32212                 y : minY + (this.unitWidth + this.gutter) * 2
32213             });
32214             
32215             return pos;
32216             
32217         }
32218         
32219         if(box[0].size == 'xs' && box[1].size == 'xs'){
32220             
32221             pos.push({
32222                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32223                 y : minY
32224             });
32225
32226             pos.push({
32227                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32228                 y : minY
32229             });
32230             
32231             pos.push({
32232                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32233                 y : minY + (this.unitWidth + this.gutter) * 1
32234             });
32235             
32236             return pos;
32237             
32238         }
32239         
32240         pos.push({
32241             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32242             y : minY
32243         });
32244
32245         pos.push({
32246             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32247             y : minY + (this.unitWidth + this.gutter) * 2
32248         });
32249
32250         pos.push({
32251             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32252             y : minY + (this.unitWidth + this.gutter) * 2
32253         });
32254             
32255         return pos;
32256         
32257     },
32258     
32259     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32260     {
32261         var pos = [];
32262         
32263         if(box[0].size == 'xs'){
32264             
32265             pos.push({
32266                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32267                 y : minY
32268             });
32269
32270             pos.push({
32271                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].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) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32277                 y : minY
32278             });
32279             
32280             pos.push({
32281                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32282                 y : minY + (this.unitWidth + this.gutter) * 1
32283             });
32284             
32285             return pos;
32286             
32287         }
32288         
32289         pos.push({
32290             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32291             y : minY
32292         });
32293         
32294         pos.push({
32295             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32296             y : minY + (this.unitWidth + this.gutter) * 2
32297         });
32298         
32299         pos.push({
32300             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].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) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32306             y : minY + (this.unitWidth + this.gutter) * 2
32307         });
32308
32309         return pos;
32310         
32311     },
32312     
32313     /**
32314     * remove a Masonry Brick
32315     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32316     */
32317     removeBrick : function(brick_id)
32318     {
32319         if (!brick_id) {
32320             return;
32321         }
32322         
32323         for (var i = 0; i<this.bricks.length; i++) {
32324             if (this.bricks[i].id == brick_id) {
32325                 this.bricks.splice(i,1);
32326                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32327                 this.initial();
32328             }
32329         }
32330     },
32331     
32332     /**
32333     * adds a Masonry Brick
32334     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32335     */
32336     addBrick : function(cfg)
32337     {
32338         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32339         //this.register(cn);
32340         cn.parentId = this.id;
32341         cn.render(this.el);
32342         return cn;
32343     },
32344     
32345     /**
32346     * register a Masonry Brick
32347     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32348     */
32349     
32350     register : function(brick)
32351     {
32352         this.bricks.push(brick);
32353         brick.masonryId = this.id;
32354     },
32355     
32356     /**
32357     * clear all the Masonry Brick
32358     */
32359     clearAll : function()
32360     {
32361         this.bricks = [];
32362         //this.getChildContainer().dom.innerHTML = "";
32363         this.el.dom.innerHTML = '';
32364     },
32365     
32366     getSelected : function()
32367     {
32368         if (!this.selectedBrick) {
32369             return false;
32370         }
32371         
32372         return this.selectedBrick;
32373     }
32374 });
32375
32376 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32377     
32378     groups: {},
32379      /**
32380     * register a Masonry Layout
32381     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32382     */
32383     
32384     register : function(layout)
32385     {
32386         this.groups[layout.id] = layout;
32387     },
32388     /**
32389     * fetch a  Masonry Layout based on the masonry layout ID
32390     * @param {string} the masonry layout to add
32391     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32392     */
32393     
32394     get: function(layout_id) {
32395         if (typeof(this.groups[layout_id]) == 'undefined') {
32396             return false;
32397         }
32398         return this.groups[layout_id] ;
32399     }
32400     
32401     
32402     
32403 });
32404
32405  
32406
32407  /**
32408  *
32409  * This is based on 
32410  * http://masonry.desandro.com
32411  *
32412  * The idea is to render all the bricks based on vertical width...
32413  *
32414  * The original code extends 'outlayer' - we might need to use that....
32415  * 
32416  */
32417
32418
32419 /**
32420  * @class Roo.bootstrap.LayoutMasonryAuto
32421  * @extends Roo.bootstrap.Component
32422  * Bootstrap Layout Masonry class
32423  * 
32424  * @constructor
32425  * Create a new Element
32426  * @param {Object} config The config object
32427  */
32428
32429 Roo.bootstrap.LayoutMasonryAuto = function(config){
32430     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32431 };
32432
32433 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32434     
32435       /**
32436      * @cfg {Boolean} isFitWidth  - resize the width..
32437      */   
32438     isFitWidth : false,  // options..
32439     /**
32440      * @cfg {Boolean} isOriginLeft = left align?
32441      */   
32442     isOriginLeft : true,
32443     /**
32444      * @cfg {Boolean} isOriginTop = top align?
32445      */   
32446     isOriginTop : false,
32447     /**
32448      * @cfg {Boolean} isLayoutInstant = no animation?
32449      */   
32450     isLayoutInstant : false, // needed?
32451     /**
32452      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32453      */   
32454     isResizingContainer : true,
32455     /**
32456      * @cfg {Number} columnWidth  width of the columns 
32457      */   
32458     
32459     columnWidth : 0,
32460     
32461     /**
32462      * @cfg {Number} maxCols maximum number of columns
32463      */   
32464     
32465     maxCols: 0,
32466     /**
32467      * @cfg {Number} padHeight padding below box..
32468      */   
32469     
32470     padHeight : 10, 
32471     
32472     /**
32473      * @cfg {Boolean} isAutoInitial defalut true
32474      */   
32475     
32476     isAutoInitial : true, 
32477     
32478     // private?
32479     gutter : 0,
32480     
32481     containerWidth: 0,
32482     initialColumnWidth : 0,
32483     currentSize : null,
32484     
32485     colYs : null, // array.
32486     maxY : 0,
32487     padWidth: 10,
32488     
32489     
32490     tag: 'div',
32491     cls: '',
32492     bricks: null, //CompositeElement
32493     cols : 0, // array?
32494     // element : null, // wrapped now this.el
32495     _isLayoutInited : null, 
32496     
32497     
32498     getAutoCreate : function(){
32499         
32500         var cfg = {
32501             tag: this.tag,
32502             cls: 'blog-masonary-wrapper ' + this.cls,
32503             cn : {
32504                 cls : 'mas-boxes masonary'
32505             }
32506         };
32507         
32508         return cfg;
32509     },
32510     
32511     getChildContainer: function( )
32512     {
32513         if (this.boxesEl) {
32514             return this.boxesEl;
32515         }
32516         
32517         this.boxesEl = this.el.select('.mas-boxes').first();
32518         
32519         return this.boxesEl;
32520     },
32521     
32522     
32523     initEvents : function()
32524     {
32525         var _this = this;
32526         
32527         if(this.isAutoInitial){
32528             Roo.log('hook children rendered');
32529             this.on('childrenrendered', function() {
32530                 Roo.log('children rendered');
32531                 _this.initial();
32532             } ,this);
32533         }
32534         
32535     },
32536     
32537     initial : function()
32538     {
32539         this.reloadItems();
32540
32541         this.currentSize = this.el.getBox(true);
32542
32543         /// was window resize... - let's see if this works..
32544         Roo.EventManager.onWindowResize(this.resize, this); 
32545
32546         if(!this.isAutoInitial){
32547             this.layout();
32548             return;
32549         }
32550         
32551         this.layout.defer(500,this);
32552     },
32553     
32554     reloadItems: function()
32555     {
32556         this.bricks = this.el.select('.masonry-brick', true);
32557         
32558         this.bricks.each(function(b) {
32559             //Roo.log(b.getSize());
32560             if (!b.attr('originalwidth')) {
32561                 b.attr('originalwidth',  b.getSize().width);
32562             }
32563             
32564         });
32565         
32566         Roo.log(this.bricks.elements.length);
32567     },
32568     
32569     resize : function()
32570     {
32571         Roo.log('resize');
32572         var cs = this.el.getBox(true);
32573         
32574         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32575             Roo.log("no change in with or X");
32576             return;
32577         }
32578         this.currentSize = cs;
32579         this.layout();
32580     },
32581     
32582     layout : function()
32583     {
32584          Roo.log('layout');
32585         this._resetLayout();
32586         //this._manageStamps();
32587       
32588         // don't animate first layout
32589         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32590         this.layoutItems( isInstant );
32591       
32592         // flag for initalized
32593         this._isLayoutInited = true;
32594     },
32595     
32596     layoutItems : function( isInstant )
32597     {
32598         //var items = this._getItemsForLayout( this.items );
32599         // original code supports filtering layout items.. we just ignore it..
32600         
32601         this._layoutItems( this.bricks , isInstant );
32602       
32603         this._postLayout();
32604     },
32605     _layoutItems : function ( items , isInstant)
32606     {
32607        //this.fireEvent( 'layout', this, items );
32608     
32609
32610         if ( !items || !items.elements.length ) {
32611           // no items, emit event with empty array
32612             return;
32613         }
32614
32615         var queue = [];
32616         items.each(function(item) {
32617             Roo.log("layout item");
32618             Roo.log(item);
32619             // get x/y object from method
32620             var position = this._getItemLayoutPosition( item );
32621             // enqueue
32622             position.item = item;
32623             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32624             queue.push( position );
32625         }, this);
32626       
32627         this._processLayoutQueue( queue );
32628     },
32629     /** Sets position of item in DOM
32630     * @param {Element} item
32631     * @param {Number} x - horizontal position
32632     * @param {Number} y - vertical position
32633     * @param {Boolean} isInstant - disables transitions
32634     */
32635     _processLayoutQueue : function( queue )
32636     {
32637         for ( var i=0, len = queue.length; i < len; i++ ) {
32638             var obj = queue[i];
32639             obj.item.position('absolute');
32640             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32641         }
32642     },
32643       
32644     
32645     /**
32646     * Any logic you want to do after each layout,
32647     * i.e. size the container
32648     */
32649     _postLayout : function()
32650     {
32651         this.resizeContainer();
32652     },
32653     
32654     resizeContainer : function()
32655     {
32656         if ( !this.isResizingContainer ) {
32657             return;
32658         }
32659         var size = this._getContainerSize();
32660         if ( size ) {
32661             this.el.setSize(size.width,size.height);
32662             this.boxesEl.setSize(size.width,size.height);
32663         }
32664     },
32665     
32666     
32667     
32668     _resetLayout : function()
32669     {
32670         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32671         this.colWidth = this.el.getWidth();
32672         //this.gutter = this.el.getWidth(); 
32673         
32674         this.measureColumns();
32675
32676         // reset column Y
32677         var i = this.cols;
32678         this.colYs = [];
32679         while (i--) {
32680             this.colYs.push( 0 );
32681         }
32682     
32683         this.maxY = 0;
32684     },
32685
32686     measureColumns : function()
32687     {
32688         this.getContainerWidth();
32689       // if columnWidth is 0, default to outerWidth of first item
32690         if ( !this.columnWidth ) {
32691             var firstItem = this.bricks.first();
32692             Roo.log(firstItem);
32693             this.columnWidth  = this.containerWidth;
32694             if (firstItem && firstItem.attr('originalwidth') ) {
32695                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32696             }
32697             // columnWidth fall back to item of first element
32698             Roo.log("set column width?");
32699                         this.initialColumnWidth = this.columnWidth  ;
32700
32701             // if first elem has no width, default to size of container
32702             
32703         }
32704         
32705         
32706         if (this.initialColumnWidth) {
32707             this.columnWidth = this.initialColumnWidth;
32708         }
32709         
32710         
32711             
32712         // column width is fixed at the top - however if container width get's smaller we should
32713         // reduce it...
32714         
32715         // this bit calcs how man columns..
32716             
32717         var columnWidth = this.columnWidth += this.gutter;
32718       
32719         // calculate columns
32720         var containerWidth = this.containerWidth + this.gutter;
32721         
32722         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32723         // fix rounding errors, typically with gutters
32724         var excess = columnWidth - containerWidth % columnWidth;
32725         
32726         
32727         // if overshoot is less than a pixel, round up, otherwise floor it
32728         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32729         cols = Math[ mathMethod ]( cols );
32730         this.cols = Math.max( cols, 1 );
32731         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32732         
32733          // padding positioning..
32734         var totalColWidth = this.cols * this.columnWidth;
32735         var padavail = this.containerWidth - totalColWidth;
32736         // so for 2 columns - we need 3 'pads'
32737         
32738         var padNeeded = (1+this.cols) * this.padWidth;
32739         
32740         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32741         
32742         this.columnWidth += padExtra
32743         //this.padWidth = Math.floor(padavail /  ( this.cols));
32744         
32745         // adjust colum width so that padding is fixed??
32746         
32747         // we have 3 columns ... total = width * 3
32748         // we have X left over... that should be used by 
32749         
32750         //if (this.expandC) {
32751             
32752         //}
32753         
32754         
32755         
32756     },
32757     
32758     getContainerWidth : function()
32759     {
32760        /* // container is parent if fit width
32761         var container = this.isFitWidth ? this.element.parentNode : this.element;
32762         // check that this.size and size are there
32763         // IE8 triggers resize on body size change, so they might not be
32764         
32765         var size = getSize( container );  //FIXME
32766         this.containerWidth = size && size.innerWidth; //FIXME
32767         */
32768          
32769         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32770         
32771     },
32772     
32773     _getItemLayoutPosition : function( item )  // what is item?
32774     {
32775         // we resize the item to our columnWidth..
32776       
32777         item.setWidth(this.columnWidth);
32778         item.autoBoxAdjust  = false;
32779         
32780         var sz = item.getSize();
32781  
32782         // how many columns does this brick span
32783         var remainder = this.containerWidth % this.columnWidth;
32784         
32785         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32786         // round if off by 1 pixel, otherwise use ceil
32787         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32788         colSpan = Math.min( colSpan, this.cols );
32789         
32790         // normally this should be '1' as we dont' currently allow multi width columns..
32791         
32792         var colGroup = this._getColGroup( colSpan );
32793         // get the minimum Y value from the columns
32794         var minimumY = Math.min.apply( Math, colGroup );
32795         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32796         
32797         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32798          
32799         // position the brick
32800         var position = {
32801             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32802             y: this.currentSize.y + minimumY + this.padHeight
32803         };
32804         
32805         Roo.log(position);
32806         // apply setHeight to necessary columns
32807         var setHeight = minimumY + sz.height + this.padHeight;
32808         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32809         
32810         var setSpan = this.cols + 1 - colGroup.length;
32811         for ( var i = 0; i < setSpan; i++ ) {
32812           this.colYs[ shortColIndex + i ] = setHeight ;
32813         }
32814       
32815         return position;
32816     },
32817     
32818     /**
32819      * @param {Number} colSpan - number of columns the element spans
32820      * @returns {Array} colGroup
32821      */
32822     _getColGroup : function( colSpan )
32823     {
32824         if ( colSpan < 2 ) {
32825           // if brick spans only one column, use all the column Ys
32826           return this.colYs;
32827         }
32828       
32829         var colGroup = [];
32830         // how many different places could this brick fit horizontally
32831         var groupCount = this.cols + 1 - colSpan;
32832         // for each group potential horizontal position
32833         for ( var i = 0; i < groupCount; i++ ) {
32834           // make an array of colY values for that one group
32835           var groupColYs = this.colYs.slice( i, i + colSpan );
32836           // and get the max value of the array
32837           colGroup[i] = Math.max.apply( Math, groupColYs );
32838         }
32839         return colGroup;
32840     },
32841     /*
32842     _manageStamp : function( stamp )
32843     {
32844         var stampSize =  stamp.getSize();
32845         var offset = stamp.getBox();
32846         // get the columns that this stamp affects
32847         var firstX = this.isOriginLeft ? offset.x : offset.right;
32848         var lastX = firstX + stampSize.width;
32849         var firstCol = Math.floor( firstX / this.columnWidth );
32850         firstCol = Math.max( 0, firstCol );
32851         
32852         var lastCol = Math.floor( lastX / this.columnWidth );
32853         // lastCol should not go over if multiple of columnWidth #425
32854         lastCol -= lastX % this.columnWidth ? 0 : 1;
32855         lastCol = Math.min( this.cols - 1, lastCol );
32856         
32857         // set colYs to bottom of the stamp
32858         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32859             stampSize.height;
32860             
32861         for ( var i = firstCol; i <= lastCol; i++ ) {
32862           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32863         }
32864     },
32865     */
32866     
32867     _getContainerSize : function()
32868     {
32869         this.maxY = Math.max.apply( Math, this.colYs );
32870         var size = {
32871             height: this.maxY
32872         };
32873       
32874         if ( this.isFitWidth ) {
32875             size.width = this._getContainerFitWidth();
32876         }
32877       
32878         return size;
32879     },
32880     
32881     _getContainerFitWidth : function()
32882     {
32883         var unusedCols = 0;
32884         // count unused columns
32885         var i = this.cols;
32886         while ( --i ) {
32887           if ( this.colYs[i] !== 0 ) {
32888             break;
32889           }
32890           unusedCols++;
32891         }
32892         // fit container to columns that have been used
32893         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32894     },
32895     
32896     needsResizeLayout : function()
32897     {
32898         var previousWidth = this.containerWidth;
32899         this.getContainerWidth();
32900         return previousWidth !== this.containerWidth;
32901     }
32902  
32903 });
32904
32905  
32906
32907  /*
32908  * - LGPL
32909  *
32910  * element
32911  * 
32912  */
32913
32914 /**
32915  * @class Roo.bootstrap.MasonryBrick
32916  * @extends Roo.bootstrap.Component
32917  * Bootstrap MasonryBrick class
32918  * 
32919  * @constructor
32920  * Create a new MasonryBrick
32921  * @param {Object} config The config object
32922  */
32923
32924 Roo.bootstrap.MasonryBrick = function(config){
32925     
32926     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32927     
32928     Roo.bootstrap.MasonryBrick.register(this);
32929     
32930     this.addEvents({
32931         // raw events
32932         /**
32933          * @event click
32934          * When a MasonryBrick is clcik
32935          * @param {Roo.bootstrap.MasonryBrick} this
32936          * @param {Roo.EventObject} e
32937          */
32938         "click" : true
32939     });
32940 };
32941
32942 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32943     
32944     /**
32945      * @cfg {String} title
32946      */   
32947     title : '',
32948     /**
32949      * @cfg {String} html
32950      */   
32951     html : '',
32952     /**
32953      * @cfg {String} bgimage
32954      */   
32955     bgimage : '',
32956     /**
32957      * @cfg {String} videourl
32958      */   
32959     videourl : '',
32960     /**
32961      * @cfg {String} cls
32962      */   
32963     cls : '',
32964     /**
32965      * @cfg {String} href
32966      */   
32967     href : '',
32968     /**
32969      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32970      */   
32971     size : 'xs',
32972     
32973     /**
32974      * @cfg {String} placetitle (center|bottom)
32975      */   
32976     placetitle : '',
32977     
32978     /**
32979      * @cfg {Boolean} isFitContainer defalut true
32980      */   
32981     isFitContainer : true, 
32982     
32983     /**
32984      * @cfg {Boolean} preventDefault defalut false
32985      */   
32986     preventDefault : false, 
32987     
32988     /**
32989      * @cfg {Boolean} inverse defalut false
32990      */   
32991     maskInverse : false, 
32992     
32993     getAutoCreate : function()
32994     {
32995         if(!this.isFitContainer){
32996             return this.getSplitAutoCreate();
32997         }
32998         
32999         var cls = 'masonry-brick masonry-brick-full';
33000         
33001         if(this.href.length){
33002             cls += ' masonry-brick-link';
33003         }
33004         
33005         if(this.bgimage.length){
33006             cls += ' masonry-brick-image';
33007         }
33008         
33009         if(this.maskInverse){
33010             cls += ' mask-inverse';
33011         }
33012         
33013         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33014             cls += ' enable-mask';
33015         }
33016         
33017         if(this.size){
33018             cls += ' masonry-' + this.size + '-brick';
33019         }
33020         
33021         if(this.placetitle.length){
33022             
33023             switch (this.placetitle) {
33024                 case 'center' :
33025                     cls += ' masonry-center-title';
33026                     break;
33027                 case 'bottom' :
33028                     cls += ' masonry-bottom-title';
33029                     break;
33030                 default:
33031                     break;
33032             }
33033             
33034         } else {
33035             if(!this.html.length && !this.bgimage.length){
33036                 cls += ' masonry-center-title';
33037             }
33038
33039             if(!this.html.length && this.bgimage.length){
33040                 cls += ' masonry-bottom-title';
33041             }
33042         }
33043         
33044         if(this.cls){
33045             cls += ' ' + this.cls;
33046         }
33047         
33048         var cfg = {
33049             tag: (this.href.length) ? 'a' : 'div',
33050             cls: cls,
33051             cn: [
33052                 {
33053                     tag: 'div',
33054                     cls: 'masonry-brick-mask'
33055                 },
33056                 {
33057                     tag: 'div',
33058                     cls: 'masonry-brick-paragraph',
33059                     cn: []
33060                 }
33061             ]
33062         };
33063         
33064         if(this.href.length){
33065             cfg.href = this.href;
33066         }
33067         
33068         var cn = cfg.cn[1].cn;
33069         
33070         if(this.title.length){
33071             cn.push({
33072                 tag: 'h4',
33073                 cls: 'masonry-brick-title',
33074                 html: this.title
33075             });
33076         }
33077         
33078         if(this.html.length){
33079             cn.push({
33080                 tag: 'p',
33081                 cls: 'masonry-brick-text',
33082                 html: this.html
33083             });
33084         }
33085         
33086         if (!this.title.length && !this.html.length) {
33087             cfg.cn[1].cls += ' hide';
33088         }
33089         
33090         if(this.bgimage.length){
33091             cfg.cn.push({
33092                 tag: 'img',
33093                 cls: 'masonry-brick-image-view',
33094                 src: this.bgimage
33095             });
33096         }
33097         
33098         if(this.videourl.length){
33099             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33100             // youtube support only?
33101             cfg.cn.push({
33102                 tag: 'iframe',
33103                 cls: 'masonry-brick-image-view',
33104                 src: vurl,
33105                 frameborder : 0,
33106                 allowfullscreen : true
33107             });
33108         }
33109         
33110         return cfg;
33111         
33112     },
33113     
33114     getSplitAutoCreate : function()
33115     {
33116         var cls = 'masonry-brick masonry-brick-split';
33117         
33118         if(this.href.length){
33119             cls += ' masonry-brick-link';
33120         }
33121         
33122         if(this.bgimage.length){
33123             cls += ' masonry-brick-image';
33124         }
33125         
33126         if(this.size){
33127             cls += ' masonry-' + this.size + '-brick';
33128         }
33129         
33130         switch (this.placetitle) {
33131             case 'center' :
33132                 cls += ' masonry-center-title';
33133                 break;
33134             case 'bottom' :
33135                 cls += ' masonry-bottom-title';
33136                 break;
33137             default:
33138                 if(!this.bgimage.length){
33139                     cls += ' masonry-center-title';
33140                 }
33141
33142                 if(this.bgimage.length){
33143                     cls += ' masonry-bottom-title';
33144                 }
33145                 break;
33146         }
33147         
33148         if(this.cls){
33149             cls += ' ' + this.cls;
33150         }
33151         
33152         var cfg = {
33153             tag: (this.href.length) ? 'a' : 'div',
33154             cls: cls,
33155             cn: [
33156                 {
33157                     tag: 'div',
33158                     cls: 'masonry-brick-split-head',
33159                     cn: [
33160                         {
33161                             tag: 'div',
33162                             cls: 'masonry-brick-paragraph',
33163                             cn: []
33164                         }
33165                     ]
33166                 },
33167                 {
33168                     tag: 'div',
33169                     cls: 'masonry-brick-split-body',
33170                     cn: []
33171                 }
33172             ]
33173         };
33174         
33175         if(this.href.length){
33176             cfg.href = this.href;
33177         }
33178         
33179         if(this.title.length){
33180             cfg.cn[0].cn[0].cn.push({
33181                 tag: 'h4',
33182                 cls: 'masonry-brick-title',
33183                 html: this.title
33184             });
33185         }
33186         
33187         if(this.html.length){
33188             cfg.cn[1].cn.push({
33189                 tag: 'p',
33190                 cls: 'masonry-brick-text',
33191                 html: this.html
33192             });
33193         }
33194
33195         if(this.bgimage.length){
33196             cfg.cn[0].cn.push({
33197                 tag: 'img',
33198                 cls: 'masonry-brick-image-view',
33199                 src: this.bgimage
33200             });
33201         }
33202         
33203         if(this.videourl.length){
33204             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33205             // youtube support only?
33206             cfg.cn[0].cn.cn.push({
33207                 tag: 'iframe',
33208                 cls: 'masonry-brick-image-view',
33209                 src: vurl,
33210                 frameborder : 0,
33211                 allowfullscreen : true
33212             });
33213         }
33214         
33215         return cfg;
33216     },
33217     
33218     initEvents: function() 
33219     {
33220         switch (this.size) {
33221             case 'xs' :
33222                 this.x = 1;
33223                 this.y = 1;
33224                 break;
33225             case 'sm' :
33226                 this.x = 2;
33227                 this.y = 2;
33228                 break;
33229             case 'md' :
33230             case 'md-left' :
33231             case 'md-right' :
33232                 this.x = 3;
33233                 this.y = 3;
33234                 break;
33235             case 'tall' :
33236                 this.x = 2;
33237                 this.y = 3;
33238                 break;
33239             case 'wide' :
33240                 this.x = 3;
33241                 this.y = 2;
33242                 break;
33243             case 'wide-thin' :
33244                 this.x = 3;
33245                 this.y = 1;
33246                 break;
33247                         
33248             default :
33249                 break;
33250         }
33251         
33252         if(Roo.isTouch){
33253             this.el.on('touchstart', this.onTouchStart, this);
33254             this.el.on('touchmove', this.onTouchMove, this);
33255             this.el.on('touchend', this.onTouchEnd, this);
33256             this.el.on('contextmenu', this.onContextMenu, this);
33257         } else {
33258             this.el.on('mouseenter'  ,this.enter, this);
33259             this.el.on('mouseleave', this.leave, this);
33260             this.el.on('click', this.onClick, this);
33261         }
33262         
33263         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33264             this.parent().bricks.push(this);   
33265         }
33266         
33267     },
33268     
33269     onClick: function(e, el)
33270     {
33271         var time = this.endTimer - this.startTimer;
33272         // Roo.log(e.preventDefault());
33273         if(Roo.isTouch){
33274             if(time > 1000){
33275                 e.preventDefault();
33276                 return;
33277             }
33278         }
33279         
33280         if(!this.preventDefault){
33281             return;
33282         }
33283         
33284         e.preventDefault();
33285         
33286         if (this.activeClass != '') {
33287             this.selectBrick();
33288         }
33289         
33290         this.fireEvent('click', this, e);
33291     },
33292     
33293     enter: function(e, el)
33294     {
33295         e.preventDefault();
33296         
33297         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33298             return;
33299         }
33300         
33301         if(this.bgimage.length && this.html.length){
33302             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33303         }
33304     },
33305     
33306     leave: function(e, el)
33307     {
33308         e.preventDefault();
33309         
33310         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33311             return;
33312         }
33313         
33314         if(this.bgimage.length && this.html.length){
33315             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33316         }
33317     },
33318     
33319     onTouchStart: function(e, el)
33320     {
33321 //        e.preventDefault();
33322         
33323         this.touchmoved = false;
33324         
33325         if(!this.isFitContainer){
33326             return;
33327         }
33328         
33329         if(!this.bgimage.length || !this.html.length){
33330             return;
33331         }
33332         
33333         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33334         
33335         this.timer = new Date().getTime();
33336         
33337     },
33338     
33339     onTouchMove: function(e, el)
33340     {
33341         this.touchmoved = true;
33342     },
33343     
33344     onContextMenu : function(e,el)
33345     {
33346         e.preventDefault();
33347         e.stopPropagation();
33348         return false;
33349     },
33350     
33351     onTouchEnd: function(e, el)
33352     {
33353 //        e.preventDefault();
33354         
33355         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33356         
33357             this.leave(e,el);
33358             
33359             return;
33360         }
33361         
33362         if(!this.bgimage.length || !this.html.length){
33363             
33364             if(this.href.length){
33365                 window.location.href = this.href;
33366             }
33367             
33368             return;
33369         }
33370         
33371         if(!this.isFitContainer){
33372             return;
33373         }
33374         
33375         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33376         
33377         window.location.href = this.href;
33378     },
33379     
33380     //selection on single brick only
33381     selectBrick : function() {
33382         
33383         if (!this.parentId) {
33384             return;
33385         }
33386         
33387         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33388         var index = m.selectedBrick.indexOf(this.id);
33389         
33390         if ( index > -1) {
33391             m.selectedBrick.splice(index,1);
33392             this.el.removeClass(this.activeClass);
33393             return;
33394         }
33395         
33396         for(var i = 0; i < m.selectedBrick.length; i++) {
33397             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33398             b.el.removeClass(b.activeClass);
33399         }
33400         
33401         m.selectedBrick = [];
33402         
33403         m.selectedBrick.push(this.id);
33404         this.el.addClass(this.activeClass);
33405         return;
33406     },
33407     
33408     isSelected : function(){
33409         return this.el.hasClass(this.activeClass);
33410         
33411     }
33412 });
33413
33414 Roo.apply(Roo.bootstrap.MasonryBrick, {
33415     
33416     //groups: {},
33417     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33418      /**
33419     * register a Masonry Brick
33420     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33421     */
33422     
33423     register : function(brick)
33424     {
33425         //this.groups[brick.id] = brick;
33426         this.groups.add(brick.id, brick);
33427     },
33428     /**
33429     * fetch a  masonry brick based on the masonry brick ID
33430     * @param {string} the masonry brick to add
33431     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33432     */
33433     
33434     get: function(brick_id) 
33435     {
33436         // if (typeof(this.groups[brick_id]) == 'undefined') {
33437         //     return false;
33438         // }
33439         // return this.groups[brick_id] ;
33440         
33441         if(this.groups.key(brick_id)) {
33442             return this.groups.key(brick_id);
33443         }
33444         
33445         return false;
33446     }
33447     
33448     
33449     
33450 });
33451
33452  /*
33453  * - LGPL
33454  *
33455  * element
33456  * 
33457  */
33458
33459 /**
33460  * @class Roo.bootstrap.Brick
33461  * @extends Roo.bootstrap.Component
33462  * Bootstrap Brick class
33463  * 
33464  * @constructor
33465  * Create a new Brick
33466  * @param {Object} config The config object
33467  */
33468
33469 Roo.bootstrap.Brick = function(config){
33470     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33471     
33472     this.addEvents({
33473         // raw events
33474         /**
33475          * @event click
33476          * When a Brick is click
33477          * @param {Roo.bootstrap.Brick} this
33478          * @param {Roo.EventObject} e
33479          */
33480         "click" : true
33481     });
33482 };
33483
33484 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33485     
33486     /**
33487      * @cfg {String} title
33488      */   
33489     title : '',
33490     /**
33491      * @cfg {String} html
33492      */   
33493     html : '',
33494     /**
33495      * @cfg {String} bgimage
33496      */   
33497     bgimage : '',
33498     /**
33499      * @cfg {String} cls
33500      */   
33501     cls : '',
33502     /**
33503      * @cfg {String} href
33504      */   
33505     href : '',
33506     /**
33507      * @cfg {String} video
33508      */   
33509     video : '',
33510     /**
33511      * @cfg {Boolean} square
33512      */   
33513     square : true,
33514     
33515     getAutoCreate : function()
33516     {
33517         var cls = 'roo-brick';
33518         
33519         if(this.href.length){
33520             cls += ' roo-brick-link';
33521         }
33522         
33523         if(this.bgimage.length){
33524             cls += ' roo-brick-image';
33525         }
33526         
33527         if(!this.html.length && !this.bgimage.length){
33528             cls += ' roo-brick-center-title';
33529         }
33530         
33531         if(!this.html.length && this.bgimage.length){
33532             cls += ' roo-brick-bottom-title';
33533         }
33534         
33535         if(this.cls){
33536             cls += ' ' + this.cls;
33537         }
33538         
33539         var cfg = {
33540             tag: (this.href.length) ? 'a' : 'div',
33541             cls: cls,
33542             cn: [
33543                 {
33544                     tag: 'div',
33545                     cls: 'roo-brick-paragraph',
33546                     cn: []
33547                 }
33548             ]
33549         };
33550         
33551         if(this.href.length){
33552             cfg.href = this.href;
33553         }
33554         
33555         var cn = cfg.cn[0].cn;
33556         
33557         if(this.title.length){
33558             cn.push({
33559                 tag: 'h4',
33560                 cls: 'roo-brick-title',
33561                 html: this.title
33562             });
33563         }
33564         
33565         if(this.html.length){
33566             cn.push({
33567                 tag: 'p',
33568                 cls: 'roo-brick-text',
33569                 html: this.html
33570             });
33571         } else {
33572             cn.cls += ' hide';
33573         }
33574         
33575         if(this.bgimage.length){
33576             cfg.cn.push({
33577                 tag: 'img',
33578                 cls: 'roo-brick-image-view',
33579                 src: this.bgimage
33580             });
33581         }
33582         
33583         return cfg;
33584     },
33585     
33586     initEvents: function() 
33587     {
33588         if(this.title.length || this.html.length){
33589             this.el.on('mouseenter'  ,this.enter, this);
33590             this.el.on('mouseleave', this.leave, this);
33591         }
33592         
33593         Roo.EventManager.onWindowResize(this.resize, this); 
33594         
33595         if(this.bgimage.length){
33596             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33597             this.imageEl.on('load', this.onImageLoad, this);
33598             return;
33599         }
33600         
33601         this.resize();
33602     },
33603     
33604     onImageLoad : function()
33605     {
33606         this.resize();
33607     },
33608     
33609     resize : function()
33610     {
33611         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33612         
33613         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33614         
33615         if(this.bgimage.length){
33616             var image = this.el.select('.roo-brick-image-view', true).first();
33617             
33618             image.setWidth(paragraph.getWidth());
33619             
33620             if(this.square){
33621                 image.setHeight(paragraph.getWidth());
33622             }
33623             
33624             this.el.setHeight(image.getHeight());
33625             paragraph.setHeight(image.getHeight());
33626             
33627         }
33628         
33629     },
33630     
33631     enter: function(e, el)
33632     {
33633         e.preventDefault();
33634         
33635         if(this.bgimage.length){
33636             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33637             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33638         }
33639     },
33640     
33641     leave: function(e, el)
33642     {
33643         e.preventDefault();
33644         
33645         if(this.bgimage.length){
33646             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33647             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33648         }
33649     }
33650     
33651 });
33652
33653  
33654
33655  /*
33656  * - LGPL
33657  *
33658  * Number field 
33659  */
33660
33661 /**
33662  * @class Roo.bootstrap.NumberField
33663  * @extends Roo.bootstrap.Input
33664  * Bootstrap NumberField class
33665  * 
33666  * 
33667  * 
33668  * 
33669  * @constructor
33670  * Create a new NumberField
33671  * @param {Object} config The config object
33672  */
33673
33674 Roo.bootstrap.NumberField = function(config){
33675     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33676 };
33677
33678 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33679     
33680     /**
33681      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33682      */
33683     allowDecimals : true,
33684     /**
33685      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33686      */
33687     decimalSeparator : ".",
33688     /**
33689      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33690      */
33691     decimalPrecision : 2,
33692     /**
33693      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33694      */
33695     allowNegative : true,
33696     
33697     /**
33698      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33699      */
33700     allowZero: true,
33701     /**
33702      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33703      */
33704     minValue : Number.NEGATIVE_INFINITY,
33705     /**
33706      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33707      */
33708     maxValue : Number.MAX_VALUE,
33709     /**
33710      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33711      */
33712     minText : "The minimum value for this field is {0}",
33713     /**
33714      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33715      */
33716     maxText : "The maximum value for this field is {0}",
33717     /**
33718      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33719      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33720      */
33721     nanText : "{0} is not a valid number",
33722     /**
33723      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33724      */
33725     thousandsDelimiter : false,
33726     /**
33727      * @cfg {String} valueAlign alignment of value
33728      */
33729     valueAlign : "left",
33730
33731     getAutoCreate : function()
33732     {
33733         var hiddenInput = {
33734             tag: 'input',
33735             type: 'hidden',
33736             id: Roo.id(),
33737             cls: 'hidden-number-input'
33738         };
33739         
33740         if (this.name) {
33741             hiddenInput.name = this.name;
33742         }
33743         
33744         this.name = '';
33745         
33746         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33747         
33748         this.name = hiddenInput.name;
33749         
33750         if(cfg.cn.length > 0) {
33751             cfg.cn.push(hiddenInput);
33752         }
33753         
33754         return cfg;
33755     },
33756
33757     // private
33758     initEvents : function()
33759     {   
33760         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33761         
33762         var allowed = "0123456789";
33763         
33764         if(this.allowDecimals){
33765             allowed += this.decimalSeparator;
33766         }
33767         
33768         if(this.allowNegative){
33769             allowed += "-";
33770         }
33771         
33772         if(this.thousandsDelimiter) {
33773             allowed += ",";
33774         }
33775         
33776         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33777         
33778         var keyPress = function(e){
33779             
33780             var k = e.getKey();
33781             
33782             var c = e.getCharCode();
33783             
33784             if(
33785                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33786                     allowed.indexOf(String.fromCharCode(c)) === -1
33787             ){
33788                 e.stopEvent();
33789                 return;
33790             }
33791             
33792             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33793                 return;
33794             }
33795             
33796             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33797                 e.stopEvent();
33798             }
33799         };
33800         
33801         this.el.on("keypress", keyPress, this);
33802     },
33803     
33804     validateValue : function(value)
33805     {
33806         
33807         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33808             return false;
33809         }
33810         
33811         var num = this.parseValue(value);
33812         
33813         if(isNaN(num)){
33814             this.markInvalid(String.format(this.nanText, value));
33815             return false;
33816         }
33817         
33818         if(num < this.minValue){
33819             this.markInvalid(String.format(this.minText, this.minValue));
33820             return false;
33821         }
33822         
33823         if(num > this.maxValue){
33824             this.markInvalid(String.format(this.maxText, this.maxValue));
33825             return false;
33826         }
33827         
33828         return true;
33829     },
33830
33831     getValue : function()
33832     {
33833         var v = this.hiddenEl().getValue();
33834         
33835         return this.fixPrecision(this.parseValue(v));
33836     },
33837
33838     parseValue : function(value)
33839     {
33840         if(this.thousandsDelimiter) {
33841             value += "";
33842             r = new RegExp(",", "g");
33843             value = value.replace(r, "");
33844         }
33845         
33846         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33847         return isNaN(value) ? '' : value;
33848     },
33849
33850     fixPrecision : function(value)
33851     {
33852         if(this.thousandsDelimiter) {
33853             value += "";
33854             r = new RegExp(",", "g");
33855             value = value.replace(r, "");
33856         }
33857         
33858         var nan = isNaN(value);
33859         
33860         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33861             return nan ? '' : value;
33862         }
33863         return parseFloat(value).toFixed(this.decimalPrecision);
33864     },
33865
33866     setValue : function(v)
33867     {
33868         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33869         
33870         this.value = v;
33871         
33872         if(this.rendered){
33873             
33874             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33875             
33876             this.inputEl().dom.value = (v == '') ? '' :
33877                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33878             
33879             if(!this.allowZero && v === '0') {
33880                 this.hiddenEl().dom.value = '';
33881                 this.inputEl().dom.value = '';
33882             }
33883             
33884             this.validate();
33885         }
33886     },
33887
33888     decimalPrecisionFcn : function(v)
33889     {
33890         return Math.floor(v);
33891     },
33892
33893     beforeBlur : function()
33894     {
33895         var v = this.parseValue(this.getRawValue());
33896         
33897         if(v || v === 0 || v === ''){
33898             this.setValue(v);
33899         }
33900     },
33901     
33902     hiddenEl : function()
33903     {
33904         return this.el.select('input.hidden-number-input',true).first();
33905     }
33906     
33907 });
33908
33909  
33910
33911 /*
33912 * Licence: LGPL
33913 */
33914
33915 /**
33916  * @class Roo.bootstrap.DocumentSlider
33917  * @extends Roo.bootstrap.Component
33918  * Bootstrap DocumentSlider class
33919  * 
33920  * @constructor
33921  * Create a new DocumentViewer
33922  * @param {Object} config The config object
33923  */
33924
33925 Roo.bootstrap.DocumentSlider = function(config){
33926     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33927     
33928     this.files = [];
33929     
33930     this.addEvents({
33931         /**
33932          * @event initial
33933          * Fire after initEvent
33934          * @param {Roo.bootstrap.DocumentSlider} this
33935          */
33936         "initial" : true,
33937         /**
33938          * @event update
33939          * Fire after update
33940          * @param {Roo.bootstrap.DocumentSlider} this
33941          */
33942         "update" : true,
33943         /**
33944          * @event click
33945          * Fire after click
33946          * @param {Roo.bootstrap.DocumentSlider} this
33947          */
33948         "click" : true
33949     });
33950 };
33951
33952 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33953     
33954     files : false,
33955     
33956     indicator : 0,
33957     
33958     getAutoCreate : function()
33959     {
33960         var cfg = {
33961             tag : 'div',
33962             cls : 'roo-document-slider',
33963             cn : [
33964                 {
33965                     tag : 'div',
33966                     cls : 'roo-document-slider-header',
33967                     cn : [
33968                         {
33969                             tag : 'div',
33970                             cls : 'roo-document-slider-header-title'
33971                         }
33972                     ]
33973                 },
33974                 {
33975                     tag : 'div',
33976                     cls : 'roo-document-slider-body',
33977                     cn : [
33978                         {
33979                             tag : 'div',
33980                             cls : 'roo-document-slider-prev',
33981                             cn : [
33982                                 {
33983                                     tag : 'i',
33984                                     cls : 'fa fa-chevron-left'
33985                                 }
33986                             ]
33987                         },
33988                         {
33989                             tag : 'div',
33990                             cls : 'roo-document-slider-thumb',
33991                             cn : [
33992                                 {
33993                                     tag : 'img',
33994                                     cls : 'roo-document-slider-image'
33995                                 }
33996                             ]
33997                         },
33998                         {
33999                             tag : 'div',
34000                             cls : 'roo-document-slider-next',
34001                             cn : [
34002                                 {
34003                                     tag : 'i',
34004                                     cls : 'fa fa-chevron-right'
34005                                 }
34006                             ]
34007                         }
34008                     ]
34009                 }
34010             ]
34011         };
34012         
34013         return cfg;
34014     },
34015     
34016     initEvents : function()
34017     {
34018         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34019         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34020         
34021         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34022         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34023         
34024         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34025         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34026         
34027         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34028         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34029         
34030         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34031         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34032         
34033         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34034         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34035         
34036         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34037         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34038         
34039         this.thumbEl.on('click', this.onClick, this);
34040         
34041         this.prevIndicator.on('click', this.prev, this);
34042         
34043         this.nextIndicator.on('click', this.next, this);
34044         
34045     },
34046     
34047     initial : function()
34048     {
34049         if(this.files.length){
34050             this.indicator = 1;
34051             this.update()
34052         }
34053         
34054         this.fireEvent('initial', this);
34055     },
34056     
34057     update : function()
34058     {
34059         this.imageEl.attr('src', this.files[this.indicator - 1]);
34060         
34061         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34062         
34063         this.prevIndicator.show();
34064         
34065         if(this.indicator == 1){
34066             this.prevIndicator.hide();
34067         }
34068         
34069         this.nextIndicator.show();
34070         
34071         if(this.indicator == this.files.length){
34072             this.nextIndicator.hide();
34073         }
34074         
34075         this.thumbEl.scrollTo('top');
34076         
34077         this.fireEvent('update', this);
34078     },
34079     
34080     onClick : function(e)
34081     {
34082         e.preventDefault();
34083         
34084         this.fireEvent('click', this);
34085     },
34086     
34087     prev : function(e)
34088     {
34089         e.preventDefault();
34090         
34091         this.indicator = Math.max(1, this.indicator - 1);
34092         
34093         this.update();
34094     },
34095     
34096     next : function(e)
34097     {
34098         e.preventDefault();
34099         
34100         this.indicator = Math.min(this.files.length, this.indicator + 1);
34101         
34102         this.update();
34103     }
34104 });
34105 /*
34106  * - LGPL
34107  *
34108  * RadioSet
34109  *
34110  *
34111  */
34112
34113 /**
34114  * @class Roo.bootstrap.RadioSet
34115  * @extends Roo.bootstrap.Input
34116  * Bootstrap RadioSet class
34117  * @cfg {String} indicatorpos (left|right) default left
34118  * @cfg {Boolean} inline (true|false) inline the element (default true)
34119  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34120  * @constructor
34121  * Create a new RadioSet
34122  * @param {Object} config The config object
34123  */
34124
34125 Roo.bootstrap.RadioSet = function(config){
34126     
34127     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34128     
34129     this.radioes = [];
34130     
34131     Roo.bootstrap.RadioSet.register(this);
34132     
34133     this.addEvents({
34134         /**
34135         * @event check
34136         * Fires when the element is checked or unchecked.
34137         * @param {Roo.bootstrap.RadioSet} this This radio
34138         * @param {Roo.bootstrap.Radio} item The checked item
34139         */
34140        check : true,
34141        /**
34142         * @event click
34143         * Fires when the element is click.
34144         * @param {Roo.bootstrap.RadioSet} this This radio set
34145         * @param {Roo.bootstrap.Radio} item The checked item
34146         * @param {Roo.EventObject} e The event object
34147         */
34148        click : true
34149     });
34150     
34151 };
34152
34153 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34154
34155     radioes : false,
34156     
34157     inline : true,
34158     
34159     weight : '',
34160     
34161     indicatorpos : 'left',
34162     
34163     getAutoCreate : function()
34164     {
34165         var label = {
34166             tag : 'label',
34167             cls : 'roo-radio-set-label',
34168             cn : [
34169                 {
34170                     tag : 'span',
34171                     html : this.fieldLabel
34172                 }
34173             ]
34174         };
34175         if (Roo.bootstrap.version == 3) {
34176             
34177             
34178             if(this.indicatorpos == 'left'){
34179                 label.cn.unshift({
34180                     tag : 'i',
34181                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34182                     tooltip : 'This field is required'
34183                 });
34184             } else {
34185                 label.cn.push({
34186                     tag : 'i',
34187                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34188                     tooltip : 'This field is required'
34189                 });
34190             }
34191         }
34192         var items = {
34193             tag : 'div',
34194             cls : 'roo-radio-set-items'
34195         };
34196         
34197         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34198         
34199         if (align === 'left' && this.fieldLabel.length) {
34200             
34201             items = {
34202                 cls : "roo-radio-set-right", 
34203                 cn: [
34204                     items
34205                 ]
34206             };
34207             
34208             if(this.labelWidth > 12){
34209                 label.style = "width: " + this.labelWidth + 'px';
34210             }
34211             
34212             if(this.labelWidth < 13 && this.labelmd == 0){
34213                 this.labelmd = this.labelWidth;
34214             }
34215             
34216             if(this.labellg > 0){
34217                 label.cls += ' col-lg-' + this.labellg;
34218                 items.cls += ' col-lg-' + (12 - this.labellg);
34219             }
34220             
34221             if(this.labelmd > 0){
34222                 label.cls += ' col-md-' + this.labelmd;
34223                 items.cls += ' col-md-' + (12 - this.labelmd);
34224             }
34225             
34226             if(this.labelsm > 0){
34227                 label.cls += ' col-sm-' + this.labelsm;
34228                 items.cls += ' col-sm-' + (12 - this.labelsm);
34229             }
34230             
34231             if(this.labelxs > 0){
34232                 label.cls += ' col-xs-' + this.labelxs;
34233                 items.cls += ' col-xs-' + (12 - this.labelxs);
34234             }
34235         }
34236         
34237         var cfg = {
34238             tag : 'div',
34239             cls : 'roo-radio-set',
34240             cn : [
34241                 {
34242                     tag : 'input',
34243                     cls : 'roo-radio-set-input',
34244                     type : 'hidden',
34245                     name : this.name,
34246                     value : this.value ? this.value :  ''
34247                 },
34248                 label,
34249                 items
34250             ]
34251         };
34252         
34253         if(this.weight.length){
34254             cfg.cls += ' roo-radio-' + this.weight;
34255         }
34256         
34257         if(this.inline) {
34258             cfg.cls += ' roo-radio-set-inline';
34259         }
34260         
34261         var settings=this;
34262         ['xs','sm','md','lg'].map(function(size){
34263             if (settings[size]) {
34264                 cfg.cls += ' col-' + size + '-' + settings[size];
34265             }
34266         });
34267         
34268         return cfg;
34269         
34270     },
34271
34272     initEvents : function()
34273     {
34274         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34275         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34276         
34277         if(!this.fieldLabel.length){
34278             this.labelEl.hide();
34279         }
34280         
34281         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34282         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34283         
34284         this.indicator = this.indicatorEl();
34285         
34286         if(this.indicator){
34287             this.indicator.addClass('invisible');
34288         }
34289         
34290         this.originalValue = this.getValue();
34291         
34292     },
34293     
34294     inputEl: function ()
34295     {
34296         return this.el.select('.roo-radio-set-input', true).first();
34297     },
34298     
34299     getChildContainer : function()
34300     {
34301         return this.itemsEl;
34302     },
34303     
34304     register : function(item)
34305     {
34306         this.radioes.push(item);
34307         
34308     },
34309     
34310     validate : function()
34311     {   
34312         if(this.getVisibilityEl().hasClass('hidden')){
34313             return true;
34314         }
34315         
34316         var valid = false;
34317         
34318         Roo.each(this.radioes, function(i){
34319             if(!i.checked){
34320                 return;
34321             }
34322             
34323             valid = true;
34324             return false;
34325         });
34326         
34327         if(this.allowBlank) {
34328             return true;
34329         }
34330         
34331         if(this.disabled || valid){
34332             this.markValid();
34333             return true;
34334         }
34335         
34336         this.markInvalid();
34337         return false;
34338         
34339     },
34340     
34341     markValid : function()
34342     {
34343         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34344             this.indicatorEl().removeClass('visible');
34345             this.indicatorEl().addClass('invisible');
34346         }
34347         
34348         
34349         if (Roo.bootstrap.version == 3) {
34350             this.el.removeClass([this.invalidClass, this.validClass]);
34351             this.el.addClass(this.validClass);
34352         } else {
34353             this.el.removeClass(['is-invalid','is-valid']);
34354             this.el.addClass(['is-valid']);
34355         }
34356         this.fireEvent('valid', this);
34357     },
34358     
34359     markInvalid : function(msg)
34360     {
34361         if(this.allowBlank || this.disabled){
34362             return;
34363         }
34364         
34365         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34366             this.indicatorEl().removeClass('invisible');
34367             this.indicatorEl().addClass('visible');
34368         }
34369         if (Roo.bootstrap.version == 3) {
34370             this.el.removeClass([this.invalidClass, this.validClass]);
34371             this.el.addClass(this.invalidClass);
34372         } else {
34373             this.el.removeClass(['is-invalid','is-valid']);
34374             this.el.addClass(['is-invalid']);
34375         }
34376         
34377         this.fireEvent('invalid', this, msg);
34378         
34379     },
34380     
34381     setValue : function(v, suppressEvent)
34382     {   
34383         if(this.value === v){
34384             return;
34385         }
34386         
34387         this.value = v;
34388         
34389         if(this.rendered){
34390             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34391         }
34392         
34393         Roo.each(this.radioes, function(i){
34394             i.checked = false;
34395             i.el.removeClass('checked');
34396         });
34397         
34398         Roo.each(this.radioes, function(i){
34399             
34400             if(i.value === v || i.value.toString() === v.toString()){
34401                 i.checked = true;
34402                 i.el.addClass('checked');
34403                 
34404                 if(suppressEvent !== true){
34405                     this.fireEvent('check', this, i);
34406                 }
34407                 
34408                 return false;
34409             }
34410             
34411         }, this);
34412         
34413         this.validate();
34414     },
34415     
34416     clearInvalid : function(){
34417         
34418         if(!this.el || this.preventMark){
34419             return;
34420         }
34421         
34422         this.el.removeClass([this.invalidClass]);
34423         
34424         this.fireEvent('valid', this);
34425     }
34426     
34427 });
34428
34429 Roo.apply(Roo.bootstrap.RadioSet, {
34430     
34431     groups: {},
34432     
34433     register : function(set)
34434     {
34435         this.groups[set.name] = set;
34436     },
34437     
34438     get: function(name) 
34439     {
34440         if (typeof(this.groups[name]) == 'undefined') {
34441             return false;
34442         }
34443         
34444         return this.groups[name] ;
34445     }
34446     
34447 });
34448 /*
34449  * Based on:
34450  * Ext JS Library 1.1.1
34451  * Copyright(c) 2006-2007, Ext JS, LLC.
34452  *
34453  * Originally Released Under LGPL - original licence link has changed is not relivant.
34454  *
34455  * Fork - LGPL
34456  * <script type="text/javascript">
34457  */
34458
34459
34460 /**
34461  * @class Roo.bootstrap.SplitBar
34462  * @extends Roo.util.Observable
34463  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34464  * <br><br>
34465  * Usage:
34466  * <pre><code>
34467 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34468                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34469 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34470 split.minSize = 100;
34471 split.maxSize = 600;
34472 split.animate = true;
34473 split.on('moved', splitterMoved);
34474 </code></pre>
34475  * @constructor
34476  * Create a new SplitBar
34477  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34478  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34479  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34480  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34481                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34482                         position of the SplitBar).
34483  */
34484 Roo.bootstrap.SplitBar = function(cfg){
34485     
34486     /** @private */
34487     
34488     //{
34489     //  dragElement : elm
34490     //  resizingElement: el,
34491         // optional..
34492     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34493     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34494         // existingProxy ???
34495     //}
34496     
34497     this.el = Roo.get(cfg.dragElement, true);
34498     this.el.dom.unselectable = "on";
34499     /** @private */
34500     this.resizingEl = Roo.get(cfg.resizingElement, true);
34501
34502     /**
34503      * @private
34504      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34505      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34506      * @type Number
34507      */
34508     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34509     
34510     /**
34511      * The minimum size of the resizing element. (Defaults to 0)
34512      * @type Number
34513      */
34514     this.minSize = 0;
34515     
34516     /**
34517      * The maximum size of the resizing element. (Defaults to 2000)
34518      * @type Number
34519      */
34520     this.maxSize = 2000;
34521     
34522     /**
34523      * Whether to animate the transition to the new size
34524      * @type Boolean
34525      */
34526     this.animate = false;
34527     
34528     /**
34529      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34530      * @type Boolean
34531      */
34532     this.useShim = false;
34533     
34534     /** @private */
34535     this.shim = null;
34536     
34537     if(!cfg.existingProxy){
34538         /** @private */
34539         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34540     }else{
34541         this.proxy = Roo.get(cfg.existingProxy).dom;
34542     }
34543     /** @private */
34544     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34545     
34546     /** @private */
34547     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34548     
34549     /** @private */
34550     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34551     
34552     /** @private */
34553     this.dragSpecs = {};
34554     
34555     /**
34556      * @private The adapter to use to positon and resize elements
34557      */
34558     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34559     this.adapter.init(this);
34560     
34561     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34562         /** @private */
34563         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34564         this.el.addClass("roo-splitbar-h");
34565     }else{
34566         /** @private */
34567         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34568         this.el.addClass("roo-splitbar-v");
34569     }
34570     
34571     this.addEvents({
34572         /**
34573          * @event resize
34574          * Fires when the splitter is moved (alias for {@link #event-moved})
34575          * @param {Roo.bootstrap.SplitBar} this
34576          * @param {Number} newSize the new width or height
34577          */
34578         "resize" : true,
34579         /**
34580          * @event moved
34581          * Fires when the splitter is moved
34582          * @param {Roo.bootstrap.SplitBar} this
34583          * @param {Number} newSize the new width or height
34584          */
34585         "moved" : true,
34586         /**
34587          * @event beforeresize
34588          * Fires before the splitter is dragged
34589          * @param {Roo.bootstrap.SplitBar} this
34590          */
34591         "beforeresize" : true,
34592
34593         "beforeapply" : true
34594     });
34595
34596     Roo.util.Observable.call(this);
34597 };
34598
34599 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34600     onStartProxyDrag : function(x, y){
34601         this.fireEvent("beforeresize", this);
34602         if(!this.overlay){
34603             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34604             o.unselectable();
34605             o.enableDisplayMode("block");
34606             // all splitbars share the same overlay
34607             Roo.bootstrap.SplitBar.prototype.overlay = o;
34608         }
34609         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34610         this.overlay.show();
34611         Roo.get(this.proxy).setDisplayed("block");
34612         var size = this.adapter.getElementSize(this);
34613         this.activeMinSize = this.getMinimumSize();;
34614         this.activeMaxSize = this.getMaximumSize();;
34615         var c1 = size - this.activeMinSize;
34616         var c2 = Math.max(this.activeMaxSize - size, 0);
34617         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34618             this.dd.resetConstraints();
34619             this.dd.setXConstraint(
34620                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34621                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34622             );
34623             this.dd.setYConstraint(0, 0);
34624         }else{
34625             this.dd.resetConstraints();
34626             this.dd.setXConstraint(0, 0);
34627             this.dd.setYConstraint(
34628                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34629                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34630             );
34631          }
34632         this.dragSpecs.startSize = size;
34633         this.dragSpecs.startPoint = [x, y];
34634         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34635     },
34636     
34637     /** 
34638      * @private Called after the drag operation by the DDProxy
34639      */
34640     onEndProxyDrag : function(e){
34641         Roo.get(this.proxy).setDisplayed(false);
34642         var endPoint = Roo.lib.Event.getXY(e);
34643         if(this.overlay){
34644             this.overlay.hide();
34645         }
34646         var newSize;
34647         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34648             newSize = this.dragSpecs.startSize + 
34649                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34650                     endPoint[0] - this.dragSpecs.startPoint[0] :
34651                     this.dragSpecs.startPoint[0] - endPoint[0]
34652                 );
34653         }else{
34654             newSize = this.dragSpecs.startSize + 
34655                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34656                     endPoint[1] - this.dragSpecs.startPoint[1] :
34657                     this.dragSpecs.startPoint[1] - endPoint[1]
34658                 );
34659         }
34660         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34661         if(newSize != this.dragSpecs.startSize){
34662             if(this.fireEvent('beforeapply', this, newSize) !== false){
34663                 this.adapter.setElementSize(this, newSize);
34664                 this.fireEvent("moved", this, newSize);
34665                 this.fireEvent("resize", this, newSize);
34666             }
34667         }
34668     },
34669     
34670     /**
34671      * Get the adapter this SplitBar uses
34672      * @return The adapter object
34673      */
34674     getAdapter : function(){
34675         return this.adapter;
34676     },
34677     
34678     /**
34679      * Set the adapter this SplitBar uses
34680      * @param {Object} adapter A SplitBar adapter object
34681      */
34682     setAdapter : function(adapter){
34683         this.adapter = adapter;
34684         this.adapter.init(this);
34685     },
34686     
34687     /**
34688      * Gets the minimum size for the resizing element
34689      * @return {Number} The minimum size
34690      */
34691     getMinimumSize : function(){
34692         return this.minSize;
34693     },
34694     
34695     /**
34696      * Sets the minimum size for the resizing element
34697      * @param {Number} minSize The minimum size
34698      */
34699     setMinimumSize : function(minSize){
34700         this.minSize = minSize;
34701     },
34702     
34703     /**
34704      * Gets the maximum size for the resizing element
34705      * @return {Number} The maximum size
34706      */
34707     getMaximumSize : function(){
34708         return this.maxSize;
34709     },
34710     
34711     /**
34712      * Sets the maximum size for the resizing element
34713      * @param {Number} maxSize The maximum size
34714      */
34715     setMaximumSize : function(maxSize){
34716         this.maxSize = maxSize;
34717     },
34718     
34719     /**
34720      * Sets the initialize size for the resizing element
34721      * @param {Number} size The initial size
34722      */
34723     setCurrentSize : function(size){
34724         var oldAnimate = this.animate;
34725         this.animate = false;
34726         this.adapter.setElementSize(this, size);
34727         this.animate = oldAnimate;
34728     },
34729     
34730     /**
34731      * Destroy this splitbar. 
34732      * @param {Boolean} removeEl True to remove the element
34733      */
34734     destroy : function(removeEl){
34735         if(this.shim){
34736             this.shim.remove();
34737         }
34738         this.dd.unreg();
34739         this.proxy.parentNode.removeChild(this.proxy);
34740         if(removeEl){
34741             this.el.remove();
34742         }
34743     }
34744 });
34745
34746 /**
34747  * @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.
34748  */
34749 Roo.bootstrap.SplitBar.createProxy = function(dir){
34750     var proxy = new Roo.Element(document.createElement("div"));
34751     proxy.unselectable();
34752     var cls = 'roo-splitbar-proxy';
34753     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34754     document.body.appendChild(proxy.dom);
34755     return proxy.dom;
34756 };
34757
34758 /** 
34759  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34760  * Default Adapter. It assumes the splitter and resizing element are not positioned
34761  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34762  */
34763 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34764 };
34765
34766 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34767     // do nothing for now
34768     init : function(s){
34769     
34770     },
34771     /**
34772      * Called before drag operations to get the current size of the resizing element. 
34773      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34774      */
34775      getElementSize : function(s){
34776         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34777             return s.resizingEl.getWidth();
34778         }else{
34779             return s.resizingEl.getHeight();
34780         }
34781     },
34782     
34783     /**
34784      * Called after drag operations to set the size of the resizing element.
34785      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34786      * @param {Number} newSize The new size to set
34787      * @param {Function} onComplete A function to be invoked when resizing is complete
34788      */
34789     setElementSize : function(s, newSize, onComplete){
34790         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34791             if(!s.animate){
34792                 s.resizingEl.setWidth(newSize);
34793                 if(onComplete){
34794                     onComplete(s, newSize);
34795                 }
34796             }else{
34797                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34798             }
34799         }else{
34800             
34801             if(!s.animate){
34802                 s.resizingEl.setHeight(newSize);
34803                 if(onComplete){
34804                     onComplete(s, newSize);
34805                 }
34806             }else{
34807                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34808             }
34809         }
34810     }
34811 };
34812
34813 /** 
34814  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34815  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34816  * Adapter that  moves the splitter element to align with the resized sizing element. 
34817  * Used with an absolute positioned SplitBar.
34818  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34819  * document.body, make sure you assign an id to the body element.
34820  */
34821 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34822     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34823     this.container = Roo.get(container);
34824 };
34825
34826 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34827     init : function(s){
34828         this.basic.init(s);
34829     },
34830     
34831     getElementSize : function(s){
34832         return this.basic.getElementSize(s);
34833     },
34834     
34835     setElementSize : function(s, newSize, onComplete){
34836         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34837     },
34838     
34839     moveSplitter : function(s){
34840         var yes = Roo.bootstrap.SplitBar;
34841         switch(s.placement){
34842             case yes.LEFT:
34843                 s.el.setX(s.resizingEl.getRight());
34844                 break;
34845             case yes.RIGHT:
34846                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34847                 break;
34848             case yes.TOP:
34849                 s.el.setY(s.resizingEl.getBottom());
34850                 break;
34851             case yes.BOTTOM:
34852                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34853                 break;
34854         }
34855     }
34856 };
34857
34858 /**
34859  * Orientation constant - Create a vertical SplitBar
34860  * @static
34861  * @type Number
34862  */
34863 Roo.bootstrap.SplitBar.VERTICAL = 1;
34864
34865 /**
34866  * Orientation constant - Create a horizontal SplitBar
34867  * @static
34868  * @type Number
34869  */
34870 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34871
34872 /**
34873  * Placement constant - The resizing element is to the left of the splitter element
34874  * @static
34875  * @type Number
34876  */
34877 Roo.bootstrap.SplitBar.LEFT = 1;
34878
34879 /**
34880  * Placement constant - The resizing element is to the right of the splitter element
34881  * @static
34882  * @type Number
34883  */
34884 Roo.bootstrap.SplitBar.RIGHT = 2;
34885
34886 /**
34887  * Placement constant - The resizing element is positioned above the splitter element
34888  * @static
34889  * @type Number
34890  */
34891 Roo.bootstrap.SplitBar.TOP = 3;
34892
34893 /**
34894  * Placement constant - The resizing element is positioned under splitter element
34895  * @static
34896  * @type Number
34897  */
34898 Roo.bootstrap.SplitBar.BOTTOM = 4;
34899 Roo.namespace("Roo.bootstrap.layout");/*
34900  * Based on:
34901  * Ext JS Library 1.1.1
34902  * Copyright(c) 2006-2007, Ext JS, LLC.
34903  *
34904  * Originally Released Under LGPL - original licence link has changed is not relivant.
34905  *
34906  * Fork - LGPL
34907  * <script type="text/javascript">
34908  */
34909
34910 /**
34911  * @class Roo.bootstrap.layout.Manager
34912  * @extends Roo.bootstrap.Component
34913  * Base class for layout managers.
34914  */
34915 Roo.bootstrap.layout.Manager = function(config)
34916 {
34917     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34918
34919
34920
34921
34922
34923     /** false to disable window resize monitoring @type Boolean */
34924     this.monitorWindowResize = true;
34925     this.regions = {};
34926     this.addEvents({
34927         /**
34928          * @event layout
34929          * Fires when a layout is performed.
34930          * @param {Roo.LayoutManager} this
34931          */
34932         "layout" : true,
34933         /**
34934          * @event regionresized
34935          * Fires when the user resizes a region.
34936          * @param {Roo.LayoutRegion} region The resized region
34937          * @param {Number} newSize The new size (width for east/west, height for north/south)
34938          */
34939         "regionresized" : true,
34940         /**
34941          * @event regioncollapsed
34942          * Fires when a region is collapsed.
34943          * @param {Roo.LayoutRegion} region The collapsed region
34944          */
34945         "regioncollapsed" : true,
34946         /**
34947          * @event regionexpanded
34948          * Fires when a region is expanded.
34949          * @param {Roo.LayoutRegion} region The expanded region
34950          */
34951         "regionexpanded" : true
34952     });
34953     this.updating = false;
34954
34955     if (config.el) {
34956         this.el = Roo.get(config.el);
34957         this.initEvents();
34958     }
34959
34960 };
34961
34962 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34963
34964
34965     regions : null,
34966
34967     monitorWindowResize : true,
34968
34969
34970     updating : false,
34971
34972
34973     onRender : function(ct, position)
34974     {
34975         if(!this.el){
34976             this.el = Roo.get(ct);
34977             this.initEvents();
34978         }
34979         //this.fireEvent('render',this);
34980     },
34981
34982
34983     initEvents: function()
34984     {
34985
34986
34987         // ie scrollbar fix
34988         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34989             document.body.scroll = "no";
34990         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34991             this.el.position('relative');
34992         }
34993         this.id = this.el.id;
34994         this.el.addClass("roo-layout-container");
34995         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34996         if(this.el.dom != document.body ) {
34997             this.el.on('resize', this.layout,this);
34998             this.el.on('show', this.layout,this);
34999         }
35000
35001     },
35002
35003     /**
35004      * Returns true if this layout is currently being updated
35005      * @return {Boolean}
35006      */
35007     isUpdating : function(){
35008         return this.updating;
35009     },
35010
35011     /**
35012      * Suspend the LayoutManager from doing auto-layouts while
35013      * making multiple add or remove calls
35014      */
35015     beginUpdate : function(){
35016         this.updating = true;
35017     },
35018
35019     /**
35020      * Restore auto-layouts and optionally disable the manager from performing a layout
35021      * @param {Boolean} noLayout true to disable a layout update
35022      */
35023     endUpdate : function(noLayout){
35024         this.updating = false;
35025         if(!noLayout){
35026             this.layout();
35027         }
35028     },
35029
35030     layout: function(){
35031         // abstract...
35032     },
35033
35034     onRegionResized : function(region, newSize){
35035         this.fireEvent("regionresized", region, newSize);
35036         this.layout();
35037     },
35038
35039     onRegionCollapsed : function(region){
35040         this.fireEvent("regioncollapsed", region);
35041     },
35042
35043     onRegionExpanded : function(region){
35044         this.fireEvent("regionexpanded", region);
35045     },
35046
35047     /**
35048      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35049      * performs box-model adjustments.
35050      * @return {Object} The size as an object {width: (the width), height: (the height)}
35051      */
35052     getViewSize : function()
35053     {
35054         var size;
35055         if(this.el.dom != document.body){
35056             size = this.el.getSize();
35057         }else{
35058             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35059         }
35060         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35061         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35062         return size;
35063     },
35064
35065     /**
35066      * Returns the Element this layout is bound to.
35067      * @return {Roo.Element}
35068      */
35069     getEl : function(){
35070         return this.el;
35071     },
35072
35073     /**
35074      * Returns the specified region.
35075      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35076      * @return {Roo.LayoutRegion}
35077      */
35078     getRegion : function(target){
35079         return this.regions[target.toLowerCase()];
35080     },
35081
35082     onWindowResize : function(){
35083         if(this.monitorWindowResize){
35084             this.layout();
35085         }
35086     }
35087 });
35088 /*
35089  * Based on:
35090  * Ext JS Library 1.1.1
35091  * Copyright(c) 2006-2007, Ext JS, LLC.
35092  *
35093  * Originally Released Under LGPL - original licence link has changed is not relivant.
35094  *
35095  * Fork - LGPL
35096  * <script type="text/javascript">
35097  */
35098 /**
35099  * @class Roo.bootstrap.layout.Border
35100  * @extends Roo.bootstrap.layout.Manager
35101  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35102  * please see: examples/bootstrap/nested.html<br><br>
35103  
35104 <b>The container the layout is rendered into can be either the body element or any other element.
35105 If it is not the body element, the container needs to either be an absolute positioned element,
35106 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35107 the container size if it is not the body element.</b>
35108
35109 * @constructor
35110 * Create a new Border
35111 * @param {Object} config Configuration options
35112  */
35113 Roo.bootstrap.layout.Border = function(config){
35114     config = config || {};
35115     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35116     
35117     
35118     
35119     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35120         if(config[region]){
35121             config[region].region = region;
35122             this.addRegion(config[region]);
35123         }
35124     },this);
35125     
35126 };
35127
35128 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35129
35130 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35131     
35132     parent : false, // this might point to a 'nest' or a ???
35133     
35134     /**
35135      * Creates and adds a new region if it doesn't already exist.
35136      * @param {String} target The target region key (north, south, east, west or center).
35137      * @param {Object} config The regions config object
35138      * @return {BorderLayoutRegion} The new region
35139      */
35140     addRegion : function(config)
35141     {
35142         if(!this.regions[config.region]){
35143             var r = this.factory(config);
35144             this.bindRegion(r);
35145         }
35146         return this.regions[config.region];
35147     },
35148
35149     // private (kinda)
35150     bindRegion : function(r){
35151         this.regions[r.config.region] = r;
35152         
35153         r.on("visibilitychange",    this.layout, this);
35154         r.on("paneladded",          this.layout, this);
35155         r.on("panelremoved",        this.layout, this);
35156         r.on("invalidated",         this.layout, this);
35157         r.on("resized",             this.onRegionResized, this);
35158         r.on("collapsed",           this.onRegionCollapsed, this);
35159         r.on("expanded",            this.onRegionExpanded, this);
35160     },
35161
35162     /**
35163      * Performs a layout update.
35164      */
35165     layout : function()
35166     {
35167         if(this.updating) {
35168             return;
35169         }
35170         
35171         // render all the rebions if they have not been done alreayd?
35172         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35173             if(this.regions[region] && !this.regions[region].bodyEl){
35174                 this.regions[region].onRender(this.el)
35175             }
35176         },this);
35177         
35178         var size = this.getViewSize();
35179         var w = size.width;
35180         var h = size.height;
35181         var centerW = w;
35182         var centerH = h;
35183         var centerY = 0;
35184         var centerX = 0;
35185         //var x = 0, y = 0;
35186
35187         var rs = this.regions;
35188         var north = rs["north"];
35189         var south = rs["south"]; 
35190         var west = rs["west"];
35191         var east = rs["east"];
35192         var center = rs["center"];
35193         //if(this.hideOnLayout){ // not supported anymore
35194             //c.el.setStyle("display", "none");
35195         //}
35196         if(north && north.isVisible()){
35197             var b = north.getBox();
35198             var m = north.getMargins();
35199             b.width = w - (m.left+m.right);
35200             b.x = m.left;
35201             b.y = m.top;
35202             centerY = b.height + b.y + m.bottom;
35203             centerH -= centerY;
35204             north.updateBox(this.safeBox(b));
35205         }
35206         if(south && south.isVisible()){
35207             var b = south.getBox();
35208             var m = south.getMargins();
35209             b.width = w - (m.left+m.right);
35210             b.x = m.left;
35211             var totalHeight = (b.height + m.top + m.bottom);
35212             b.y = h - totalHeight + m.top;
35213             centerH -= totalHeight;
35214             south.updateBox(this.safeBox(b));
35215         }
35216         if(west && west.isVisible()){
35217             var b = west.getBox();
35218             var m = west.getMargins();
35219             b.height = centerH - (m.top+m.bottom);
35220             b.x = m.left;
35221             b.y = centerY + m.top;
35222             var totalWidth = (b.width + m.left + m.right);
35223             centerX += totalWidth;
35224             centerW -= totalWidth;
35225             west.updateBox(this.safeBox(b));
35226         }
35227         if(east && east.isVisible()){
35228             var b = east.getBox();
35229             var m = east.getMargins();
35230             b.height = centerH - (m.top+m.bottom);
35231             var totalWidth = (b.width + m.left + m.right);
35232             b.x = w - totalWidth + m.left;
35233             b.y = centerY + m.top;
35234             centerW -= totalWidth;
35235             east.updateBox(this.safeBox(b));
35236         }
35237         if(center){
35238             var m = center.getMargins();
35239             var centerBox = {
35240                 x: centerX + m.left,
35241                 y: centerY + m.top,
35242                 width: centerW - (m.left+m.right),
35243                 height: centerH - (m.top+m.bottom)
35244             };
35245             //if(this.hideOnLayout){
35246                 //center.el.setStyle("display", "block");
35247             //}
35248             center.updateBox(this.safeBox(centerBox));
35249         }
35250         this.el.repaint();
35251         this.fireEvent("layout", this);
35252     },
35253
35254     // private
35255     safeBox : function(box){
35256         box.width = Math.max(0, box.width);
35257         box.height = Math.max(0, box.height);
35258         return box;
35259     },
35260
35261     /**
35262      * Adds a ContentPanel (or subclass) to this layout.
35263      * @param {String} target The target region key (north, south, east, west or center).
35264      * @param {Roo.ContentPanel} panel The panel to add
35265      * @return {Roo.ContentPanel} The added panel
35266      */
35267     add : function(target, panel){
35268          
35269         target = target.toLowerCase();
35270         return this.regions[target].add(panel);
35271     },
35272
35273     /**
35274      * Remove a ContentPanel (or subclass) to this layout.
35275      * @param {String} target The target region key (north, south, east, west or center).
35276      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35277      * @return {Roo.ContentPanel} The removed panel
35278      */
35279     remove : function(target, panel){
35280         target = target.toLowerCase();
35281         return this.regions[target].remove(panel);
35282     },
35283
35284     /**
35285      * Searches all regions for a panel with the specified id
35286      * @param {String} panelId
35287      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35288      */
35289     findPanel : function(panelId){
35290         var rs = this.regions;
35291         for(var target in rs){
35292             if(typeof rs[target] != "function"){
35293                 var p = rs[target].getPanel(panelId);
35294                 if(p){
35295                     return p;
35296                 }
35297             }
35298         }
35299         return null;
35300     },
35301
35302     /**
35303      * Searches all regions for a panel with the specified id and activates (shows) it.
35304      * @param {String/ContentPanel} panelId The panels id or the panel itself
35305      * @return {Roo.ContentPanel} The shown panel or null
35306      */
35307     showPanel : function(panelId) {
35308       var rs = this.regions;
35309       for(var target in rs){
35310          var r = rs[target];
35311          if(typeof r != "function"){
35312             if(r.hasPanel(panelId)){
35313                return r.showPanel(panelId);
35314             }
35315          }
35316       }
35317       return null;
35318    },
35319
35320    /**
35321      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35322      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35323      */
35324    /*
35325     restoreState : function(provider){
35326         if(!provider){
35327             provider = Roo.state.Manager;
35328         }
35329         var sm = new Roo.LayoutStateManager();
35330         sm.init(this, provider);
35331     },
35332 */
35333  
35334  
35335     /**
35336      * Adds a xtype elements to the layout.
35337      * <pre><code>
35338
35339 layout.addxtype({
35340        xtype : 'ContentPanel',
35341        region: 'west',
35342        items: [ .... ]
35343    }
35344 );
35345
35346 layout.addxtype({
35347         xtype : 'NestedLayoutPanel',
35348         region: 'west',
35349         layout: {
35350            center: { },
35351            west: { }   
35352         },
35353         items : [ ... list of content panels or nested layout panels.. ]
35354    }
35355 );
35356 </code></pre>
35357      * @param {Object} cfg Xtype definition of item to add.
35358      */
35359     addxtype : function(cfg)
35360     {
35361         // basically accepts a pannel...
35362         // can accept a layout region..!?!?
35363         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35364         
35365         
35366         // theory?  children can only be panels??
35367         
35368         //if (!cfg.xtype.match(/Panel$/)) {
35369         //    return false;
35370         //}
35371         var ret = false;
35372         
35373         if (typeof(cfg.region) == 'undefined') {
35374             Roo.log("Failed to add Panel, region was not set");
35375             Roo.log(cfg);
35376             return false;
35377         }
35378         var region = cfg.region;
35379         delete cfg.region;
35380         
35381           
35382         var xitems = [];
35383         if (cfg.items) {
35384             xitems = cfg.items;
35385             delete cfg.items;
35386         }
35387         var nb = false;
35388         
35389         if ( region == 'center') {
35390             Roo.log("Center: " + cfg.title);
35391         }
35392         
35393         
35394         switch(cfg.xtype) 
35395         {
35396             case 'Content':  // ContentPanel (el, cfg)
35397             case 'Scroll':  // ContentPanel (el, cfg)
35398             case 'View': 
35399                 cfg.autoCreate = cfg.autoCreate || true;
35400                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35401                 //} else {
35402                 //    var el = this.el.createChild();
35403                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35404                 //}
35405                 
35406                 this.add(region, ret);
35407                 break;
35408             
35409             /*
35410             case 'TreePanel': // our new panel!
35411                 cfg.el = this.el.createChild();
35412                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35413                 this.add(region, ret);
35414                 break;
35415             */
35416             
35417             case 'Nest': 
35418                 // create a new Layout (which is  a Border Layout...
35419                 
35420                 var clayout = cfg.layout;
35421                 clayout.el  = this.el.createChild();
35422                 clayout.items   = clayout.items  || [];
35423                 
35424                 delete cfg.layout;
35425                 
35426                 // replace this exitems with the clayout ones..
35427                 xitems = clayout.items;
35428                  
35429                 // force background off if it's in center...
35430                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35431                     cfg.background = false;
35432                 }
35433                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35434                 
35435                 
35436                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35437                 //console.log('adding nested layout panel '  + cfg.toSource());
35438                 this.add(region, ret);
35439                 nb = {}; /// find first...
35440                 break;
35441             
35442             case 'Grid':
35443                 
35444                 // needs grid and region
35445                 
35446                 //var el = this.getRegion(region).el.createChild();
35447                 /*
35448                  *var el = this.el.createChild();
35449                 // create the grid first...
35450                 cfg.grid.container = el;
35451                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35452                 */
35453                 
35454                 if (region == 'center' && this.active ) {
35455                     cfg.background = false;
35456                 }
35457                 
35458                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35459                 
35460                 this.add(region, ret);
35461                 /*
35462                 if (cfg.background) {
35463                     // render grid on panel activation (if panel background)
35464                     ret.on('activate', function(gp) {
35465                         if (!gp.grid.rendered) {
35466                     //        gp.grid.render(el);
35467                         }
35468                     });
35469                 } else {
35470                   //  cfg.grid.render(el);
35471                 }
35472                 */
35473                 break;
35474            
35475            
35476             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35477                 // it was the old xcomponent building that caused this before.
35478                 // espeically if border is the top element in the tree.
35479                 ret = this;
35480                 break; 
35481                 
35482                     
35483                 
35484                 
35485                 
35486             default:
35487                 /*
35488                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35489                     
35490                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35491                     this.add(region, ret);
35492                 } else {
35493                 */
35494                     Roo.log(cfg);
35495                     throw "Can not add '" + cfg.xtype + "' to Border";
35496                     return null;
35497              
35498                                 
35499              
35500         }
35501         this.beginUpdate();
35502         // add children..
35503         var region = '';
35504         var abn = {};
35505         Roo.each(xitems, function(i)  {
35506             region = nb && i.region ? i.region : false;
35507             
35508             var add = ret.addxtype(i);
35509            
35510             if (region) {
35511                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35512                 if (!i.background) {
35513                     abn[region] = nb[region] ;
35514                 }
35515             }
35516             
35517         });
35518         this.endUpdate();
35519
35520         // make the last non-background panel active..
35521         //if (nb) { Roo.log(abn); }
35522         if (nb) {
35523             
35524             for(var r in abn) {
35525                 region = this.getRegion(r);
35526                 if (region) {
35527                     // tried using nb[r], but it does not work..
35528                      
35529                     region.showPanel(abn[r]);
35530                    
35531                 }
35532             }
35533         }
35534         return ret;
35535         
35536     },
35537     
35538     
35539 // private
35540     factory : function(cfg)
35541     {
35542         
35543         var validRegions = Roo.bootstrap.layout.Border.regions;
35544
35545         var target = cfg.region;
35546         cfg.mgr = this;
35547         
35548         var r = Roo.bootstrap.layout;
35549         Roo.log(target);
35550         switch(target){
35551             case "north":
35552                 return new r.North(cfg);
35553             case "south":
35554                 return new r.South(cfg);
35555             case "east":
35556                 return new r.East(cfg);
35557             case "west":
35558                 return new r.West(cfg);
35559             case "center":
35560                 return new r.Center(cfg);
35561         }
35562         throw 'Layout region "'+target+'" not supported.';
35563     }
35564     
35565     
35566 });
35567  /*
35568  * Based on:
35569  * Ext JS Library 1.1.1
35570  * Copyright(c) 2006-2007, Ext JS, LLC.
35571  *
35572  * Originally Released Under LGPL - original licence link has changed is not relivant.
35573  *
35574  * Fork - LGPL
35575  * <script type="text/javascript">
35576  */
35577  
35578 /**
35579  * @class Roo.bootstrap.layout.Basic
35580  * @extends Roo.util.Observable
35581  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35582  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35583  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35584  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35585  * @cfg {string}   region  the region that it inhabits..
35586  * @cfg {bool}   skipConfig skip config?
35587  * 
35588
35589  */
35590 Roo.bootstrap.layout.Basic = function(config){
35591     
35592     this.mgr = config.mgr;
35593     
35594     this.position = config.region;
35595     
35596     var skipConfig = config.skipConfig;
35597     
35598     this.events = {
35599         /**
35600          * @scope Roo.BasicLayoutRegion
35601          */
35602         
35603         /**
35604          * @event beforeremove
35605          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35606          * @param {Roo.LayoutRegion} this
35607          * @param {Roo.ContentPanel} panel The panel
35608          * @param {Object} e The cancel event object
35609          */
35610         "beforeremove" : true,
35611         /**
35612          * @event invalidated
35613          * Fires when the layout for this region is changed.
35614          * @param {Roo.LayoutRegion} this
35615          */
35616         "invalidated" : true,
35617         /**
35618          * @event visibilitychange
35619          * Fires when this region is shown or hidden 
35620          * @param {Roo.LayoutRegion} this
35621          * @param {Boolean} visibility true or false
35622          */
35623         "visibilitychange" : true,
35624         /**
35625          * @event paneladded
35626          * Fires when a panel is added. 
35627          * @param {Roo.LayoutRegion} this
35628          * @param {Roo.ContentPanel} panel The panel
35629          */
35630         "paneladded" : true,
35631         /**
35632          * @event panelremoved
35633          * Fires when a panel is removed. 
35634          * @param {Roo.LayoutRegion} this
35635          * @param {Roo.ContentPanel} panel The panel
35636          */
35637         "panelremoved" : true,
35638         /**
35639          * @event beforecollapse
35640          * Fires when this region before collapse.
35641          * @param {Roo.LayoutRegion} this
35642          */
35643         "beforecollapse" : true,
35644         /**
35645          * @event collapsed
35646          * Fires when this region is collapsed.
35647          * @param {Roo.LayoutRegion} this
35648          */
35649         "collapsed" : true,
35650         /**
35651          * @event expanded
35652          * Fires when this region is expanded.
35653          * @param {Roo.LayoutRegion} this
35654          */
35655         "expanded" : true,
35656         /**
35657          * @event slideshow
35658          * Fires when this region is slid into view.
35659          * @param {Roo.LayoutRegion} this
35660          */
35661         "slideshow" : true,
35662         /**
35663          * @event slidehide
35664          * Fires when this region slides out of view. 
35665          * @param {Roo.LayoutRegion} this
35666          */
35667         "slidehide" : true,
35668         /**
35669          * @event panelactivated
35670          * Fires when a panel is activated. 
35671          * @param {Roo.LayoutRegion} this
35672          * @param {Roo.ContentPanel} panel The activated panel
35673          */
35674         "panelactivated" : true,
35675         /**
35676          * @event resized
35677          * Fires when the user resizes this region. 
35678          * @param {Roo.LayoutRegion} this
35679          * @param {Number} newSize The new size (width for east/west, height for north/south)
35680          */
35681         "resized" : true
35682     };
35683     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35684     this.panels = new Roo.util.MixedCollection();
35685     this.panels.getKey = this.getPanelId.createDelegate(this);
35686     this.box = null;
35687     this.activePanel = null;
35688     // ensure listeners are added...
35689     
35690     if (config.listeners || config.events) {
35691         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35692             listeners : config.listeners || {},
35693             events : config.events || {}
35694         });
35695     }
35696     
35697     if(skipConfig !== true){
35698         this.applyConfig(config);
35699     }
35700 };
35701
35702 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35703 {
35704     getPanelId : function(p){
35705         return p.getId();
35706     },
35707     
35708     applyConfig : function(config){
35709         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35710         this.config = config;
35711         
35712     },
35713     
35714     /**
35715      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35716      * the width, for horizontal (north, south) the height.
35717      * @param {Number} newSize The new width or height
35718      */
35719     resizeTo : function(newSize){
35720         var el = this.el ? this.el :
35721                  (this.activePanel ? this.activePanel.getEl() : null);
35722         if(el){
35723             switch(this.position){
35724                 case "east":
35725                 case "west":
35726                     el.setWidth(newSize);
35727                     this.fireEvent("resized", this, newSize);
35728                 break;
35729                 case "north":
35730                 case "south":
35731                     el.setHeight(newSize);
35732                     this.fireEvent("resized", this, newSize);
35733                 break;                
35734             }
35735         }
35736     },
35737     
35738     getBox : function(){
35739         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35740     },
35741     
35742     getMargins : function(){
35743         return this.margins;
35744     },
35745     
35746     updateBox : function(box){
35747         this.box = box;
35748         var el = this.activePanel.getEl();
35749         el.dom.style.left = box.x + "px";
35750         el.dom.style.top = box.y + "px";
35751         this.activePanel.setSize(box.width, box.height);
35752     },
35753     
35754     /**
35755      * Returns the container element for this region.
35756      * @return {Roo.Element}
35757      */
35758     getEl : function(){
35759         return this.activePanel;
35760     },
35761     
35762     /**
35763      * Returns true if this region is currently visible.
35764      * @return {Boolean}
35765      */
35766     isVisible : function(){
35767         return this.activePanel ? true : false;
35768     },
35769     
35770     setActivePanel : function(panel){
35771         panel = this.getPanel(panel);
35772         if(this.activePanel && this.activePanel != panel){
35773             this.activePanel.setActiveState(false);
35774             this.activePanel.getEl().setLeftTop(-10000,-10000);
35775         }
35776         this.activePanel = panel;
35777         panel.setActiveState(true);
35778         if(this.box){
35779             panel.setSize(this.box.width, this.box.height);
35780         }
35781         this.fireEvent("panelactivated", this, panel);
35782         this.fireEvent("invalidated");
35783     },
35784     
35785     /**
35786      * Show the specified panel.
35787      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35788      * @return {Roo.ContentPanel} The shown panel or null
35789      */
35790     showPanel : function(panel){
35791         panel = this.getPanel(panel);
35792         if(panel){
35793             this.setActivePanel(panel);
35794         }
35795         return panel;
35796     },
35797     
35798     /**
35799      * Get the active panel for this region.
35800      * @return {Roo.ContentPanel} The active panel or null
35801      */
35802     getActivePanel : function(){
35803         return this.activePanel;
35804     },
35805     
35806     /**
35807      * Add the passed ContentPanel(s)
35808      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35809      * @return {Roo.ContentPanel} The panel added (if only one was added)
35810      */
35811     add : function(panel){
35812         if(arguments.length > 1){
35813             for(var i = 0, len = arguments.length; i < len; i++) {
35814                 this.add(arguments[i]);
35815             }
35816             return null;
35817         }
35818         if(this.hasPanel(panel)){
35819             this.showPanel(panel);
35820             return panel;
35821         }
35822         var el = panel.getEl();
35823         if(el.dom.parentNode != this.mgr.el.dom){
35824             this.mgr.el.dom.appendChild(el.dom);
35825         }
35826         if(panel.setRegion){
35827             panel.setRegion(this);
35828         }
35829         this.panels.add(panel);
35830         el.setStyle("position", "absolute");
35831         if(!panel.background){
35832             this.setActivePanel(panel);
35833             if(this.config.initialSize && this.panels.getCount()==1){
35834                 this.resizeTo(this.config.initialSize);
35835             }
35836         }
35837         this.fireEvent("paneladded", this, panel);
35838         return panel;
35839     },
35840     
35841     /**
35842      * Returns true if the panel is in this region.
35843      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35844      * @return {Boolean}
35845      */
35846     hasPanel : function(panel){
35847         if(typeof panel == "object"){ // must be panel obj
35848             panel = panel.getId();
35849         }
35850         return this.getPanel(panel) ? true : false;
35851     },
35852     
35853     /**
35854      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35855      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35856      * @param {Boolean} preservePanel Overrides the config preservePanel option
35857      * @return {Roo.ContentPanel} The panel that was removed
35858      */
35859     remove : function(panel, preservePanel){
35860         panel = this.getPanel(panel);
35861         if(!panel){
35862             return null;
35863         }
35864         var e = {};
35865         this.fireEvent("beforeremove", this, panel, e);
35866         if(e.cancel === true){
35867             return null;
35868         }
35869         var panelId = panel.getId();
35870         this.panels.removeKey(panelId);
35871         return panel;
35872     },
35873     
35874     /**
35875      * Returns the panel specified or null if it's not in this region.
35876      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35877      * @return {Roo.ContentPanel}
35878      */
35879     getPanel : function(id){
35880         if(typeof id == "object"){ // must be panel obj
35881             return id;
35882         }
35883         return this.panels.get(id);
35884     },
35885     
35886     /**
35887      * Returns this regions position (north/south/east/west/center).
35888      * @return {String} 
35889      */
35890     getPosition: function(){
35891         return this.position;    
35892     }
35893 });/*
35894  * Based on:
35895  * Ext JS Library 1.1.1
35896  * Copyright(c) 2006-2007, Ext JS, LLC.
35897  *
35898  * Originally Released Under LGPL - original licence link has changed is not relivant.
35899  *
35900  * Fork - LGPL
35901  * <script type="text/javascript">
35902  */
35903  
35904 /**
35905  * @class Roo.bootstrap.layout.Region
35906  * @extends Roo.bootstrap.layout.Basic
35907  * This class represents a region in a layout manager.
35908  
35909  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35910  * @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})
35911  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35912  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35913  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35914  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35915  * @cfg {String}    title           The title for the region (overrides panel titles)
35916  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35917  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35918  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35919  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35920  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35921  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35922  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35923  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35924  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35925  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35926
35927  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35928  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35929  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35930  * @cfg {Number}    width           For East/West panels
35931  * @cfg {Number}    height          For North/South panels
35932  * @cfg {Boolean}   split           To show the splitter
35933  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35934  * 
35935  * @cfg {string}   cls             Extra CSS classes to add to region
35936  * 
35937  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35938  * @cfg {string}   region  the region that it inhabits..
35939  *
35940
35941  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35942  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35943
35944  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35945  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35946  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35947  */
35948 Roo.bootstrap.layout.Region = function(config)
35949 {
35950     this.applyConfig(config);
35951
35952     var mgr = config.mgr;
35953     var pos = config.region;
35954     config.skipConfig = true;
35955     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35956     
35957     if (mgr.el) {
35958         this.onRender(mgr.el);   
35959     }
35960      
35961     this.visible = true;
35962     this.collapsed = false;
35963     this.unrendered_panels = [];
35964 };
35965
35966 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35967
35968     position: '', // set by wrapper (eg. north/south etc..)
35969     unrendered_panels : null,  // unrendered panels.
35970     
35971     tabPosition : false,
35972     
35973     mgr: false, // points to 'Border'
35974     
35975     
35976     createBody : function(){
35977         /** This region's body element 
35978         * @type Roo.Element */
35979         this.bodyEl = this.el.createChild({
35980                 tag: "div",
35981                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35982         });
35983     },
35984
35985     onRender: function(ctr, pos)
35986     {
35987         var dh = Roo.DomHelper;
35988         /** This region's container element 
35989         * @type Roo.Element */
35990         this.el = dh.append(ctr.dom, {
35991                 tag: "div",
35992                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35993             }, true);
35994         /** This region's title element 
35995         * @type Roo.Element */
35996     
35997         this.titleEl = dh.append(this.el.dom,  {
35998                 tag: "div",
35999                 unselectable: "on",
36000                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36001                 children:[
36002                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36003                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36004                 ]
36005             }, true);
36006         
36007         this.titleEl.enableDisplayMode();
36008         /** This region's title text element 
36009         * @type HTMLElement */
36010         this.titleTextEl = this.titleEl.dom.firstChild;
36011         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36012         /*
36013         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36014         this.closeBtn.enableDisplayMode();
36015         this.closeBtn.on("click", this.closeClicked, this);
36016         this.closeBtn.hide();
36017     */
36018         this.createBody(this.config);
36019         if(this.config.hideWhenEmpty){
36020             this.hide();
36021             this.on("paneladded", this.validateVisibility, this);
36022             this.on("panelremoved", this.validateVisibility, this);
36023         }
36024         if(this.autoScroll){
36025             this.bodyEl.setStyle("overflow", "auto");
36026         }else{
36027             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36028         }
36029         //if(c.titlebar !== false){
36030             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36031                 this.titleEl.hide();
36032             }else{
36033                 this.titleEl.show();
36034                 if(this.config.title){
36035                     this.titleTextEl.innerHTML = this.config.title;
36036                 }
36037             }
36038         //}
36039         if(this.config.collapsed){
36040             this.collapse(true);
36041         }
36042         if(this.config.hidden){
36043             this.hide();
36044         }
36045         
36046         if (this.unrendered_panels && this.unrendered_panels.length) {
36047             for (var i =0;i< this.unrendered_panels.length; i++) {
36048                 this.add(this.unrendered_panels[i]);
36049             }
36050             this.unrendered_panels = null;
36051             
36052         }
36053         
36054     },
36055     
36056     applyConfig : function(c)
36057     {
36058         /*
36059          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36060             var dh = Roo.DomHelper;
36061             if(c.titlebar !== false){
36062                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36063                 this.collapseBtn.on("click", this.collapse, this);
36064                 this.collapseBtn.enableDisplayMode();
36065                 /*
36066                 if(c.showPin === true || this.showPin){
36067                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36068                     this.stickBtn.enableDisplayMode();
36069                     this.stickBtn.on("click", this.expand, this);
36070                     this.stickBtn.hide();
36071                 }
36072                 
36073             }
36074             */
36075             /** This region's collapsed element
36076             * @type Roo.Element */
36077             /*
36078              *
36079             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36080                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36081             ]}, true);
36082             
36083             if(c.floatable !== false){
36084                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36085                this.collapsedEl.on("click", this.collapseClick, this);
36086             }
36087
36088             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36089                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36090                    id: "message", unselectable: "on", style:{"float":"left"}});
36091                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36092              }
36093             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36094             this.expandBtn.on("click", this.expand, this);
36095             
36096         }
36097         
36098         if(this.collapseBtn){
36099             this.collapseBtn.setVisible(c.collapsible == true);
36100         }
36101         
36102         this.cmargins = c.cmargins || this.cmargins ||
36103                          (this.position == "west" || this.position == "east" ?
36104                              {top: 0, left: 2, right:2, bottom: 0} :
36105                              {top: 2, left: 0, right:0, bottom: 2});
36106         */
36107         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36108         
36109         
36110         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36111         
36112         this.autoScroll = c.autoScroll || false;
36113         
36114         
36115        
36116         
36117         this.duration = c.duration || .30;
36118         this.slideDuration = c.slideDuration || .45;
36119         this.config = c;
36120        
36121     },
36122     /**
36123      * Returns true if this region is currently visible.
36124      * @return {Boolean}
36125      */
36126     isVisible : function(){
36127         return this.visible;
36128     },
36129
36130     /**
36131      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36132      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36133      */
36134     //setCollapsedTitle : function(title){
36135     //    title = title || "&#160;";
36136      //   if(this.collapsedTitleTextEl){
36137       //      this.collapsedTitleTextEl.innerHTML = title;
36138        // }
36139     //},
36140
36141     getBox : function(){
36142         var b;
36143       //  if(!this.collapsed){
36144             b = this.el.getBox(false, true);
36145        // }else{
36146           //  b = this.collapsedEl.getBox(false, true);
36147         //}
36148         return b;
36149     },
36150
36151     getMargins : function(){
36152         return this.margins;
36153         //return this.collapsed ? this.cmargins : this.margins;
36154     },
36155 /*
36156     highlight : function(){
36157         this.el.addClass("x-layout-panel-dragover");
36158     },
36159
36160     unhighlight : function(){
36161         this.el.removeClass("x-layout-panel-dragover");
36162     },
36163 */
36164     updateBox : function(box)
36165     {
36166         if (!this.bodyEl) {
36167             return; // not rendered yet..
36168         }
36169         
36170         this.box = box;
36171         if(!this.collapsed){
36172             this.el.dom.style.left = box.x + "px";
36173             this.el.dom.style.top = box.y + "px";
36174             this.updateBody(box.width, box.height);
36175         }else{
36176             this.collapsedEl.dom.style.left = box.x + "px";
36177             this.collapsedEl.dom.style.top = box.y + "px";
36178             this.collapsedEl.setSize(box.width, box.height);
36179         }
36180         if(this.tabs){
36181             this.tabs.autoSizeTabs();
36182         }
36183     },
36184
36185     updateBody : function(w, h)
36186     {
36187         if(w !== null){
36188             this.el.setWidth(w);
36189             w -= this.el.getBorderWidth("rl");
36190             if(this.config.adjustments){
36191                 w += this.config.adjustments[0];
36192             }
36193         }
36194         if(h !== null && h > 0){
36195             this.el.setHeight(h);
36196             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36197             h -= this.el.getBorderWidth("tb");
36198             if(this.config.adjustments){
36199                 h += this.config.adjustments[1];
36200             }
36201             this.bodyEl.setHeight(h);
36202             if(this.tabs){
36203                 h = this.tabs.syncHeight(h);
36204             }
36205         }
36206         if(this.panelSize){
36207             w = w !== null ? w : this.panelSize.width;
36208             h = h !== null ? h : this.panelSize.height;
36209         }
36210         if(this.activePanel){
36211             var el = this.activePanel.getEl();
36212             w = w !== null ? w : el.getWidth();
36213             h = h !== null ? h : el.getHeight();
36214             this.panelSize = {width: w, height: h};
36215             this.activePanel.setSize(w, h);
36216         }
36217         if(Roo.isIE && this.tabs){
36218             this.tabs.el.repaint();
36219         }
36220     },
36221
36222     /**
36223      * Returns the container element for this region.
36224      * @return {Roo.Element}
36225      */
36226     getEl : function(){
36227         return this.el;
36228     },
36229
36230     /**
36231      * Hides this region.
36232      */
36233     hide : function(){
36234         //if(!this.collapsed){
36235             this.el.dom.style.left = "-2000px";
36236             this.el.hide();
36237         //}else{
36238          //   this.collapsedEl.dom.style.left = "-2000px";
36239          //   this.collapsedEl.hide();
36240        // }
36241         this.visible = false;
36242         this.fireEvent("visibilitychange", this, false);
36243     },
36244
36245     /**
36246      * Shows this region if it was previously hidden.
36247      */
36248     show : function(){
36249         //if(!this.collapsed){
36250             this.el.show();
36251         //}else{
36252         //    this.collapsedEl.show();
36253        // }
36254         this.visible = true;
36255         this.fireEvent("visibilitychange", this, true);
36256     },
36257 /*
36258     closeClicked : function(){
36259         if(this.activePanel){
36260             this.remove(this.activePanel);
36261         }
36262     },
36263
36264     collapseClick : function(e){
36265         if(this.isSlid){
36266            e.stopPropagation();
36267            this.slideIn();
36268         }else{
36269            e.stopPropagation();
36270            this.slideOut();
36271         }
36272     },
36273 */
36274     /**
36275      * Collapses this region.
36276      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36277      */
36278     /*
36279     collapse : function(skipAnim, skipCheck = false){
36280         if(this.collapsed) {
36281             return;
36282         }
36283         
36284         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36285             
36286             this.collapsed = true;
36287             if(this.split){
36288                 this.split.el.hide();
36289             }
36290             if(this.config.animate && skipAnim !== true){
36291                 this.fireEvent("invalidated", this);
36292                 this.animateCollapse();
36293             }else{
36294                 this.el.setLocation(-20000,-20000);
36295                 this.el.hide();
36296                 this.collapsedEl.show();
36297                 this.fireEvent("collapsed", this);
36298                 this.fireEvent("invalidated", this);
36299             }
36300         }
36301         
36302     },
36303 */
36304     animateCollapse : function(){
36305         // overridden
36306     },
36307
36308     /**
36309      * Expands this region if it was previously collapsed.
36310      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36311      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36312      */
36313     /*
36314     expand : function(e, skipAnim){
36315         if(e) {
36316             e.stopPropagation();
36317         }
36318         if(!this.collapsed || this.el.hasActiveFx()) {
36319             return;
36320         }
36321         if(this.isSlid){
36322             this.afterSlideIn();
36323             skipAnim = true;
36324         }
36325         this.collapsed = false;
36326         if(this.config.animate && skipAnim !== true){
36327             this.animateExpand();
36328         }else{
36329             this.el.show();
36330             if(this.split){
36331                 this.split.el.show();
36332             }
36333             this.collapsedEl.setLocation(-2000,-2000);
36334             this.collapsedEl.hide();
36335             this.fireEvent("invalidated", this);
36336             this.fireEvent("expanded", this);
36337         }
36338     },
36339 */
36340     animateExpand : function(){
36341         // overridden
36342     },
36343
36344     initTabs : function()
36345     {
36346         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36347         
36348         var ts = new Roo.bootstrap.panel.Tabs({
36349             el: this.bodyEl.dom,
36350             region : this,
36351             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36352             disableTooltips: this.config.disableTabTips,
36353             toolbar : this.config.toolbar
36354         });
36355         
36356         if(this.config.hideTabs){
36357             ts.stripWrap.setDisplayed(false);
36358         }
36359         this.tabs = ts;
36360         ts.resizeTabs = this.config.resizeTabs === true;
36361         ts.minTabWidth = this.config.minTabWidth || 40;
36362         ts.maxTabWidth = this.config.maxTabWidth || 250;
36363         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36364         ts.monitorResize = false;
36365         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36366         ts.bodyEl.addClass('roo-layout-tabs-body');
36367         this.panels.each(this.initPanelAsTab, this);
36368     },
36369
36370     initPanelAsTab : function(panel){
36371         var ti = this.tabs.addTab(
36372             panel.getEl().id,
36373             panel.getTitle(),
36374             null,
36375             this.config.closeOnTab && panel.isClosable(),
36376             panel.tpl
36377         );
36378         if(panel.tabTip !== undefined){
36379             ti.setTooltip(panel.tabTip);
36380         }
36381         ti.on("activate", function(){
36382               this.setActivePanel(panel);
36383         }, this);
36384         
36385         if(this.config.closeOnTab){
36386             ti.on("beforeclose", function(t, e){
36387                 e.cancel = true;
36388                 this.remove(panel);
36389             }, this);
36390         }
36391         
36392         panel.tabItem = ti;
36393         
36394         return ti;
36395     },
36396
36397     updatePanelTitle : function(panel, title)
36398     {
36399         if(this.activePanel == panel){
36400             this.updateTitle(title);
36401         }
36402         if(this.tabs){
36403             var ti = this.tabs.getTab(panel.getEl().id);
36404             ti.setText(title);
36405             if(panel.tabTip !== undefined){
36406                 ti.setTooltip(panel.tabTip);
36407             }
36408         }
36409     },
36410
36411     updateTitle : function(title){
36412         if(this.titleTextEl && !this.config.title){
36413             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36414         }
36415     },
36416
36417     setActivePanel : function(panel)
36418     {
36419         panel = this.getPanel(panel);
36420         if(this.activePanel && this.activePanel != panel){
36421             if(this.activePanel.setActiveState(false) === false){
36422                 return;
36423             }
36424         }
36425         this.activePanel = panel;
36426         panel.setActiveState(true);
36427         if(this.panelSize){
36428             panel.setSize(this.panelSize.width, this.panelSize.height);
36429         }
36430         if(this.closeBtn){
36431             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36432         }
36433         this.updateTitle(panel.getTitle());
36434         if(this.tabs){
36435             this.fireEvent("invalidated", this);
36436         }
36437         this.fireEvent("panelactivated", this, panel);
36438     },
36439
36440     /**
36441      * Shows the specified panel.
36442      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36443      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36444      */
36445     showPanel : function(panel)
36446     {
36447         panel = this.getPanel(panel);
36448         if(panel){
36449             if(this.tabs){
36450                 var tab = this.tabs.getTab(panel.getEl().id);
36451                 if(tab.isHidden()){
36452                     this.tabs.unhideTab(tab.id);
36453                 }
36454                 tab.activate();
36455             }else{
36456                 this.setActivePanel(panel);
36457             }
36458         }
36459         return panel;
36460     },
36461
36462     /**
36463      * Get the active panel for this region.
36464      * @return {Roo.ContentPanel} The active panel or null
36465      */
36466     getActivePanel : function(){
36467         return this.activePanel;
36468     },
36469
36470     validateVisibility : function(){
36471         if(this.panels.getCount() < 1){
36472             this.updateTitle("&#160;");
36473             this.closeBtn.hide();
36474             this.hide();
36475         }else{
36476             if(!this.isVisible()){
36477                 this.show();
36478             }
36479         }
36480     },
36481
36482     /**
36483      * Adds the passed ContentPanel(s) to this region.
36484      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36485      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36486      */
36487     add : function(panel)
36488     {
36489         if(arguments.length > 1){
36490             for(var i = 0, len = arguments.length; i < len; i++) {
36491                 this.add(arguments[i]);
36492             }
36493             return null;
36494         }
36495         
36496         // if we have not been rendered yet, then we can not really do much of this..
36497         if (!this.bodyEl) {
36498             this.unrendered_panels.push(panel);
36499             return panel;
36500         }
36501         
36502         
36503         
36504         
36505         if(this.hasPanel(panel)){
36506             this.showPanel(panel);
36507             return panel;
36508         }
36509         panel.setRegion(this);
36510         this.panels.add(panel);
36511        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36512             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36513             // and hide them... ???
36514             this.bodyEl.dom.appendChild(panel.getEl().dom);
36515             if(panel.background !== true){
36516                 this.setActivePanel(panel);
36517             }
36518             this.fireEvent("paneladded", this, panel);
36519             return panel;
36520         }
36521         */
36522         if(!this.tabs){
36523             this.initTabs();
36524         }else{
36525             this.initPanelAsTab(panel);
36526         }
36527         
36528         
36529         if(panel.background !== true){
36530             this.tabs.activate(panel.getEl().id);
36531         }
36532         this.fireEvent("paneladded", this, panel);
36533         return panel;
36534     },
36535
36536     /**
36537      * Hides the tab for the specified panel.
36538      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36539      */
36540     hidePanel : function(panel){
36541         if(this.tabs && (panel = this.getPanel(panel))){
36542             this.tabs.hideTab(panel.getEl().id);
36543         }
36544     },
36545
36546     /**
36547      * Unhides the tab for a previously hidden panel.
36548      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36549      */
36550     unhidePanel : function(panel){
36551         if(this.tabs && (panel = this.getPanel(panel))){
36552             this.tabs.unhideTab(panel.getEl().id);
36553         }
36554     },
36555
36556     clearPanels : function(){
36557         while(this.panels.getCount() > 0){
36558              this.remove(this.panels.first());
36559         }
36560     },
36561
36562     /**
36563      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36564      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36565      * @param {Boolean} preservePanel Overrides the config preservePanel option
36566      * @return {Roo.ContentPanel} The panel that was removed
36567      */
36568     remove : function(panel, preservePanel)
36569     {
36570         panel = this.getPanel(panel);
36571         if(!panel){
36572             return null;
36573         }
36574         var e = {};
36575         this.fireEvent("beforeremove", this, panel, e);
36576         if(e.cancel === true){
36577             return null;
36578         }
36579         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36580         var panelId = panel.getId();
36581         this.panels.removeKey(panelId);
36582         if(preservePanel){
36583             document.body.appendChild(panel.getEl().dom);
36584         }
36585         if(this.tabs){
36586             this.tabs.removeTab(panel.getEl().id);
36587         }else if (!preservePanel){
36588             this.bodyEl.dom.removeChild(panel.getEl().dom);
36589         }
36590         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36591             var p = this.panels.first();
36592             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36593             tempEl.appendChild(p.getEl().dom);
36594             this.bodyEl.update("");
36595             this.bodyEl.dom.appendChild(p.getEl().dom);
36596             tempEl = null;
36597             this.updateTitle(p.getTitle());
36598             this.tabs = null;
36599             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36600             this.setActivePanel(p);
36601         }
36602         panel.setRegion(null);
36603         if(this.activePanel == panel){
36604             this.activePanel = null;
36605         }
36606         if(this.config.autoDestroy !== false && preservePanel !== true){
36607             try{panel.destroy();}catch(e){}
36608         }
36609         this.fireEvent("panelremoved", this, panel);
36610         return panel;
36611     },
36612
36613     /**
36614      * Returns the TabPanel component used by this region
36615      * @return {Roo.TabPanel}
36616      */
36617     getTabs : function(){
36618         return this.tabs;
36619     },
36620
36621     createTool : function(parentEl, className){
36622         var btn = Roo.DomHelper.append(parentEl, {
36623             tag: "div",
36624             cls: "x-layout-tools-button",
36625             children: [ {
36626                 tag: "div",
36627                 cls: "roo-layout-tools-button-inner " + className,
36628                 html: "&#160;"
36629             }]
36630         }, true);
36631         btn.addClassOnOver("roo-layout-tools-button-over");
36632         return btn;
36633     }
36634 });/*
36635  * Based on:
36636  * Ext JS Library 1.1.1
36637  * Copyright(c) 2006-2007, Ext JS, LLC.
36638  *
36639  * Originally Released Under LGPL - original licence link has changed is not relivant.
36640  *
36641  * Fork - LGPL
36642  * <script type="text/javascript">
36643  */
36644  
36645
36646
36647 /**
36648  * @class Roo.SplitLayoutRegion
36649  * @extends Roo.LayoutRegion
36650  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36651  */
36652 Roo.bootstrap.layout.Split = function(config){
36653     this.cursor = config.cursor;
36654     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36655 };
36656
36657 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36658 {
36659     splitTip : "Drag to resize.",
36660     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36661     useSplitTips : false,
36662
36663     applyConfig : function(config){
36664         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36665     },
36666     
36667     onRender : function(ctr,pos) {
36668         
36669         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36670         if(!this.config.split){
36671             return;
36672         }
36673         if(!this.split){
36674             
36675             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36676                             tag: "div",
36677                             id: this.el.id + "-split",
36678                             cls: "roo-layout-split roo-layout-split-"+this.position,
36679                             html: "&#160;"
36680             });
36681             /** The SplitBar for this region 
36682             * @type Roo.SplitBar */
36683             // does not exist yet...
36684             Roo.log([this.position, this.orientation]);
36685             
36686             this.split = new Roo.bootstrap.SplitBar({
36687                 dragElement : splitEl,
36688                 resizingElement: this.el,
36689                 orientation : this.orientation
36690             });
36691             
36692             this.split.on("moved", this.onSplitMove, this);
36693             this.split.useShim = this.config.useShim === true;
36694             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36695             if(this.useSplitTips){
36696                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36697             }
36698             //if(config.collapsible){
36699             //    this.split.el.on("dblclick", this.collapse,  this);
36700             //}
36701         }
36702         if(typeof this.config.minSize != "undefined"){
36703             this.split.minSize = this.config.minSize;
36704         }
36705         if(typeof this.config.maxSize != "undefined"){
36706             this.split.maxSize = this.config.maxSize;
36707         }
36708         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36709             this.hideSplitter();
36710         }
36711         
36712     },
36713
36714     getHMaxSize : function(){
36715          var cmax = this.config.maxSize || 10000;
36716          var center = this.mgr.getRegion("center");
36717          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36718     },
36719
36720     getVMaxSize : function(){
36721          var cmax = this.config.maxSize || 10000;
36722          var center = this.mgr.getRegion("center");
36723          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36724     },
36725
36726     onSplitMove : function(split, newSize){
36727         this.fireEvent("resized", this, newSize);
36728     },
36729     
36730     /** 
36731      * Returns the {@link Roo.SplitBar} for this region.
36732      * @return {Roo.SplitBar}
36733      */
36734     getSplitBar : function(){
36735         return this.split;
36736     },
36737     
36738     hide : function(){
36739         this.hideSplitter();
36740         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36741     },
36742
36743     hideSplitter : function(){
36744         if(this.split){
36745             this.split.el.setLocation(-2000,-2000);
36746             this.split.el.hide();
36747         }
36748     },
36749
36750     show : function(){
36751         if(this.split){
36752             this.split.el.show();
36753         }
36754         Roo.bootstrap.layout.Split.superclass.show.call(this);
36755     },
36756     
36757     beforeSlide: function(){
36758         if(Roo.isGecko){// firefox overflow auto bug workaround
36759             this.bodyEl.clip();
36760             if(this.tabs) {
36761                 this.tabs.bodyEl.clip();
36762             }
36763             if(this.activePanel){
36764                 this.activePanel.getEl().clip();
36765                 
36766                 if(this.activePanel.beforeSlide){
36767                     this.activePanel.beforeSlide();
36768                 }
36769             }
36770         }
36771     },
36772     
36773     afterSlide : function(){
36774         if(Roo.isGecko){// firefox overflow auto bug workaround
36775             this.bodyEl.unclip();
36776             if(this.tabs) {
36777                 this.tabs.bodyEl.unclip();
36778             }
36779             if(this.activePanel){
36780                 this.activePanel.getEl().unclip();
36781                 if(this.activePanel.afterSlide){
36782                     this.activePanel.afterSlide();
36783                 }
36784             }
36785         }
36786     },
36787
36788     initAutoHide : function(){
36789         if(this.autoHide !== false){
36790             if(!this.autoHideHd){
36791                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36792                 this.autoHideHd = {
36793                     "mouseout": function(e){
36794                         if(!e.within(this.el, true)){
36795                             st.delay(500);
36796                         }
36797                     },
36798                     "mouseover" : function(e){
36799                         st.cancel();
36800                     },
36801                     scope : this
36802                 };
36803             }
36804             this.el.on(this.autoHideHd);
36805         }
36806     },
36807
36808     clearAutoHide : function(){
36809         if(this.autoHide !== false){
36810             this.el.un("mouseout", this.autoHideHd.mouseout);
36811             this.el.un("mouseover", this.autoHideHd.mouseover);
36812         }
36813     },
36814
36815     clearMonitor : function(){
36816         Roo.get(document).un("click", this.slideInIf, this);
36817     },
36818
36819     // these names are backwards but not changed for compat
36820     slideOut : function(){
36821         if(this.isSlid || this.el.hasActiveFx()){
36822             return;
36823         }
36824         this.isSlid = true;
36825         if(this.collapseBtn){
36826             this.collapseBtn.hide();
36827         }
36828         this.closeBtnState = this.closeBtn.getStyle('display');
36829         this.closeBtn.hide();
36830         if(this.stickBtn){
36831             this.stickBtn.show();
36832         }
36833         this.el.show();
36834         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36835         this.beforeSlide();
36836         this.el.setStyle("z-index", 10001);
36837         this.el.slideIn(this.getSlideAnchor(), {
36838             callback: function(){
36839                 this.afterSlide();
36840                 this.initAutoHide();
36841                 Roo.get(document).on("click", this.slideInIf, this);
36842                 this.fireEvent("slideshow", this);
36843             },
36844             scope: this,
36845             block: true
36846         });
36847     },
36848
36849     afterSlideIn : function(){
36850         this.clearAutoHide();
36851         this.isSlid = false;
36852         this.clearMonitor();
36853         this.el.setStyle("z-index", "");
36854         if(this.collapseBtn){
36855             this.collapseBtn.show();
36856         }
36857         this.closeBtn.setStyle('display', this.closeBtnState);
36858         if(this.stickBtn){
36859             this.stickBtn.hide();
36860         }
36861         this.fireEvent("slidehide", this);
36862     },
36863
36864     slideIn : function(cb){
36865         if(!this.isSlid || this.el.hasActiveFx()){
36866             Roo.callback(cb);
36867             return;
36868         }
36869         this.isSlid = false;
36870         this.beforeSlide();
36871         this.el.slideOut(this.getSlideAnchor(), {
36872             callback: function(){
36873                 this.el.setLeftTop(-10000, -10000);
36874                 this.afterSlide();
36875                 this.afterSlideIn();
36876                 Roo.callback(cb);
36877             },
36878             scope: this,
36879             block: true
36880         });
36881     },
36882     
36883     slideInIf : function(e){
36884         if(!e.within(this.el)){
36885             this.slideIn();
36886         }
36887     },
36888
36889     animateCollapse : function(){
36890         this.beforeSlide();
36891         this.el.setStyle("z-index", 20000);
36892         var anchor = this.getSlideAnchor();
36893         this.el.slideOut(anchor, {
36894             callback : function(){
36895                 this.el.setStyle("z-index", "");
36896                 this.collapsedEl.slideIn(anchor, {duration:.3});
36897                 this.afterSlide();
36898                 this.el.setLocation(-10000,-10000);
36899                 this.el.hide();
36900                 this.fireEvent("collapsed", this);
36901             },
36902             scope: this,
36903             block: true
36904         });
36905     },
36906
36907     animateExpand : function(){
36908         this.beforeSlide();
36909         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36910         this.el.setStyle("z-index", 20000);
36911         this.collapsedEl.hide({
36912             duration:.1
36913         });
36914         this.el.slideIn(this.getSlideAnchor(), {
36915             callback : function(){
36916                 this.el.setStyle("z-index", "");
36917                 this.afterSlide();
36918                 if(this.split){
36919                     this.split.el.show();
36920                 }
36921                 this.fireEvent("invalidated", this);
36922                 this.fireEvent("expanded", this);
36923             },
36924             scope: this,
36925             block: true
36926         });
36927     },
36928
36929     anchors : {
36930         "west" : "left",
36931         "east" : "right",
36932         "north" : "top",
36933         "south" : "bottom"
36934     },
36935
36936     sanchors : {
36937         "west" : "l",
36938         "east" : "r",
36939         "north" : "t",
36940         "south" : "b"
36941     },
36942
36943     canchors : {
36944         "west" : "tl-tr",
36945         "east" : "tr-tl",
36946         "north" : "tl-bl",
36947         "south" : "bl-tl"
36948     },
36949
36950     getAnchor : function(){
36951         return this.anchors[this.position];
36952     },
36953
36954     getCollapseAnchor : function(){
36955         return this.canchors[this.position];
36956     },
36957
36958     getSlideAnchor : function(){
36959         return this.sanchors[this.position];
36960     },
36961
36962     getAlignAdj : function(){
36963         var cm = this.cmargins;
36964         switch(this.position){
36965             case "west":
36966                 return [0, 0];
36967             break;
36968             case "east":
36969                 return [0, 0];
36970             break;
36971             case "north":
36972                 return [0, 0];
36973             break;
36974             case "south":
36975                 return [0, 0];
36976             break;
36977         }
36978     },
36979
36980     getExpandAdj : function(){
36981         var c = this.collapsedEl, cm = this.cmargins;
36982         switch(this.position){
36983             case "west":
36984                 return [-(cm.right+c.getWidth()+cm.left), 0];
36985             break;
36986             case "east":
36987                 return [cm.right+c.getWidth()+cm.left, 0];
36988             break;
36989             case "north":
36990                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36991             break;
36992             case "south":
36993                 return [0, cm.top+cm.bottom+c.getHeight()];
36994             break;
36995         }
36996     }
36997 });/*
36998  * Based on:
36999  * Ext JS Library 1.1.1
37000  * Copyright(c) 2006-2007, Ext JS, LLC.
37001  *
37002  * Originally Released Under LGPL - original licence link has changed is not relivant.
37003  *
37004  * Fork - LGPL
37005  * <script type="text/javascript">
37006  */
37007 /*
37008  * These classes are private internal classes
37009  */
37010 Roo.bootstrap.layout.Center = function(config){
37011     config.region = "center";
37012     Roo.bootstrap.layout.Region.call(this, config);
37013     this.visible = true;
37014     this.minWidth = config.minWidth || 20;
37015     this.minHeight = config.minHeight || 20;
37016 };
37017
37018 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37019     hide : function(){
37020         // center panel can't be hidden
37021     },
37022     
37023     show : function(){
37024         // center panel can't be hidden
37025     },
37026     
37027     getMinWidth: function(){
37028         return this.minWidth;
37029     },
37030     
37031     getMinHeight: function(){
37032         return this.minHeight;
37033     }
37034 });
37035
37036
37037
37038
37039  
37040
37041
37042
37043
37044
37045
37046 Roo.bootstrap.layout.North = function(config)
37047 {
37048     config.region = 'north';
37049     config.cursor = 'n-resize';
37050     
37051     Roo.bootstrap.layout.Split.call(this, config);
37052     
37053     
37054     if(this.split){
37055         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37056         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37057         this.split.el.addClass("roo-layout-split-v");
37058     }
37059     var size = config.initialSize || config.height;
37060     if(typeof size != "undefined"){
37061         this.el.setHeight(size);
37062     }
37063 };
37064 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37065 {
37066     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37067     
37068     
37069     
37070     getBox : function(){
37071         if(this.collapsed){
37072             return this.collapsedEl.getBox();
37073         }
37074         var box = this.el.getBox();
37075         if(this.split){
37076             box.height += this.split.el.getHeight();
37077         }
37078         return box;
37079     },
37080     
37081     updateBox : function(box){
37082         if(this.split && !this.collapsed){
37083             box.height -= this.split.el.getHeight();
37084             this.split.el.setLeft(box.x);
37085             this.split.el.setTop(box.y+box.height);
37086             this.split.el.setWidth(box.width);
37087         }
37088         if(this.collapsed){
37089             this.updateBody(box.width, null);
37090         }
37091         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37092     }
37093 });
37094
37095
37096
37097
37098
37099 Roo.bootstrap.layout.South = function(config){
37100     config.region = 'south';
37101     config.cursor = 's-resize';
37102     Roo.bootstrap.layout.Split.call(this, config);
37103     if(this.split){
37104         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37105         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37106         this.split.el.addClass("roo-layout-split-v");
37107     }
37108     var size = config.initialSize || config.height;
37109     if(typeof size != "undefined"){
37110         this.el.setHeight(size);
37111     }
37112 };
37113
37114 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37115     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37116     getBox : function(){
37117         if(this.collapsed){
37118             return this.collapsedEl.getBox();
37119         }
37120         var box = this.el.getBox();
37121         if(this.split){
37122             var sh = this.split.el.getHeight();
37123             box.height += sh;
37124             box.y -= sh;
37125         }
37126         return box;
37127     },
37128     
37129     updateBox : function(box){
37130         if(this.split && !this.collapsed){
37131             var sh = this.split.el.getHeight();
37132             box.height -= sh;
37133             box.y += sh;
37134             this.split.el.setLeft(box.x);
37135             this.split.el.setTop(box.y-sh);
37136             this.split.el.setWidth(box.width);
37137         }
37138         if(this.collapsed){
37139             this.updateBody(box.width, null);
37140         }
37141         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37142     }
37143 });
37144
37145 Roo.bootstrap.layout.East = function(config){
37146     config.region = "east";
37147     config.cursor = "e-resize";
37148     Roo.bootstrap.layout.Split.call(this, config);
37149     if(this.split){
37150         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37151         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37152         this.split.el.addClass("roo-layout-split-h");
37153     }
37154     var size = config.initialSize || config.width;
37155     if(typeof size != "undefined"){
37156         this.el.setWidth(size);
37157     }
37158 };
37159 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37160     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37161     getBox : function(){
37162         if(this.collapsed){
37163             return this.collapsedEl.getBox();
37164         }
37165         var box = this.el.getBox();
37166         if(this.split){
37167             var sw = this.split.el.getWidth();
37168             box.width += sw;
37169             box.x -= sw;
37170         }
37171         return box;
37172     },
37173
37174     updateBox : function(box){
37175         if(this.split && !this.collapsed){
37176             var sw = this.split.el.getWidth();
37177             box.width -= sw;
37178             this.split.el.setLeft(box.x);
37179             this.split.el.setTop(box.y);
37180             this.split.el.setHeight(box.height);
37181             box.x += sw;
37182         }
37183         if(this.collapsed){
37184             this.updateBody(null, box.height);
37185         }
37186         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37187     }
37188 });
37189
37190 Roo.bootstrap.layout.West = function(config){
37191     config.region = "west";
37192     config.cursor = "w-resize";
37193     
37194     Roo.bootstrap.layout.Split.call(this, config);
37195     if(this.split){
37196         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37197         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37198         this.split.el.addClass("roo-layout-split-h");
37199     }
37200     
37201 };
37202 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37203     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37204     
37205     onRender: function(ctr, pos)
37206     {
37207         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37208         var size = this.config.initialSize || this.config.width;
37209         if(typeof size != "undefined"){
37210             this.el.setWidth(size);
37211         }
37212     },
37213     
37214     getBox : function(){
37215         if(this.collapsed){
37216             return this.collapsedEl.getBox();
37217         }
37218         var box = this.el.getBox();
37219         if(this.split){
37220             box.width += this.split.el.getWidth();
37221         }
37222         return box;
37223     },
37224     
37225     updateBox : function(box){
37226         if(this.split && !this.collapsed){
37227             var sw = this.split.el.getWidth();
37228             box.width -= sw;
37229             this.split.el.setLeft(box.x+box.width);
37230             this.split.el.setTop(box.y);
37231             this.split.el.setHeight(box.height);
37232         }
37233         if(this.collapsed){
37234             this.updateBody(null, box.height);
37235         }
37236         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37237     }
37238 });Roo.namespace("Roo.bootstrap.panel");/*
37239  * Based on:
37240  * Ext JS Library 1.1.1
37241  * Copyright(c) 2006-2007, Ext JS, LLC.
37242  *
37243  * Originally Released Under LGPL - original licence link has changed is not relivant.
37244  *
37245  * Fork - LGPL
37246  * <script type="text/javascript">
37247  */
37248 /**
37249  * @class Roo.ContentPanel
37250  * @extends Roo.util.Observable
37251  * A basic ContentPanel element.
37252  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37253  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37254  * @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
37255  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37256  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37257  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37258  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37259  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37260  * @cfg {String} title          The title for this panel
37261  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37262  * @cfg {String} url            Calls {@link #setUrl} with this value
37263  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37264  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37265  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37266  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37267  * @cfg {Boolean} badges render the badges
37268
37269  * @constructor
37270  * Create a new ContentPanel.
37271  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37272  * @param {String/Object} config A string to set only the title or a config object
37273  * @param {String} content (optional) Set the HTML content for this panel
37274  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37275  */
37276 Roo.bootstrap.panel.Content = function( config){
37277     
37278     this.tpl = config.tpl || false;
37279     
37280     var el = config.el;
37281     var content = config.content;
37282
37283     if(config.autoCreate){ // xtype is available if this is called from factory
37284         el = Roo.id();
37285     }
37286     this.el = Roo.get(el);
37287     if(!this.el && config && config.autoCreate){
37288         if(typeof config.autoCreate == "object"){
37289             if(!config.autoCreate.id){
37290                 config.autoCreate.id = config.id||el;
37291             }
37292             this.el = Roo.DomHelper.append(document.body,
37293                         config.autoCreate, true);
37294         }else{
37295             var elcfg =  {   tag: "div",
37296                             cls: "roo-layout-inactive-content",
37297                             id: config.id||el
37298                             };
37299             if (config.html) {
37300                 elcfg.html = config.html;
37301                 
37302             }
37303                         
37304             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37305         }
37306     } 
37307     this.closable = false;
37308     this.loaded = false;
37309     this.active = false;
37310    
37311       
37312     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37313         
37314         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37315         
37316         this.wrapEl = this.el; //this.el.wrap();
37317         var ti = [];
37318         if (config.toolbar.items) {
37319             ti = config.toolbar.items ;
37320             delete config.toolbar.items ;
37321         }
37322         
37323         var nitems = [];
37324         this.toolbar.render(this.wrapEl, 'before');
37325         for(var i =0;i < ti.length;i++) {
37326           //  Roo.log(['add child', items[i]]);
37327             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37328         }
37329         this.toolbar.items = nitems;
37330         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37331         delete config.toolbar;
37332         
37333     }
37334     /*
37335     // xtype created footer. - not sure if will work as we normally have to render first..
37336     if (this.footer && !this.footer.el && this.footer.xtype) {
37337         if (!this.wrapEl) {
37338             this.wrapEl = this.el.wrap();
37339         }
37340     
37341         this.footer.container = this.wrapEl.createChild();
37342          
37343         this.footer = Roo.factory(this.footer, Roo);
37344         
37345     }
37346     */
37347     
37348      if(typeof config == "string"){
37349         this.title = config;
37350     }else{
37351         Roo.apply(this, config);
37352     }
37353     
37354     if(this.resizeEl){
37355         this.resizeEl = Roo.get(this.resizeEl, true);
37356     }else{
37357         this.resizeEl = this.el;
37358     }
37359     // handle view.xtype
37360     
37361  
37362     
37363     
37364     this.addEvents({
37365         /**
37366          * @event activate
37367          * Fires when this panel is activated. 
37368          * @param {Roo.ContentPanel} this
37369          */
37370         "activate" : true,
37371         /**
37372          * @event deactivate
37373          * Fires when this panel is activated. 
37374          * @param {Roo.ContentPanel} this
37375          */
37376         "deactivate" : true,
37377
37378         /**
37379          * @event resize
37380          * Fires when this panel is resized if fitToFrame is true.
37381          * @param {Roo.ContentPanel} this
37382          * @param {Number} width The width after any component adjustments
37383          * @param {Number} height The height after any component adjustments
37384          */
37385         "resize" : true,
37386         
37387          /**
37388          * @event render
37389          * Fires when this tab is created
37390          * @param {Roo.ContentPanel} this
37391          */
37392         "render" : true
37393         
37394         
37395         
37396     });
37397     
37398
37399     
37400     
37401     if(this.autoScroll){
37402         this.resizeEl.setStyle("overflow", "auto");
37403     } else {
37404         // fix randome scrolling
37405         //this.el.on('scroll', function() {
37406         //    Roo.log('fix random scolling');
37407         //    this.scrollTo('top',0); 
37408         //});
37409     }
37410     content = content || this.content;
37411     if(content){
37412         this.setContent(content);
37413     }
37414     if(config && config.url){
37415         this.setUrl(this.url, this.params, this.loadOnce);
37416     }
37417     
37418     
37419     
37420     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37421     
37422     if (this.view && typeof(this.view.xtype) != 'undefined') {
37423         this.view.el = this.el.appendChild(document.createElement("div"));
37424         this.view = Roo.factory(this.view); 
37425         this.view.render  &&  this.view.render(false, '');  
37426     }
37427     
37428     
37429     this.fireEvent('render', this);
37430 };
37431
37432 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37433     
37434     tabTip : '',
37435     
37436     setRegion : function(region){
37437         this.region = region;
37438         this.setActiveClass(region && !this.background);
37439     },
37440     
37441     
37442     setActiveClass: function(state)
37443     {
37444         if(state){
37445            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37446            this.el.setStyle('position','relative');
37447         }else{
37448            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37449            this.el.setStyle('position', 'absolute');
37450         } 
37451     },
37452     
37453     /**
37454      * Returns the toolbar for this Panel if one was configured. 
37455      * @return {Roo.Toolbar} 
37456      */
37457     getToolbar : function(){
37458         return this.toolbar;
37459     },
37460     
37461     setActiveState : function(active)
37462     {
37463         this.active = active;
37464         this.setActiveClass(active);
37465         if(!active){
37466             if(this.fireEvent("deactivate", this) === false){
37467                 return false;
37468             }
37469             return true;
37470         }
37471         this.fireEvent("activate", this);
37472         return true;
37473     },
37474     /**
37475      * Updates this panel's element
37476      * @param {String} content The new content
37477      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37478     */
37479     setContent : function(content, loadScripts){
37480         this.el.update(content, loadScripts);
37481     },
37482
37483     ignoreResize : function(w, h){
37484         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37485             return true;
37486         }else{
37487             this.lastSize = {width: w, height: h};
37488             return false;
37489         }
37490     },
37491     /**
37492      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37493      * @return {Roo.UpdateManager} The UpdateManager
37494      */
37495     getUpdateManager : function(){
37496         return this.el.getUpdateManager();
37497     },
37498      /**
37499      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37500      * @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:
37501 <pre><code>
37502 panel.load({
37503     url: "your-url.php",
37504     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37505     callback: yourFunction,
37506     scope: yourObject, //(optional scope)
37507     discardUrl: false,
37508     nocache: false,
37509     text: "Loading...",
37510     timeout: 30,
37511     scripts: false
37512 });
37513 </code></pre>
37514      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37515      * 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.
37516      * @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}
37517      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37518      * @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.
37519      * @return {Roo.ContentPanel} this
37520      */
37521     load : function(){
37522         var um = this.el.getUpdateManager();
37523         um.update.apply(um, arguments);
37524         return this;
37525     },
37526
37527
37528     /**
37529      * 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.
37530      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37531      * @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)
37532      * @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)
37533      * @return {Roo.UpdateManager} The UpdateManager
37534      */
37535     setUrl : function(url, params, loadOnce){
37536         if(this.refreshDelegate){
37537             this.removeListener("activate", this.refreshDelegate);
37538         }
37539         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37540         this.on("activate", this.refreshDelegate);
37541         return this.el.getUpdateManager();
37542     },
37543     
37544     _handleRefresh : function(url, params, loadOnce){
37545         if(!loadOnce || !this.loaded){
37546             var updater = this.el.getUpdateManager();
37547             updater.update(url, params, this._setLoaded.createDelegate(this));
37548         }
37549     },
37550     
37551     _setLoaded : function(){
37552         this.loaded = true;
37553     }, 
37554     
37555     /**
37556      * Returns this panel's id
37557      * @return {String} 
37558      */
37559     getId : function(){
37560         return this.el.id;
37561     },
37562     
37563     /** 
37564      * Returns this panel's element - used by regiosn to add.
37565      * @return {Roo.Element} 
37566      */
37567     getEl : function(){
37568         return this.wrapEl || this.el;
37569     },
37570     
37571    
37572     
37573     adjustForComponents : function(width, height)
37574     {
37575         //Roo.log('adjustForComponents ');
37576         if(this.resizeEl != this.el){
37577             width -= this.el.getFrameWidth('lr');
37578             height -= this.el.getFrameWidth('tb');
37579         }
37580         if(this.toolbar){
37581             var te = this.toolbar.getEl();
37582             te.setWidth(width);
37583             height -= te.getHeight();
37584         }
37585         if(this.footer){
37586             var te = this.footer.getEl();
37587             te.setWidth(width);
37588             height -= te.getHeight();
37589         }
37590         
37591         
37592         if(this.adjustments){
37593             width += this.adjustments[0];
37594             height += this.adjustments[1];
37595         }
37596         return {"width": width, "height": height};
37597     },
37598     
37599     setSize : function(width, height){
37600         if(this.fitToFrame && !this.ignoreResize(width, height)){
37601             if(this.fitContainer && this.resizeEl != this.el){
37602                 this.el.setSize(width, height);
37603             }
37604             var size = this.adjustForComponents(width, height);
37605             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37606             this.fireEvent('resize', this, size.width, size.height);
37607         }
37608     },
37609     
37610     /**
37611      * Returns this panel's title
37612      * @return {String} 
37613      */
37614     getTitle : function(){
37615         
37616         if (typeof(this.title) != 'object') {
37617             return this.title;
37618         }
37619         
37620         var t = '';
37621         for (var k in this.title) {
37622             if (!this.title.hasOwnProperty(k)) {
37623                 continue;
37624             }
37625             
37626             if (k.indexOf('-') >= 0) {
37627                 var s = k.split('-');
37628                 for (var i = 0; i<s.length; i++) {
37629                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37630                 }
37631             } else {
37632                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37633             }
37634         }
37635         return t;
37636     },
37637     
37638     /**
37639      * Set this panel's title
37640      * @param {String} title
37641      */
37642     setTitle : function(title){
37643         this.title = title;
37644         if(this.region){
37645             this.region.updatePanelTitle(this, title);
37646         }
37647     },
37648     
37649     /**
37650      * Returns true is this panel was configured to be closable
37651      * @return {Boolean} 
37652      */
37653     isClosable : function(){
37654         return this.closable;
37655     },
37656     
37657     beforeSlide : function(){
37658         this.el.clip();
37659         this.resizeEl.clip();
37660     },
37661     
37662     afterSlide : function(){
37663         this.el.unclip();
37664         this.resizeEl.unclip();
37665     },
37666     
37667     /**
37668      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37669      *   Will fail silently if the {@link #setUrl} method has not been called.
37670      *   This does not activate the panel, just updates its content.
37671      */
37672     refresh : function(){
37673         if(this.refreshDelegate){
37674            this.loaded = false;
37675            this.refreshDelegate();
37676         }
37677     },
37678     
37679     /**
37680      * Destroys this panel
37681      */
37682     destroy : function(){
37683         this.el.removeAllListeners();
37684         var tempEl = document.createElement("span");
37685         tempEl.appendChild(this.el.dom);
37686         tempEl.innerHTML = "";
37687         this.el.remove();
37688         this.el = null;
37689     },
37690     
37691     /**
37692      * form - if the content panel contains a form - this is a reference to it.
37693      * @type {Roo.form.Form}
37694      */
37695     form : false,
37696     /**
37697      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37698      *    This contains a reference to it.
37699      * @type {Roo.View}
37700      */
37701     view : false,
37702     
37703       /**
37704      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37705      * <pre><code>
37706
37707 layout.addxtype({
37708        xtype : 'Form',
37709        items: [ .... ]
37710    }
37711 );
37712
37713 </code></pre>
37714      * @param {Object} cfg Xtype definition of item to add.
37715      */
37716     
37717     
37718     getChildContainer: function () {
37719         return this.getEl();
37720     }
37721     
37722     
37723     /*
37724         var  ret = new Roo.factory(cfg);
37725         return ret;
37726         
37727         
37728         // add form..
37729         if (cfg.xtype.match(/^Form$/)) {
37730             
37731             var el;
37732             //if (this.footer) {
37733             //    el = this.footer.container.insertSibling(false, 'before');
37734             //} else {
37735                 el = this.el.createChild();
37736             //}
37737
37738             this.form = new  Roo.form.Form(cfg);
37739             
37740             
37741             if ( this.form.allItems.length) {
37742                 this.form.render(el.dom);
37743             }
37744             return this.form;
37745         }
37746         // should only have one of theses..
37747         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37748             // views.. should not be just added - used named prop 'view''
37749             
37750             cfg.el = this.el.appendChild(document.createElement("div"));
37751             // factory?
37752             
37753             var ret = new Roo.factory(cfg);
37754              
37755              ret.render && ret.render(false, ''); // render blank..
37756             this.view = ret;
37757             return ret;
37758         }
37759         return false;
37760     }
37761     \*/
37762 });
37763  
37764 /**
37765  * @class Roo.bootstrap.panel.Grid
37766  * @extends Roo.bootstrap.panel.Content
37767  * @constructor
37768  * Create a new GridPanel.
37769  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37770  * @param {Object} config A the config object
37771   
37772  */
37773
37774
37775
37776 Roo.bootstrap.panel.Grid = function(config)
37777 {
37778     
37779       
37780     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37781         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37782
37783     config.el = this.wrapper;
37784     //this.el = this.wrapper;
37785     
37786       if (config.container) {
37787         // ctor'ed from a Border/panel.grid
37788         
37789         
37790         this.wrapper.setStyle("overflow", "hidden");
37791         this.wrapper.addClass('roo-grid-container');
37792
37793     }
37794     
37795     
37796     if(config.toolbar){
37797         var tool_el = this.wrapper.createChild();    
37798         this.toolbar = Roo.factory(config.toolbar);
37799         var ti = [];
37800         if (config.toolbar.items) {
37801             ti = config.toolbar.items ;
37802             delete config.toolbar.items ;
37803         }
37804         
37805         var nitems = [];
37806         this.toolbar.render(tool_el);
37807         for(var i =0;i < ti.length;i++) {
37808           //  Roo.log(['add child', items[i]]);
37809             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37810         }
37811         this.toolbar.items = nitems;
37812         
37813         delete config.toolbar;
37814     }
37815     
37816     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37817     config.grid.scrollBody = true;;
37818     config.grid.monitorWindowResize = false; // turn off autosizing
37819     config.grid.autoHeight = false;
37820     config.grid.autoWidth = false;
37821     
37822     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37823     
37824     if (config.background) {
37825         // render grid on panel activation (if panel background)
37826         this.on('activate', function(gp) {
37827             if (!gp.grid.rendered) {
37828                 gp.grid.render(this.wrapper);
37829                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37830             }
37831         });
37832             
37833     } else {
37834         this.grid.render(this.wrapper);
37835         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37836
37837     }
37838     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37839     // ??? needed ??? config.el = this.wrapper;
37840     
37841     
37842     
37843   
37844     // xtype created footer. - not sure if will work as we normally have to render first..
37845     if (this.footer && !this.footer.el && this.footer.xtype) {
37846         
37847         var ctr = this.grid.getView().getFooterPanel(true);
37848         this.footer.dataSource = this.grid.dataSource;
37849         this.footer = Roo.factory(this.footer, Roo);
37850         this.footer.render(ctr);
37851         
37852     }
37853     
37854     
37855     
37856     
37857      
37858 };
37859
37860 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37861     getId : function(){
37862         return this.grid.id;
37863     },
37864     
37865     /**
37866      * Returns the grid for this panel
37867      * @return {Roo.bootstrap.Table} 
37868      */
37869     getGrid : function(){
37870         return this.grid;    
37871     },
37872     
37873     setSize : function(width, height){
37874         if(!this.ignoreResize(width, height)){
37875             var grid = this.grid;
37876             var size = this.adjustForComponents(width, height);
37877             var gridel = grid.getGridEl();
37878             gridel.setSize(size.width, size.height);
37879             /*
37880             var thd = grid.getGridEl().select('thead',true).first();
37881             var tbd = grid.getGridEl().select('tbody', true).first();
37882             if (tbd) {
37883                 tbd.setSize(width, height - thd.getHeight());
37884             }
37885             */
37886             grid.autoSize();
37887         }
37888     },
37889      
37890     
37891     
37892     beforeSlide : function(){
37893         this.grid.getView().scroller.clip();
37894     },
37895     
37896     afterSlide : function(){
37897         this.grid.getView().scroller.unclip();
37898     },
37899     
37900     destroy : function(){
37901         this.grid.destroy();
37902         delete this.grid;
37903         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37904     }
37905 });
37906
37907 /**
37908  * @class Roo.bootstrap.panel.Nest
37909  * @extends Roo.bootstrap.panel.Content
37910  * @constructor
37911  * Create a new Panel, that can contain a layout.Border.
37912  * 
37913  * 
37914  * @param {Roo.BorderLayout} layout The layout for this panel
37915  * @param {String/Object} config A string to set only the title or a config object
37916  */
37917 Roo.bootstrap.panel.Nest = function(config)
37918 {
37919     // construct with only one argument..
37920     /* FIXME - implement nicer consturctors
37921     if (layout.layout) {
37922         config = layout;
37923         layout = config.layout;
37924         delete config.layout;
37925     }
37926     if (layout.xtype && !layout.getEl) {
37927         // then layout needs constructing..
37928         layout = Roo.factory(layout, Roo);
37929     }
37930     */
37931     
37932     config.el =  config.layout.getEl();
37933     
37934     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37935     
37936     config.layout.monitorWindowResize = false; // turn off autosizing
37937     this.layout = config.layout;
37938     this.layout.getEl().addClass("roo-layout-nested-layout");
37939     this.layout.parent = this;
37940     
37941     
37942     
37943     
37944 };
37945
37946 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37947
37948     setSize : function(width, height){
37949         if(!this.ignoreResize(width, height)){
37950             var size = this.adjustForComponents(width, height);
37951             var el = this.layout.getEl();
37952             if (size.height < 1) {
37953                 el.setWidth(size.width);   
37954             } else {
37955                 el.setSize(size.width, size.height);
37956             }
37957             var touch = el.dom.offsetWidth;
37958             this.layout.layout();
37959             // ie requires a double layout on the first pass
37960             if(Roo.isIE && !this.initialized){
37961                 this.initialized = true;
37962                 this.layout.layout();
37963             }
37964         }
37965     },
37966     
37967     // activate all subpanels if not currently active..
37968     
37969     setActiveState : function(active){
37970         this.active = active;
37971         this.setActiveClass(active);
37972         
37973         if(!active){
37974             this.fireEvent("deactivate", this);
37975             return;
37976         }
37977         
37978         this.fireEvent("activate", this);
37979         // not sure if this should happen before or after..
37980         if (!this.layout) {
37981             return; // should not happen..
37982         }
37983         var reg = false;
37984         for (var r in this.layout.regions) {
37985             reg = this.layout.getRegion(r);
37986             if (reg.getActivePanel()) {
37987                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37988                 reg.setActivePanel(reg.getActivePanel());
37989                 continue;
37990             }
37991             if (!reg.panels.length) {
37992                 continue;
37993             }
37994             reg.showPanel(reg.getPanel(0));
37995         }
37996         
37997         
37998         
37999         
38000     },
38001     
38002     /**
38003      * Returns the nested BorderLayout for this panel
38004      * @return {Roo.BorderLayout} 
38005      */
38006     getLayout : function(){
38007         return this.layout;
38008     },
38009     
38010      /**
38011      * Adds a xtype elements to the layout of the nested panel
38012      * <pre><code>
38013
38014 panel.addxtype({
38015        xtype : 'ContentPanel',
38016        region: 'west',
38017        items: [ .... ]
38018    }
38019 );
38020
38021 panel.addxtype({
38022         xtype : 'NestedLayoutPanel',
38023         region: 'west',
38024         layout: {
38025            center: { },
38026            west: { }   
38027         },
38028         items : [ ... list of content panels or nested layout panels.. ]
38029    }
38030 );
38031 </code></pre>
38032      * @param {Object} cfg Xtype definition of item to add.
38033      */
38034     addxtype : function(cfg) {
38035         return this.layout.addxtype(cfg);
38036     
38037     }
38038 });/*
38039  * Based on:
38040  * Ext JS Library 1.1.1
38041  * Copyright(c) 2006-2007, Ext JS, LLC.
38042  *
38043  * Originally Released Under LGPL - original licence link has changed is not relivant.
38044  *
38045  * Fork - LGPL
38046  * <script type="text/javascript">
38047  */
38048 /**
38049  * @class Roo.TabPanel
38050  * @extends Roo.util.Observable
38051  * A lightweight tab container.
38052  * <br><br>
38053  * Usage:
38054  * <pre><code>
38055 // basic tabs 1, built from existing content
38056 var tabs = new Roo.TabPanel("tabs1");
38057 tabs.addTab("script", "View Script");
38058 tabs.addTab("markup", "View Markup");
38059 tabs.activate("script");
38060
38061 // more advanced tabs, built from javascript
38062 var jtabs = new Roo.TabPanel("jtabs");
38063 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38064
38065 // set up the UpdateManager
38066 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38067 var updater = tab2.getUpdateManager();
38068 updater.setDefaultUrl("ajax1.htm");
38069 tab2.on('activate', updater.refresh, updater, true);
38070
38071 // Use setUrl for Ajax loading
38072 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38073 tab3.setUrl("ajax2.htm", null, true);
38074
38075 // Disabled tab
38076 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38077 tab4.disable();
38078
38079 jtabs.activate("jtabs-1");
38080  * </code></pre>
38081  * @constructor
38082  * Create a new TabPanel.
38083  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38084  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38085  */
38086 Roo.bootstrap.panel.Tabs = function(config){
38087     /**
38088     * The container element for this TabPanel.
38089     * @type Roo.Element
38090     */
38091     this.el = Roo.get(config.el);
38092     delete config.el;
38093     if(config){
38094         if(typeof config == "boolean"){
38095             this.tabPosition = config ? "bottom" : "top";
38096         }else{
38097             Roo.apply(this, config);
38098         }
38099     }
38100     
38101     if(this.tabPosition == "bottom"){
38102         // if tabs are at the bottom = create the body first.
38103         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38104         this.el.addClass("roo-tabs-bottom");
38105     }
38106     // next create the tabs holders
38107     
38108     if (this.tabPosition == "west"){
38109         
38110         var reg = this.region; // fake it..
38111         while (reg) {
38112             if (!reg.mgr.parent) {
38113                 break;
38114             }
38115             reg = reg.mgr.parent.region;
38116         }
38117         Roo.log("got nest?");
38118         Roo.log(reg);
38119         if (reg.mgr.getRegion('west')) {
38120             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38121             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38122             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38123             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38124             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38125         
38126             
38127         }
38128         
38129         
38130     } else {
38131      
38132         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38133         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38134         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38135         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38136     }
38137     
38138     
38139     if(Roo.isIE){
38140         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38141     }
38142     
38143     // finally - if tabs are at the top, then create the body last..
38144     if(this.tabPosition != "bottom"){
38145         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38146          * @type Roo.Element
38147          */
38148         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38149         this.el.addClass("roo-tabs-top");
38150     }
38151     this.items = [];
38152
38153     this.bodyEl.setStyle("position", "relative");
38154
38155     this.active = null;
38156     this.activateDelegate = this.activate.createDelegate(this);
38157
38158     this.addEvents({
38159         /**
38160          * @event tabchange
38161          * Fires when the active tab changes
38162          * @param {Roo.TabPanel} this
38163          * @param {Roo.TabPanelItem} activePanel The new active tab
38164          */
38165         "tabchange": true,
38166         /**
38167          * @event beforetabchange
38168          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38169          * @param {Roo.TabPanel} this
38170          * @param {Object} e Set cancel to true on this object to cancel the tab change
38171          * @param {Roo.TabPanelItem} tab The tab being changed to
38172          */
38173         "beforetabchange" : true
38174     });
38175
38176     Roo.EventManager.onWindowResize(this.onResize, this);
38177     this.cpad = this.el.getPadding("lr");
38178     this.hiddenCount = 0;
38179
38180
38181     // toolbar on the tabbar support...
38182     if (this.toolbar) {
38183         alert("no toolbar support yet");
38184         this.toolbar  = false;
38185         /*
38186         var tcfg = this.toolbar;
38187         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38188         this.toolbar = new Roo.Toolbar(tcfg);
38189         if (Roo.isSafari) {
38190             var tbl = tcfg.container.child('table', true);
38191             tbl.setAttribute('width', '100%');
38192         }
38193         */
38194         
38195     }
38196    
38197
38198
38199     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38200 };
38201
38202 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38203     /*
38204      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38205      */
38206     tabPosition : "top",
38207     /*
38208      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38209      */
38210     currentTabWidth : 0,
38211     /*
38212      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38213      */
38214     minTabWidth : 40,
38215     /*
38216      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38217      */
38218     maxTabWidth : 250,
38219     /*
38220      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38221      */
38222     preferredTabWidth : 175,
38223     /*
38224      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38225      */
38226     resizeTabs : false,
38227     /*
38228      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38229      */
38230     monitorResize : true,
38231     /*
38232      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38233      */
38234     toolbar : false,  // set by caller..
38235     
38236     region : false, /// set by caller
38237     
38238     disableTooltips : true, // not used yet...
38239
38240     /**
38241      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38242      * @param {String} id The id of the div to use <b>or create</b>
38243      * @param {String} text The text for the tab
38244      * @param {String} content (optional) Content to put in the TabPanelItem body
38245      * @param {Boolean} closable (optional) True to create a close icon on the tab
38246      * @return {Roo.TabPanelItem} The created TabPanelItem
38247      */
38248     addTab : function(id, text, content, closable, tpl)
38249     {
38250         var item = new Roo.bootstrap.panel.TabItem({
38251             panel: this,
38252             id : id,
38253             text : text,
38254             closable : closable,
38255             tpl : tpl
38256         });
38257         this.addTabItem(item);
38258         if(content){
38259             item.setContent(content);
38260         }
38261         return item;
38262     },
38263
38264     /**
38265      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38266      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38267      * @return {Roo.TabPanelItem}
38268      */
38269     getTab : function(id){
38270         return this.items[id];
38271     },
38272
38273     /**
38274      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38275      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38276      */
38277     hideTab : function(id){
38278         var t = this.items[id];
38279         if(!t.isHidden()){
38280            t.setHidden(true);
38281            this.hiddenCount++;
38282            this.autoSizeTabs();
38283         }
38284     },
38285
38286     /**
38287      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38288      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38289      */
38290     unhideTab : function(id){
38291         var t = this.items[id];
38292         if(t.isHidden()){
38293            t.setHidden(false);
38294            this.hiddenCount--;
38295            this.autoSizeTabs();
38296         }
38297     },
38298
38299     /**
38300      * Adds an existing {@link Roo.TabPanelItem}.
38301      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38302      */
38303     addTabItem : function(item)
38304     {
38305         this.items[item.id] = item;
38306         this.items.push(item);
38307         this.autoSizeTabs();
38308       //  if(this.resizeTabs){
38309     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38310   //         this.autoSizeTabs();
38311 //        }else{
38312 //            item.autoSize();
38313        // }
38314     },
38315
38316     /**
38317      * Removes a {@link Roo.TabPanelItem}.
38318      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38319      */
38320     removeTab : function(id){
38321         var items = this.items;
38322         var tab = items[id];
38323         if(!tab) { return; }
38324         var index = items.indexOf(tab);
38325         if(this.active == tab && items.length > 1){
38326             var newTab = this.getNextAvailable(index);
38327             if(newTab) {
38328                 newTab.activate();
38329             }
38330         }
38331         this.stripEl.dom.removeChild(tab.pnode.dom);
38332         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38333             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38334         }
38335         items.splice(index, 1);
38336         delete this.items[tab.id];
38337         tab.fireEvent("close", tab);
38338         tab.purgeListeners();
38339         this.autoSizeTabs();
38340     },
38341
38342     getNextAvailable : function(start){
38343         var items = this.items;
38344         var index = start;
38345         // look for a next tab that will slide over to
38346         // replace the one being removed
38347         while(index < items.length){
38348             var item = items[++index];
38349             if(item && !item.isHidden()){
38350                 return item;
38351             }
38352         }
38353         // if one isn't found select the previous tab (on the left)
38354         index = start;
38355         while(index >= 0){
38356             var item = items[--index];
38357             if(item && !item.isHidden()){
38358                 return item;
38359             }
38360         }
38361         return null;
38362     },
38363
38364     /**
38365      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38366      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38367      */
38368     disableTab : function(id){
38369         var tab = this.items[id];
38370         if(tab && this.active != tab){
38371             tab.disable();
38372         }
38373     },
38374
38375     /**
38376      * Enables a {@link Roo.TabPanelItem} that is disabled.
38377      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38378      */
38379     enableTab : function(id){
38380         var tab = this.items[id];
38381         tab.enable();
38382     },
38383
38384     /**
38385      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38386      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38387      * @return {Roo.TabPanelItem} The TabPanelItem.
38388      */
38389     activate : function(id)
38390     {
38391         //Roo.log('activite:'  + id);
38392         
38393         var tab = this.items[id];
38394         if(!tab){
38395             return null;
38396         }
38397         if(tab == this.active || tab.disabled){
38398             return tab;
38399         }
38400         var e = {};
38401         this.fireEvent("beforetabchange", this, e, tab);
38402         if(e.cancel !== true && !tab.disabled){
38403             if(this.active){
38404                 this.active.hide();
38405             }
38406             this.active = this.items[id];
38407             this.active.show();
38408             this.fireEvent("tabchange", this, this.active);
38409         }
38410         return tab;
38411     },
38412
38413     /**
38414      * Gets the active {@link Roo.TabPanelItem}.
38415      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38416      */
38417     getActiveTab : function(){
38418         return this.active;
38419     },
38420
38421     /**
38422      * Updates the tab body element to fit the height of the container element
38423      * for overflow scrolling
38424      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38425      */
38426     syncHeight : function(targetHeight){
38427         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38428         var bm = this.bodyEl.getMargins();
38429         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38430         this.bodyEl.setHeight(newHeight);
38431         return newHeight;
38432     },
38433
38434     onResize : function(){
38435         if(this.monitorResize){
38436             this.autoSizeTabs();
38437         }
38438     },
38439
38440     /**
38441      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38442      */
38443     beginUpdate : function(){
38444         this.updating = true;
38445     },
38446
38447     /**
38448      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38449      */
38450     endUpdate : function(){
38451         this.updating = false;
38452         this.autoSizeTabs();
38453     },
38454
38455     /**
38456      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38457      */
38458     autoSizeTabs : function()
38459     {
38460         var count = this.items.length;
38461         var vcount = count - this.hiddenCount;
38462         
38463         if (vcount < 2) {
38464             this.stripEl.hide();
38465         } else {
38466             this.stripEl.show();
38467         }
38468         
38469         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38470             return;
38471         }
38472         
38473         
38474         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38475         var availWidth = Math.floor(w / vcount);
38476         var b = this.stripBody;
38477         if(b.getWidth() > w){
38478             var tabs = this.items;
38479             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38480             if(availWidth < this.minTabWidth){
38481                 /*if(!this.sleft){    // incomplete scrolling code
38482                     this.createScrollButtons();
38483                 }
38484                 this.showScroll();
38485                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38486             }
38487         }else{
38488             if(this.currentTabWidth < this.preferredTabWidth){
38489                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38490             }
38491         }
38492     },
38493
38494     /**
38495      * Returns the number of tabs in this TabPanel.
38496      * @return {Number}
38497      */
38498      getCount : function(){
38499          return this.items.length;
38500      },
38501
38502     /**
38503      * Resizes all the tabs to the passed width
38504      * @param {Number} The new width
38505      */
38506     setTabWidth : function(width){
38507         this.currentTabWidth = width;
38508         for(var i = 0, len = this.items.length; i < len; i++) {
38509                 if(!this.items[i].isHidden()) {
38510                 this.items[i].setWidth(width);
38511             }
38512         }
38513     },
38514
38515     /**
38516      * Destroys this TabPanel
38517      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38518      */
38519     destroy : function(removeEl){
38520         Roo.EventManager.removeResizeListener(this.onResize, this);
38521         for(var i = 0, len = this.items.length; i < len; i++){
38522             this.items[i].purgeListeners();
38523         }
38524         if(removeEl === true){
38525             this.el.update("");
38526             this.el.remove();
38527         }
38528     },
38529     
38530     createStrip : function(container)
38531     {
38532         var strip = document.createElement("nav");
38533         strip.className = Roo.bootstrap.version == 4 ?
38534             "navbar-light bg-light" : 
38535             "navbar navbar-default"; //"x-tabs-wrap";
38536         container.appendChild(strip);
38537         return strip;
38538     },
38539     
38540     createStripList : function(strip)
38541     {
38542         // div wrapper for retard IE
38543         // returns the "tr" element.
38544         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38545         //'<div class="x-tabs-strip-wrap">'+
38546           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38547           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38548         return strip.firstChild; //.firstChild.firstChild.firstChild;
38549     },
38550     createBody : function(container)
38551     {
38552         var body = document.createElement("div");
38553         Roo.id(body, "tab-body");
38554         //Roo.fly(body).addClass("x-tabs-body");
38555         Roo.fly(body).addClass("tab-content");
38556         container.appendChild(body);
38557         return body;
38558     },
38559     createItemBody :function(bodyEl, id){
38560         var body = Roo.getDom(id);
38561         if(!body){
38562             body = document.createElement("div");
38563             body.id = id;
38564         }
38565         //Roo.fly(body).addClass("x-tabs-item-body");
38566         Roo.fly(body).addClass("tab-pane");
38567          bodyEl.insertBefore(body, bodyEl.firstChild);
38568         return body;
38569     },
38570     /** @private */
38571     createStripElements :  function(stripEl, text, closable, tpl)
38572     {
38573         var td = document.createElement("li"); // was td..
38574         td.className = 'nav-item';
38575         
38576         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38577         
38578         
38579         stripEl.appendChild(td);
38580         /*if(closable){
38581             td.className = "x-tabs-closable";
38582             if(!this.closeTpl){
38583                 this.closeTpl = new Roo.Template(
38584                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38585                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38586                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38587                 );
38588             }
38589             var el = this.closeTpl.overwrite(td, {"text": text});
38590             var close = el.getElementsByTagName("div")[0];
38591             var inner = el.getElementsByTagName("em")[0];
38592             return {"el": el, "close": close, "inner": inner};
38593         } else {
38594         */
38595         // not sure what this is..
38596 //            if(!this.tabTpl){
38597                 //this.tabTpl = new Roo.Template(
38598                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38599                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38600                 //);
38601 //                this.tabTpl = new Roo.Template(
38602 //                   '<a href="#">' +
38603 //                   '<span unselectable="on"' +
38604 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38605 //                            ' >{text}</span></a>'
38606 //                );
38607 //                
38608 //            }
38609
38610
38611             var template = tpl || this.tabTpl || false;
38612             
38613             if(!template){
38614                 template =  new Roo.Template(
38615                         Roo.bootstrap.version == 4 ? 
38616                             (
38617                                 '<a class="nav-link" href="#" unselectable="on"' +
38618                                      (this.disableTooltips ? '' : ' title="{text}"') +
38619                                      ' >{text}</a>'
38620                             ) : (
38621                                 '<a class="nav-link" href="#">' +
38622                                 '<span unselectable="on"' +
38623                                          (this.disableTooltips ? '' : ' title="{text}"') +
38624                                     ' >{text}</span></a>'
38625                             )
38626                 );
38627             }
38628             
38629             switch (typeof(template)) {
38630                 case 'object' :
38631                     break;
38632                 case 'string' :
38633                     template = new Roo.Template(template);
38634                     break;
38635                 default :
38636                     break;
38637             }
38638             
38639             var el = template.overwrite(td, {"text": text});
38640             
38641             var inner = el.getElementsByTagName("span")[0];
38642             
38643             return {"el": el, "inner": inner};
38644             
38645     }
38646         
38647     
38648 });
38649
38650 /**
38651  * @class Roo.TabPanelItem
38652  * @extends Roo.util.Observable
38653  * Represents an individual item (tab plus body) in a TabPanel.
38654  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38655  * @param {String} id The id of this TabPanelItem
38656  * @param {String} text The text for the tab of this TabPanelItem
38657  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38658  */
38659 Roo.bootstrap.panel.TabItem = function(config){
38660     /**
38661      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38662      * @type Roo.TabPanel
38663      */
38664     this.tabPanel = config.panel;
38665     /**
38666      * The id for this TabPanelItem
38667      * @type String
38668      */
38669     this.id = config.id;
38670     /** @private */
38671     this.disabled = false;
38672     /** @private */
38673     this.text = config.text;
38674     /** @private */
38675     this.loaded = false;
38676     this.closable = config.closable;
38677
38678     /**
38679      * The body element for this TabPanelItem.
38680      * @type Roo.Element
38681      */
38682     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38683     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38684     this.bodyEl.setStyle("display", "block");
38685     this.bodyEl.setStyle("zoom", "1");
38686     //this.hideAction();
38687
38688     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38689     /** @private */
38690     this.el = Roo.get(els.el);
38691     this.inner = Roo.get(els.inner, true);
38692      this.textEl = Roo.bootstrap.version == 4 ?
38693         this.el : Roo.get(this.el.dom.firstChild, true);
38694
38695     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38696     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38697
38698     
38699 //    this.el.on("mousedown", this.onTabMouseDown, this);
38700     this.el.on("click", this.onTabClick, this);
38701     /** @private */
38702     if(config.closable){
38703         var c = Roo.get(els.close, true);
38704         c.dom.title = this.closeText;
38705         c.addClassOnOver("close-over");
38706         c.on("click", this.closeClick, this);
38707      }
38708
38709     this.addEvents({
38710          /**
38711          * @event activate
38712          * Fires when this tab becomes the active tab.
38713          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38714          * @param {Roo.TabPanelItem} this
38715          */
38716         "activate": true,
38717         /**
38718          * @event beforeclose
38719          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38720          * @param {Roo.TabPanelItem} this
38721          * @param {Object} e Set cancel to true on this object to cancel the close.
38722          */
38723         "beforeclose": true,
38724         /**
38725          * @event close
38726          * Fires when this tab is closed.
38727          * @param {Roo.TabPanelItem} this
38728          */
38729          "close": true,
38730         /**
38731          * @event deactivate
38732          * Fires when this tab is no longer the active tab.
38733          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38734          * @param {Roo.TabPanelItem} this
38735          */
38736          "deactivate" : true
38737     });
38738     this.hidden = false;
38739
38740     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38741 };
38742
38743 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38744            {
38745     purgeListeners : function(){
38746        Roo.util.Observable.prototype.purgeListeners.call(this);
38747        this.el.removeAllListeners();
38748     },
38749     /**
38750      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38751      */
38752     show : function(){
38753         this.status_node.addClass("active");
38754         this.showAction();
38755         if(Roo.isOpera){
38756             this.tabPanel.stripWrap.repaint();
38757         }
38758         this.fireEvent("activate", this.tabPanel, this);
38759     },
38760
38761     /**
38762      * Returns true if this tab is the active tab.
38763      * @return {Boolean}
38764      */
38765     isActive : function(){
38766         return this.tabPanel.getActiveTab() == this;
38767     },
38768
38769     /**
38770      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38771      */
38772     hide : function(){
38773         this.status_node.removeClass("active");
38774         this.hideAction();
38775         this.fireEvent("deactivate", this.tabPanel, this);
38776     },
38777
38778     hideAction : function(){
38779         this.bodyEl.hide();
38780         this.bodyEl.setStyle("position", "absolute");
38781         this.bodyEl.setLeft("-20000px");
38782         this.bodyEl.setTop("-20000px");
38783     },
38784
38785     showAction : function(){
38786         this.bodyEl.setStyle("position", "relative");
38787         this.bodyEl.setTop("");
38788         this.bodyEl.setLeft("");
38789         this.bodyEl.show();
38790     },
38791
38792     /**
38793      * Set the tooltip for the tab.
38794      * @param {String} tooltip The tab's tooltip
38795      */
38796     setTooltip : function(text){
38797         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38798             this.textEl.dom.qtip = text;
38799             this.textEl.dom.removeAttribute('title');
38800         }else{
38801             this.textEl.dom.title = text;
38802         }
38803     },
38804
38805     onTabClick : function(e){
38806         e.preventDefault();
38807         this.tabPanel.activate(this.id);
38808     },
38809
38810     onTabMouseDown : function(e){
38811         e.preventDefault();
38812         this.tabPanel.activate(this.id);
38813     },
38814 /*
38815     getWidth : function(){
38816         return this.inner.getWidth();
38817     },
38818
38819     setWidth : function(width){
38820         var iwidth = width - this.linode.getPadding("lr");
38821         this.inner.setWidth(iwidth);
38822         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38823         this.linode.setWidth(width);
38824     },
38825 */
38826     /**
38827      * Show or hide the tab
38828      * @param {Boolean} hidden True to hide or false to show.
38829      */
38830     setHidden : function(hidden){
38831         this.hidden = hidden;
38832         this.linode.setStyle("display", hidden ? "none" : "");
38833     },
38834
38835     /**
38836      * Returns true if this tab is "hidden"
38837      * @return {Boolean}
38838      */
38839     isHidden : function(){
38840         return this.hidden;
38841     },
38842
38843     /**
38844      * Returns the text for this tab
38845      * @return {String}
38846      */
38847     getText : function(){
38848         return this.text;
38849     },
38850     /*
38851     autoSize : function(){
38852         //this.el.beginMeasure();
38853         this.textEl.setWidth(1);
38854         /*
38855          *  #2804 [new] Tabs in Roojs
38856          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38857          */
38858         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38859         //this.el.endMeasure();
38860     //},
38861
38862     /**
38863      * Sets the text for the tab (Note: this also sets the tooltip text)
38864      * @param {String} text The tab's text and tooltip
38865      */
38866     setText : function(text){
38867         this.text = text;
38868         this.textEl.update(text);
38869         this.setTooltip(text);
38870         //if(!this.tabPanel.resizeTabs){
38871         //    this.autoSize();
38872         //}
38873     },
38874     /**
38875      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38876      */
38877     activate : function(){
38878         this.tabPanel.activate(this.id);
38879     },
38880
38881     /**
38882      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38883      */
38884     disable : function(){
38885         if(this.tabPanel.active != this){
38886             this.disabled = true;
38887             this.status_node.addClass("disabled");
38888         }
38889     },
38890
38891     /**
38892      * Enables this TabPanelItem if it was previously disabled.
38893      */
38894     enable : function(){
38895         this.disabled = false;
38896         this.status_node.removeClass("disabled");
38897     },
38898
38899     /**
38900      * Sets the content for this TabPanelItem.
38901      * @param {String} content The content
38902      * @param {Boolean} loadScripts true to look for and load scripts
38903      */
38904     setContent : function(content, loadScripts){
38905         this.bodyEl.update(content, loadScripts);
38906     },
38907
38908     /**
38909      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38910      * @return {Roo.UpdateManager} The UpdateManager
38911      */
38912     getUpdateManager : function(){
38913         return this.bodyEl.getUpdateManager();
38914     },
38915
38916     /**
38917      * Set a URL to be used to load the content for this TabPanelItem.
38918      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38919      * @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)
38920      * @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)
38921      * @return {Roo.UpdateManager} The UpdateManager
38922      */
38923     setUrl : function(url, params, loadOnce){
38924         if(this.refreshDelegate){
38925             this.un('activate', this.refreshDelegate);
38926         }
38927         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38928         this.on("activate", this.refreshDelegate);
38929         return this.bodyEl.getUpdateManager();
38930     },
38931
38932     /** @private */
38933     _handleRefresh : function(url, params, loadOnce){
38934         if(!loadOnce || !this.loaded){
38935             var updater = this.bodyEl.getUpdateManager();
38936             updater.update(url, params, this._setLoaded.createDelegate(this));
38937         }
38938     },
38939
38940     /**
38941      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38942      *   Will fail silently if the setUrl method has not been called.
38943      *   This does not activate the panel, just updates its content.
38944      */
38945     refresh : function(){
38946         if(this.refreshDelegate){
38947            this.loaded = false;
38948            this.refreshDelegate();
38949         }
38950     },
38951
38952     /** @private */
38953     _setLoaded : function(){
38954         this.loaded = true;
38955     },
38956
38957     /** @private */
38958     closeClick : function(e){
38959         var o = {};
38960         e.stopEvent();
38961         this.fireEvent("beforeclose", this, o);
38962         if(o.cancel !== true){
38963             this.tabPanel.removeTab(this.id);
38964         }
38965     },
38966     /**
38967      * The text displayed in the tooltip for the close icon.
38968      * @type String
38969      */
38970     closeText : "Close this tab"
38971 });
38972 /**
38973 *    This script refer to:
38974 *    Title: International Telephone Input
38975 *    Author: Jack O'Connor
38976 *    Code version:  v12.1.12
38977 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38978 **/
38979
38980 Roo.bootstrap.PhoneInputData = function() {
38981     var d = [
38982       [
38983         "Afghanistan (‫افغانستان‬‎)",
38984         "af",
38985         "93"
38986       ],
38987       [
38988         "Albania (Shqipëri)",
38989         "al",
38990         "355"
38991       ],
38992       [
38993         "Algeria (‫الجزائر‬‎)",
38994         "dz",
38995         "213"
38996       ],
38997       [
38998         "American Samoa",
38999         "as",
39000         "1684"
39001       ],
39002       [
39003         "Andorra",
39004         "ad",
39005         "376"
39006       ],
39007       [
39008         "Angola",
39009         "ao",
39010         "244"
39011       ],
39012       [
39013         "Anguilla",
39014         "ai",
39015         "1264"
39016       ],
39017       [
39018         "Antigua and Barbuda",
39019         "ag",
39020         "1268"
39021       ],
39022       [
39023         "Argentina",
39024         "ar",
39025         "54"
39026       ],
39027       [
39028         "Armenia (Հայաստան)",
39029         "am",
39030         "374"
39031       ],
39032       [
39033         "Aruba",
39034         "aw",
39035         "297"
39036       ],
39037       [
39038         "Australia",
39039         "au",
39040         "61",
39041         0
39042       ],
39043       [
39044         "Austria (Österreich)",
39045         "at",
39046         "43"
39047       ],
39048       [
39049         "Azerbaijan (Azərbaycan)",
39050         "az",
39051         "994"
39052       ],
39053       [
39054         "Bahamas",
39055         "bs",
39056         "1242"
39057       ],
39058       [
39059         "Bahrain (‫البحرين‬‎)",
39060         "bh",
39061         "973"
39062       ],
39063       [
39064         "Bangladesh (বাংলাদেশ)",
39065         "bd",
39066         "880"
39067       ],
39068       [
39069         "Barbados",
39070         "bb",
39071         "1246"
39072       ],
39073       [
39074         "Belarus (Беларусь)",
39075         "by",
39076         "375"
39077       ],
39078       [
39079         "Belgium (België)",
39080         "be",
39081         "32"
39082       ],
39083       [
39084         "Belize",
39085         "bz",
39086         "501"
39087       ],
39088       [
39089         "Benin (Bénin)",
39090         "bj",
39091         "229"
39092       ],
39093       [
39094         "Bermuda",
39095         "bm",
39096         "1441"
39097       ],
39098       [
39099         "Bhutan (འབྲུག)",
39100         "bt",
39101         "975"
39102       ],
39103       [
39104         "Bolivia",
39105         "bo",
39106         "591"
39107       ],
39108       [
39109         "Bosnia and Herzegovina (Босна и Херцеговина)",
39110         "ba",
39111         "387"
39112       ],
39113       [
39114         "Botswana",
39115         "bw",
39116         "267"
39117       ],
39118       [
39119         "Brazil (Brasil)",
39120         "br",
39121         "55"
39122       ],
39123       [
39124         "British Indian Ocean Territory",
39125         "io",
39126         "246"
39127       ],
39128       [
39129         "British Virgin Islands",
39130         "vg",
39131         "1284"
39132       ],
39133       [
39134         "Brunei",
39135         "bn",
39136         "673"
39137       ],
39138       [
39139         "Bulgaria (България)",
39140         "bg",
39141         "359"
39142       ],
39143       [
39144         "Burkina Faso",
39145         "bf",
39146         "226"
39147       ],
39148       [
39149         "Burundi (Uburundi)",
39150         "bi",
39151         "257"
39152       ],
39153       [
39154         "Cambodia (កម្ពុជា)",
39155         "kh",
39156         "855"
39157       ],
39158       [
39159         "Cameroon (Cameroun)",
39160         "cm",
39161         "237"
39162       ],
39163       [
39164         "Canada",
39165         "ca",
39166         "1",
39167         1,
39168         ["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"]
39169       ],
39170       [
39171         "Cape Verde (Kabu Verdi)",
39172         "cv",
39173         "238"
39174       ],
39175       [
39176         "Caribbean Netherlands",
39177         "bq",
39178         "599",
39179         1
39180       ],
39181       [
39182         "Cayman Islands",
39183         "ky",
39184         "1345"
39185       ],
39186       [
39187         "Central African Republic (République centrafricaine)",
39188         "cf",
39189         "236"
39190       ],
39191       [
39192         "Chad (Tchad)",
39193         "td",
39194         "235"
39195       ],
39196       [
39197         "Chile",
39198         "cl",
39199         "56"
39200       ],
39201       [
39202         "China (中国)",
39203         "cn",
39204         "86"
39205       ],
39206       [
39207         "Christmas Island",
39208         "cx",
39209         "61",
39210         2
39211       ],
39212       [
39213         "Cocos (Keeling) Islands",
39214         "cc",
39215         "61",
39216         1
39217       ],
39218       [
39219         "Colombia",
39220         "co",
39221         "57"
39222       ],
39223       [
39224         "Comoros (‫جزر القمر‬‎)",
39225         "km",
39226         "269"
39227       ],
39228       [
39229         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39230         "cd",
39231         "243"
39232       ],
39233       [
39234         "Congo (Republic) (Congo-Brazzaville)",
39235         "cg",
39236         "242"
39237       ],
39238       [
39239         "Cook Islands",
39240         "ck",
39241         "682"
39242       ],
39243       [
39244         "Costa Rica",
39245         "cr",
39246         "506"
39247       ],
39248       [
39249         "Côte d’Ivoire",
39250         "ci",
39251         "225"
39252       ],
39253       [
39254         "Croatia (Hrvatska)",
39255         "hr",
39256         "385"
39257       ],
39258       [
39259         "Cuba",
39260         "cu",
39261         "53"
39262       ],
39263       [
39264         "Curaçao",
39265         "cw",
39266         "599",
39267         0
39268       ],
39269       [
39270         "Cyprus (Κύπρος)",
39271         "cy",
39272         "357"
39273       ],
39274       [
39275         "Czech Republic (Česká republika)",
39276         "cz",
39277         "420"
39278       ],
39279       [
39280         "Denmark (Danmark)",
39281         "dk",
39282         "45"
39283       ],
39284       [
39285         "Djibouti",
39286         "dj",
39287         "253"
39288       ],
39289       [
39290         "Dominica",
39291         "dm",
39292         "1767"
39293       ],
39294       [
39295         "Dominican Republic (República Dominicana)",
39296         "do",
39297         "1",
39298         2,
39299         ["809", "829", "849"]
39300       ],
39301       [
39302         "Ecuador",
39303         "ec",
39304         "593"
39305       ],
39306       [
39307         "Egypt (‫مصر‬‎)",
39308         "eg",
39309         "20"
39310       ],
39311       [
39312         "El Salvador",
39313         "sv",
39314         "503"
39315       ],
39316       [
39317         "Equatorial Guinea (Guinea Ecuatorial)",
39318         "gq",
39319         "240"
39320       ],
39321       [
39322         "Eritrea",
39323         "er",
39324         "291"
39325       ],
39326       [
39327         "Estonia (Eesti)",
39328         "ee",
39329         "372"
39330       ],
39331       [
39332         "Ethiopia",
39333         "et",
39334         "251"
39335       ],
39336       [
39337         "Falkland Islands (Islas Malvinas)",
39338         "fk",
39339         "500"
39340       ],
39341       [
39342         "Faroe Islands (Føroyar)",
39343         "fo",
39344         "298"
39345       ],
39346       [
39347         "Fiji",
39348         "fj",
39349         "679"
39350       ],
39351       [
39352         "Finland (Suomi)",
39353         "fi",
39354         "358",
39355         0
39356       ],
39357       [
39358         "France",
39359         "fr",
39360         "33"
39361       ],
39362       [
39363         "French Guiana (Guyane française)",
39364         "gf",
39365         "594"
39366       ],
39367       [
39368         "French Polynesia (Polynésie française)",
39369         "pf",
39370         "689"
39371       ],
39372       [
39373         "Gabon",
39374         "ga",
39375         "241"
39376       ],
39377       [
39378         "Gambia",
39379         "gm",
39380         "220"
39381       ],
39382       [
39383         "Georgia (საქართველო)",
39384         "ge",
39385         "995"
39386       ],
39387       [
39388         "Germany (Deutschland)",
39389         "de",
39390         "49"
39391       ],
39392       [
39393         "Ghana (Gaana)",
39394         "gh",
39395         "233"
39396       ],
39397       [
39398         "Gibraltar",
39399         "gi",
39400         "350"
39401       ],
39402       [
39403         "Greece (Ελλάδα)",
39404         "gr",
39405         "30"
39406       ],
39407       [
39408         "Greenland (Kalaallit Nunaat)",
39409         "gl",
39410         "299"
39411       ],
39412       [
39413         "Grenada",
39414         "gd",
39415         "1473"
39416       ],
39417       [
39418         "Guadeloupe",
39419         "gp",
39420         "590",
39421         0
39422       ],
39423       [
39424         "Guam",
39425         "gu",
39426         "1671"
39427       ],
39428       [
39429         "Guatemala",
39430         "gt",
39431         "502"
39432       ],
39433       [
39434         "Guernsey",
39435         "gg",
39436         "44",
39437         1
39438       ],
39439       [
39440         "Guinea (Guinée)",
39441         "gn",
39442         "224"
39443       ],
39444       [
39445         "Guinea-Bissau (Guiné Bissau)",
39446         "gw",
39447         "245"
39448       ],
39449       [
39450         "Guyana",
39451         "gy",
39452         "592"
39453       ],
39454       [
39455         "Haiti",
39456         "ht",
39457         "509"
39458       ],
39459       [
39460         "Honduras",
39461         "hn",
39462         "504"
39463       ],
39464       [
39465         "Hong Kong (香港)",
39466         "hk",
39467         "852"
39468       ],
39469       [
39470         "Hungary (Magyarország)",
39471         "hu",
39472         "36"
39473       ],
39474       [
39475         "Iceland (Ísland)",
39476         "is",
39477         "354"
39478       ],
39479       [
39480         "India (भारत)",
39481         "in",
39482         "91"
39483       ],
39484       [
39485         "Indonesia",
39486         "id",
39487         "62"
39488       ],
39489       [
39490         "Iran (‫ایران‬‎)",
39491         "ir",
39492         "98"
39493       ],
39494       [
39495         "Iraq (‫العراق‬‎)",
39496         "iq",
39497         "964"
39498       ],
39499       [
39500         "Ireland",
39501         "ie",
39502         "353"
39503       ],
39504       [
39505         "Isle of Man",
39506         "im",
39507         "44",
39508         2
39509       ],
39510       [
39511         "Israel (‫ישראל‬‎)",
39512         "il",
39513         "972"
39514       ],
39515       [
39516         "Italy (Italia)",
39517         "it",
39518         "39",
39519         0
39520       ],
39521       [
39522         "Jamaica",
39523         "jm",
39524         "1876"
39525       ],
39526       [
39527         "Japan (日本)",
39528         "jp",
39529         "81"
39530       ],
39531       [
39532         "Jersey",
39533         "je",
39534         "44",
39535         3
39536       ],
39537       [
39538         "Jordan (‫الأردن‬‎)",
39539         "jo",
39540         "962"
39541       ],
39542       [
39543         "Kazakhstan (Казахстан)",
39544         "kz",
39545         "7",
39546         1
39547       ],
39548       [
39549         "Kenya",
39550         "ke",
39551         "254"
39552       ],
39553       [
39554         "Kiribati",
39555         "ki",
39556         "686"
39557       ],
39558       [
39559         "Kosovo",
39560         "xk",
39561         "383"
39562       ],
39563       [
39564         "Kuwait (‫الكويت‬‎)",
39565         "kw",
39566         "965"
39567       ],
39568       [
39569         "Kyrgyzstan (Кыргызстан)",
39570         "kg",
39571         "996"
39572       ],
39573       [
39574         "Laos (ລາວ)",
39575         "la",
39576         "856"
39577       ],
39578       [
39579         "Latvia (Latvija)",
39580         "lv",
39581         "371"
39582       ],
39583       [
39584         "Lebanon (‫لبنان‬‎)",
39585         "lb",
39586         "961"
39587       ],
39588       [
39589         "Lesotho",
39590         "ls",
39591         "266"
39592       ],
39593       [
39594         "Liberia",
39595         "lr",
39596         "231"
39597       ],
39598       [
39599         "Libya (‫ليبيا‬‎)",
39600         "ly",
39601         "218"
39602       ],
39603       [
39604         "Liechtenstein",
39605         "li",
39606         "423"
39607       ],
39608       [
39609         "Lithuania (Lietuva)",
39610         "lt",
39611         "370"
39612       ],
39613       [
39614         "Luxembourg",
39615         "lu",
39616         "352"
39617       ],
39618       [
39619         "Macau (澳門)",
39620         "mo",
39621         "853"
39622       ],
39623       [
39624         "Macedonia (FYROM) (Македонија)",
39625         "mk",
39626         "389"
39627       ],
39628       [
39629         "Madagascar (Madagasikara)",
39630         "mg",
39631         "261"
39632       ],
39633       [
39634         "Malawi",
39635         "mw",
39636         "265"
39637       ],
39638       [
39639         "Malaysia",
39640         "my",
39641         "60"
39642       ],
39643       [
39644         "Maldives",
39645         "mv",
39646         "960"
39647       ],
39648       [
39649         "Mali",
39650         "ml",
39651         "223"
39652       ],
39653       [
39654         "Malta",
39655         "mt",
39656         "356"
39657       ],
39658       [
39659         "Marshall Islands",
39660         "mh",
39661         "692"
39662       ],
39663       [
39664         "Martinique",
39665         "mq",
39666         "596"
39667       ],
39668       [
39669         "Mauritania (‫موريتانيا‬‎)",
39670         "mr",
39671         "222"
39672       ],
39673       [
39674         "Mauritius (Moris)",
39675         "mu",
39676         "230"
39677       ],
39678       [
39679         "Mayotte",
39680         "yt",
39681         "262",
39682         1
39683       ],
39684       [
39685         "Mexico (México)",
39686         "mx",
39687         "52"
39688       ],
39689       [
39690         "Micronesia",
39691         "fm",
39692         "691"
39693       ],
39694       [
39695         "Moldova (Republica Moldova)",
39696         "md",
39697         "373"
39698       ],
39699       [
39700         "Monaco",
39701         "mc",
39702         "377"
39703       ],
39704       [
39705         "Mongolia (Монгол)",
39706         "mn",
39707         "976"
39708       ],
39709       [
39710         "Montenegro (Crna Gora)",
39711         "me",
39712         "382"
39713       ],
39714       [
39715         "Montserrat",
39716         "ms",
39717         "1664"
39718       ],
39719       [
39720         "Morocco (‫المغرب‬‎)",
39721         "ma",
39722         "212",
39723         0
39724       ],
39725       [
39726         "Mozambique (Moçambique)",
39727         "mz",
39728         "258"
39729       ],
39730       [
39731         "Myanmar (Burma) (မြန်မာ)",
39732         "mm",
39733         "95"
39734       ],
39735       [
39736         "Namibia (Namibië)",
39737         "na",
39738         "264"
39739       ],
39740       [
39741         "Nauru",
39742         "nr",
39743         "674"
39744       ],
39745       [
39746         "Nepal (नेपाल)",
39747         "np",
39748         "977"
39749       ],
39750       [
39751         "Netherlands (Nederland)",
39752         "nl",
39753         "31"
39754       ],
39755       [
39756         "New Caledonia (Nouvelle-Calédonie)",
39757         "nc",
39758         "687"
39759       ],
39760       [
39761         "New Zealand",
39762         "nz",
39763         "64"
39764       ],
39765       [
39766         "Nicaragua",
39767         "ni",
39768         "505"
39769       ],
39770       [
39771         "Niger (Nijar)",
39772         "ne",
39773         "227"
39774       ],
39775       [
39776         "Nigeria",
39777         "ng",
39778         "234"
39779       ],
39780       [
39781         "Niue",
39782         "nu",
39783         "683"
39784       ],
39785       [
39786         "Norfolk Island",
39787         "nf",
39788         "672"
39789       ],
39790       [
39791         "North Korea (조선 민주주의 인민 공화국)",
39792         "kp",
39793         "850"
39794       ],
39795       [
39796         "Northern Mariana Islands",
39797         "mp",
39798         "1670"
39799       ],
39800       [
39801         "Norway (Norge)",
39802         "no",
39803         "47",
39804         0
39805       ],
39806       [
39807         "Oman (‫عُمان‬‎)",
39808         "om",
39809         "968"
39810       ],
39811       [
39812         "Pakistan (‫پاکستان‬‎)",
39813         "pk",
39814         "92"
39815       ],
39816       [
39817         "Palau",
39818         "pw",
39819         "680"
39820       ],
39821       [
39822         "Palestine (‫فلسطين‬‎)",
39823         "ps",
39824         "970"
39825       ],
39826       [
39827         "Panama (Panamá)",
39828         "pa",
39829         "507"
39830       ],
39831       [
39832         "Papua New Guinea",
39833         "pg",
39834         "675"
39835       ],
39836       [
39837         "Paraguay",
39838         "py",
39839         "595"
39840       ],
39841       [
39842         "Peru (Perú)",
39843         "pe",
39844         "51"
39845       ],
39846       [
39847         "Philippines",
39848         "ph",
39849         "63"
39850       ],
39851       [
39852         "Poland (Polska)",
39853         "pl",
39854         "48"
39855       ],
39856       [
39857         "Portugal",
39858         "pt",
39859         "351"
39860       ],
39861       [
39862         "Puerto Rico",
39863         "pr",
39864         "1",
39865         3,
39866         ["787", "939"]
39867       ],
39868       [
39869         "Qatar (‫قطر‬‎)",
39870         "qa",
39871         "974"
39872       ],
39873       [
39874         "Réunion (La Réunion)",
39875         "re",
39876         "262",
39877         0
39878       ],
39879       [
39880         "Romania (România)",
39881         "ro",
39882         "40"
39883       ],
39884       [
39885         "Russia (Россия)",
39886         "ru",
39887         "7",
39888         0
39889       ],
39890       [
39891         "Rwanda",
39892         "rw",
39893         "250"
39894       ],
39895       [
39896         "Saint Barthélemy",
39897         "bl",
39898         "590",
39899         1
39900       ],
39901       [
39902         "Saint Helena",
39903         "sh",
39904         "290"
39905       ],
39906       [
39907         "Saint Kitts and Nevis",
39908         "kn",
39909         "1869"
39910       ],
39911       [
39912         "Saint Lucia",
39913         "lc",
39914         "1758"
39915       ],
39916       [
39917         "Saint Martin (Saint-Martin (partie française))",
39918         "mf",
39919         "590",
39920         2
39921       ],
39922       [
39923         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39924         "pm",
39925         "508"
39926       ],
39927       [
39928         "Saint Vincent and the Grenadines",
39929         "vc",
39930         "1784"
39931       ],
39932       [
39933         "Samoa",
39934         "ws",
39935         "685"
39936       ],
39937       [
39938         "San Marino",
39939         "sm",
39940         "378"
39941       ],
39942       [
39943         "São Tomé and Príncipe (São Tomé e Príncipe)",
39944         "st",
39945         "239"
39946       ],
39947       [
39948         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39949         "sa",
39950         "966"
39951       ],
39952       [
39953         "Senegal (Sénégal)",
39954         "sn",
39955         "221"
39956       ],
39957       [
39958         "Serbia (Србија)",
39959         "rs",
39960         "381"
39961       ],
39962       [
39963         "Seychelles",
39964         "sc",
39965         "248"
39966       ],
39967       [
39968         "Sierra Leone",
39969         "sl",
39970         "232"
39971       ],
39972       [
39973         "Singapore",
39974         "sg",
39975         "65"
39976       ],
39977       [
39978         "Sint Maarten",
39979         "sx",
39980         "1721"
39981       ],
39982       [
39983         "Slovakia (Slovensko)",
39984         "sk",
39985         "421"
39986       ],
39987       [
39988         "Slovenia (Slovenija)",
39989         "si",
39990         "386"
39991       ],
39992       [
39993         "Solomon Islands",
39994         "sb",
39995         "677"
39996       ],
39997       [
39998         "Somalia (Soomaaliya)",
39999         "so",
40000         "252"
40001       ],
40002       [
40003         "South Africa",
40004         "za",
40005         "27"
40006       ],
40007       [
40008         "South Korea (대한민국)",
40009         "kr",
40010         "82"
40011       ],
40012       [
40013         "South Sudan (‫جنوب السودان‬‎)",
40014         "ss",
40015         "211"
40016       ],
40017       [
40018         "Spain (España)",
40019         "es",
40020         "34"
40021       ],
40022       [
40023         "Sri Lanka (ශ්‍රී ලංකාව)",
40024         "lk",
40025         "94"
40026       ],
40027       [
40028         "Sudan (‫السودان‬‎)",
40029         "sd",
40030         "249"
40031       ],
40032       [
40033         "Suriname",
40034         "sr",
40035         "597"
40036       ],
40037       [
40038         "Svalbard and Jan Mayen",
40039         "sj",
40040         "47",
40041         1
40042       ],
40043       [
40044         "Swaziland",
40045         "sz",
40046         "268"
40047       ],
40048       [
40049         "Sweden (Sverige)",
40050         "se",
40051         "46"
40052       ],
40053       [
40054         "Switzerland (Schweiz)",
40055         "ch",
40056         "41"
40057       ],
40058       [
40059         "Syria (‫سوريا‬‎)",
40060         "sy",
40061         "963"
40062       ],
40063       [
40064         "Taiwan (台灣)",
40065         "tw",
40066         "886"
40067       ],
40068       [
40069         "Tajikistan",
40070         "tj",
40071         "992"
40072       ],
40073       [
40074         "Tanzania",
40075         "tz",
40076         "255"
40077       ],
40078       [
40079         "Thailand (ไทย)",
40080         "th",
40081         "66"
40082       ],
40083       [
40084         "Timor-Leste",
40085         "tl",
40086         "670"
40087       ],
40088       [
40089         "Togo",
40090         "tg",
40091         "228"
40092       ],
40093       [
40094         "Tokelau",
40095         "tk",
40096         "690"
40097       ],
40098       [
40099         "Tonga",
40100         "to",
40101         "676"
40102       ],
40103       [
40104         "Trinidad and Tobago",
40105         "tt",
40106         "1868"
40107       ],
40108       [
40109         "Tunisia (‫تونس‬‎)",
40110         "tn",
40111         "216"
40112       ],
40113       [
40114         "Turkey (Türkiye)",
40115         "tr",
40116         "90"
40117       ],
40118       [
40119         "Turkmenistan",
40120         "tm",
40121         "993"
40122       ],
40123       [
40124         "Turks and Caicos Islands",
40125         "tc",
40126         "1649"
40127       ],
40128       [
40129         "Tuvalu",
40130         "tv",
40131         "688"
40132       ],
40133       [
40134         "U.S. Virgin Islands",
40135         "vi",
40136         "1340"
40137       ],
40138       [
40139         "Uganda",
40140         "ug",
40141         "256"
40142       ],
40143       [
40144         "Ukraine (Україна)",
40145         "ua",
40146         "380"
40147       ],
40148       [
40149         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40150         "ae",
40151         "971"
40152       ],
40153       [
40154         "United Kingdom",
40155         "gb",
40156         "44",
40157         0
40158       ],
40159       [
40160         "United States",
40161         "us",
40162         "1",
40163         0
40164       ],
40165       [
40166         "Uruguay",
40167         "uy",
40168         "598"
40169       ],
40170       [
40171         "Uzbekistan (Oʻzbekiston)",
40172         "uz",
40173         "998"
40174       ],
40175       [
40176         "Vanuatu",
40177         "vu",
40178         "678"
40179       ],
40180       [
40181         "Vatican City (Città del Vaticano)",
40182         "va",
40183         "39",
40184         1
40185       ],
40186       [
40187         "Venezuela",
40188         "ve",
40189         "58"
40190       ],
40191       [
40192         "Vietnam (Việt Nam)",
40193         "vn",
40194         "84"
40195       ],
40196       [
40197         "Wallis and Futuna (Wallis-et-Futuna)",
40198         "wf",
40199         "681"
40200       ],
40201       [
40202         "Western Sahara (‫الصحراء الغربية‬‎)",
40203         "eh",
40204         "212",
40205         1
40206       ],
40207       [
40208         "Yemen (‫اليمن‬‎)",
40209         "ye",
40210         "967"
40211       ],
40212       [
40213         "Zambia",
40214         "zm",
40215         "260"
40216       ],
40217       [
40218         "Zimbabwe",
40219         "zw",
40220         "263"
40221       ],
40222       [
40223         "Åland Islands",
40224         "ax",
40225         "358",
40226         1
40227       ]
40228   ];
40229   
40230   return d;
40231 }/**
40232 *    This script refer to:
40233 *    Title: International Telephone Input
40234 *    Author: Jack O'Connor
40235 *    Code version:  v12.1.12
40236 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40237 **/
40238
40239 /**
40240  * @class Roo.bootstrap.PhoneInput
40241  * @extends Roo.bootstrap.TriggerField
40242  * An input with International dial-code selection
40243  
40244  * @cfg {String} defaultDialCode default '+852'
40245  * @cfg {Array} preferedCountries default []
40246   
40247  * @constructor
40248  * Create a new PhoneInput.
40249  * @param {Object} config Configuration options
40250  */
40251
40252 Roo.bootstrap.PhoneInput = function(config) {
40253     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40254 };
40255
40256 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40257         
40258         listWidth: undefined,
40259         
40260         selectedClass: 'active',
40261         
40262         invalidClass : "has-warning",
40263         
40264         validClass: 'has-success',
40265         
40266         allowed: '0123456789',
40267         
40268         max_length: 15,
40269         
40270         /**
40271          * @cfg {String} defaultDialCode The default dial code when initializing the input
40272          */
40273         defaultDialCode: '+852',
40274         
40275         /**
40276          * @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
40277          */
40278         preferedCountries: false,
40279         
40280         getAutoCreate : function()
40281         {
40282             var data = Roo.bootstrap.PhoneInputData();
40283             var align = this.labelAlign || this.parentLabelAlign();
40284             var id = Roo.id();
40285             
40286             this.allCountries = [];
40287             this.dialCodeMapping = [];
40288             
40289             for (var i = 0; i < data.length; i++) {
40290               var c = data[i];
40291               this.allCountries[i] = {
40292                 name: c[0],
40293                 iso2: c[1],
40294                 dialCode: c[2],
40295                 priority: c[3] || 0,
40296                 areaCodes: c[4] || null
40297               };
40298               this.dialCodeMapping[c[2]] = {
40299                   name: c[0],
40300                   iso2: c[1],
40301                   priority: c[3] || 0,
40302                   areaCodes: c[4] || null
40303               };
40304             }
40305             
40306             var cfg = {
40307                 cls: 'form-group',
40308                 cn: []
40309             };
40310             
40311             var input =  {
40312                 tag: 'input',
40313                 id : id,
40314                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40315                 maxlength: this.max_length,
40316                 cls : 'form-control tel-input',
40317                 autocomplete: 'new-password'
40318             };
40319             
40320             var hiddenInput = {
40321                 tag: 'input',
40322                 type: 'hidden',
40323                 cls: 'hidden-tel-input'
40324             };
40325             
40326             if (this.name) {
40327                 hiddenInput.name = this.name;
40328             }
40329             
40330             if (this.disabled) {
40331                 input.disabled = true;
40332             }
40333             
40334             var flag_container = {
40335                 tag: 'div',
40336                 cls: 'flag-box',
40337                 cn: [
40338                     {
40339                         tag: 'div',
40340                         cls: 'flag'
40341                     },
40342                     {
40343                         tag: 'div',
40344                         cls: 'caret'
40345                     }
40346                 ]
40347             };
40348             
40349             var box = {
40350                 tag: 'div',
40351                 cls: this.hasFeedback ? 'has-feedback' : '',
40352                 cn: [
40353                     hiddenInput,
40354                     input,
40355                     {
40356                         tag: 'input',
40357                         cls: 'dial-code-holder',
40358                         disabled: true
40359                     }
40360                 ]
40361             };
40362             
40363             var container = {
40364                 cls: 'roo-select2-container input-group',
40365                 cn: [
40366                     flag_container,
40367                     box
40368                 ]
40369             };
40370             
40371             if (this.fieldLabel.length) {
40372                 var indicator = {
40373                     tag: 'i',
40374                     tooltip: 'This field is required'
40375                 };
40376                 
40377                 var label = {
40378                     tag: 'label',
40379                     'for':  id,
40380                     cls: 'control-label',
40381                     cn: []
40382                 };
40383                 
40384                 var label_text = {
40385                     tag: 'span',
40386                     html: this.fieldLabel
40387                 };
40388                 
40389                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40390                 label.cn = [
40391                     indicator,
40392                     label_text
40393                 ];
40394                 
40395                 if(this.indicatorpos == 'right') {
40396                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40397                     label.cn = [
40398                         label_text,
40399                         indicator
40400                     ];
40401                 }
40402                 
40403                 if(align == 'left') {
40404                     container = {
40405                         tag: 'div',
40406                         cn: [
40407                             container
40408                         ]
40409                     };
40410                     
40411                     if(this.labelWidth > 12){
40412                         label.style = "width: " + this.labelWidth + 'px';
40413                     }
40414                     if(this.labelWidth < 13 && this.labelmd == 0){
40415                         this.labelmd = this.labelWidth;
40416                     }
40417                     if(this.labellg > 0){
40418                         label.cls += ' col-lg-' + this.labellg;
40419                         input.cls += ' col-lg-' + (12 - this.labellg);
40420                     }
40421                     if(this.labelmd > 0){
40422                         label.cls += ' col-md-' + this.labelmd;
40423                         container.cls += ' col-md-' + (12 - this.labelmd);
40424                     }
40425                     if(this.labelsm > 0){
40426                         label.cls += ' col-sm-' + this.labelsm;
40427                         container.cls += ' col-sm-' + (12 - this.labelsm);
40428                     }
40429                     if(this.labelxs > 0){
40430                         label.cls += ' col-xs-' + this.labelxs;
40431                         container.cls += ' col-xs-' + (12 - this.labelxs);
40432                     }
40433                 }
40434             }
40435             
40436             cfg.cn = [
40437                 label,
40438                 container
40439             ];
40440             
40441             var settings = this;
40442             
40443             ['xs','sm','md','lg'].map(function(size){
40444                 if (settings[size]) {
40445                     cfg.cls += ' col-' + size + '-' + settings[size];
40446                 }
40447             });
40448             
40449             this.store = new Roo.data.Store({
40450                 proxy : new Roo.data.MemoryProxy({}),
40451                 reader : new Roo.data.JsonReader({
40452                     fields : [
40453                         {
40454                             'name' : 'name',
40455                             'type' : 'string'
40456                         },
40457                         {
40458                             'name' : 'iso2',
40459                             'type' : 'string'
40460                         },
40461                         {
40462                             'name' : 'dialCode',
40463                             'type' : 'string'
40464                         },
40465                         {
40466                             'name' : 'priority',
40467                             'type' : 'string'
40468                         },
40469                         {
40470                             'name' : 'areaCodes',
40471                             'type' : 'string'
40472                         }
40473                     ]
40474                 })
40475             });
40476             
40477             if(!this.preferedCountries) {
40478                 this.preferedCountries = [
40479                     'hk',
40480                     'gb',
40481                     'us'
40482                 ];
40483             }
40484             
40485             var p = this.preferedCountries.reverse();
40486             
40487             if(p) {
40488                 for (var i = 0; i < p.length; i++) {
40489                     for (var j = 0; j < this.allCountries.length; j++) {
40490                         if(this.allCountries[j].iso2 == p[i]) {
40491                             var t = this.allCountries[j];
40492                             this.allCountries.splice(j,1);
40493                             this.allCountries.unshift(t);
40494                         }
40495                     } 
40496                 }
40497             }
40498             
40499             this.store.proxy.data = {
40500                 success: true,
40501                 data: this.allCountries
40502             };
40503             
40504             return cfg;
40505         },
40506         
40507         initEvents : function()
40508         {
40509             this.createList();
40510             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40511             
40512             this.indicator = this.indicatorEl();
40513             this.flag = this.flagEl();
40514             this.dialCodeHolder = this.dialCodeHolderEl();
40515             
40516             this.trigger = this.el.select('div.flag-box',true).first();
40517             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40518             
40519             var _this = this;
40520             
40521             (function(){
40522                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40523                 _this.list.setWidth(lw);
40524             }).defer(100);
40525             
40526             this.list.on('mouseover', this.onViewOver, this);
40527             this.list.on('mousemove', this.onViewMove, this);
40528             this.inputEl().on("keyup", this.onKeyUp, this);
40529             this.inputEl().on("keypress", this.onKeyPress, this);
40530             
40531             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40532
40533             this.view = new Roo.View(this.list, this.tpl, {
40534                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40535             });
40536             
40537             this.view.on('click', this.onViewClick, this);
40538             this.setValue(this.defaultDialCode);
40539         },
40540         
40541         onTriggerClick : function(e)
40542         {
40543             Roo.log('trigger click');
40544             if(this.disabled){
40545                 return;
40546             }
40547             
40548             if(this.isExpanded()){
40549                 this.collapse();
40550                 this.hasFocus = false;
40551             }else {
40552                 this.store.load({});
40553                 this.hasFocus = true;
40554                 this.expand();
40555             }
40556         },
40557         
40558         isExpanded : function()
40559         {
40560             return this.list.isVisible();
40561         },
40562         
40563         collapse : function()
40564         {
40565             if(!this.isExpanded()){
40566                 return;
40567             }
40568             this.list.hide();
40569             Roo.get(document).un('mousedown', this.collapseIf, this);
40570             Roo.get(document).un('mousewheel', this.collapseIf, this);
40571             this.fireEvent('collapse', this);
40572             this.validate();
40573         },
40574         
40575         expand : function()
40576         {
40577             Roo.log('expand');
40578
40579             if(this.isExpanded() || !this.hasFocus){
40580                 return;
40581             }
40582             
40583             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40584             this.list.setWidth(lw);
40585             
40586             this.list.show();
40587             this.restrictHeight();
40588             
40589             Roo.get(document).on('mousedown', this.collapseIf, this);
40590             Roo.get(document).on('mousewheel', this.collapseIf, this);
40591             
40592             this.fireEvent('expand', this);
40593         },
40594         
40595         restrictHeight : function()
40596         {
40597             this.list.alignTo(this.inputEl(), this.listAlign);
40598             this.list.alignTo(this.inputEl(), this.listAlign);
40599         },
40600         
40601         onViewOver : function(e, t)
40602         {
40603             if(this.inKeyMode){
40604                 return;
40605             }
40606             var item = this.view.findItemFromChild(t);
40607             
40608             if(item){
40609                 var index = this.view.indexOf(item);
40610                 this.select(index, false);
40611             }
40612         },
40613
40614         // private
40615         onViewClick : function(view, doFocus, el, e)
40616         {
40617             var index = this.view.getSelectedIndexes()[0];
40618             
40619             var r = this.store.getAt(index);
40620             
40621             if(r){
40622                 this.onSelect(r, index);
40623             }
40624             if(doFocus !== false && !this.blockFocus){
40625                 this.inputEl().focus();
40626             }
40627         },
40628         
40629         onViewMove : function(e, t)
40630         {
40631             this.inKeyMode = false;
40632         },
40633         
40634         select : function(index, scrollIntoView)
40635         {
40636             this.selectedIndex = index;
40637             this.view.select(index);
40638             if(scrollIntoView !== false){
40639                 var el = this.view.getNode(index);
40640                 if(el){
40641                     this.list.scrollChildIntoView(el, false);
40642                 }
40643             }
40644         },
40645         
40646         createList : function()
40647         {
40648             this.list = Roo.get(document.body).createChild({
40649                 tag: 'ul',
40650                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40651                 style: 'display:none'
40652             });
40653             
40654             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40655         },
40656         
40657         collapseIf : function(e)
40658         {
40659             var in_combo  = e.within(this.el);
40660             var in_list =  e.within(this.list);
40661             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40662             
40663             if (in_combo || in_list || is_list) {
40664                 return;
40665             }
40666             this.collapse();
40667         },
40668         
40669         onSelect : function(record, index)
40670         {
40671             if(this.fireEvent('beforeselect', this, record, index) !== false){
40672                 
40673                 this.setFlagClass(record.data.iso2);
40674                 this.setDialCode(record.data.dialCode);
40675                 this.hasFocus = false;
40676                 this.collapse();
40677                 this.fireEvent('select', this, record, index);
40678             }
40679         },
40680         
40681         flagEl : function()
40682         {
40683             var flag = this.el.select('div.flag',true).first();
40684             if(!flag){
40685                 return false;
40686             }
40687             return flag;
40688         },
40689         
40690         dialCodeHolderEl : function()
40691         {
40692             var d = this.el.select('input.dial-code-holder',true).first();
40693             if(!d){
40694                 return false;
40695             }
40696             return d;
40697         },
40698         
40699         setDialCode : function(v)
40700         {
40701             this.dialCodeHolder.dom.value = '+'+v;
40702         },
40703         
40704         setFlagClass : function(n)
40705         {
40706             this.flag.dom.className = 'flag '+n;
40707         },
40708         
40709         getValue : function()
40710         {
40711             var v = this.inputEl().getValue();
40712             if(this.dialCodeHolder) {
40713                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40714             }
40715             return v;
40716         },
40717         
40718         setValue : function(v)
40719         {
40720             var d = this.getDialCode(v);
40721             
40722             //invalid dial code
40723             if(v.length == 0 || !d || d.length == 0) {
40724                 if(this.rendered){
40725                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40726                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40727                 }
40728                 return;
40729             }
40730             
40731             //valid dial code
40732             this.setFlagClass(this.dialCodeMapping[d].iso2);
40733             this.setDialCode(d);
40734             this.inputEl().dom.value = v.replace('+'+d,'');
40735             this.hiddenEl().dom.value = this.getValue();
40736             
40737             this.validate();
40738         },
40739         
40740         getDialCode : function(v)
40741         {
40742             v = v ||  '';
40743             
40744             if (v.length == 0) {
40745                 return this.dialCodeHolder.dom.value;
40746             }
40747             
40748             var dialCode = "";
40749             if (v.charAt(0) != "+") {
40750                 return false;
40751             }
40752             var numericChars = "";
40753             for (var i = 1; i < v.length; i++) {
40754               var c = v.charAt(i);
40755               if (!isNaN(c)) {
40756                 numericChars += c;
40757                 if (this.dialCodeMapping[numericChars]) {
40758                   dialCode = v.substr(1, i);
40759                 }
40760                 if (numericChars.length == 4) {
40761                   break;
40762                 }
40763               }
40764             }
40765             return dialCode;
40766         },
40767         
40768         reset : function()
40769         {
40770             this.setValue(this.defaultDialCode);
40771             this.validate();
40772         },
40773         
40774         hiddenEl : function()
40775         {
40776             return this.el.select('input.hidden-tel-input',true).first();
40777         },
40778         
40779         // after setting val
40780         onKeyUp : function(e){
40781             this.setValue(this.getValue());
40782         },
40783         
40784         onKeyPress : function(e){
40785             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40786                 e.stopEvent();
40787             }
40788         }
40789         
40790 });
40791 /**
40792  * @class Roo.bootstrap.MoneyField
40793  * @extends Roo.bootstrap.ComboBox
40794  * Bootstrap MoneyField class
40795  * 
40796  * @constructor
40797  * Create a new MoneyField.
40798  * @param {Object} config Configuration options
40799  */
40800
40801 Roo.bootstrap.MoneyField = function(config) {
40802     
40803     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40804     
40805 };
40806
40807 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40808     
40809     /**
40810      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40811      */
40812     allowDecimals : true,
40813     /**
40814      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40815      */
40816     decimalSeparator : ".",
40817     /**
40818      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40819      */
40820     decimalPrecision : 0,
40821     /**
40822      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40823      */
40824     allowNegative : true,
40825     /**
40826      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40827      */
40828     allowZero: true,
40829     /**
40830      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40831      */
40832     minValue : Number.NEGATIVE_INFINITY,
40833     /**
40834      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40835      */
40836     maxValue : Number.MAX_VALUE,
40837     /**
40838      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40839      */
40840     minText : "The minimum value for this field is {0}",
40841     /**
40842      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40843      */
40844     maxText : "The maximum value for this field is {0}",
40845     /**
40846      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40847      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40848      */
40849     nanText : "{0} is not a valid number",
40850     /**
40851      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40852      */
40853     castInt : true,
40854     /**
40855      * @cfg {String} defaults currency of the MoneyField
40856      * value should be in lkey
40857      */
40858     defaultCurrency : false,
40859     /**
40860      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40861      */
40862     thousandsDelimiter : false,
40863     /**
40864      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40865      */
40866     max_length: false,
40867     
40868     inputlg : 9,
40869     inputmd : 9,
40870     inputsm : 9,
40871     inputxs : 6,
40872     
40873     store : false,
40874     
40875     getAutoCreate : function()
40876     {
40877         var align = this.labelAlign || this.parentLabelAlign();
40878         
40879         var id = Roo.id();
40880
40881         var cfg = {
40882             cls: 'form-group',
40883             cn: []
40884         };
40885
40886         var input =  {
40887             tag: 'input',
40888             id : id,
40889             cls : 'form-control roo-money-amount-input',
40890             autocomplete: 'new-password'
40891         };
40892         
40893         var hiddenInput = {
40894             tag: 'input',
40895             type: 'hidden',
40896             id: Roo.id(),
40897             cls: 'hidden-number-input'
40898         };
40899         
40900         if(this.max_length) {
40901             input.maxlength = this.max_length; 
40902         }
40903         
40904         if (this.name) {
40905             hiddenInput.name = this.name;
40906         }
40907
40908         if (this.disabled) {
40909             input.disabled = true;
40910         }
40911
40912         var clg = 12 - this.inputlg;
40913         var cmd = 12 - this.inputmd;
40914         var csm = 12 - this.inputsm;
40915         var cxs = 12 - this.inputxs;
40916         
40917         var container = {
40918             tag : 'div',
40919             cls : 'row roo-money-field',
40920             cn : [
40921                 {
40922                     tag : 'div',
40923                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40924                     cn : [
40925                         {
40926                             tag : 'div',
40927                             cls: 'roo-select2-container input-group',
40928                             cn: [
40929                                 {
40930                                     tag : 'input',
40931                                     cls : 'form-control roo-money-currency-input',
40932                                     autocomplete: 'new-password',
40933                                     readOnly : 1,
40934                                     name : this.currencyName
40935                                 },
40936                                 {
40937                                     tag :'span',
40938                                     cls : 'input-group-addon',
40939                                     cn : [
40940                                         {
40941                                             tag: 'span',
40942                                             cls: 'caret'
40943                                         }
40944                                     ]
40945                                 }
40946                             ]
40947                         }
40948                     ]
40949                 },
40950                 {
40951                     tag : 'div',
40952                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40953                     cn : [
40954                         {
40955                             tag: 'div',
40956                             cls: this.hasFeedback ? 'has-feedback' : '',
40957                             cn: [
40958                                 input
40959                             ]
40960                         }
40961                     ]
40962                 }
40963             ]
40964             
40965         };
40966         
40967         if (this.fieldLabel.length) {
40968             var indicator = {
40969                 tag: 'i',
40970                 tooltip: 'This field is required'
40971             };
40972
40973             var label = {
40974                 tag: 'label',
40975                 'for':  id,
40976                 cls: 'control-label',
40977                 cn: []
40978             };
40979
40980             var label_text = {
40981                 tag: 'span',
40982                 html: this.fieldLabel
40983             };
40984
40985             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40986             label.cn = [
40987                 indicator,
40988                 label_text
40989             ];
40990
40991             if(this.indicatorpos == 'right') {
40992                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40993                 label.cn = [
40994                     label_text,
40995                     indicator
40996                 ];
40997             }
40998
40999             if(align == 'left') {
41000                 container = {
41001                     tag: 'div',
41002                     cn: [
41003                         container
41004                     ]
41005                 };
41006
41007                 if(this.labelWidth > 12){
41008                     label.style = "width: " + this.labelWidth + 'px';
41009                 }
41010                 if(this.labelWidth < 13 && this.labelmd == 0){
41011                     this.labelmd = this.labelWidth;
41012                 }
41013                 if(this.labellg > 0){
41014                     label.cls += ' col-lg-' + this.labellg;
41015                     input.cls += ' col-lg-' + (12 - this.labellg);
41016                 }
41017                 if(this.labelmd > 0){
41018                     label.cls += ' col-md-' + this.labelmd;
41019                     container.cls += ' col-md-' + (12 - this.labelmd);
41020                 }
41021                 if(this.labelsm > 0){
41022                     label.cls += ' col-sm-' + this.labelsm;
41023                     container.cls += ' col-sm-' + (12 - this.labelsm);
41024                 }
41025                 if(this.labelxs > 0){
41026                     label.cls += ' col-xs-' + this.labelxs;
41027                     container.cls += ' col-xs-' + (12 - this.labelxs);
41028                 }
41029             }
41030         }
41031
41032         cfg.cn = [
41033             label,
41034             container,
41035             hiddenInput
41036         ];
41037         
41038         var settings = this;
41039
41040         ['xs','sm','md','lg'].map(function(size){
41041             if (settings[size]) {
41042                 cfg.cls += ' col-' + size + '-' + settings[size];
41043             }
41044         });
41045         
41046         return cfg;
41047     },
41048     
41049     initEvents : function()
41050     {
41051         this.indicator = this.indicatorEl();
41052         
41053         this.initCurrencyEvent();
41054         
41055         this.initNumberEvent();
41056     },
41057     
41058     initCurrencyEvent : function()
41059     {
41060         if (!this.store) {
41061             throw "can not find store for combo";
41062         }
41063         
41064         this.store = Roo.factory(this.store, Roo.data);
41065         this.store.parent = this;
41066         
41067         this.createList();
41068         
41069         this.triggerEl = this.el.select('.input-group-addon', true).first();
41070         
41071         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41072         
41073         var _this = this;
41074         
41075         (function(){
41076             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41077             _this.list.setWidth(lw);
41078         }).defer(100);
41079         
41080         this.list.on('mouseover', this.onViewOver, this);
41081         this.list.on('mousemove', this.onViewMove, this);
41082         this.list.on('scroll', this.onViewScroll, this);
41083         
41084         if(!this.tpl){
41085             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41086         }
41087         
41088         this.view = new Roo.View(this.list, this.tpl, {
41089             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41090         });
41091         
41092         this.view.on('click', this.onViewClick, this);
41093         
41094         this.store.on('beforeload', this.onBeforeLoad, this);
41095         this.store.on('load', this.onLoad, this);
41096         this.store.on('loadexception', this.onLoadException, this);
41097         
41098         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41099             "up" : function(e){
41100                 this.inKeyMode = true;
41101                 this.selectPrev();
41102             },
41103
41104             "down" : function(e){
41105                 if(!this.isExpanded()){
41106                     this.onTriggerClick();
41107                 }else{
41108                     this.inKeyMode = true;
41109                     this.selectNext();
41110                 }
41111             },
41112
41113             "enter" : function(e){
41114                 this.collapse();
41115                 
41116                 if(this.fireEvent("specialkey", this, e)){
41117                     this.onViewClick(false);
41118                 }
41119                 
41120                 return true;
41121             },
41122
41123             "esc" : function(e){
41124                 this.collapse();
41125             },
41126
41127             "tab" : function(e){
41128                 this.collapse();
41129                 
41130                 if(this.fireEvent("specialkey", this, e)){
41131                     this.onViewClick(false);
41132                 }
41133                 
41134                 return true;
41135             },
41136
41137             scope : this,
41138
41139             doRelay : function(foo, bar, hname){
41140                 if(hname == 'down' || this.scope.isExpanded()){
41141                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41142                 }
41143                 return true;
41144             },
41145
41146             forceKeyDown: true
41147         });
41148         
41149         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41150         
41151     },
41152     
41153     initNumberEvent : function(e)
41154     {
41155         this.inputEl().on("keydown" , this.fireKey,  this);
41156         this.inputEl().on("focus", this.onFocus,  this);
41157         this.inputEl().on("blur", this.onBlur,  this);
41158         
41159         this.inputEl().relayEvent('keyup', this);
41160         
41161         if(this.indicator){
41162             this.indicator.addClass('invisible');
41163         }
41164  
41165         this.originalValue = this.getValue();
41166         
41167         if(this.validationEvent == 'keyup'){
41168             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41169             this.inputEl().on('keyup', this.filterValidation, this);
41170         }
41171         else if(this.validationEvent !== false){
41172             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41173         }
41174         
41175         if(this.selectOnFocus){
41176             this.on("focus", this.preFocus, this);
41177             
41178         }
41179         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41180             this.inputEl().on("keypress", this.filterKeys, this);
41181         } else {
41182             this.inputEl().relayEvent('keypress', this);
41183         }
41184         
41185         var allowed = "0123456789";
41186         
41187         if(this.allowDecimals){
41188             allowed += this.decimalSeparator;
41189         }
41190         
41191         if(this.allowNegative){
41192             allowed += "-";
41193         }
41194         
41195         if(this.thousandsDelimiter) {
41196             allowed += ",";
41197         }
41198         
41199         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41200         
41201         var keyPress = function(e){
41202             
41203             var k = e.getKey();
41204             
41205             var c = e.getCharCode();
41206             
41207             if(
41208                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41209                     allowed.indexOf(String.fromCharCode(c)) === -1
41210             ){
41211                 e.stopEvent();
41212                 return;
41213             }
41214             
41215             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41216                 return;
41217             }
41218             
41219             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41220                 e.stopEvent();
41221             }
41222         };
41223         
41224         this.inputEl().on("keypress", keyPress, this);
41225         
41226     },
41227     
41228     onTriggerClick : function(e)
41229     {   
41230         if(this.disabled){
41231             return;
41232         }
41233         
41234         this.page = 0;
41235         this.loadNext = false;
41236         
41237         if(this.isExpanded()){
41238             this.collapse();
41239             return;
41240         }
41241         
41242         this.hasFocus = true;
41243         
41244         if(this.triggerAction == 'all') {
41245             this.doQuery(this.allQuery, true);
41246             return;
41247         }
41248         
41249         this.doQuery(this.getRawValue());
41250     },
41251     
41252     getCurrency : function()
41253     {   
41254         var v = this.currencyEl().getValue();
41255         
41256         return v;
41257     },
41258     
41259     restrictHeight : function()
41260     {
41261         this.list.alignTo(this.currencyEl(), this.listAlign);
41262         this.list.alignTo(this.currencyEl(), this.listAlign);
41263     },
41264     
41265     onViewClick : function(view, doFocus, el, e)
41266     {
41267         var index = this.view.getSelectedIndexes()[0];
41268         
41269         var r = this.store.getAt(index);
41270         
41271         if(r){
41272             this.onSelect(r, index);
41273         }
41274     },
41275     
41276     onSelect : function(record, index){
41277         
41278         if(this.fireEvent('beforeselect', this, record, index) !== false){
41279         
41280             this.setFromCurrencyData(index > -1 ? record.data : false);
41281             
41282             this.collapse();
41283             
41284             this.fireEvent('select', this, record, index);
41285         }
41286     },
41287     
41288     setFromCurrencyData : function(o)
41289     {
41290         var currency = '';
41291         
41292         this.lastCurrency = o;
41293         
41294         if (this.currencyField) {
41295             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41296         } else {
41297             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41298         }
41299         
41300         this.lastSelectionText = currency;
41301         
41302         //setting default currency
41303         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41304             this.setCurrency(this.defaultCurrency);
41305             return;
41306         }
41307         
41308         this.setCurrency(currency);
41309     },
41310     
41311     setFromData : function(o)
41312     {
41313         var c = {};
41314         
41315         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41316         
41317         this.setFromCurrencyData(c);
41318         
41319         var value = '';
41320         
41321         if (this.name) {
41322             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41323         } else {
41324             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41325         }
41326         
41327         this.setValue(value);
41328         
41329     },
41330     
41331     setCurrency : function(v)
41332     {   
41333         this.currencyValue = v;
41334         
41335         if(this.rendered){
41336             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41337             this.validate();
41338         }
41339     },
41340     
41341     setValue : function(v)
41342     {
41343         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41344         
41345         this.value = v;
41346         
41347         if(this.rendered){
41348             
41349             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41350             
41351             this.inputEl().dom.value = (v == '') ? '' :
41352                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41353             
41354             if(!this.allowZero && v === '0') {
41355                 this.hiddenEl().dom.value = '';
41356                 this.inputEl().dom.value = '';
41357             }
41358             
41359             this.validate();
41360         }
41361     },
41362     
41363     getRawValue : function()
41364     {
41365         var v = this.inputEl().getValue();
41366         
41367         return v;
41368     },
41369     
41370     getValue : function()
41371     {
41372         return this.fixPrecision(this.parseValue(this.getRawValue()));
41373     },
41374     
41375     parseValue : function(value)
41376     {
41377         if(this.thousandsDelimiter) {
41378             value += "";
41379             r = new RegExp(",", "g");
41380             value = value.replace(r, "");
41381         }
41382         
41383         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41384         return isNaN(value) ? '' : value;
41385         
41386     },
41387     
41388     fixPrecision : function(value)
41389     {
41390         if(this.thousandsDelimiter) {
41391             value += "";
41392             r = new RegExp(",", "g");
41393             value = value.replace(r, "");
41394         }
41395         
41396         var nan = isNaN(value);
41397         
41398         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41399             return nan ? '' : value;
41400         }
41401         return parseFloat(value).toFixed(this.decimalPrecision);
41402     },
41403     
41404     decimalPrecisionFcn : function(v)
41405     {
41406         return Math.floor(v);
41407     },
41408     
41409     validateValue : function(value)
41410     {
41411         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41412             return false;
41413         }
41414         
41415         var num = this.parseValue(value);
41416         
41417         if(isNaN(num)){
41418             this.markInvalid(String.format(this.nanText, value));
41419             return false;
41420         }
41421         
41422         if(num < this.minValue){
41423             this.markInvalid(String.format(this.minText, this.minValue));
41424             return false;
41425         }
41426         
41427         if(num > this.maxValue){
41428             this.markInvalid(String.format(this.maxText, this.maxValue));
41429             return false;
41430         }
41431         
41432         return true;
41433     },
41434     
41435     validate : function()
41436     {
41437         if(this.disabled || this.allowBlank){
41438             this.markValid();
41439             return true;
41440         }
41441         
41442         var currency = this.getCurrency();
41443         
41444         if(this.validateValue(this.getRawValue()) && currency.length){
41445             this.markValid();
41446             return true;
41447         }
41448         
41449         this.markInvalid();
41450         return false;
41451     },
41452     
41453     getName: function()
41454     {
41455         return this.name;
41456     },
41457     
41458     beforeBlur : function()
41459     {
41460         if(!this.castInt){
41461             return;
41462         }
41463         
41464         var v = this.parseValue(this.getRawValue());
41465         
41466         if(v || v == 0){
41467             this.setValue(v);
41468         }
41469     },
41470     
41471     onBlur : function()
41472     {
41473         this.beforeBlur();
41474         
41475         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41476             //this.el.removeClass(this.focusClass);
41477         }
41478         
41479         this.hasFocus = false;
41480         
41481         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41482             this.validate();
41483         }
41484         
41485         var v = this.getValue();
41486         
41487         if(String(v) !== String(this.startValue)){
41488             this.fireEvent('change', this, v, this.startValue);
41489         }
41490         
41491         this.fireEvent("blur", this);
41492     },
41493     
41494     inputEl : function()
41495     {
41496         return this.el.select('.roo-money-amount-input', true).first();
41497     },
41498     
41499     currencyEl : function()
41500     {
41501         return this.el.select('.roo-money-currency-input', true).first();
41502     },
41503     
41504     hiddenEl : function()
41505     {
41506         return this.el.select('input.hidden-number-input',true).first();
41507     }
41508     
41509 });