roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass('hidden');
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');
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 fs
593  * @cfg {String} badge text for badge
594  * @cfg {String} theme (default|glow)  
595  * @cfg {Boolean} inverse dark themed version
596  * @cfg {Boolean} toggle is it a slidy toggle button
597  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
598  * @cfg {String} ontext text for on slidy toggle state
599  * @cfg {String} offtext text for off slidy toggle state
600  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
601  * @cfg {Boolean} removeClass remove the standard class..
602  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
603  * 
604  * @constructor
605  * Create a new button
606  * @param {Object} config The config object
607  */
608
609
610 Roo.bootstrap.Button = function(config){
611     Roo.bootstrap.Button.superclass.constructor.call(this, config);
612     this.weightClass = ["btn-default btn-outline-secondary", 
613                        "btn-primary", 
614                        "btn-success", 
615                        "btn-info", 
616                        "btn-warning",
617                        "btn-danger",
618                        "btn-link"
619                       ],  
620     this.addEvents({
621         // raw events
622         /**
623          * @event click
624          * When a butotn is pressed
625          * @param {Roo.bootstrap.Button} btn
626          * @param {Roo.EventObject} e
627          */
628         "click" : true,
629          /**
630          * @event toggle
631          * After the button has been toggles
632          * @param {Roo.bootstrap.Button} btn
633          * @param {Roo.EventObject} e
634          * @param {boolean} pressed (also available as button.pressed)
635          */
636         "toggle" : true
637     });
638 };
639
640 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
641     html: false,
642     active: false,
643     weight: '',
644     badge_weight: '',
645     outline : false,
646     size: '',
647     tag: 'button',
648     href: '',
649     disabled: false,
650     isClose: false,
651     glyphicon: '',
652     fa: '',
653     badge: '',
654     theme: 'default',
655     inverse: false,
656     
657     toggle: false,
658     ontext: 'ON',
659     offtext: 'OFF',
660     defaulton: true,
661     preventDefault: true,
662     removeClass: false,
663     name: false,
664     target: false,
665      
666     pressed : null,
667      
668     
669     getAutoCreate : function(){
670         
671         var cfg = {
672             tag : 'button',
673             cls : 'roo-button',
674             html: ''
675         };
676         
677         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
678             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
679             this.tag = 'button';
680         } else {
681             cfg.tag = this.tag;
682         }
683         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
684         
685         if (this.toggle == true) {
686             cfg={
687                 tag: 'div',
688                 cls: 'slider-frame roo-button',
689                 cn: [
690                     {
691                         tag: 'span',
692                         'data-on-text':'ON',
693                         'data-off-text':'OFF',
694                         cls: 'slider-button',
695                         html: this.offtext
696                     }
697                 ]
698             };
699             
700             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
701                 cfg.cls += ' '+this.weight;
702             }
703             
704             return cfg;
705         }
706         
707         if (this.isClose) {
708             cfg.cls += ' close';
709             
710             cfg["aria-hidden"] = true;
711             
712             cfg.html = "&times;";
713             
714             return cfg;
715         }
716         
717          
718         if (this.theme==='default') {
719             cfg.cls = 'btn roo-button';
720             
721             //if (this.parentType != 'Navbar') {
722             this.weight = this.weight.length ?  this.weight : 'default';
723             //}
724             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
725                 
726                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
727                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
728                 cfg.cls += ' btn-' + outline + weight;
729                 if (this.weight == 'default') {
730                     // BC
731                     cfg.cls += ' btn-' + this.weight;
732                 }
733             }
734         } else if (this.theme==='glow') {
735             
736             cfg.tag = 'a';
737             cfg.cls = 'btn-glow roo-button';
738             
739             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
740                 
741                 cfg.cls += ' ' + this.weight;
742             }
743         }
744    
745         
746         if (this.inverse) {
747             this.cls += ' inverse';
748         }
749         
750         
751         if (this.active || this.pressed === true) {
752             cfg.cls += ' active';
753         }
754         
755         if (this.disabled) {
756             cfg.disabled = 'disabled';
757         }
758         
759         if (this.items) {
760             Roo.log('changing to ul' );
761             cfg.tag = 'ul';
762             this.glyphicon = 'caret';
763             if (Roo.bootstrap.version == 4) {
764                 this.fa = 'caret-down';
765             }
766             
767         }
768         
769         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
770          
771         //gsRoo.log(this.parentType);
772         if (this.parentType === 'Navbar' && !this.parent().bar) {
773             Roo.log('changing to li?');
774             
775             cfg.tag = 'li';
776             
777             cfg.cls = '';
778             cfg.cn =  [{
779                 tag : 'a',
780                 cls : 'roo-button',
781                 html : this.html,
782                 href : this.href || '#'
783             }];
784             if (this.menu) {
785                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
786                 cfg.cls += ' dropdown';
787             }   
788             
789             delete cfg.html;
790             
791         }
792         
793        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
794         
795         if (this.glyphicon) {
796             cfg.html = ' ' + cfg.html;
797             
798             cfg.cn = [
799                 {
800                     tag: 'span',
801                     cls: 'glyphicon glyphicon-' + this.glyphicon
802                 }
803             ];
804         }
805         if (this.fa) {
806             cfg.html = ' ' + cfg.html;
807             
808             cfg.cn = [
809                 {
810                     tag: 'i',
811                     cls: 'fa fas fa-' + this.fa
812                 }
813             ];
814         }
815         
816         if (this.badge) {
817             cfg.html += ' ';
818             
819             cfg.tag = 'a';
820             
821 //            cfg.cls='btn roo-button';
822             
823             cfg.href=this.href;
824             
825             var value = cfg.html;
826             
827             if(this.glyphicon){
828                 value = {
829                     tag: 'span',
830                     cls: 'glyphicon glyphicon-' + this.glyphicon,
831                     html: this.html
832                 };
833             }
834             if(this.fa){
835                 value = {
836                     tag: 'i',
837                     cls: 'fa fas fa-' + this.fa,
838                     html: this.html
839                 };
840             }
841             
842             var bw = this.badge_weight.length ? this.badge_weight :
843                 (this.weight.length ? this.weight : 'secondary');
844             bw = bw == 'default' ? 'secondary' : bw;
845             
846             cfg.cn = [
847                 value,
848                 {
849                     tag: 'span',
850                     cls: 'badge badge-' + bw,
851                     html: this.badge
852                 }
853             ];
854             
855             cfg.html='';
856         }
857         
858         if (this.menu) {
859             cfg.cls += ' dropdown';
860             cfg.html = typeof(cfg.html) != 'undefined' ?
861                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
862         }
863         
864         if (cfg.tag !== 'a' && this.href !== '') {
865             throw "Tag must be a to set href.";
866         } else if (this.href.length > 0) {
867             cfg.href = this.href;
868         }
869         
870         if(this.removeClass){
871             cfg.cls = '';
872         }
873         
874         if(this.target){
875             cfg.target = this.target;
876         }
877         
878         return cfg;
879     },
880     initEvents: function() {
881        // Roo.log('init events?');
882 //        Roo.log(this.el.dom);
883         // add the menu...
884         
885         if (typeof (this.menu) != 'undefined') {
886             this.menu.parentType = this.xtype;
887             this.menu.triggerEl = this.el;
888             this.addxtype(Roo.apply({}, this.menu));
889         }
890
891
892        if (this.el.hasClass('roo-button')) {
893             this.el.on('click', this.onClick, this);
894        } else {
895             this.el.select('.roo-button').on('click', this.onClick, this);
896        }
897        
898        if(this.removeClass){
899            this.el.on('click', this.onClick, this);
900        }
901        
902        this.el.enableDisplayMode();
903         
904     },
905     onClick : function(e)
906     {
907         if (this.disabled) {
908             return;
909         }
910         
911         Roo.log('button on click ');
912         if(this.preventDefault){
913             e.preventDefault();
914         }
915         
916         if (this.pressed === true || this.pressed === false) {
917             this.toggleActive(e);
918         }
919         
920         
921         this.fireEvent('click', this, e);
922     },
923     
924     /**
925      * Enables this button
926      */
927     enable : function()
928     {
929         this.disabled = false;
930         this.el.removeClass('disabled');
931     },
932     
933     /**
934      * Disable this button
935      */
936     disable : function()
937     {
938         this.disabled = true;
939         this.el.addClass('disabled');
940     },
941      /**
942      * sets the active state on/off, 
943      * @param {Boolean} state (optional) Force a particular state
944      */
945     setActive : function(v) {
946         
947         this.el[v ? 'addClass' : 'removeClass']('active');
948         this.pressed = v;
949     },
950      /**
951      * toggles the current active state 
952      */
953     toggleActive : function(e)
954     {
955         this.setActive(!this.pressed);
956         this.fireEvent('toggle', this, e, !this.pressed);
957     },
958      /**
959      * get the current active state
960      * @return {boolean} true if it's active
961      */
962     isActive : function()
963     {
964         return this.el.hasClass('active');
965     },
966     /**
967      * set the text of the first selected button
968      */
969     setText : function(str)
970     {
971         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
972     },
973     /**
974      * get the text of the first selected button
975      */
976     getText : function()
977     {
978         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
979     },
980     
981     setWeight : function(str)
982     {
983         this.el.removeClass(this.weightClass);
984         this.weight = str;
985         var outline = this.outline ? 'outline-' : '';
986         if (str == 'default') {
987             this.el.addClass('btn-default btn-outline-secondary');        
988             return;
989         }
990         this.el.addClass('btn-' + outline + str);        
991     }
992     
993     
994 });
995
996  /*
997  * - LGPL
998  *
999  * column
1000  * 
1001  */
1002
1003 /**
1004  * @class Roo.bootstrap.Column
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Column class
1007  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1008  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1009  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1010  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1011  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1012  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1013  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1014  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1015  *
1016  * 
1017  * @cfg {Boolean} hidden (true|false) hide the element
1018  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1019  * @cfg {String} fa (ban|check|...) font awesome icon
1020  * @cfg {Number} fasize (1|2|....) font awsome size
1021
1022  * @cfg {String} icon (info-sign|check|...) glyphicon name
1023
1024  * @cfg {String} html content of column.
1025  * 
1026  * @constructor
1027  * Create a new Column
1028  * @param {Object} config The config object
1029  */
1030
1031 Roo.bootstrap.Column = function(config){
1032     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1033 };
1034
1035 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1036     
1037     xs: false,
1038     sm: false,
1039     md: false,
1040     lg: false,
1041     xsoff: false,
1042     smoff: false,
1043     mdoff: false,
1044     lgoff: false,
1045     html: '',
1046     offset: 0,
1047     alert: false,
1048     fa: false,
1049     icon : false,
1050     hidden : false,
1051     fasize : 1,
1052     
1053     getAutoCreate : function(){
1054         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1055         
1056         cfg = {
1057             tag: 'div',
1058             cls: 'column'
1059         };
1060         
1061         var settings=this;
1062         ['xs','sm','md','lg'].map(function(size){
1063             //Roo.log( size + ':' + settings[size]);
1064             
1065             if (settings[size+'off'] !== false) {
1066                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1067             }
1068             
1069             if (settings[size] === false) {
1070                 return;
1071             }
1072             
1073             if (!settings[size]) { // 0 = hidden
1074                 cfg.cls += ' hidden-' + size;
1075                 return;
1076             }
1077             cfg.cls += ' col-' + size + '-' + settings[size];
1078             
1079         });
1080         
1081         if (this.hidden) {
1082             cfg.cls += ' hidden';
1083         }
1084         
1085         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1086             cfg.cls +=' alert alert-' + this.alert;
1087         }
1088         
1089         
1090         if (this.html.length) {
1091             cfg.html = this.html;
1092         }
1093         if (this.fa) {
1094             var fasize = '';
1095             if (this.fasize > 1) {
1096                 fasize = ' fa-' + this.fasize + 'x';
1097             }
1098             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1099             
1100             
1101         }
1102         if (this.icon) {
1103             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1104         }
1105         
1106         return cfg;
1107     }
1108    
1109 });
1110
1111  
1112
1113  /*
1114  * - LGPL
1115  *
1116  * page container.
1117  * 
1118  */
1119
1120
1121 /**
1122  * @class Roo.bootstrap.Container
1123  * @extends Roo.bootstrap.Component
1124  * Bootstrap Container class
1125  * @cfg {Boolean} jumbotron is it a jumbotron element
1126  * @cfg {String} html content of element
1127  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1128  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1129  * @cfg {String} header content of header (for panel)
1130  * @cfg {String} footer content of footer (for panel)
1131  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1132  * @cfg {String} tag (header|aside|section) type of HTML tag.
1133  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1134  * @cfg {String} fa font awesome icon
1135  * @cfg {String} icon (info-sign|check|...) glyphicon name
1136  * @cfg {Boolean} hidden (true|false) hide the element
1137  * @cfg {Boolean} expandable (true|false) default false
1138  * @cfg {Boolean} expanded (true|false) default true
1139  * @cfg {String} rheader contet on the right of header
1140  * @cfg {Boolean} clickable (true|false) default false
1141
1142  *     
1143  * @constructor
1144  * Create a new Container
1145  * @param {Object} config The config object
1146  */
1147
1148 Roo.bootstrap.Container = function(config){
1149     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1150     
1151     this.addEvents({
1152         // raw events
1153          /**
1154          * @event expand
1155          * After the panel has been expand
1156          * 
1157          * @param {Roo.bootstrap.Container} this
1158          */
1159         "expand" : true,
1160         /**
1161          * @event collapse
1162          * After the panel has been collapsed
1163          * 
1164          * @param {Roo.bootstrap.Container} this
1165          */
1166         "collapse" : true,
1167         /**
1168          * @event click
1169          * When a element is chick
1170          * @param {Roo.bootstrap.Container} this
1171          * @param {Roo.EventObject} e
1172          */
1173         "click" : true
1174     });
1175 };
1176
1177 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1178     
1179     jumbotron : false,
1180     well: '',
1181     panel : '',
1182     header: '',
1183     footer : '',
1184     sticky: '',
1185     tag : false,
1186     alert : false,
1187     fa: false,
1188     icon : false,
1189     expandable : false,
1190     rheader : '',
1191     expanded : true,
1192     clickable: false,
1193   
1194      
1195     getChildContainer : function() {
1196         
1197         if(!this.el){
1198             return false;
1199         }
1200         
1201         if (this.panel.length) {
1202             return this.el.select('.panel-body',true).first();
1203         }
1204         
1205         return this.el;
1206     },
1207     
1208     
1209     getAutoCreate : function(){
1210         
1211         var cfg = {
1212             tag : this.tag || 'div',
1213             html : '',
1214             cls : ''
1215         };
1216         if (this.jumbotron) {
1217             cfg.cls = 'jumbotron';
1218         }
1219         
1220         
1221         
1222         // - this is applied by the parent..
1223         //if (this.cls) {
1224         //    cfg.cls = this.cls + '';
1225         //}
1226         
1227         if (this.sticky.length) {
1228             
1229             var bd = Roo.get(document.body);
1230             if (!bd.hasClass('bootstrap-sticky')) {
1231                 bd.addClass('bootstrap-sticky');
1232                 Roo.select('html',true).setStyle('height', '100%');
1233             }
1234              
1235             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1236         }
1237         
1238         
1239         if (this.well.length) {
1240             switch (this.well) {
1241                 case 'lg':
1242                 case 'sm':
1243                     cfg.cls +=' well well-' +this.well;
1244                     break;
1245                 default:
1246                     cfg.cls +=' well';
1247                     break;
1248             }
1249         }
1250         
1251         if (this.hidden) {
1252             cfg.cls += ' hidden';
1253         }
1254         
1255         
1256         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1257             cfg.cls +=' alert alert-' + this.alert;
1258         }
1259         
1260         var body = cfg;
1261         
1262         if (this.panel.length) {
1263             cfg.cls += ' panel panel-' + this.panel;
1264             cfg.cn = [];
1265             if (this.header.length) {
1266                 
1267                 var h = [];
1268                 
1269                 if(this.expandable){
1270                     
1271                     cfg.cls = cfg.cls + ' expandable';
1272                     
1273                     h.push({
1274                         tag: 'i',
1275                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1276                     });
1277                     
1278                 }
1279                 
1280                 h.push(
1281                     {
1282                         tag: 'span',
1283                         cls : 'panel-title',
1284                         html : (this.expandable ? '&nbsp;' : '') + this.header
1285                     },
1286                     {
1287                         tag: 'span',
1288                         cls: 'panel-header-right',
1289                         html: this.rheader
1290                     }
1291                 );
1292                 
1293                 cfg.cn.push({
1294                     cls : 'panel-heading',
1295                     style : this.expandable ? 'cursor: pointer' : '',
1296                     cn : h
1297                 });
1298                 
1299             }
1300             
1301             body = false;
1302             cfg.cn.push({
1303                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1304                 html : this.html
1305             });
1306             
1307             
1308             if (this.footer.length) {
1309                 cfg.cn.push({
1310                     cls : 'panel-footer',
1311                     html : this.footer
1312                     
1313                 });
1314             }
1315             
1316         }
1317         
1318         if (body) {
1319             body.html = this.html || cfg.html;
1320             // prefix with the icons..
1321             if (this.fa) {
1322                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1323             }
1324             if (this.icon) {
1325                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1326             }
1327             
1328             
1329         }
1330         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1331             cfg.cls =  'container';
1332         }
1333         
1334         return cfg;
1335     },
1336     
1337     initEvents: function() 
1338     {
1339         if(this.expandable){
1340             var headerEl = this.headerEl();
1341         
1342             if(headerEl){
1343                 headerEl.on('click', this.onToggleClick, this);
1344             }
1345         }
1346         
1347         if(this.clickable){
1348             this.el.on('click', this.onClick, this);
1349         }
1350         
1351     },
1352     
1353     onToggleClick : function()
1354     {
1355         var headerEl = this.headerEl();
1356         
1357         if(!headerEl){
1358             return;
1359         }
1360         
1361         if(this.expanded){
1362             this.collapse();
1363             return;
1364         }
1365         
1366         this.expand();
1367     },
1368     
1369     expand : function()
1370     {
1371         if(this.fireEvent('expand', this)) {
1372             
1373             this.expanded = true;
1374             
1375             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1376             
1377             this.el.select('.panel-body',true).first().removeClass('hide');
1378             
1379             var toggleEl = this.toggleEl();
1380
1381             if(!toggleEl){
1382                 return;
1383             }
1384
1385             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1386         }
1387         
1388     },
1389     
1390     collapse : function()
1391     {
1392         if(this.fireEvent('collapse', this)) {
1393             
1394             this.expanded = false;
1395             
1396             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1397             this.el.select('.panel-body',true).first().addClass('hide');
1398         
1399             var toggleEl = this.toggleEl();
1400
1401             if(!toggleEl){
1402                 return;
1403             }
1404
1405             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1406         }
1407     },
1408     
1409     toggleEl : function()
1410     {
1411         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1412             return;
1413         }
1414         
1415         return this.el.select('.panel-heading .fa',true).first();
1416     },
1417     
1418     headerEl : function()
1419     {
1420         if(!this.el || !this.panel.length || !this.header.length){
1421             return;
1422         }
1423         
1424         return this.el.select('.panel-heading',true).first()
1425     },
1426     
1427     bodyEl : function()
1428     {
1429         if(!this.el || !this.panel.length){
1430             return;
1431         }
1432         
1433         return this.el.select('.panel-body',true).first()
1434     },
1435     
1436     titleEl : function()
1437     {
1438         if(!this.el || !this.panel.length || !this.header.length){
1439             return;
1440         }
1441         
1442         return this.el.select('.panel-title',true).first();
1443     },
1444     
1445     setTitle : function(v)
1446     {
1447         var titleEl = this.titleEl();
1448         
1449         if(!titleEl){
1450             return;
1451         }
1452         
1453         titleEl.dom.innerHTML = v;
1454     },
1455     
1456     getTitle : function()
1457     {
1458         
1459         var titleEl = this.titleEl();
1460         
1461         if(!titleEl){
1462             return '';
1463         }
1464         
1465         return titleEl.dom.innerHTML;
1466     },
1467     
1468     setRightTitle : function(v)
1469     {
1470         var t = this.el.select('.panel-header-right',true).first();
1471         
1472         if(!t){
1473             return;
1474         }
1475         
1476         t.dom.innerHTML = v;
1477     },
1478     
1479     onClick : function(e)
1480     {
1481         e.preventDefault();
1482         
1483         this.fireEvent('click', this, e);
1484     }
1485 });
1486
1487  /*
1488  * - LGPL
1489  *
1490  * image
1491  * 
1492  */
1493
1494
1495 /**
1496  * @class Roo.bootstrap.Img
1497  * @extends Roo.bootstrap.Component
1498  * Bootstrap Img class
1499  * @cfg {Boolean} imgResponsive false | true
1500  * @cfg {String} border rounded | circle | thumbnail
1501  * @cfg {String} src image source
1502  * @cfg {String} alt image alternative text
1503  * @cfg {String} href a tag href
1504  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1505  * @cfg {String} xsUrl xs image source
1506  * @cfg {String} smUrl sm image source
1507  * @cfg {String} mdUrl md image source
1508  * @cfg {String} lgUrl lg image source
1509  * 
1510  * @constructor
1511  * Create a new Input
1512  * @param {Object} config The config object
1513  */
1514
1515 Roo.bootstrap.Img = function(config){
1516     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1517     
1518     this.addEvents({
1519         // img events
1520         /**
1521          * @event click
1522          * The img click event for the img.
1523          * @param {Roo.EventObject} e
1524          */
1525         "click" : true
1526     });
1527 };
1528
1529 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1530     
1531     imgResponsive: true,
1532     border: '',
1533     src: 'about:blank',
1534     href: false,
1535     target: false,
1536     xsUrl: '',
1537     smUrl: '',
1538     mdUrl: '',
1539     lgUrl: '',
1540
1541     getAutoCreate : function()
1542     {   
1543         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1544             return this.createSingleImg();
1545         }
1546         
1547         var cfg = {
1548             tag: 'div',
1549             cls: 'roo-image-responsive-group',
1550             cn: []
1551         };
1552         var _this = this;
1553         
1554         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1555             
1556             if(!_this[size + 'Url']){
1557                 return;
1558             }
1559             
1560             var img = {
1561                 tag: 'img',
1562                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1563                 html: _this.html || cfg.html,
1564                 src: _this[size + 'Url']
1565             };
1566             
1567             img.cls += ' roo-image-responsive-' + size;
1568             
1569             var s = ['xs', 'sm', 'md', 'lg'];
1570             
1571             s.splice(s.indexOf(size), 1);
1572             
1573             Roo.each(s, function(ss){
1574                 img.cls += ' hidden-' + ss;
1575             });
1576             
1577             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1578                 cfg.cls += ' img-' + _this.border;
1579             }
1580             
1581             if(_this.alt){
1582                 cfg.alt = _this.alt;
1583             }
1584             
1585             if(_this.href){
1586                 var a = {
1587                     tag: 'a',
1588                     href: _this.href,
1589                     cn: [
1590                         img
1591                     ]
1592                 };
1593
1594                 if(this.target){
1595                     a.target = _this.target;
1596                 }
1597             }
1598             
1599             cfg.cn.push((_this.href) ? a : img);
1600             
1601         });
1602         
1603         return cfg;
1604     },
1605     
1606     createSingleImg : function()
1607     {
1608         var cfg = {
1609             tag: 'img',
1610             cls: (this.imgResponsive) ? 'img-responsive' : '',
1611             html : null,
1612             src : 'about:blank'  // just incase src get's set to undefined?!?
1613         };
1614         
1615         cfg.html = this.html || cfg.html;
1616         
1617         cfg.src = this.src || cfg.src;
1618         
1619         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1620             cfg.cls += ' img-' + this.border;
1621         }
1622         
1623         if(this.alt){
1624             cfg.alt = this.alt;
1625         }
1626         
1627         if(this.href){
1628             var a = {
1629                 tag: 'a',
1630                 href: this.href,
1631                 cn: [
1632                     cfg
1633                 ]
1634             };
1635             
1636             if(this.target){
1637                 a.target = this.target;
1638             }
1639             
1640         }
1641         
1642         return (this.href) ? a : cfg;
1643     },
1644     
1645     initEvents: function() 
1646     {
1647         if(!this.href){
1648             this.el.on('click', this.onClick, this);
1649         }
1650         
1651     },
1652     
1653     onClick : function(e)
1654     {
1655         Roo.log('img onclick');
1656         this.fireEvent('click', this, e);
1657     },
1658     /**
1659      * Sets the url of the image - used to update it
1660      * @param {String} url the url of the image
1661      */
1662     
1663     setSrc : function(url)
1664     {
1665         this.src =  url;
1666         
1667         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1668             this.el.dom.src =  url;
1669             return;
1670         }
1671         
1672         this.el.select('img', true).first().dom.src =  url;
1673     }
1674     
1675     
1676    
1677 });
1678
1679  /*
1680  * - LGPL
1681  *
1682  * image
1683  * 
1684  */
1685
1686
1687 /**
1688  * @class Roo.bootstrap.Link
1689  * @extends Roo.bootstrap.Component
1690  * Bootstrap Link Class
1691  * @cfg {String} alt image alternative text
1692  * @cfg {String} href a tag href
1693  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1694  * @cfg {String} html the content of the link.
1695  * @cfg {String} anchor name for the anchor link
1696  * @cfg {String} fa - favicon
1697
1698  * @cfg {Boolean} preventDefault (true | false) default false
1699
1700  * 
1701  * @constructor
1702  * Create a new Input
1703  * @param {Object} config The config object
1704  */
1705
1706 Roo.bootstrap.Link = function(config){
1707     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1708     
1709     this.addEvents({
1710         // img events
1711         /**
1712          * @event click
1713          * The img click event for the img.
1714          * @param {Roo.EventObject} e
1715          */
1716         "click" : true
1717     });
1718 };
1719
1720 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1721     
1722     href: false,
1723     target: false,
1724     preventDefault: false,
1725     anchor : false,
1726     alt : false,
1727     fa: false,
1728
1729
1730     getAutoCreate : function()
1731     {
1732         var html = this.html || '';
1733         
1734         if (this.fa !== false) {
1735             html = '<i class="fa fa-' + this.fa + '"></i>';
1736         }
1737         var cfg = {
1738             tag: 'a'
1739         };
1740         // anchor's do not require html/href...
1741         if (this.anchor === false) {
1742             cfg.html = html;
1743             cfg.href = this.href || '#';
1744         } else {
1745             cfg.name = this.anchor;
1746             if (this.html !== false || this.fa !== false) {
1747                 cfg.html = html;
1748             }
1749             if (this.href !== false) {
1750                 cfg.href = this.href;
1751             }
1752         }
1753         
1754         if(this.alt !== false){
1755             cfg.alt = this.alt;
1756         }
1757         
1758         
1759         if(this.target !== false) {
1760             cfg.target = this.target;
1761         }
1762         
1763         return cfg;
1764     },
1765     
1766     initEvents: function() {
1767         
1768         if(!this.href || this.preventDefault){
1769             this.el.on('click', this.onClick, this);
1770         }
1771     },
1772     
1773     onClick : function(e)
1774     {
1775         if(this.preventDefault){
1776             e.preventDefault();
1777         }
1778         //Roo.log('img onclick');
1779         this.fireEvent('click', this, e);
1780     }
1781    
1782 });
1783
1784  /*
1785  * - LGPL
1786  *
1787  * header
1788  * 
1789  */
1790
1791 /**
1792  * @class Roo.bootstrap.Header
1793  * @extends Roo.bootstrap.Component
1794  * Bootstrap Header class
1795  * @cfg {String} html content of header
1796  * @cfg {Number} level (1|2|3|4|5|6) default 1
1797  * 
1798  * @constructor
1799  * Create a new Header
1800  * @param {Object} config The config object
1801  */
1802
1803
1804 Roo.bootstrap.Header  = function(config){
1805     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1806 };
1807
1808 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1809     
1810     //href : false,
1811     html : false,
1812     level : 1,
1813     
1814     
1815     
1816     getAutoCreate : function(){
1817         
1818         
1819         
1820         var cfg = {
1821             tag: 'h' + (1 *this.level),
1822             html: this.html || ''
1823         } ;
1824         
1825         return cfg;
1826     }
1827    
1828 });
1829
1830  
1831
1832  /*
1833  * Based on:
1834  * Ext JS Library 1.1.1
1835  * Copyright(c) 2006-2007, Ext JS, LLC.
1836  *
1837  * Originally Released Under LGPL - original licence link has changed is not relivant.
1838  *
1839  * Fork - LGPL
1840  * <script type="text/javascript">
1841  */
1842  
1843 /**
1844  * @class Roo.bootstrap.MenuMgr
1845  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1846  * @singleton
1847  */
1848 Roo.bootstrap.MenuMgr = function(){
1849    var menus, active, groups = {}, attached = false, lastShow = new Date();
1850
1851    // private - called when first menu is created
1852    function init(){
1853        menus = {};
1854        active = new Roo.util.MixedCollection();
1855        Roo.get(document).addKeyListener(27, function(){
1856            if(active.length > 0){
1857                hideAll();
1858            }
1859        });
1860    }
1861
1862    // private
1863    function hideAll(){
1864        if(active && active.length > 0){
1865            var c = active.clone();
1866            c.each(function(m){
1867                m.hide();
1868            });
1869        }
1870    }
1871
1872    // private
1873    function onHide(m){
1874        active.remove(m);
1875        if(active.length < 1){
1876            Roo.get(document).un("mouseup", onMouseDown);
1877             
1878            attached = false;
1879        }
1880    }
1881
1882    // private
1883    function onShow(m){
1884        var last = active.last();
1885        lastShow = new Date();
1886        active.add(m);
1887        if(!attached){
1888           Roo.get(document).on("mouseup", onMouseDown);
1889            
1890            attached = true;
1891        }
1892        if(m.parentMenu){
1893           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1894           m.parentMenu.activeChild = m;
1895        }else if(last && last.isVisible()){
1896           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1897        }
1898    }
1899
1900    // private
1901    function onBeforeHide(m){
1902        if(m.activeChild){
1903            m.activeChild.hide();
1904        }
1905        if(m.autoHideTimer){
1906            clearTimeout(m.autoHideTimer);
1907            delete m.autoHideTimer;
1908        }
1909    }
1910
1911    // private
1912    function onBeforeShow(m){
1913        var pm = m.parentMenu;
1914        if(!pm && !m.allowOtherMenus){
1915            hideAll();
1916        }else if(pm && pm.activeChild && active != m){
1917            pm.activeChild.hide();
1918        }
1919    }
1920
1921    // private this should really trigger on mouseup..
1922    function onMouseDown(e){
1923         Roo.log("on Mouse Up");
1924         
1925         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1926             Roo.log("MenuManager hideAll");
1927             hideAll();
1928             e.stopEvent();
1929         }
1930         
1931         
1932    }
1933
1934    // private
1935    function onBeforeCheck(mi, state){
1936        if(state){
1937            var g = groups[mi.group];
1938            for(var i = 0, l = g.length; i < l; i++){
1939                if(g[i] != mi){
1940                    g[i].setChecked(false);
1941                }
1942            }
1943        }
1944    }
1945
1946    return {
1947
1948        /**
1949         * Hides all menus that are currently visible
1950         */
1951        hideAll : function(){
1952             hideAll();  
1953        },
1954
1955        // private
1956        register : function(menu){
1957            if(!menus){
1958                init();
1959            }
1960            menus[menu.id] = menu;
1961            menu.on("beforehide", onBeforeHide);
1962            menu.on("hide", onHide);
1963            menu.on("beforeshow", onBeforeShow);
1964            menu.on("show", onShow);
1965            var g = menu.group;
1966            if(g && menu.events["checkchange"]){
1967                if(!groups[g]){
1968                    groups[g] = [];
1969                }
1970                groups[g].push(menu);
1971                menu.on("checkchange", onCheck);
1972            }
1973        },
1974
1975         /**
1976          * Returns a {@link Roo.menu.Menu} object
1977          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1978          * be used to generate and return a new Menu instance.
1979          */
1980        get : function(menu){
1981            if(typeof menu == "string"){ // menu id
1982                return menus[menu];
1983            }else if(menu.events){  // menu instance
1984                return menu;
1985            }
1986            /*else if(typeof menu.length == 'number'){ // array of menu items?
1987                return new Roo.bootstrap.Menu({items:menu});
1988            }else{ // otherwise, must be a config
1989                return new Roo.bootstrap.Menu(menu);
1990            }
1991            */
1992            return false;
1993        },
1994
1995        // private
1996        unregister : function(menu){
1997            delete menus[menu.id];
1998            menu.un("beforehide", onBeforeHide);
1999            menu.un("hide", onHide);
2000            menu.un("beforeshow", onBeforeShow);
2001            menu.un("show", onShow);
2002            var g = menu.group;
2003            if(g && menu.events["checkchange"]){
2004                groups[g].remove(menu);
2005                menu.un("checkchange", onCheck);
2006            }
2007        },
2008
2009        // private
2010        registerCheckable : function(menuItem){
2011            var g = menuItem.group;
2012            if(g){
2013                if(!groups[g]){
2014                    groups[g] = [];
2015                }
2016                groups[g].push(menuItem);
2017                menuItem.on("beforecheckchange", onBeforeCheck);
2018            }
2019        },
2020
2021        // private
2022        unregisterCheckable : function(menuItem){
2023            var g = menuItem.group;
2024            if(g){
2025                groups[g].remove(menuItem);
2026                menuItem.un("beforecheckchange", onBeforeCheck);
2027            }
2028        }
2029    };
2030 }();/*
2031  * - LGPL
2032  *
2033  * menu
2034  * 
2035  */
2036
2037 /**
2038  * @class Roo.bootstrap.Menu
2039  * @extends Roo.bootstrap.Component
2040  * Bootstrap Menu class - container for MenuItems
2041  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2042  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2043  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2044  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2045  * 
2046  * @constructor
2047  * Create a new Menu
2048  * @param {Object} config The config object
2049  */
2050
2051
2052 Roo.bootstrap.Menu = function(config){
2053     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2054     if (this.registerMenu && this.type != 'treeview')  {
2055         Roo.bootstrap.MenuMgr.register(this);
2056     }
2057     
2058     
2059     this.addEvents({
2060         /**
2061          * @event beforeshow
2062          * Fires before this menu is displayed
2063          * @param {Roo.menu.Menu} this
2064          */
2065         beforeshow : true,
2066         /**
2067          * @event beforehide
2068          * Fires before this menu is hidden
2069          * @param {Roo.menu.Menu} this
2070          */
2071         beforehide : true,
2072         /**
2073          * @event show
2074          * Fires after this menu is displayed
2075          * @param {Roo.menu.Menu} this
2076          */
2077         show : true,
2078         /**
2079          * @event hide
2080          * Fires after this menu is hidden
2081          * @param {Roo.menu.Menu} this
2082          */
2083         hide : true,
2084         /**
2085          * @event click
2086          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2087          * @param {Roo.menu.Menu} this
2088          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2089          * @param {Roo.EventObject} e
2090          */
2091         click : true,
2092         /**
2093          * @event mouseover
2094          * Fires when the mouse is hovering over this menu
2095          * @param {Roo.menu.Menu} this
2096          * @param {Roo.EventObject} e
2097          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2098          */
2099         mouseover : true,
2100         /**
2101          * @event mouseout
2102          * Fires when the mouse exits this menu
2103          * @param {Roo.menu.Menu} this
2104          * @param {Roo.EventObject} e
2105          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2106          */
2107         mouseout : true,
2108         /**
2109          * @event itemclick
2110          * Fires when a menu item contained in this menu is clicked
2111          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2112          * @param {Roo.EventObject} e
2113          */
2114         itemclick: true
2115     });
2116     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2117 };
2118
2119 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2120     
2121    /// html : false,
2122     //align : '',
2123     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2124     type: false,
2125     /**
2126      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2127      */
2128     registerMenu : true,
2129     
2130     menuItems :false, // stores the menu items..
2131     
2132     hidden:true,
2133         
2134     parentMenu : false,
2135     
2136     stopEvent : true,
2137     
2138     isLink : false,
2139     
2140     getChildContainer : function() {
2141         return this.el;  
2142     },
2143     
2144     getAutoCreate : function(){
2145          
2146         //if (['right'].indexOf(this.align)!==-1) {
2147         //    cfg.cn[1].cls += ' pull-right'
2148         //}
2149         
2150         
2151         var cfg = {
2152             tag : 'ul',
2153             cls : 'dropdown-menu' ,
2154             style : 'z-index:1000'
2155             
2156         };
2157         
2158         if (this.type === 'submenu') {
2159             cfg.cls = 'submenu active';
2160         }
2161         if (this.type === 'treeview') {
2162             cfg.cls = 'treeview-menu';
2163         }
2164         
2165         return cfg;
2166     },
2167     initEvents : function() {
2168         
2169        // Roo.log("ADD event");
2170        // Roo.log(this.triggerEl.dom);
2171         
2172         this.triggerEl.on('click', this.onTriggerClick, this);
2173         
2174         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2175         
2176         
2177         if (this.triggerEl.hasClass('nav-item')) {
2178             // dropdown toggle on the 'a' in BS4?
2179             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2180         } else {
2181             this.triggerEl.addClass('dropdown-toggle');
2182         }
2183         if (Roo.isTouch) {
2184             this.el.on('touchstart'  , this.onTouch, this);
2185         }
2186         this.el.on('click' , this.onClick, this);
2187
2188         this.el.on("mouseover", this.onMouseOver, this);
2189         this.el.on("mouseout", this.onMouseOut, this);
2190         
2191     },
2192     
2193     findTargetItem : function(e)
2194     {
2195         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2196         if(!t){
2197             return false;
2198         }
2199         //Roo.log(t);         Roo.log(t.id);
2200         if(t && t.id){
2201             //Roo.log(this.menuitems);
2202             return this.menuitems.get(t.id);
2203             
2204             //return this.items.get(t.menuItemId);
2205         }
2206         
2207         return false;
2208     },
2209     
2210     onTouch : function(e) 
2211     {
2212         Roo.log("menu.onTouch");
2213         //e.stopEvent(); this make the user popdown broken
2214         this.onClick(e);
2215     },
2216     
2217     onClick : function(e)
2218     {
2219         Roo.log("menu.onClick");
2220         
2221         var t = this.findTargetItem(e);
2222         if(!t || t.isContainer){
2223             return;
2224         }
2225         Roo.log(e);
2226         /*
2227         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2228             if(t == this.activeItem && t.shouldDeactivate(e)){
2229                 this.activeItem.deactivate();
2230                 delete this.activeItem;
2231                 return;
2232             }
2233             if(t.canActivate){
2234                 this.setActiveItem(t, true);
2235             }
2236             return;
2237             
2238             
2239         }
2240         */
2241        
2242         Roo.log('pass click event');
2243         
2244         t.onClick(e);
2245         
2246         this.fireEvent("click", this, t, e);
2247         
2248         var _this = this;
2249         
2250         if(!t.href.length || t.href == '#'){
2251             (function() { _this.hide(); }).defer(100);
2252         }
2253         
2254     },
2255     
2256     onMouseOver : function(e){
2257         var t  = this.findTargetItem(e);
2258         //Roo.log(t);
2259         //if(t){
2260         //    if(t.canActivate && !t.disabled){
2261         //        this.setActiveItem(t, true);
2262         //    }
2263         //}
2264         
2265         this.fireEvent("mouseover", this, e, t);
2266     },
2267     isVisible : function(){
2268         return !this.hidden;
2269     },
2270      onMouseOut : function(e){
2271         var t  = this.findTargetItem(e);
2272         
2273         //if(t ){
2274         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2275         //        this.activeItem.deactivate();
2276         //        delete this.activeItem;
2277         //    }
2278         //}
2279         this.fireEvent("mouseout", this, e, t);
2280     },
2281     
2282     
2283     /**
2284      * Displays this menu relative to another element
2285      * @param {String/HTMLElement/Roo.Element} element The element to align to
2286      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2287      * the element (defaults to this.defaultAlign)
2288      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2289      */
2290     show : function(el, pos, parentMenu){
2291         this.parentMenu = parentMenu;
2292         if(!this.el){
2293             this.render();
2294         }
2295         this.fireEvent("beforeshow", this);
2296         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2297     },
2298      /**
2299      * Displays this menu at a specific xy position
2300      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2301      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2302      */
2303     showAt : function(xy, parentMenu, /* private: */_e){
2304         this.parentMenu = parentMenu;
2305         if(!this.el){
2306             this.render();
2307         }
2308         if(_e !== false){
2309             this.fireEvent("beforeshow", this);
2310             //xy = this.el.adjustForConstraints(xy);
2311         }
2312         
2313         //this.el.show();
2314         this.hideMenuItems();
2315         this.hidden = false;
2316         this.triggerEl.addClass('open');
2317         this.el.addClass('show');
2318         
2319         // reassign x when hitting right
2320         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2321             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2322         }
2323         
2324         // reassign y when hitting bottom
2325         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2326             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2327         }
2328         
2329         // but the list may align on trigger left or trigger top... should it be a properity?
2330         
2331         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2332             this.el.setXY(xy);
2333         }
2334         
2335         this.focus();
2336         this.fireEvent("show", this);
2337     },
2338     
2339     focus : function(){
2340         return;
2341         if(!this.hidden){
2342             this.doFocus.defer(50, this);
2343         }
2344     },
2345
2346     doFocus : function(){
2347         if(!this.hidden){
2348             this.focusEl.focus();
2349         }
2350     },
2351
2352     /**
2353      * Hides this menu and optionally all parent menus
2354      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2355      */
2356     hide : function(deep)
2357     {
2358         
2359         this.hideMenuItems();
2360         if(this.el && this.isVisible()){
2361             this.fireEvent("beforehide", this);
2362             if(this.activeItem){
2363                 this.activeItem.deactivate();
2364                 this.activeItem = null;
2365             }
2366             this.triggerEl.removeClass('open');;
2367             this.el.removeClass('show');
2368             this.hidden = true;
2369             this.fireEvent("hide", this);
2370         }
2371         if(deep === true && this.parentMenu){
2372             this.parentMenu.hide(true);
2373         }
2374     },
2375     
2376     onTriggerClick : function(e)
2377     {
2378         Roo.log('trigger click');
2379         
2380         var target = e.getTarget();
2381         
2382         Roo.log(target.nodeName.toLowerCase());
2383         
2384         if(target.nodeName.toLowerCase() === 'i'){
2385             e.preventDefault();
2386         }
2387         
2388     },
2389     
2390     onTriggerPress  : function(e)
2391     {
2392         Roo.log('trigger press');
2393         //Roo.log(e.getTarget());
2394        // Roo.log(this.triggerEl.dom);
2395        
2396         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2397         var pel = Roo.get(e.getTarget());
2398         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2399             Roo.log('is treeview or dropdown?');
2400             return;
2401         }
2402         
2403         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2404             return;
2405         }
2406         
2407         if (this.isVisible()) {
2408             Roo.log('hide');
2409             this.hide();
2410         } else {
2411             Roo.log('show');
2412             this.show(this.triggerEl, false, false);
2413         }
2414         
2415         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2416             e.stopEvent();
2417         }
2418         
2419     },
2420        
2421     
2422     hideMenuItems : function()
2423     {
2424         Roo.log("hide Menu Items");
2425         if (!this.el) { 
2426             return;
2427         }
2428         //$(backdrop).remove()
2429         this.el.select('.open',true).each(function(aa) {
2430             
2431             aa.removeClass('open');
2432           //var parent = getParent($(this))
2433           //var relatedTarget = { relatedTarget: this }
2434           
2435            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2436           //if (e.isDefaultPrevented()) return
2437            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2438         });
2439     },
2440     addxtypeChild : function (tree, cntr) {
2441         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2442           
2443         this.menuitems.add(comp);
2444         return comp;
2445
2446     },
2447     getEl : function()
2448     {
2449         Roo.log(this.el);
2450         return this.el;
2451     },
2452     
2453     clear : function()
2454     {
2455         this.getEl().dom.innerHTML = '';
2456         this.menuitems.clear();
2457     }
2458 });
2459
2460  
2461  /*
2462  * - LGPL
2463  *
2464  * menu item
2465  * 
2466  */
2467
2468
2469 /**
2470  * @class Roo.bootstrap.MenuItem
2471  * @extends Roo.bootstrap.Component
2472  * Bootstrap MenuItem class
2473  * @cfg {String} html the menu label
2474  * @cfg {String} href the link
2475  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2476  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2477  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2478  * @cfg {String} fa favicon to show on left of menu item.
2479  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2480  * 
2481  * 
2482  * @constructor
2483  * Create a new MenuItem
2484  * @param {Object} config The config object
2485  */
2486
2487
2488 Roo.bootstrap.MenuItem = function(config){
2489     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2490     this.addEvents({
2491         // raw events
2492         /**
2493          * @event click
2494          * The raw click event for the entire grid.
2495          * @param {Roo.bootstrap.MenuItem} this
2496          * @param {Roo.EventObject} e
2497          */
2498         "click" : true
2499     });
2500 };
2501
2502 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2503     
2504     href : false,
2505     html : false,
2506     preventDefault: false,
2507     isContainer : false,
2508     active : false,
2509     fa: false,
2510     
2511     getAutoCreate : function(){
2512         
2513         if(this.isContainer){
2514             return {
2515                 tag: 'li',
2516                 cls: 'dropdown-menu-item '
2517             };
2518         }
2519         var ctag = {
2520             tag: 'span',
2521             html: 'Link'
2522         };
2523         
2524         var anc = {
2525             tag : 'a',
2526             cls : 'dropdown-item',
2527             href : '#',
2528             cn : [  ]
2529         };
2530         
2531         if (this.fa !== false) {
2532             anc.cn.push({
2533                 tag : 'i',
2534                 cls : 'fa fa-' + this.fa
2535             });
2536         }
2537         
2538         anc.cn.push(ctag);
2539         
2540         
2541         var cfg= {
2542             tag: 'li',
2543             cls: 'dropdown-menu-item',
2544             cn: [ anc ]
2545         };
2546         if (this.parent().type == 'treeview') {
2547             cfg.cls = 'treeview-menu';
2548         }
2549         if (this.active) {
2550             cfg.cls += ' active';
2551         }
2552         
2553         
2554         
2555         anc.href = this.href || cfg.cn[0].href ;
2556         ctag.html = this.html || cfg.cn[0].html ;
2557         return cfg;
2558     },
2559     
2560     initEvents: function()
2561     {
2562         if (this.parent().type == 'treeview') {
2563             this.el.select('a').on('click', this.onClick, this);
2564         }
2565         
2566         if (this.menu) {
2567             this.menu.parentType = this.xtype;
2568             this.menu.triggerEl = this.el;
2569             this.menu = this.addxtype(Roo.apply({}, this.menu));
2570         }
2571         
2572     },
2573     onClick : function(e)
2574     {
2575         Roo.log('item on click ');
2576         
2577         if(this.preventDefault){
2578             e.preventDefault();
2579         }
2580         //this.parent().hideMenuItems();
2581         
2582         this.fireEvent('click', this, e);
2583     },
2584     getEl : function()
2585     {
2586         return this.el;
2587     } 
2588 });
2589
2590  
2591
2592  /*
2593  * - LGPL
2594  *
2595  * menu separator
2596  * 
2597  */
2598
2599
2600 /**
2601  * @class Roo.bootstrap.MenuSeparator
2602  * @extends Roo.bootstrap.Component
2603  * Bootstrap MenuSeparator class
2604  * 
2605  * @constructor
2606  * Create a new MenuItem
2607  * @param {Object} config The config object
2608  */
2609
2610
2611 Roo.bootstrap.MenuSeparator = function(config){
2612     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2613 };
2614
2615 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2616     
2617     getAutoCreate : function(){
2618         var cfg = {
2619             cls: 'divider',
2620             tag : 'li'
2621         };
2622         
2623         return cfg;
2624     }
2625    
2626 });
2627
2628  
2629
2630  
2631 /*
2632 * Licence: LGPL
2633 */
2634
2635 /**
2636  * @class Roo.bootstrap.Modal
2637  * @extends Roo.bootstrap.Component
2638  * Bootstrap Modal class
2639  * @cfg {String} title Title of dialog
2640  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2641  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2642  * @cfg {Boolean} specificTitle default false
2643  * @cfg {Array} buttons Array of buttons or standard button set..
2644  * @cfg {String} buttonPosition (left|right|center) default right
2645  * @cfg {Boolean} animate default true
2646  * @cfg {Boolean} allow_close default true
2647  * @cfg {Boolean} fitwindow default false
2648  * @cfg {String} size (sm|lg) default empty
2649  * @cfg {Number} max_width set the max width of modal
2650  *
2651  *
2652  * @constructor
2653  * Create a new Modal Dialog
2654  * @param {Object} config The config object
2655  */
2656
2657 Roo.bootstrap.Modal = function(config){
2658     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2659     this.addEvents({
2660         // raw events
2661         /**
2662          * @event btnclick
2663          * The raw btnclick event for the button
2664          * @param {Roo.EventObject} e
2665          */
2666         "btnclick" : true,
2667         /**
2668          * @event resize
2669          * Fire when dialog resize
2670          * @param {Roo.bootstrap.Modal} this
2671          * @param {Roo.EventObject} e
2672          */
2673         "resize" : true
2674     });
2675     this.buttons = this.buttons || [];
2676
2677     if (this.tmpl) {
2678         this.tmpl = Roo.factory(this.tmpl);
2679     }
2680
2681 };
2682
2683 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2684
2685     title : 'test dialog',
2686
2687     buttons : false,
2688
2689     // set on load...
2690
2691     html: false,
2692
2693     tmp: false,
2694
2695     specificTitle: false,
2696
2697     buttonPosition: 'right',
2698
2699     allow_close : true,
2700
2701     animate : true,
2702
2703     fitwindow: false,
2704     
2705      // private
2706     dialogEl: false,
2707     bodyEl:  false,
2708     footerEl:  false,
2709     titleEl:  false,
2710     closeEl:  false,
2711
2712     size: '',
2713     
2714     max_width: 0,
2715     
2716     max_height: 0,
2717     
2718     fit_content: false,
2719
2720     onRender : function(ct, position)
2721     {
2722         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2723
2724         if(!this.el){
2725             var cfg = Roo.apply({},  this.getAutoCreate());
2726             cfg.id = Roo.id();
2727             //if(!cfg.name){
2728             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2729             //}
2730             //if (!cfg.name.length) {
2731             //    delete cfg.name;
2732            // }
2733             if (this.cls) {
2734                 cfg.cls += ' ' + this.cls;
2735             }
2736             if (this.style) {
2737                 cfg.style = this.style;
2738             }
2739             this.el = Roo.get(document.body).createChild(cfg, position);
2740         }
2741         //var type = this.el.dom.type;
2742
2743
2744         if(this.tabIndex !== undefined){
2745             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2746         }
2747
2748         this.dialogEl = this.el.select('.modal-dialog',true).first();
2749         this.bodyEl = this.el.select('.modal-body',true).first();
2750         this.closeEl = this.el.select('.modal-header .close', true).first();
2751         this.headerEl = this.el.select('.modal-header',true).first();
2752         this.titleEl = this.el.select('.modal-title',true).first();
2753         this.footerEl = this.el.select('.modal-footer',true).first();
2754
2755         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2756         
2757         //this.el.addClass("x-dlg-modal");
2758
2759         if (this.buttons.length) {
2760             Roo.each(this.buttons, function(bb) {
2761                 var b = Roo.apply({}, bb);
2762                 b.xns = b.xns || Roo.bootstrap;
2763                 b.xtype = b.xtype || 'Button';
2764                 if (typeof(b.listeners) == 'undefined') {
2765                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2766                 }
2767
2768                 var btn = Roo.factory(b);
2769
2770                 btn.render(this.el.select('.modal-footer div').first());
2771
2772             },this);
2773         }
2774         // render the children.
2775         var nitems = [];
2776
2777         if(typeof(this.items) != 'undefined'){
2778             var items = this.items;
2779             delete this.items;
2780
2781             for(var i =0;i < items.length;i++) {
2782                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2783             }
2784         }
2785
2786         this.items = nitems;
2787
2788         // where are these used - they used to be body/close/footer
2789
2790
2791         this.initEvents();
2792         //this.el.addClass([this.fieldClass, this.cls]);
2793
2794     },
2795
2796     getAutoCreate : function()
2797     {
2798         var bdy = {
2799                 cls : 'modal-body',
2800                 html : this.html || ''
2801         };
2802
2803         var title = {
2804             tag: 'h4',
2805             cls : 'modal-title',
2806             html : this.title
2807         };
2808
2809         if(this.specificTitle){
2810             title = this.title;
2811
2812         };
2813
2814         var header = [];
2815         if (this.allow_close && Roo.bootstrap.version == 3) {
2816             header.push({
2817                 tag: 'button',
2818                 cls : 'close',
2819                 html : '&times'
2820             });
2821         }
2822
2823         header.push(title);
2824
2825         if (this.allow_close && Roo.bootstrap.version == 4) {
2826             header.push({
2827                 tag: 'button',
2828                 cls : 'close',
2829                 html : '&times'
2830             });
2831         }
2832         
2833         var size = '';
2834
2835         if(this.size.length){
2836             size = 'modal-' + this.size;
2837         }
2838
2839         var modal = {
2840             cls: "modal",
2841              cn : [
2842                 {
2843                     cls: "modal-dialog " + size,
2844                     cn : [
2845                         {
2846                             cls : "modal-content",
2847                             cn : [
2848                                 {
2849                                     cls : 'modal-header',
2850                                     cn : header
2851                                 },
2852                                 bdy,
2853                                 {
2854                                     cls : 'modal-footer',
2855                                     cn : [
2856                                         {
2857                                             tag: 'div',
2858                                             cls: 'btn-' + this.buttonPosition
2859                                         }
2860                                     ]
2861
2862                                 }
2863
2864
2865                             ]
2866
2867                         }
2868                     ]
2869
2870                 }
2871             ]
2872         };
2873
2874         if(this.animate){
2875             modal.cls += ' fade';
2876         }
2877
2878         return modal;
2879
2880     },
2881     getChildContainer : function() {
2882
2883          return this.bodyEl;
2884
2885     },
2886     getButtonContainer : function() {
2887          return this.el.select('.modal-footer div',true).first();
2888
2889     },
2890     initEvents : function()
2891     {
2892         if (this.allow_close) {
2893             this.closeEl.on('click', this.hide, this);
2894         }
2895         Roo.EventManager.onWindowResize(this.resize, this, true);
2896
2897
2898     },
2899
2900     resize : function()
2901     {
2902         this.maskEl.setSize(
2903             Roo.lib.Dom.getViewWidth(true),
2904             Roo.lib.Dom.getViewHeight(true)
2905         );
2906         
2907         if (this.fitwindow) {
2908             this.setSize(
2909                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2910                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2911             );
2912             return;
2913         }
2914         
2915         if(this.max_width !== 0) {
2916             
2917             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2918             
2919             if(this.height) {
2920                 this.setSize(w, this.height);
2921                 return;
2922             }
2923             
2924             if(this.max_height) {
2925                 this.setSize(w,Math.min(
2926                     this.max_height,
2927                     Roo.lib.Dom.getViewportHeight(true) - 60
2928                 ));
2929                 
2930                 return;
2931             }
2932             
2933             if(!this.fit_content) {
2934                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2935                 return;
2936             }
2937             
2938             this.setSize(w, Math.min(
2939                 60 +
2940                 this.headerEl.getHeight() + 
2941                 this.footerEl.getHeight() + 
2942                 this.getChildHeight(this.bodyEl.dom.childNodes),
2943                 Roo.lib.Dom.getViewportHeight(true) - 60)
2944             );
2945         }
2946         
2947     },
2948
2949     setSize : function(w,h)
2950     {
2951         if (!w && !h) {
2952             return;
2953         }
2954         
2955         this.resizeTo(w,h);
2956     },
2957
2958     show : function() {
2959
2960         if (!this.rendered) {
2961             this.render();
2962         }
2963
2964         //this.el.setStyle('display', 'block');
2965         this.el.removeClass('hideing');
2966         this.el.dom.style.display='block';
2967         
2968         Roo.get(document.body).addClass('modal-open');
2969  
2970         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2971             var _this = this;
2972             (function(){
2973                 this.el.addClass('show');
2974                 this.el.addClass('in');
2975             }).defer(50, this);
2976         }else{
2977             this.el.addClass('show');
2978             this.el.addClass('in');
2979         }
2980
2981         // not sure how we can show data in here..
2982         //if (this.tmpl) {
2983         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2984         //}
2985
2986         Roo.get(document.body).addClass("x-body-masked");
2987         
2988         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2989         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2990         this.maskEl.dom.style.display = 'block';
2991         this.maskEl.addClass('show');
2992         
2993         
2994         this.resize();
2995         
2996         this.fireEvent('show', this);
2997
2998         // set zindex here - otherwise it appears to be ignored...
2999         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3000
3001         (function () {
3002             this.items.forEach( function(e) {
3003                 e.layout ? e.layout() : false;
3004
3005             });
3006         }).defer(100,this);
3007
3008     },
3009     hide : function()
3010     {
3011         if(this.fireEvent("beforehide", this) !== false){
3012             
3013             this.maskEl.removeClass('show');
3014             
3015             this.maskEl.dom.style.display = '';
3016             Roo.get(document.body).removeClass("x-body-masked");
3017             this.el.removeClass('in');
3018             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3019
3020             if(this.animate){ // why
3021                 this.el.addClass('hideing');
3022                 this.el.removeClass('show');
3023                 (function(){
3024                     if (!this.el.hasClass('hideing')) {
3025                         return; // it's been shown again...
3026                     }
3027                     
3028                     this.el.dom.style.display='';
3029
3030                     Roo.get(document.body).removeClass('modal-open');
3031                     this.el.removeClass('hideing');
3032                 }).defer(150,this);
3033                 
3034             }else{
3035                 this.el.removeClass('show');
3036                 this.el.dom.style.display='';
3037                 Roo.get(document.body).removeClass('modal-open');
3038
3039             }
3040             this.fireEvent('hide', this);
3041         }
3042     },
3043     isVisible : function()
3044     {
3045         
3046         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3047         
3048     },
3049
3050     addButton : function(str, cb)
3051     {
3052
3053
3054         var b = Roo.apply({}, { html : str } );
3055         b.xns = b.xns || Roo.bootstrap;
3056         b.xtype = b.xtype || 'Button';
3057         if (typeof(b.listeners) == 'undefined') {
3058             b.listeners = { click : cb.createDelegate(this)  };
3059         }
3060
3061         var btn = Roo.factory(b);
3062
3063         btn.render(this.el.select('.modal-footer div').first());
3064
3065         return btn;
3066
3067     },
3068
3069     setDefaultButton : function(btn)
3070     {
3071         //this.el.select('.modal-footer').()
3072     },
3073     diff : false,
3074
3075     resizeTo: function(w,h)
3076     {
3077         // skip.. ?? why??
3078
3079         this.dialogEl.setWidth(w);
3080         if (this.diff === false) {
3081             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3082         }
3083
3084         this.bodyEl.setHeight(h - this.diff);
3085
3086         this.fireEvent('resize', this);
3087
3088     },
3089     setContentSize  : function(w, h)
3090     {
3091
3092     },
3093     onButtonClick: function(btn,e)
3094     {
3095         //Roo.log([a,b,c]);
3096         this.fireEvent('btnclick', btn.name, e);
3097     },
3098      /**
3099      * Set the title of the Dialog
3100      * @param {String} str new Title
3101      */
3102     setTitle: function(str) {
3103         this.titleEl.dom.innerHTML = str;
3104     },
3105     /**
3106      * Set the body of the Dialog
3107      * @param {String} str new Title
3108      */
3109     setBody: function(str) {
3110         this.bodyEl.dom.innerHTML = str;
3111     },
3112     /**
3113      * Set the body of the Dialog using the template
3114      * @param {Obj} data - apply this data to the template and replace the body contents.
3115      */
3116     applyBody: function(obj)
3117     {
3118         if (!this.tmpl) {
3119             Roo.log("Error - using apply Body without a template");
3120             //code
3121         }
3122         this.tmpl.overwrite(this.bodyEl, obj);
3123     },
3124     
3125     getChildHeight : function(child_nodes)
3126     {
3127         if(
3128             !child_nodes ||
3129             child_nodes.length == 0
3130         ) {
3131             return;
3132         }
3133         
3134         var child_height = 0;
3135         
3136         for(var i = 0; i < child_nodes.length; i++) {
3137             
3138             /*
3139             * for modal with tabs...
3140             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3141                 
3142                 var layout_childs = child_nodes[i].childNodes;
3143                 
3144                 for(var j = 0; j < layout_childs.length; j++) {
3145                     
3146                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3147                         
3148                         var layout_body_childs = layout_childs[j].childNodes;
3149                         
3150                         for(var k = 0; k < layout_body_childs.length; k++) {
3151                             
3152                             if(layout_body_childs[k].classList.contains('navbar')) {
3153                                 child_height += layout_body_childs[k].offsetHeight;
3154                                 continue;
3155                             }
3156                             
3157                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3158                                 
3159                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3160                                 
3161                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3162                                     
3163                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3164                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3165                                         continue;
3166                                     }
3167                                     
3168                                 }
3169                                 
3170                             }
3171                             
3172                         }
3173                     }
3174                 }
3175                 continue;
3176             }
3177             */
3178             
3179             child_height += child_nodes[i].offsetHeight;
3180             // Roo.log(child_nodes[i].offsetHeight);
3181         }
3182         
3183         return child_height;
3184     }
3185
3186 });
3187
3188
3189 Roo.apply(Roo.bootstrap.Modal,  {
3190     /**
3191          * Button config that displays a single OK button
3192          * @type Object
3193          */
3194         OK :  [{
3195             name : 'ok',
3196             weight : 'primary',
3197             html : 'OK'
3198         }],
3199         /**
3200          * Button config that displays Yes and No buttons
3201          * @type Object
3202          */
3203         YESNO : [
3204             {
3205                 name  : 'no',
3206                 html : 'No'
3207             },
3208             {
3209                 name  :'yes',
3210                 weight : 'primary',
3211                 html : 'Yes'
3212             }
3213         ],
3214
3215         /**
3216          * Button config that displays OK and Cancel buttons
3217          * @type Object
3218          */
3219         OKCANCEL : [
3220             {
3221                name : 'cancel',
3222                 html : 'Cancel'
3223             },
3224             {
3225                 name : 'ok',
3226                 weight : 'primary',
3227                 html : 'OK'
3228             }
3229         ],
3230         /**
3231          * Button config that displays Yes, No and Cancel buttons
3232          * @type Object
3233          */
3234         YESNOCANCEL : [
3235             {
3236                 name : 'yes',
3237                 weight : 'primary',
3238                 html : 'Yes'
3239             },
3240             {
3241                 name : 'no',
3242                 html : 'No'
3243             },
3244             {
3245                 name : 'cancel',
3246                 html : 'Cancel'
3247             }
3248         ],
3249         
3250         zIndex : 10001
3251 });
3252 /*
3253  * - LGPL
3254  *
3255  * messagebox - can be used as a replace
3256  * 
3257  */
3258 /**
3259  * @class Roo.MessageBox
3260  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3261  * Example usage:
3262  *<pre><code>
3263 // Basic alert:
3264 Roo.Msg.alert('Status', 'Changes saved successfully.');
3265
3266 // Prompt for user data:
3267 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3268     if (btn == 'ok'){
3269         // process text value...
3270     }
3271 });
3272
3273 // Show a dialog using config options:
3274 Roo.Msg.show({
3275    title:'Save Changes?',
3276    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3277    buttons: Roo.Msg.YESNOCANCEL,
3278    fn: processResult,
3279    animEl: 'elId'
3280 });
3281 </code></pre>
3282  * @singleton
3283  */
3284 Roo.bootstrap.MessageBox = function(){
3285     var dlg, opt, mask, waitTimer;
3286     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3287     var buttons, activeTextEl, bwidth;
3288
3289     
3290     // private
3291     var handleButton = function(button){
3292         dlg.hide();
3293         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3294     };
3295
3296     // private
3297     var handleHide = function(){
3298         if(opt && opt.cls){
3299             dlg.el.removeClass(opt.cls);
3300         }
3301         //if(waitTimer){
3302         //    Roo.TaskMgr.stop(waitTimer);
3303         //    waitTimer = null;
3304         //}
3305     };
3306
3307     // private
3308     var updateButtons = function(b){
3309         var width = 0;
3310         if(!b){
3311             buttons["ok"].hide();
3312             buttons["cancel"].hide();
3313             buttons["yes"].hide();
3314             buttons["no"].hide();
3315             //dlg.footer.dom.style.display = 'none';
3316             return width;
3317         }
3318         dlg.footerEl.dom.style.display = '';
3319         for(var k in buttons){
3320             if(typeof buttons[k] != "function"){
3321                 if(b[k]){
3322                     buttons[k].show();
3323                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3324                     width += buttons[k].el.getWidth()+15;
3325                 }else{
3326                     buttons[k].hide();
3327                 }
3328             }
3329         }
3330         return width;
3331     };
3332
3333     // private
3334     var handleEsc = function(d, k, e){
3335         if(opt && opt.closable !== false){
3336             dlg.hide();
3337         }
3338         if(e){
3339             e.stopEvent();
3340         }
3341     };
3342
3343     return {
3344         /**
3345          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3346          * @return {Roo.BasicDialog} The BasicDialog element
3347          */
3348         getDialog : function(){
3349            if(!dlg){
3350                 dlg = new Roo.bootstrap.Modal( {
3351                     //draggable: true,
3352                     //resizable:false,
3353                     //constraintoviewport:false,
3354                     //fixedcenter:true,
3355                     //collapsible : false,
3356                     //shim:true,
3357                     //modal: true,
3358                 //    width: 'auto',
3359                   //  height:100,
3360                     //buttonAlign:"center",
3361                     closeClick : function(){
3362                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3363                             handleButton("no");
3364                         }else{
3365                             handleButton("cancel");
3366                         }
3367                     }
3368                 });
3369                 dlg.render();
3370                 dlg.on("hide", handleHide);
3371                 mask = dlg.mask;
3372                 //dlg.addKeyListener(27, handleEsc);
3373                 buttons = {};
3374                 this.buttons = buttons;
3375                 var bt = this.buttonText;
3376                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3377                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3378                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3379                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3380                 //Roo.log(buttons);
3381                 bodyEl = dlg.bodyEl.createChild({
3382
3383                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3384                         '<textarea class="roo-mb-textarea"></textarea>' +
3385                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3386                 });
3387                 msgEl = bodyEl.dom.firstChild;
3388                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3389                 textboxEl.enableDisplayMode();
3390                 textboxEl.addKeyListener([10,13], function(){
3391                     if(dlg.isVisible() && opt && opt.buttons){
3392                         if(opt.buttons.ok){
3393                             handleButton("ok");
3394                         }else if(opt.buttons.yes){
3395                             handleButton("yes");
3396                         }
3397                     }
3398                 });
3399                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3400                 textareaEl.enableDisplayMode();
3401                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3402                 progressEl.enableDisplayMode();
3403                 
3404                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3405                 var pf = progressEl.dom.firstChild;
3406                 if (pf) {
3407                     pp = Roo.get(pf.firstChild);
3408                     pp.setHeight(pf.offsetHeight);
3409                 }
3410                 
3411             }
3412             return dlg;
3413         },
3414
3415         /**
3416          * Updates the message box body text
3417          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3418          * the XHTML-compliant non-breaking space character '&amp;#160;')
3419          * @return {Roo.MessageBox} This message box
3420          */
3421         updateText : function(text)
3422         {
3423             if(!dlg.isVisible() && !opt.width){
3424                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3425                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3426             }
3427             msgEl.innerHTML = text || '&#160;';
3428       
3429             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3430             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3431             var w = Math.max(
3432                     Math.min(opt.width || cw , this.maxWidth), 
3433                     Math.max(opt.minWidth || this.minWidth, bwidth)
3434             );
3435             if(opt.prompt){
3436                 activeTextEl.setWidth(w);
3437             }
3438             if(dlg.isVisible()){
3439                 dlg.fixedcenter = false;
3440             }
3441             // to big, make it scroll. = But as usual stupid IE does not support
3442             // !important..
3443             
3444             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3445                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3446                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3447             } else {
3448                 bodyEl.dom.style.height = '';
3449                 bodyEl.dom.style.overflowY = '';
3450             }
3451             if (cw > w) {
3452                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3453             } else {
3454                 bodyEl.dom.style.overflowX = '';
3455             }
3456             
3457             dlg.setContentSize(w, bodyEl.getHeight());
3458             if(dlg.isVisible()){
3459                 dlg.fixedcenter = true;
3460             }
3461             return this;
3462         },
3463
3464         /**
3465          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3466          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3467          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3468          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3469          * @return {Roo.MessageBox} This message box
3470          */
3471         updateProgress : function(value, text){
3472             if(text){
3473                 this.updateText(text);
3474             }
3475             
3476             if (pp) { // weird bug on my firefox - for some reason this is not defined
3477                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3478                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3479             }
3480             return this;
3481         },        
3482
3483         /**
3484          * Returns true if the message box is currently displayed
3485          * @return {Boolean} True if the message box is visible, else false
3486          */
3487         isVisible : function(){
3488             return dlg && dlg.isVisible();  
3489         },
3490
3491         /**
3492          * Hides the message box if it is displayed
3493          */
3494         hide : function(){
3495             if(this.isVisible()){
3496                 dlg.hide();
3497             }  
3498         },
3499
3500         /**
3501          * Displays a new message box, or reinitializes an existing message box, based on the config options
3502          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3503          * The following config object properties are supported:
3504          * <pre>
3505 Property    Type             Description
3506 ----------  ---------------  ------------------------------------------------------------------------------------
3507 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3508                                    closes (defaults to undefined)
3509 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3510                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3511 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3512                                    progress and wait dialogs will ignore this property and always hide the
3513                                    close button as they can only be closed programmatically.
3514 cls               String           A custom CSS class to apply to the message box element
3515 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3516                                    displayed (defaults to 75)
3517 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3518                                    function will be btn (the name of the button that was clicked, if applicable,
3519                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3520                                    Progress and wait dialogs will ignore this option since they do not respond to
3521                                    user actions and can only be closed programmatically, so any required function
3522                                    should be called by the same code after it closes the dialog.
3523 icon              String           A CSS class that provides a background image to be used as an icon for
3524                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3525 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3526 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3527 modal             Boolean          False to allow user interaction with the page while the message box is
3528                                    displayed (defaults to true)
3529 msg               String           A string that will replace the existing message box body text (defaults
3530                                    to the XHTML-compliant non-breaking space character '&#160;')
3531 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3532 progress          Boolean          True to display a progress bar (defaults to false)
3533 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3534 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3535 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3536 title             String           The title text
3537 value             String           The string value to set into the active textbox element if displayed
3538 wait              Boolean          True to display a progress bar (defaults to false)
3539 width             Number           The width of the dialog in pixels
3540 </pre>
3541          *
3542          * Example usage:
3543          * <pre><code>
3544 Roo.Msg.show({
3545    title: 'Address',
3546    msg: 'Please enter your address:',
3547    width: 300,
3548    buttons: Roo.MessageBox.OKCANCEL,
3549    multiline: true,
3550    fn: saveAddress,
3551    animEl: 'addAddressBtn'
3552 });
3553 </code></pre>
3554          * @param {Object} config Configuration options
3555          * @return {Roo.MessageBox} This message box
3556          */
3557         show : function(options)
3558         {
3559             
3560             // this causes nightmares if you show one dialog after another
3561             // especially on callbacks..
3562              
3563             if(this.isVisible()){
3564                 
3565                 this.hide();
3566                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3567                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3568                 Roo.log("New Dialog Message:" +  options.msg )
3569                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3570                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3571                 
3572             }
3573             var d = this.getDialog();
3574             opt = options;
3575             d.setTitle(opt.title || "&#160;");
3576             d.closeEl.setDisplayed(opt.closable !== false);
3577             activeTextEl = textboxEl;
3578             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3579             if(opt.prompt){
3580                 if(opt.multiline){
3581                     textboxEl.hide();
3582                     textareaEl.show();
3583                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3584                         opt.multiline : this.defaultTextHeight);
3585                     activeTextEl = textareaEl;
3586                 }else{
3587                     textboxEl.show();
3588                     textareaEl.hide();
3589                 }
3590             }else{
3591                 textboxEl.hide();
3592                 textareaEl.hide();
3593             }
3594             progressEl.setDisplayed(opt.progress === true);
3595             this.updateProgress(0);
3596             activeTextEl.dom.value = opt.value || "";
3597             if(opt.prompt){
3598                 dlg.setDefaultButton(activeTextEl);
3599             }else{
3600                 var bs = opt.buttons;
3601                 var db = null;
3602                 if(bs && bs.ok){
3603                     db = buttons["ok"];
3604                 }else if(bs && bs.yes){
3605                     db = buttons["yes"];
3606                 }
3607                 dlg.setDefaultButton(db);
3608             }
3609             bwidth = updateButtons(opt.buttons);
3610             this.updateText(opt.msg);
3611             if(opt.cls){
3612                 d.el.addClass(opt.cls);
3613             }
3614             d.proxyDrag = opt.proxyDrag === true;
3615             d.modal = opt.modal !== false;
3616             d.mask = opt.modal !== false ? mask : false;
3617             if(!d.isVisible()){
3618                 // force it to the end of the z-index stack so it gets a cursor in FF
3619                 document.body.appendChild(dlg.el.dom);
3620                 d.animateTarget = null;
3621                 d.show(options.animEl);
3622             }
3623             return this;
3624         },
3625
3626         /**
3627          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3628          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3629          * and closing the message box when the process is complete.
3630          * @param {String} title The title bar text
3631          * @param {String} msg The message box body text
3632          * @return {Roo.MessageBox} This message box
3633          */
3634         progress : function(title, msg){
3635             this.show({
3636                 title : title,
3637                 msg : msg,
3638                 buttons: false,
3639                 progress:true,
3640                 closable:false,
3641                 minWidth: this.minProgressWidth,
3642                 modal : true
3643             });
3644             return this;
3645         },
3646
3647         /**
3648          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3649          * If a callback function is passed it will be called after the user clicks the button, and the
3650          * id of the button that was clicked will be passed as the only parameter to the callback
3651          * (could also be the top-right close button).
3652          * @param {String} title The title bar text
3653          * @param {String} msg The message box body text
3654          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3655          * @param {Object} scope (optional) The scope of the callback function
3656          * @return {Roo.MessageBox} This message box
3657          */
3658         alert : function(title, msg, fn, scope)
3659         {
3660             this.show({
3661                 title : title,
3662                 msg : msg,
3663                 buttons: this.OK,
3664                 fn: fn,
3665                 closable : false,
3666                 scope : scope,
3667                 modal : true
3668             });
3669             return this;
3670         },
3671
3672         /**
3673          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3674          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3675          * You are responsible for closing the message box when the process is complete.
3676          * @param {String} msg The message box body text
3677          * @param {String} title (optional) The title bar text
3678          * @return {Roo.MessageBox} This message box
3679          */
3680         wait : function(msg, title){
3681             this.show({
3682                 title : title,
3683                 msg : msg,
3684                 buttons: false,
3685                 closable:false,
3686                 progress:true,
3687                 modal:true,
3688                 width:300,
3689                 wait:true
3690             });
3691             waitTimer = Roo.TaskMgr.start({
3692                 run: function(i){
3693                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3694                 },
3695                 interval: 1000
3696             });
3697             return this;
3698         },
3699
3700         /**
3701          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3702          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3703          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3704          * @param {String} title The title bar text
3705          * @param {String} msg The message box body text
3706          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3707          * @param {Object} scope (optional) The scope of the callback function
3708          * @return {Roo.MessageBox} This message box
3709          */
3710         confirm : function(title, msg, fn, scope){
3711             this.show({
3712                 title : title,
3713                 msg : msg,
3714                 buttons: this.YESNO,
3715                 fn: fn,
3716                 scope : scope,
3717                 modal : true
3718             });
3719             return this;
3720         },
3721
3722         /**
3723          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3724          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3725          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3726          * (could also be the top-right close button) and the text that was entered will be passed as the two
3727          * parameters to the callback.
3728          * @param {String} title The title bar text
3729          * @param {String} msg The message box body text
3730          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3731          * @param {Object} scope (optional) The scope of the callback function
3732          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3733          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3734          * @return {Roo.MessageBox} This message box
3735          */
3736         prompt : function(title, msg, fn, scope, multiline){
3737             this.show({
3738                 title : title,
3739                 msg : msg,
3740                 buttons: this.OKCANCEL,
3741                 fn: fn,
3742                 minWidth:250,
3743                 scope : scope,
3744                 prompt:true,
3745                 multiline: multiline,
3746                 modal : true
3747             });
3748             return this;
3749         },
3750
3751         /**
3752          * Button config that displays a single OK button
3753          * @type Object
3754          */
3755         OK : {ok:true},
3756         /**
3757          * Button config that displays Yes and No buttons
3758          * @type Object
3759          */
3760         YESNO : {yes:true, no:true},
3761         /**
3762          * Button config that displays OK and Cancel buttons
3763          * @type Object
3764          */
3765         OKCANCEL : {ok:true, cancel:true},
3766         /**
3767          * Button config that displays Yes, No and Cancel buttons
3768          * @type Object
3769          */
3770         YESNOCANCEL : {yes:true, no:true, cancel:true},
3771
3772         /**
3773          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3774          * @type Number
3775          */
3776         defaultTextHeight : 75,
3777         /**
3778          * The maximum width in pixels of the message box (defaults to 600)
3779          * @type Number
3780          */
3781         maxWidth : 600,
3782         /**
3783          * The minimum width in pixels of the message box (defaults to 100)
3784          * @type Number
3785          */
3786         minWidth : 100,
3787         /**
3788          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3789          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3790          * @type Number
3791          */
3792         minProgressWidth : 250,
3793         /**
3794          * An object containing the default button text strings that can be overriden for localized language support.
3795          * Supported properties are: ok, cancel, yes and no.
3796          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3797          * @type Object
3798          */
3799         buttonText : {
3800             ok : "OK",
3801             cancel : "Cancel",
3802             yes : "Yes",
3803             no : "No"
3804         }
3805     };
3806 }();
3807
3808 /**
3809  * Shorthand for {@link Roo.MessageBox}
3810  */
3811 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3812 Roo.Msg = Roo.Msg || Roo.MessageBox;
3813 /*
3814  * - LGPL
3815  *
3816  * navbar
3817  * 
3818  */
3819
3820 /**
3821  * @class Roo.bootstrap.Navbar
3822  * @extends Roo.bootstrap.Component
3823  * Bootstrap Navbar class
3824
3825  * @constructor
3826  * Create a new Navbar
3827  * @param {Object} config The config object
3828  */
3829
3830
3831 Roo.bootstrap.Navbar = function(config){
3832     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3833     this.addEvents({
3834         // raw events
3835         /**
3836          * @event beforetoggle
3837          * Fire before toggle the menu
3838          * @param {Roo.EventObject} e
3839          */
3840         "beforetoggle" : true
3841     });
3842 };
3843
3844 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3845     
3846     
3847    
3848     // private
3849     navItems : false,
3850     loadMask : false,
3851     
3852     
3853     getAutoCreate : function(){
3854         
3855         
3856         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3857         
3858     },
3859     
3860     initEvents :function ()
3861     {
3862         //Roo.log(this.el.select('.navbar-toggle',true));
3863         this.el.select('.navbar-toggle',true).on('click', function() {
3864             if(this.fireEvent('beforetoggle', this) !== false){
3865                 var ce = this.el.select('.navbar-collapse',true).first();
3866                 ce.toggleClass('in'); // old...
3867                 if (ce.hasClass('collapse')) {
3868                     // show it...
3869                     ce.removeClass('collapse');
3870                     ce.addClass('show');
3871                     var h = ce.getHeight();
3872                     Roo.log(h);
3873                     ce.removeClass('show');
3874                     // at this point we should be able to see it..
3875                     ce.addClass('collapsing');
3876                     
3877                     ce.setHeight(0); // resize it ...
3878                     ce.on('transitionend', function() {
3879                         Roo.log('done transition');
3880                         ce.removeClass('collapsing');
3881                         ce.addClass('show');
3882                         ce.removeClass('collapse');
3883
3884                         ce.dom.style.height = '';
3885                     }, this, { single: true} );
3886                     ce.setHeight(h);
3887                     
3888                 } else {
3889                     ce.setHeight(ce.getHeight());
3890                     ce.removeClass('show');
3891                     ce.addClass('collapsing');
3892                     
3893                     ce.on('transitionend', function() {
3894                         ce.dom.style.height = '';
3895                         ce.removeClass('collapsing');
3896                         ce.addClass('collapse');
3897                     }, this, { single: true} );
3898                     ce.setHeight(0);
3899                 }
3900             }
3901             
3902         }, this);
3903         
3904         var mark = {
3905             tag: "div",
3906             cls:"x-dlg-mask"
3907         };
3908         
3909         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3910         
3911         var size = this.el.getSize();
3912         this.maskEl.setSize(size.width, size.height);
3913         this.maskEl.enableDisplayMode("block");
3914         this.maskEl.hide();
3915         
3916         if(this.loadMask){
3917             this.maskEl.show();
3918         }
3919     },
3920     
3921     
3922     getChildContainer : function()
3923     {
3924         if (this.el.select('.collapse').getCount()) {
3925             return this.el.select('.collapse',true).first();
3926         }
3927         
3928         return this.el;
3929     },
3930     
3931     mask : function()
3932     {
3933         this.maskEl.show();
3934     },
3935     
3936     unmask : function()
3937     {
3938         this.maskEl.hide();
3939     } 
3940     
3941     
3942     
3943     
3944 });
3945
3946
3947
3948  
3949
3950  /*
3951  * - LGPL
3952  *
3953  * navbar
3954  * 
3955  */
3956
3957 /**
3958  * @class Roo.bootstrap.NavSimplebar
3959  * @extends Roo.bootstrap.Navbar
3960  * Bootstrap Sidebar class
3961  *
3962  * @cfg {Boolean} inverse is inverted color
3963  * 
3964  * @cfg {String} type (nav | pills | tabs)
3965  * @cfg {Boolean} arrangement stacked | justified
3966  * @cfg {String} align (left | right) alignment
3967  * 
3968  * @cfg {Boolean} main (true|false) main nav bar? default false
3969  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3970  * 
3971  * @cfg {String} tag (header|footer|nav|div) default is nav 
3972
3973  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3974  * 
3975  * 
3976  * @constructor
3977  * Create a new Sidebar
3978  * @param {Object} config The config object
3979  */
3980
3981
3982 Roo.bootstrap.NavSimplebar = function(config){
3983     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3984 };
3985
3986 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3987     
3988     inverse: false,
3989     
3990     type: false,
3991     arrangement: '',
3992     align : false,
3993     
3994     weight : 'light',
3995     
3996     main : false,
3997     
3998     
3999     tag : false,
4000     
4001     
4002     getAutoCreate : function(){
4003         
4004         
4005         var cfg = {
4006             tag : this.tag || 'div',
4007             cls : 'navbar navbar-expand-lg'
4008         };
4009         if (['light','white'].indexOf(this.weight) > -1) {
4010             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4011         }
4012         cfg.cls += ' bg-' + this.weight;
4013         
4014           
4015         
4016         cfg.cn = [
4017             {
4018                 cls: 'nav',
4019                 tag : 'ul'
4020             }
4021         ];
4022         
4023          
4024         this.type = this.type || 'nav';
4025         if (['tabs','pills'].indexOf(this.type)!==-1) {
4026             cfg.cn[0].cls += ' nav-' + this.type
4027         
4028         
4029         } else {
4030             if (this.type!=='nav') {
4031                 Roo.log('nav type must be nav/tabs/pills')
4032             }
4033             cfg.cn[0].cls += ' navbar-nav'
4034         }
4035         
4036         
4037         
4038         
4039         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4040             cfg.cn[0].cls += ' nav-' + this.arrangement;
4041         }
4042         
4043         
4044         if (this.align === 'right') {
4045             cfg.cn[0].cls += ' navbar-right';
4046         }
4047         
4048         if (this.inverse) {
4049             cfg.cls += ' navbar-inverse';
4050             
4051         }
4052         
4053         
4054         return cfg;
4055     
4056         
4057     }
4058     
4059     
4060     
4061 });
4062
4063
4064
4065  
4066
4067  
4068        /*
4069  * - LGPL
4070  *
4071  * navbar
4072  * navbar-fixed-top
4073  * navbar-expand-md  fixed-top 
4074  */
4075
4076 /**
4077  * @class Roo.bootstrap.NavHeaderbar
4078  * @extends Roo.bootstrap.NavSimplebar
4079  * Bootstrap Sidebar class
4080  *
4081  * @cfg {String} brand what is brand
4082  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4083  * @cfg {String} brand_href href of the brand
4084  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4085  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4086  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4087  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4088  * 
4089  * @constructor
4090  * Create a new Sidebar
4091  * @param {Object} config The config object
4092  */
4093
4094
4095 Roo.bootstrap.NavHeaderbar = function(config){
4096     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4097       
4098 };
4099
4100 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4101     
4102     position: '',
4103     brand: '',
4104     brand_href: false,
4105     srButton : true,
4106     autohide : false,
4107     desktopCenter : false,
4108    
4109     
4110     getAutoCreate : function(){
4111         
4112         var   cfg = {
4113             tag: this.nav || 'nav',
4114             cls: 'navbar navbar-expand-md',
4115             role: 'navigation',
4116             cn: []
4117         };
4118         
4119         var cn = cfg.cn;
4120         if (this.desktopCenter) {
4121             cn.push({cls : 'container', cn : []});
4122             cn = cn[0].cn;
4123         }
4124         
4125         if(this.srButton){
4126             var btn = {
4127                 tag: 'button',
4128                 type: 'button',
4129                 cls: 'navbar-toggle navbar-toggler',
4130                 'data-toggle': 'collapse',
4131                 cn: [
4132                     {
4133                         tag: 'span',
4134                         cls: 'sr-only',
4135                         html: 'Toggle navigation'
4136                     },
4137                     {
4138                         tag: 'span',
4139                         cls: 'icon-bar navbar-toggler-icon'
4140                     },
4141                     {
4142                         tag: 'span',
4143                         cls: 'icon-bar'
4144                     },
4145                     {
4146                         tag: 'span',
4147                         cls: 'icon-bar'
4148                     }
4149                 ]
4150             };
4151             
4152             cn.push( Roo.bootstrap.version == 4 ? btn : {
4153                 tag: 'div',
4154                 cls: 'navbar-header',
4155                 cn: [
4156                     btn
4157                 ]
4158             });
4159         }
4160         
4161         cn.push({
4162             tag: 'div',
4163             cls: 'collapse navbar-collapse',
4164             cn : []
4165         });
4166         
4167         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4168         
4169         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4170             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4171             
4172             // tag can override this..
4173             
4174             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4175         }
4176         
4177         if (this.brand !== '') {
4178             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4179             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4180                 tag: 'a',
4181                 href: this.brand_href ? this.brand_href : '#',
4182                 cls: 'navbar-brand',
4183                 cn: [
4184                 this.brand
4185                 ]
4186             });
4187         }
4188         
4189         if(this.main){
4190             cfg.cls += ' main-nav';
4191         }
4192         
4193         
4194         return cfg;
4195
4196         
4197     },
4198     getHeaderChildContainer : function()
4199     {
4200         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4201             return this.el.select('.navbar-header',true).first();
4202         }
4203         
4204         return this.getChildContainer();
4205     },
4206     
4207     
4208     initEvents : function()
4209     {
4210         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4211         
4212         if (this.autohide) {
4213             
4214             var prevScroll = 0;
4215             var ft = this.el;
4216             
4217             Roo.get(document).on('scroll',function(e) {
4218                 var ns = Roo.get(document).getScroll().top;
4219                 var os = prevScroll;
4220                 prevScroll = ns;
4221                 
4222                 if(ns > os){
4223                     ft.removeClass('slideDown');
4224                     ft.addClass('slideUp');
4225                     return;
4226                 }
4227                 ft.removeClass('slideUp');
4228                 ft.addClass('slideDown');
4229                  
4230               
4231           },this);
4232         }
4233     }    
4234     
4235 });
4236
4237
4238
4239  
4240
4241  /*
4242  * - LGPL
4243  *
4244  * navbar
4245  * 
4246  */
4247
4248 /**
4249  * @class Roo.bootstrap.NavSidebar
4250  * @extends Roo.bootstrap.Navbar
4251  * Bootstrap Sidebar class
4252  * 
4253  * @constructor
4254  * Create a new Sidebar
4255  * @param {Object} config The config object
4256  */
4257
4258
4259 Roo.bootstrap.NavSidebar = function(config){
4260     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4261 };
4262
4263 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4264     
4265     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4266     
4267     getAutoCreate : function(){
4268         
4269         
4270         return  {
4271             tag: 'div',
4272             cls: 'sidebar sidebar-nav'
4273         };
4274     
4275         
4276     }
4277     
4278     
4279     
4280 });
4281
4282
4283
4284  
4285
4286  /*
4287  * - LGPL
4288  *
4289  * nav group
4290  * 
4291  */
4292
4293 /**
4294  * @class Roo.bootstrap.NavGroup
4295  * @extends Roo.bootstrap.Component
4296  * Bootstrap NavGroup class
4297  * @cfg {String} align (left|right)
4298  * @cfg {Boolean} inverse
4299  * @cfg {String} type (nav|pills|tab) default nav
4300  * @cfg {String} navId - reference Id for navbar.
4301
4302  * 
4303  * @constructor
4304  * Create a new nav group
4305  * @param {Object} config The config object
4306  */
4307
4308 Roo.bootstrap.NavGroup = function(config){
4309     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4310     this.navItems = [];
4311    
4312     Roo.bootstrap.NavGroup.register(this);
4313      this.addEvents({
4314         /**
4315              * @event changed
4316              * Fires when the active item changes
4317              * @param {Roo.bootstrap.NavGroup} this
4318              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4319              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4320          */
4321         'changed': true
4322      });
4323     
4324 };
4325
4326 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4327     
4328     align: '',
4329     inverse: false,
4330     form: false,
4331     type: 'nav',
4332     navId : '',
4333     // private
4334     
4335     navItems : false, 
4336     
4337     getAutoCreate : function()
4338     {
4339         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4340         
4341         cfg = {
4342             tag : 'ul',
4343             cls: 'nav' 
4344         };
4345         
4346         if (['tabs','pills'].indexOf(this.type)!==-1) {
4347             cfg.cls += ' nav-' + this.type
4348         } else {
4349             if (this.type!=='nav') {
4350                 Roo.log('nav type must be nav/tabs/pills')
4351             }
4352             cfg.cls += ' navbar-nav'
4353         }
4354         
4355         if (this.parent() && this.parent().sidebar) {
4356             cfg = {
4357                 tag: 'ul',
4358                 cls: 'dashboard-menu sidebar-menu'
4359             };
4360             
4361             return cfg;
4362         }
4363         
4364         if (this.form === true) {
4365             cfg = {
4366                 tag: 'form',
4367                 cls: 'navbar-form'
4368             };
4369             
4370             if (this.align === 'right') {
4371                 cfg.cls += ' navbar-right ml-md-auto';
4372             } else {
4373                 cfg.cls += ' navbar-left';
4374             }
4375         }
4376         
4377         if (this.align === 'right') {
4378             cfg.cls += ' navbar-right ml-md-auto';
4379         } else {
4380             cfg.cls += ' mr-auto';
4381         }
4382         
4383         if (this.inverse) {
4384             cfg.cls += ' navbar-inverse';
4385             
4386         }
4387         
4388         
4389         return cfg;
4390     },
4391     /**
4392     * sets the active Navigation item
4393     * @param {Roo.bootstrap.NavItem} the new current navitem
4394     */
4395     setActiveItem : function(item)
4396     {
4397         var prev = false;
4398         Roo.each(this.navItems, function(v){
4399             if (v == item) {
4400                 return ;
4401             }
4402             if (v.isActive()) {
4403                 v.setActive(false, true);
4404                 prev = v;
4405                 
4406             }
4407             
4408         });
4409
4410         item.setActive(true, true);
4411         this.fireEvent('changed', this, item, prev);
4412         
4413         
4414     },
4415     /**
4416     * gets the active Navigation item
4417     * @return {Roo.bootstrap.NavItem} the current navitem
4418     */
4419     getActive : function()
4420     {
4421         
4422         var prev = false;
4423         Roo.each(this.navItems, function(v){
4424             
4425             if (v.isActive()) {
4426                 prev = v;
4427                 
4428             }
4429             
4430         });
4431         return prev;
4432     },
4433     
4434     indexOfNav : function()
4435     {
4436         
4437         var prev = false;
4438         Roo.each(this.navItems, function(v,i){
4439             
4440             if (v.isActive()) {
4441                 prev = i;
4442                 
4443             }
4444             
4445         });
4446         return prev;
4447     },
4448     /**
4449     * adds a Navigation item
4450     * @param {Roo.bootstrap.NavItem} the navitem to add
4451     */
4452     addItem : function(cfg)
4453     {
4454         var cn = new Roo.bootstrap.NavItem(cfg);
4455         this.register(cn);
4456         cn.parentId = this.id;
4457         cn.onRender(this.el, null);
4458         return cn;
4459     },
4460     /**
4461     * register a Navigation item
4462     * @param {Roo.bootstrap.NavItem} the navitem to add
4463     */
4464     register : function(item)
4465     {
4466         this.navItems.push( item);
4467         item.navId = this.navId;
4468     
4469     },
4470     
4471     /**
4472     * clear all the Navigation item
4473     */
4474    
4475     clearAll : function()
4476     {
4477         this.navItems = [];
4478         this.el.dom.innerHTML = '';
4479     },
4480     
4481     getNavItem: function(tabId)
4482     {
4483         var ret = false;
4484         Roo.each(this.navItems, function(e) {
4485             if (e.tabId == tabId) {
4486                ret =  e;
4487                return false;
4488             }
4489             return true;
4490             
4491         });
4492         return ret;
4493     },
4494     
4495     setActiveNext : function()
4496     {
4497         var i = this.indexOfNav(this.getActive());
4498         if (i > this.navItems.length) {
4499             return;
4500         }
4501         this.setActiveItem(this.navItems[i+1]);
4502     },
4503     setActivePrev : function()
4504     {
4505         var i = this.indexOfNav(this.getActive());
4506         if (i  < 1) {
4507             return;
4508         }
4509         this.setActiveItem(this.navItems[i-1]);
4510     },
4511     clearWasActive : function(except) {
4512         Roo.each(this.navItems, function(e) {
4513             if (e.tabId != except.tabId && e.was_active) {
4514                e.was_active = false;
4515                return false;
4516             }
4517             return true;
4518             
4519         });
4520     },
4521     getWasActive : function ()
4522     {
4523         var r = false;
4524         Roo.each(this.navItems, function(e) {
4525             if (e.was_active) {
4526                r = e;
4527                return false;
4528             }
4529             return true;
4530             
4531         });
4532         return r;
4533     }
4534     
4535     
4536 });
4537
4538  
4539 Roo.apply(Roo.bootstrap.NavGroup, {
4540     
4541     groups: {},
4542      /**
4543     * register a Navigation Group
4544     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4545     */
4546     register : function(navgrp)
4547     {
4548         this.groups[navgrp.navId] = navgrp;
4549         
4550     },
4551     /**
4552     * fetch a Navigation Group based on the navigation ID
4553     * @param {string} the navgroup to add
4554     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4555     */
4556     get: function(navId) {
4557         if (typeof(this.groups[navId]) == 'undefined') {
4558             return false;
4559             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4560         }
4561         return this.groups[navId] ;
4562     }
4563     
4564     
4565     
4566 });
4567
4568  /*
4569  * - LGPL
4570  *
4571  * row
4572  * 
4573  */
4574
4575 /**
4576  * @class Roo.bootstrap.NavItem
4577  * @extends Roo.bootstrap.Component
4578  * Bootstrap Navbar.NavItem class
4579  * @cfg {String} href  link to
4580  * @cfg {String} html content of button
4581  * @cfg {String} badge text inside badge
4582  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4583  * @cfg {String} glyphicon DEPRICATED - use fa
4584  * @cfg {String} icon DEPRICATED - use fa
4585  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4586  * @cfg {Boolean} active Is item active
4587  * @cfg {Boolean} disabled Is item disabled
4588  
4589  * @cfg {Boolean} preventDefault (true | false) default false
4590  * @cfg {String} tabId the tab that this item activates.
4591  * @cfg {String} tagtype (a|span) render as a href or span?
4592  * @cfg {Boolean} animateRef (true|false) link to element default false  
4593   
4594  * @constructor
4595  * Create a new Navbar Item
4596  * @param {Object} config The config object
4597  */
4598 Roo.bootstrap.NavItem = function(config){
4599     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4600     this.addEvents({
4601         // raw events
4602         /**
4603          * @event click
4604          * The raw click event for the entire grid.
4605          * @param {Roo.EventObject} e
4606          */
4607         "click" : true,
4608          /**
4609             * @event changed
4610             * Fires when the active item active state changes
4611             * @param {Roo.bootstrap.NavItem} this
4612             * @param {boolean} state the new state
4613              
4614          */
4615         'changed': true,
4616         /**
4617             * @event scrollto
4618             * Fires when scroll to element
4619             * @param {Roo.bootstrap.NavItem} this
4620             * @param {Object} options
4621             * @param {Roo.EventObject} e
4622              
4623          */
4624         'scrollto': true
4625     });
4626    
4627 };
4628
4629 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4630     
4631     href: false,
4632     html: '',
4633     badge: '',
4634     icon: false,
4635     fa : false,
4636     glyphicon: false,
4637     active: false,
4638     preventDefault : false,
4639     tabId : false,
4640     tagtype : 'a',
4641     disabled : false,
4642     animateRef : false,
4643     was_active : false,
4644     
4645     getAutoCreate : function(){
4646          
4647         var cfg = {
4648             tag: 'li',
4649             cls: 'nav-item'
4650             
4651         };
4652         
4653         if (this.active) {
4654             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4655         }
4656         if (this.disabled) {
4657             cfg.cls += ' disabled';
4658         }
4659         
4660         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4661             cfg.cn = [
4662                 {
4663                     tag: this.tagtype,
4664                     href : this.href || "#",
4665                     html: this.html || ''
4666                 }
4667             ];
4668             if (this.tagtype == 'a') {
4669                 cfg.cn[0].cls = 'nav-link';
4670             }
4671             if (this.icon) {
4672                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4673             }
4674             if (this.fa) {
4675                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4676             }
4677             if(this.glyphicon) {
4678                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4679             }
4680             
4681             if (this.menu) {
4682                 
4683                 cfg.cn[0].html += " <span class='caret'></span>";
4684              
4685             }
4686             
4687             if (this.badge !== '') {
4688                  
4689                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4690             }
4691         }
4692         
4693         
4694         
4695         return cfg;
4696     },
4697     initEvents: function() 
4698     {
4699         if (typeof (this.menu) != 'undefined') {
4700             this.menu.parentType = this.xtype;
4701             this.menu.triggerEl = this.el;
4702             this.menu = this.addxtype(Roo.apply({}, this.menu));
4703         }
4704         
4705         this.el.select('a',true).on('click', this.onClick, this);
4706         
4707         if(this.tagtype == 'span'){
4708             this.el.select('span',true).on('click', this.onClick, this);
4709         }
4710        
4711         // at this point parent should be available..
4712         this.parent().register(this);
4713     },
4714     
4715     onClick : function(e)
4716     {
4717         if (e.getTarget('.dropdown-menu-item')) {
4718             // did you click on a menu itemm.... - then don't trigger onclick..
4719             return;
4720         }
4721         
4722         if(
4723                 this.preventDefault || 
4724                 this.href == '#' 
4725         ){
4726             Roo.log("NavItem - prevent Default?");
4727             e.preventDefault();
4728         }
4729         
4730         if (this.disabled) {
4731             return;
4732         }
4733         
4734         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4735         if (tg && tg.transition) {
4736             Roo.log("waiting for the transitionend");
4737             return;
4738         }
4739         
4740         
4741         
4742         //Roo.log("fire event clicked");
4743         if(this.fireEvent('click', this, e) === false){
4744             return;
4745         };
4746         
4747         if(this.tagtype == 'span'){
4748             return;
4749         }
4750         
4751         //Roo.log(this.href);
4752         var ael = this.el.select('a',true).first();
4753         //Roo.log(ael);
4754         
4755         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4756             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4757             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4758                 return; // ignore... - it's a 'hash' to another page.
4759             }
4760             Roo.log("NavItem - prevent Default?");
4761             e.preventDefault();
4762             this.scrollToElement(e);
4763         }
4764         
4765         
4766         var p =  this.parent();
4767    
4768         if (['tabs','pills'].indexOf(p.type)!==-1) {
4769             if (typeof(p.setActiveItem) !== 'undefined') {
4770                 p.setActiveItem(this);
4771             }
4772         }
4773         
4774         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4775         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4776             // remove the collapsed menu expand...
4777             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4778         }
4779     },
4780     
4781     isActive: function () {
4782         return this.active
4783     },
4784     setActive : function(state, fire, is_was_active)
4785     {
4786         if (this.active && !state && this.navId) {
4787             this.was_active = true;
4788             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4789             if (nv) {
4790                 nv.clearWasActive(this);
4791             }
4792             
4793         }
4794         this.active = state;
4795         
4796         if (!state ) {
4797             this.el.removeClass('active');
4798         } else if (!this.el.hasClass('active')) {
4799             this.el.addClass('active');
4800         }
4801         if (fire) {
4802             this.fireEvent('changed', this, state);
4803         }
4804         
4805         // show a panel if it's registered and related..
4806         
4807         if (!this.navId || !this.tabId || !state || is_was_active) {
4808             return;
4809         }
4810         
4811         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4812         if (!tg) {
4813             return;
4814         }
4815         var pan = tg.getPanelByName(this.tabId);
4816         if (!pan) {
4817             return;
4818         }
4819         // if we can not flip to new panel - go back to old nav highlight..
4820         if (false == tg.showPanel(pan)) {
4821             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4822             if (nv) {
4823                 var onav = nv.getWasActive();
4824                 if (onav) {
4825                     onav.setActive(true, false, true);
4826                 }
4827             }
4828             
4829         }
4830         
4831         
4832         
4833     },
4834      // this should not be here...
4835     setDisabled : function(state)
4836     {
4837         this.disabled = state;
4838         if (!state ) {
4839             this.el.removeClass('disabled');
4840         } else if (!this.el.hasClass('disabled')) {
4841             this.el.addClass('disabled');
4842         }
4843         
4844     },
4845     
4846     /**
4847      * Fetch the element to display the tooltip on.
4848      * @return {Roo.Element} defaults to this.el
4849      */
4850     tooltipEl : function()
4851     {
4852         return this.el.select('' + this.tagtype + '', true).first();
4853     },
4854     
4855     scrollToElement : function(e)
4856     {
4857         var c = document.body;
4858         
4859         /*
4860          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4861          */
4862         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4863             c = document.documentElement;
4864         }
4865         
4866         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4867         
4868         if(!target){
4869             return;
4870         }
4871
4872         var o = target.calcOffsetsTo(c);
4873         
4874         var options = {
4875             target : target,
4876             value : o[1]
4877         };
4878         
4879         this.fireEvent('scrollto', this, options, e);
4880         
4881         Roo.get(c).scrollTo('top', options.value, true);
4882         
4883         return;
4884     }
4885 });
4886  
4887
4888  /*
4889  * - LGPL
4890  *
4891  * sidebar item
4892  *
4893  *  li
4894  *    <span> icon </span>
4895  *    <span> text </span>
4896  *    <span>badge </span>
4897  */
4898
4899 /**
4900  * @class Roo.bootstrap.NavSidebarItem
4901  * @extends Roo.bootstrap.NavItem
4902  * Bootstrap Navbar.NavSidebarItem class
4903  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4904  * {Boolean} open is the menu open
4905  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4906  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4907  * {String} buttonSize (sm|md|lg)the extra classes for the button
4908  * {Boolean} showArrow show arrow next to the text (default true)
4909  * @constructor
4910  * Create a new Navbar Button
4911  * @param {Object} config The config object
4912  */
4913 Roo.bootstrap.NavSidebarItem = function(config){
4914     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4915     this.addEvents({
4916         // raw events
4917         /**
4918          * @event click
4919          * The raw click event for the entire grid.
4920          * @param {Roo.EventObject} e
4921          */
4922         "click" : true,
4923          /**
4924             * @event changed
4925             * Fires when the active item active state changes
4926             * @param {Roo.bootstrap.NavSidebarItem} this
4927             * @param {boolean} state the new state
4928              
4929          */
4930         'changed': true
4931     });
4932    
4933 };
4934
4935 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4936     
4937     badgeWeight : 'default',
4938     
4939     open: false,
4940     
4941     buttonView : false,
4942     
4943     buttonWeight : 'default',
4944     
4945     buttonSize : 'md',
4946     
4947     showArrow : true,
4948     
4949     getAutoCreate : function(){
4950         
4951         
4952         var a = {
4953                 tag: 'a',
4954                 href : this.href || '#',
4955                 cls: '',
4956                 html : '',
4957                 cn : []
4958         };
4959         
4960         if(this.buttonView){
4961             a = {
4962                 tag: 'button',
4963                 href : this.href || '#',
4964                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4965                 html : this.html,
4966                 cn : []
4967             };
4968         }
4969         
4970         var cfg = {
4971             tag: 'li',
4972             cls: '',
4973             cn: [ a ]
4974         };
4975         
4976         if (this.active) {
4977             cfg.cls += ' active';
4978         }
4979         
4980         if (this.disabled) {
4981             cfg.cls += ' disabled';
4982         }
4983         if (this.open) {
4984             cfg.cls += ' open x-open';
4985         }
4986         // left icon..
4987         if (this.glyphicon || this.icon) {
4988             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4989             a.cn.push({ tag : 'i', cls : c }) ;
4990         }
4991         
4992         if(!this.buttonView){
4993             var span = {
4994                 tag: 'span',
4995                 html : this.html || ''
4996             };
4997
4998             a.cn.push(span);
4999             
5000         }
5001         
5002         if (this.badge !== '') {
5003             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5004         }
5005         
5006         if (this.menu) {
5007             
5008             if(this.showArrow){
5009                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5010             }
5011             
5012             a.cls += ' dropdown-toggle treeview' ;
5013         }
5014         
5015         return cfg;
5016     },
5017     
5018     initEvents : function()
5019     { 
5020         if (typeof (this.menu) != 'undefined') {
5021             this.menu.parentType = this.xtype;
5022             this.menu.triggerEl = this.el;
5023             this.menu = this.addxtype(Roo.apply({}, this.menu));
5024         }
5025         
5026         this.el.on('click', this.onClick, this);
5027         
5028         if(this.badge !== ''){
5029             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5030         }
5031         
5032     },
5033     
5034     onClick : function(e)
5035     {
5036         if(this.disabled){
5037             e.preventDefault();
5038             return;
5039         }
5040         
5041         if(this.preventDefault){
5042             e.preventDefault();
5043         }
5044         
5045         this.fireEvent('click', this);
5046     },
5047     
5048     disable : function()
5049     {
5050         this.setDisabled(true);
5051     },
5052     
5053     enable : function()
5054     {
5055         this.setDisabled(false);
5056     },
5057     
5058     setDisabled : function(state)
5059     {
5060         if(this.disabled == state){
5061             return;
5062         }
5063         
5064         this.disabled = state;
5065         
5066         if (state) {
5067             this.el.addClass('disabled');
5068             return;
5069         }
5070         
5071         this.el.removeClass('disabled');
5072         
5073         return;
5074     },
5075     
5076     setActive : function(state)
5077     {
5078         if(this.active == state){
5079             return;
5080         }
5081         
5082         this.active = state;
5083         
5084         if (state) {
5085             this.el.addClass('active');
5086             return;
5087         }
5088         
5089         this.el.removeClass('active');
5090         
5091         return;
5092     },
5093     
5094     isActive: function () 
5095     {
5096         return this.active;
5097     },
5098     
5099     setBadge : function(str)
5100     {
5101         if(!this.badgeEl){
5102             return;
5103         }
5104         
5105         this.badgeEl.dom.innerHTML = str;
5106     }
5107     
5108    
5109      
5110  
5111 });
5112  
5113
5114  /*
5115  * - LGPL
5116  *
5117  * row
5118  * 
5119  */
5120
5121 /**
5122  * @class Roo.bootstrap.Row
5123  * @extends Roo.bootstrap.Component
5124  * Bootstrap Row class (contains columns...)
5125  * 
5126  * @constructor
5127  * Create a new Row
5128  * @param {Object} config The config object
5129  */
5130
5131 Roo.bootstrap.Row = function(config){
5132     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5133 };
5134
5135 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5136     
5137     getAutoCreate : function(){
5138        return {
5139             cls: 'row clearfix'
5140        };
5141     }
5142     
5143     
5144 });
5145
5146  
5147
5148  /*
5149  * - LGPL
5150  *
5151  * element
5152  * 
5153  */
5154
5155 /**
5156  * @class Roo.bootstrap.Element
5157  * @extends Roo.bootstrap.Component
5158  * Bootstrap Element class
5159  * @cfg {String} html contents of the element
5160  * @cfg {String} tag tag of the element
5161  * @cfg {String} cls class of the element
5162  * @cfg {Boolean} preventDefault (true|false) default false
5163  * @cfg {Boolean} clickable (true|false) default false
5164  * 
5165  * @constructor
5166  * Create a new Element
5167  * @param {Object} config The config object
5168  */
5169
5170 Roo.bootstrap.Element = function(config){
5171     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5172     
5173     this.addEvents({
5174         // raw events
5175         /**
5176          * @event click
5177          * When a element is chick
5178          * @param {Roo.bootstrap.Element} this
5179          * @param {Roo.EventObject} e
5180          */
5181         "click" : true
5182     });
5183 };
5184
5185 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5186     
5187     tag: 'div',
5188     cls: '',
5189     html: '',
5190     preventDefault: false, 
5191     clickable: false,
5192     
5193     getAutoCreate : function(){
5194         
5195         var cfg = {
5196             tag: this.tag,
5197             // cls: this.cls, double assign in parent class Component.js :: onRender
5198             html: this.html
5199         };
5200         
5201         return cfg;
5202     },
5203     
5204     initEvents: function() 
5205     {
5206         Roo.bootstrap.Element.superclass.initEvents.call(this);
5207         
5208         if(this.clickable){
5209             this.el.on('click', this.onClick, this);
5210         }
5211         
5212     },
5213     
5214     onClick : function(e)
5215     {
5216         if(this.preventDefault){
5217             e.preventDefault();
5218         }
5219         
5220         this.fireEvent('click', this, e);
5221     },
5222     
5223     getValue : function()
5224     {
5225         return this.el.dom.innerHTML;
5226     },
5227     
5228     setValue : function(value)
5229     {
5230         this.el.dom.innerHTML = value;
5231     }
5232    
5233 });
5234
5235  
5236
5237  /*
5238  * - LGPL
5239  *
5240  * pagination
5241  * 
5242  */
5243
5244 /**
5245  * @class Roo.bootstrap.Pagination
5246  * @extends Roo.bootstrap.Component
5247  * Bootstrap Pagination class
5248  * @cfg {String} size xs | sm | md | lg
5249  * @cfg {Boolean} inverse false | true
5250  * 
5251  * @constructor
5252  * Create a new Pagination
5253  * @param {Object} config The config object
5254  */
5255
5256 Roo.bootstrap.Pagination = function(config){
5257     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5258 };
5259
5260 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5261     
5262     cls: false,
5263     size: false,
5264     inverse: false,
5265     
5266     getAutoCreate : function(){
5267         var cfg = {
5268             tag: 'ul',
5269                 cls: 'pagination'
5270         };
5271         if (this.inverse) {
5272             cfg.cls += ' inverse';
5273         }
5274         if (this.html) {
5275             cfg.html=this.html;
5276         }
5277         if (this.cls) {
5278             cfg.cls += " " + this.cls;
5279         }
5280         return cfg;
5281     }
5282    
5283 });
5284
5285  
5286
5287  /*
5288  * - LGPL
5289  *
5290  * Pagination item
5291  * 
5292  */
5293
5294
5295 /**
5296  * @class Roo.bootstrap.PaginationItem
5297  * @extends Roo.bootstrap.Component
5298  * Bootstrap PaginationItem class
5299  * @cfg {String} html text
5300  * @cfg {String} href the link
5301  * @cfg {Boolean} preventDefault (true | false) default true
5302  * @cfg {Boolean} active (true | false) default false
5303  * @cfg {Boolean} disabled default false
5304  * 
5305  * 
5306  * @constructor
5307  * Create a new PaginationItem
5308  * @param {Object} config The config object
5309  */
5310
5311
5312 Roo.bootstrap.PaginationItem = function(config){
5313     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5314     this.addEvents({
5315         // raw events
5316         /**
5317          * @event click
5318          * The raw click event for the entire grid.
5319          * @param {Roo.EventObject} e
5320          */
5321         "click" : true
5322     });
5323 };
5324
5325 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5326     
5327     href : false,
5328     html : false,
5329     preventDefault: true,
5330     active : false,
5331     cls : false,
5332     disabled: false,
5333     
5334     getAutoCreate : function(){
5335         var cfg= {
5336             tag: 'li',
5337             cn: [
5338                 {
5339                     tag : 'a',
5340                     href : this.href ? this.href : '#',
5341                     html : this.html ? this.html : ''
5342                 }
5343             ]
5344         };
5345         
5346         if(this.cls){
5347             cfg.cls = this.cls;
5348         }
5349         
5350         if(this.disabled){
5351             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5352         }
5353         
5354         if(this.active){
5355             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5356         }
5357         
5358         return cfg;
5359     },
5360     
5361     initEvents: function() {
5362         
5363         this.el.on('click', this.onClick, this);
5364         
5365     },
5366     onClick : function(e)
5367     {
5368         Roo.log('PaginationItem on click ');
5369         if(this.preventDefault){
5370             e.preventDefault();
5371         }
5372         
5373         if(this.disabled){
5374             return;
5375         }
5376         
5377         this.fireEvent('click', this, e);
5378     }
5379    
5380 });
5381
5382  
5383
5384  /*
5385  * - LGPL
5386  *
5387  * slider
5388  * 
5389  */
5390
5391
5392 /**
5393  * @class Roo.bootstrap.Slider
5394  * @extends Roo.bootstrap.Component
5395  * Bootstrap Slider class
5396  *    
5397  * @constructor
5398  * Create a new Slider
5399  * @param {Object} config The config object
5400  */
5401
5402 Roo.bootstrap.Slider = function(config){
5403     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5404 };
5405
5406 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5407     
5408     getAutoCreate : function(){
5409         
5410         var cfg = {
5411             tag: 'div',
5412             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5413             cn: [
5414                 {
5415                     tag: 'a',
5416                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5417                 }
5418             ]
5419         };
5420         
5421         return cfg;
5422     }
5423    
5424 });
5425
5426  /*
5427  * Based on:
5428  * Ext JS Library 1.1.1
5429  * Copyright(c) 2006-2007, Ext JS, LLC.
5430  *
5431  * Originally Released Under LGPL - original licence link has changed is not relivant.
5432  *
5433  * Fork - LGPL
5434  * <script type="text/javascript">
5435  */
5436  
5437
5438 /**
5439  * @class Roo.grid.ColumnModel
5440  * @extends Roo.util.Observable
5441  * This is the default implementation of a ColumnModel used by the Grid. It defines
5442  * the columns in the grid.
5443  * <br>Usage:<br>
5444  <pre><code>
5445  var colModel = new Roo.grid.ColumnModel([
5446         {header: "Ticker", width: 60, sortable: true, locked: true},
5447         {header: "Company Name", width: 150, sortable: true},
5448         {header: "Market Cap.", width: 100, sortable: true},
5449         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5450         {header: "Employees", width: 100, sortable: true, resizable: false}
5451  ]);
5452  </code></pre>
5453  * <p>
5454  
5455  * The config options listed for this class are options which may appear in each
5456  * individual column definition.
5457  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5458  * @constructor
5459  * @param {Object} config An Array of column config objects. See this class's
5460  * config objects for details.
5461 */
5462 Roo.grid.ColumnModel = function(config){
5463         /**
5464      * The config passed into the constructor
5465      */
5466     this.config = config;
5467     this.lookup = {};
5468
5469     // if no id, create one
5470     // if the column does not have a dataIndex mapping,
5471     // map it to the order it is in the config
5472     for(var i = 0, len = config.length; i < len; i++){
5473         var c = config[i];
5474         if(typeof c.dataIndex == "undefined"){
5475             c.dataIndex = i;
5476         }
5477         if(typeof c.renderer == "string"){
5478             c.renderer = Roo.util.Format[c.renderer];
5479         }
5480         if(typeof c.id == "undefined"){
5481             c.id = Roo.id();
5482         }
5483         if(c.editor && c.editor.xtype){
5484             c.editor  = Roo.factory(c.editor, Roo.grid);
5485         }
5486         if(c.editor && c.editor.isFormField){
5487             c.editor = new Roo.grid.GridEditor(c.editor);
5488         }
5489         this.lookup[c.id] = c;
5490     }
5491
5492     /**
5493      * The width of columns which have no width specified (defaults to 100)
5494      * @type Number
5495      */
5496     this.defaultWidth = 100;
5497
5498     /**
5499      * Default sortable of columns which have no sortable specified (defaults to false)
5500      * @type Boolean
5501      */
5502     this.defaultSortable = false;
5503
5504     this.addEvents({
5505         /**
5506              * @event widthchange
5507              * Fires when the width of a column changes.
5508              * @param {ColumnModel} this
5509              * @param {Number} columnIndex The column index
5510              * @param {Number} newWidth The new width
5511              */
5512             "widthchange": true,
5513         /**
5514              * @event headerchange
5515              * Fires when the text of a header changes.
5516              * @param {ColumnModel} this
5517              * @param {Number} columnIndex The column index
5518              * @param {Number} newText The new header text
5519              */
5520             "headerchange": true,
5521         /**
5522              * @event hiddenchange
5523              * Fires when a column is hidden or "unhidden".
5524              * @param {ColumnModel} this
5525              * @param {Number} columnIndex The column index
5526              * @param {Boolean} hidden true if hidden, false otherwise
5527              */
5528             "hiddenchange": true,
5529             /**
5530          * @event columnmoved
5531          * Fires when a column is moved.
5532          * @param {ColumnModel} this
5533          * @param {Number} oldIndex
5534          * @param {Number} newIndex
5535          */
5536         "columnmoved" : true,
5537         /**
5538          * @event columlockchange
5539          * Fires when a column's locked state is changed
5540          * @param {ColumnModel} this
5541          * @param {Number} colIndex
5542          * @param {Boolean} locked true if locked
5543          */
5544         "columnlockchange" : true
5545     });
5546     Roo.grid.ColumnModel.superclass.constructor.call(this);
5547 };
5548 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5549     /**
5550      * @cfg {String} header The header text to display in the Grid view.
5551      */
5552     /**
5553      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5554      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5555      * specified, the column's index is used as an index into the Record's data Array.
5556      */
5557     /**
5558      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5559      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5560      */
5561     /**
5562      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5563      * Defaults to the value of the {@link #defaultSortable} property.
5564      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5565      */
5566     /**
5567      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5568      */
5569     /**
5570      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5571      */
5572     /**
5573      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5574      */
5575     /**
5576      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5577      */
5578     /**
5579      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5580      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5581      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5582      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5583      */
5584        /**
5585      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5586      */
5587     /**
5588      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5589      */
5590     /**
5591      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5592      */
5593     /**
5594      * @cfg {String} cursor (Optional)
5595      */
5596     /**
5597      * @cfg {String} tooltip (Optional)
5598      */
5599     /**
5600      * @cfg {Number} xs (Optional)
5601      */
5602     /**
5603      * @cfg {Number} sm (Optional)
5604      */
5605     /**
5606      * @cfg {Number} md (Optional)
5607      */
5608     /**
5609      * @cfg {Number} lg (Optional)
5610      */
5611     /**
5612      * Returns the id of the column at the specified index.
5613      * @param {Number} index The column index
5614      * @return {String} the id
5615      */
5616     getColumnId : function(index){
5617         return this.config[index].id;
5618     },
5619
5620     /**
5621      * Returns the column for a specified id.
5622      * @param {String} id The column id
5623      * @return {Object} the column
5624      */
5625     getColumnById : function(id){
5626         return this.lookup[id];
5627     },
5628
5629     
5630     /**
5631      * Returns the column for a specified dataIndex.
5632      * @param {String} dataIndex The column dataIndex
5633      * @return {Object|Boolean} the column or false if not found
5634      */
5635     getColumnByDataIndex: function(dataIndex){
5636         var index = this.findColumnIndex(dataIndex);
5637         return index > -1 ? this.config[index] : false;
5638     },
5639     
5640     /**
5641      * Returns the index for a specified column id.
5642      * @param {String} id The column id
5643      * @return {Number} the index, or -1 if not found
5644      */
5645     getIndexById : function(id){
5646         for(var i = 0, len = this.config.length; i < len; i++){
5647             if(this.config[i].id == id){
5648                 return i;
5649             }
5650         }
5651         return -1;
5652     },
5653     
5654     /**
5655      * Returns the index for a specified column dataIndex.
5656      * @param {String} dataIndex The column dataIndex
5657      * @return {Number} the index, or -1 if not found
5658      */
5659     
5660     findColumnIndex : function(dataIndex){
5661         for(var i = 0, len = this.config.length; i < len; i++){
5662             if(this.config[i].dataIndex == dataIndex){
5663                 return i;
5664             }
5665         }
5666         return -1;
5667     },
5668     
5669     
5670     moveColumn : function(oldIndex, newIndex){
5671         var c = this.config[oldIndex];
5672         this.config.splice(oldIndex, 1);
5673         this.config.splice(newIndex, 0, c);
5674         this.dataMap = null;
5675         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5676     },
5677
5678     isLocked : function(colIndex){
5679         return this.config[colIndex].locked === true;
5680     },
5681
5682     setLocked : function(colIndex, value, suppressEvent){
5683         if(this.isLocked(colIndex) == value){
5684             return;
5685         }
5686         this.config[colIndex].locked = value;
5687         if(!suppressEvent){
5688             this.fireEvent("columnlockchange", this, colIndex, value);
5689         }
5690     },
5691
5692     getTotalLockedWidth : function(){
5693         var totalWidth = 0;
5694         for(var i = 0; i < this.config.length; i++){
5695             if(this.isLocked(i) && !this.isHidden(i)){
5696                 this.totalWidth += this.getColumnWidth(i);
5697             }
5698         }
5699         return totalWidth;
5700     },
5701
5702     getLockedCount : function(){
5703         for(var i = 0, len = this.config.length; i < len; i++){
5704             if(!this.isLocked(i)){
5705                 return i;
5706             }
5707         }
5708         
5709         return this.config.length;
5710     },
5711
5712     /**
5713      * Returns the number of columns.
5714      * @return {Number}
5715      */
5716     getColumnCount : function(visibleOnly){
5717         if(visibleOnly === true){
5718             var c = 0;
5719             for(var i = 0, len = this.config.length; i < len; i++){
5720                 if(!this.isHidden(i)){
5721                     c++;
5722                 }
5723             }
5724             return c;
5725         }
5726         return this.config.length;
5727     },
5728
5729     /**
5730      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5731      * @param {Function} fn
5732      * @param {Object} scope (optional)
5733      * @return {Array} result
5734      */
5735     getColumnsBy : function(fn, scope){
5736         var r = [];
5737         for(var i = 0, len = this.config.length; i < len; i++){
5738             var c = this.config[i];
5739             if(fn.call(scope||this, c, i) === true){
5740                 r[r.length] = c;
5741             }
5742         }
5743         return r;
5744     },
5745
5746     /**
5747      * Returns true if the specified column is sortable.
5748      * @param {Number} col The column index
5749      * @return {Boolean}
5750      */
5751     isSortable : function(col){
5752         if(typeof this.config[col].sortable == "undefined"){
5753             return this.defaultSortable;
5754         }
5755         return this.config[col].sortable;
5756     },
5757
5758     /**
5759      * Returns the rendering (formatting) function defined for the column.
5760      * @param {Number} col The column index.
5761      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5762      */
5763     getRenderer : function(col){
5764         if(!this.config[col].renderer){
5765             return Roo.grid.ColumnModel.defaultRenderer;
5766         }
5767         return this.config[col].renderer;
5768     },
5769
5770     /**
5771      * Sets the rendering (formatting) function for a column.
5772      * @param {Number} col The column index
5773      * @param {Function} fn The function to use to process the cell's raw data
5774      * to return HTML markup for the grid view. The render function is called with
5775      * the following parameters:<ul>
5776      * <li>Data value.</li>
5777      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5778      * <li>css A CSS style string to apply to the table cell.</li>
5779      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5780      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5781      * <li>Row index</li>
5782      * <li>Column index</li>
5783      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5784      */
5785     setRenderer : function(col, fn){
5786         this.config[col].renderer = fn;
5787     },
5788
5789     /**
5790      * Returns the width for the specified column.
5791      * @param {Number} col The column index
5792      * @return {Number}
5793      */
5794     getColumnWidth : function(col){
5795         return this.config[col].width * 1 || this.defaultWidth;
5796     },
5797
5798     /**
5799      * Sets the width for a column.
5800      * @param {Number} col The column index
5801      * @param {Number} width The new width
5802      */
5803     setColumnWidth : function(col, width, suppressEvent){
5804         this.config[col].width = width;
5805         this.totalWidth = null;
5806         if(!suppressEvent){
5807              this.fireEvent("widthchange", this, col, width);
5808         }
5809     },
5810
5811     /**
5812      * Returns the total width of all columns.
5813      * @param {Boolean} includeHidden True to include hidden column widths
5814      * @return {Number}
5815      */
5816     getTotalWidth : function(includeHidden){
5817         if(!this.totalWidth){
5818             this.totalWidth = 0;
5819             for(var i = 0, len = this.config.length; i < len; i++){
5820                 if(includeHidden || !this.isHidden(i)){
5821                     this.totalWidth += this.getColumnWidth(i);
5822                 }
5823             }
5824         }
5825         return this.totalWidth;
5826     },
5827
5828     /**
5829      * Returns the header for the specified column.
5830      * @param {Number} col The column index
5831      * @return {String}
5832      */
5833     getColumnHeader : function(col){
5834         return this.config[col].header;
5835     },
5836
5837     /**
5838      * Sets the header for a column.
5839      * @param {Number} col The column index
5840      * @param {String} header The new header
5841      */
5842     setColumnHeader : function(col, header){
5843         this.config[col].header = header;
5844         this.fireEvent("headerchange", this, col, header);
5845     },
5846
5847     /**
5848      * Returns the tooltip for the specified column.
5849      * @param {Number} col The column index
5850      * @return {String}
5851      */
5852     getColumnTooltip : function(col){
5853             return this.config[col].tooltip;
5854     },
5855     /**
5856      * Sets the tooltip for a column.
5857      * @param {Number} col The column index
5858      * @param {String} tooltip The new tooltip
5859      */
5860     setColumnTooltip : function(col, tooltip){
5861             this.config[col].tooltip = tooltip;
5862     },
5863
5864     /**
5865      * Returns the dataIndex for the specified column.
5866      * @param {Number} col The column index
5867      * @return {Number}
5868      */
5869     getDataIndex : function(col){
5870         return this.config[col].dataIndex;
5871     },
5872
5873     /**
5874      * Sets the dataIndex for a column.
5875      * @param {Number} col The column index
5876      * @param {Number} dataIndex The new dataIndex
5877      */
5878     setDataIndex : function(col, dataIndex){
5879         this.config[col].dataIndex = dataIndex;
5880     },
5881
5882     
5883     
5884     /**
5885      * Returns true if the cell is editable.
5886      * @param {Number} colIndex The column index
5887      * @param {Number} rowIndex The row index - this is nto actually used..?
5888      * @return {Boolean}
5889      */
5890     isCellEditable : function(colIndex, rowIndex){
5891         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5892     },
5893
5894     /**
5895      * Returns the editor defined for the cell/column.
5896      * return false or null to disable editing.
5897      * @param {Number} colIndex The column index
5898      * @param {Number} rowIndex The row index
5899      * @return {Object}
5900      */
5901     getCellEditor : function(colIndex, rowIndex){
5902         return this.config[colIndex].editor;
5903     },
5904
5905     /**
5906      * Sets if a column is editable.
5907      * @param {Number} col The column index
5908      * @param {Boolean} editable True if the column is editable
5909      */
5910     setEditable : function(col, editable){
5911         this.config[col].editable = editable;
5912     },
5913
5914
5915     /**
5916      * Returns true if the column is hidden.
5917      * @param {Number} colIndex The column index
5918      * @return {Boolean}
5919      */
5920     isHidden : function(colIndex){
5921         return this.config[colIndex].hidden;
5922     },
5923
5924
5925     /**
5926      * Returns true if the column width cannot be changed
5927      */
5928     isFixed : function(colIndex){
5929         return this.config[colIndex].fixed;
5930     },
5931
5932     /**
5933      * Returns true if the column can be resized
5934      * @return {Boolean}
5935      */
5936     isResizable : function(colIndex){
5937         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5938     },
5939     /**
5940      * Sets if a column is hidden.
5941      * @param {Number} colIndex The column index
5942      * @param {Boolean} hidden True if the column is hidden
5943      */
5944     setHidden : function(colIndex, hidden){
5945         this.config[colIndex].hidden = hidden;
5946         this.totalWidth = null;
5947         this.fireEvent("hiddenchange", this, colIndex, hidden);
5948     },
5949
5950     /**
5951      * Sets the editor for a column.
5952      * @param {Number} col The column index
5953      * @param {Object} editor The editor object
5954      */
5955     setEditor : function(col, editor){
5956         this.config[col].editor = editor;
5957     }
5958 });
5959
5960 Roo.grid.ColumnModel.defaultRenderer = function(value)
5961 {
5962     if(typeof value == "object") {
5963         return value;
5964     }
5965         if(typeof value == "string" && value.length < 1){
5966             return "&#160;";
5967         }
5968     
5969         return String.format("{0}", value);
5970 };
5971
5972 // Alias for backwards compatibility
5973 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5974 /*
5975  * Based on:
5976  * Ext JS Library 1.1.1
5977  * Copyright(c) 2006-2007, Ext JS, LLC.
5978  *
5979  * Originally Released Under LGPL - original licence link has changed is not relivant.
5980  *
5981  * Fork - LGPL
5982  * <script type="text/javascript">
5983  */
5984  
5985 /**
5986  * @class Roo.LoadMask
5987  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5988  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5989  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5990  * element's UpdateManager load indicator and will be destroyed after the initial load.
5991  * @constructor
5992  * Create a new LoadMask
5993  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5994  * @param {Object} config The config object
5995  */
5996 Roo.LoadMask = function(el, config){
5997     this.el = Roo.get(el);
5998     Roo.apply(this, config);
5999     if(this.store){
6000         this.store.on('beforeload', this.onBeforeLoad, this);
6001         this.store.on('load', this.onLoad, this);
6002         this.store.on('loadexception', this.onLoadException, this);
6003         this.removeMask = false;
6004     }else{
6005         var um = this.el.getUpdateManager();
6006         um.showLoadIndicator = false; // disable the default indicator
6007         um.on('beforeupdate', this.onBeforeLoad, this);
6008         um.on('update', this.onLoad, this);
6009         um.on('failure', this.onLoad, this);
6010         this.removeMask = true;
6011     }
6012 };
6013
6014 Roo.LoadMask.prototype = {
6015     /**
6016      * @cfg {Boolean} removeMask
6017      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6018      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6019      */
6020     /**
6021      * @cfg {String} msg
6022      * The text to display in a centered loading message box (defaults to 'Loading...')
6023      */
6024     msg : 'Loading...',
6025     /**
6026      * @cfg {String} msgCls
6027      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6028      */
6029     msgCls : 'x-mask-loading',
6030
6031     /**
6032      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6033      * @type Boolean
6034      */
6035     disabled: false,
6036
6037     /**
6038      * Disables the mask to prevent it from being displayed
6039      */
6040     disable : function(){
6041        this.disabled = true;
6042     },
6043
6044     /**
6045      * Enables the mask so that it can be displayed
6046      */
6047     enable : function(){
6048         this.disabled = false;
6049     },
6050     
6051     onLoadException : function()
6052     {
6053         Roo.log(arguments);
6054         
6055         if (typeof(arguments[3]) != 'undefined') {
6056             Roo.MessageBox.alert("Error loading",arguments[3]);
6057         } 
6058         /*
6059         try {
6060             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6061                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6062             }   
6063         } catch(e) {
6064             
6065         }
6066         */
6067     
6068         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6069     },
6070     // private
6071     onLoad : function()
6072     {
6073         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6074     },
6075
6076     // private
6077     onBeforeLoad : function(){
6078         if(!this.disabled){
6079             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6080         }
6081     },
6082
6083     // private
6084     destroy : function(){
6085         if(this.store){
6086             this.store.un('beforeload', this.onBeforeLoad, this);
6087             this.store.un('load', this.onLoad, this);
6088             this.store.un('loadexception', this.onLoadException, this);
6089         }else{
6090             var um = this.el.getUpdateManager();
6091             um.un('beforeupdate', this.onBeforeLoad, this);
6092             um.un('update', this.onLoad, this);
6093             um.un('failure', this.onLoad, this);
6094         }
6095     }
6096 };/*
6097  * - LGPL
6098  *
6099  * table
6100  * 
6101  */
6102
6103 /**
6104  * @class Roo.bootstrap.Table
6105  * @extends Roo.bootstrap.Component
6106  * Bootstrap Table class
6107  * @cfg {String} cls table class
6108  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6109  * @cfg {String} bgcolor Specifies the background color for a table
6110  * @cfg {Number} border Specifies whether the table cells should have borders or not
6111  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6112  * @cfg {Number} cellspacing Specifies the space between cells
6113  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6114  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6115  * @cfg {String} sortable Specifies that the table should be sortable
6116  * @cfg {String} summary Specifies a summary of the content of a table
6117  * @cfg {Number} width Specifies the width of a table
6118  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6119  * 
6120  * @cfg {boolean} striped Should the rows be alternative striped
6121  * @cfg {boolean} bordered Add borders to the table
6122  * @cfg {boolean} hover Add hover highlighting
6123  * @cfg {boolean} condensed Format condensed
6124  * @cfg {boolean} responsive Format condensed
6125  * @cfg {Boolean} loadMask (true|false) default false
6126  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6127  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6128  * @cfg {Boolean} rowSelection (true|false) default false
6129  * @cfg {Boolean} cellSelection (true|false) default false
6130  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6131  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6132  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6133  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6134  
6135  * 
6136  * @constructor
6137  * Create a new Table
6138  * @param {Object} config The config object
6139  */
6140
6141 Roo.bootstrap.Table = function(config){
6142     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6143     
6144   
6145     
6146     // BC...
6147     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6148     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6149     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6150     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6151     
6152     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6153     if (this.sm) {
6154         this.sm.grid = this;
6155         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6156         this.sm = this.selModel;
6157         this.sm.xmodule = this.xmodule || false;
6158     }
6159     
6160     if (this.cm && typeof(this.cm.config) == 'undefined') {
6161         this.colModel = new Roo.grid.ColumnModel(this.cm);
6162         this.cm = this.colModel;
6163         this.cm.xmodule = this.xmodule || false;
6164     }
6165     if (this.store) {
6166         this.store= Roo.factory(this.store, Roo.data);
6167         this.ds = this.store;
6168         this.ds.xmodule = this.xmodule || false;
6169          
6170     }
6171     if (this.footer && this.store) {
6172         this.footer.dataSource = this.ds;
6173         this.footer = Roo.factory(this.footer);
6174     }
6175     
6176     /** @private */
6177     this.addEvents({
6178         /**
6179          * @event cellclick
6180          * Fires when a cell is clicked
6181          * @param {Roo.bootstrap.Table} this
6182          * @param {Roo.Element} el
6183          * @param {Number} rowIndex
6184          * @param {Number} columnIndex
6185          * @param {Roo.EventObject} e
6186          */
6187         "cellclick" : true,
6188         /**
6189          * @event celldblclick
6190          * Fires when a cell is double clicked
6191          * @param {Roo.bootstrap.Table} this
6192          * @param {Roo.Element} el
6193          * @param {Number} rowIndex
6194          * @param {Number} columnIndex
6195          * @param {Roo.EventObject} e
6196          */
6197         "celldblclick" : true,
6198         /**
6199          * @event rowclick
6200          * Fires when a row is clicked
6201          * @param {Roo.bootstrap.Table} this
6202          * @param {Roo.Element} el
6203          * @param {Number} rowIndex
6204          * @param {Roo.EventObject} e
6205          */
6206         "rowclick" : true,
6207         /**
6208          * @event rowdblclick
6209          * Fires when a row is double clicked
6210          * @param {Roo.bootstrap.Table} this
6211          * @param {Roo.Element} el
6212          * @param {Number} rowIndex
6213          * @param {Roo.EventObject} e
6214          */
6215         "rowdblclick" : true,
6216         /**
6217          * @event mouseover
6218          * Fires when a mouseover occur
6219          * @param {Roo.bootstrap.Table} this
6220          * @param {Roo.Element} el
6221          * @param {Number} rowIndex
6222          * @param {Number} columnIndex
6223          * @param {Roo.EventObject} e
6224          */
6225         "mouseover" : true,
6226         /**
6227          * @event mouseout
6228          * Fires when a mouseout occur
6229          * @param {Roo.bootstrap.Table} this
6230          * @param {Roo.Element} el
6231          * @param {Number} rowIndex
6232          * @param {Number} columnIndex
6233          * @param {Roo.EventObject} e
6234          */
6235         "mouseout" : true,
6236         /**
6237          * @event rowclass
6238          * Fires when a row is rendered, so you can change add a style to it.
6239          * @param {Roo.bootstrap.Table} this
6240          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6241          */
6242         'rowclass' : true,
6243           /**
6244          * @event rowsrendered
6245          * Fires when all the  rows have been rendered
6246          * @param {Roo.bootstrap.Table} this
6247          */
6248         'rowsrendered' : true,
6249         /**
6250          * @event contextmenu
6251          * The raw contextmenu event for the entire grid.
6252          * @param {Roo.EventObject} e
6253          */
6254         "contextmenu" : true,
6255         /**
6256          * @event rowcontextmenu
6257          * Fires when a row is right clicked
6258          * @param {Roo.bootstrap.Table} this
6259          * @param {Number} rowIndex
6260          * @param {Roo.EventObject} e
6261          */
6262         "rowcontextmenu" : true,
6263         /**
6264          * @event cellcontextmenu
6265          * Fires when a cell is right clicked
6266          * @param {Roo.bootstrap.Table} this
6267          * @param {Number} rowIndex
6268          * @param {Number} cellIndex
6269          * @param {Roo.EventObject} e
6270          */
6271          "cellcontextmenu" : true,
6272          /**
6273          * @event headercontextmenu
6274          * Fires when a header is right clicked
6275          * @param {Roo.bootstrap.Table} this
6276          * @param {Number} columnIndex
6277          * @param {Roo.EventObject} e
6278          */
6279         "headercontextmenu" : true
6280     });
6281 };
6282
6283 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6284     
6285     cls: false,
6286     align: false,
6287     bgcolor: false,
6288     border: false,
6289     cellpadding: false,
6290     cellspacing: false,
6291     frame: false,
6292     rules: false,
6293     sortable: false,
6294     summary: false,
6295     width: false,
6296     striped : false,
6297     scrollBody : false,
6298     bordered: false,
6299     hover:  false,
6300     condensed : false,
6301     responsive : false,
6302     sm : false,
6303     cm : false,
6304     store : false,
6305     loadMask : false,
6306     footerShow : true,
6307     headerShow : true,
6308   
6309     rowSelection : false,
6310     cellSelection : false,
6311     layout : false,
6312     
6313     // Roo.Element - the tbody
6314     mainBody: false,
6315     // Roo.Element - thead element
6316     mainHead: false,
6317     
6318     container: false, // used by gridpanel...
6319     
6320     lazyLoad : false,
6321     
6322     CSS : Roo.util.CSS,
6323     
6324     auto_hide_footer : false,
6325     
6326     getAutoCreate : function()
6327     {
6328         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6329         
6330         cfg = {
6331             tag: 'table',
6332             cls : 'table',
6333             cn : []
6334         };
6335         if (this.scrollBody) {
6336             cfg.cls += ' table-body-fixed';
6337         }    
6338         if (this.striped) {
6339             cfg.cls += ' table-striped';
6340         }
6341         
6342         if (this.hover) {
6343             cfg.cls += ' table-hover';
6344         }
6345         if (this.bordered) {
6346             cfg.cls += ' table-bordered';
6347         }
6348         if (this.condensed) {
6349             cfg.cls += ' table-condensed';
6350         }
6351         if (this.responsive) {
6352             cfg.cls += ' table-responsive';
6353         }
6354         
6355         if (this.cls) {
6356             cfg.cls+=  ' ' +this.cls;
6357         }
6358         
6359         // this lot should be simplifed...
6360         var _t = this;
6361         var cp = [
6362             'align',
6363             'bgcolor',
6364             'border',
6365             'cellpadding',
6366             'cellspacing',
6367             'frame',
6368             'rules',
6369             'sortable',
6370             'summary',
6371             'width'
6372         ].forEach(function(k) {
6373             if (_t[k]) {
6374                 cfg[k] = _t[k];
6375             }
6376         });
6377         
6378         
6379         if (this.layout) {
6380             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6381         }
6382         
6383         if(this.store || this.cm){
6384             if(this.headerShow){
6385                 cfg.cn.push(this.renderHeader());
6386             }
6387             
6388             cfg.cn.push(this.renderBody());
6389             
6390             if(this.footerShow){
6391                 cfg.cn.push(this.renderFooter());
6392             }
6393             // where does this come from?
6394             //cfg.cls+=  ' TableGrid';
6395         }
6396         
6397         return { cn : [ cfg ] };
6398     },
6399     
6400     initEvents : function()
6401     {   
6402         if(!this.store || !this.cm){
6403             return;
6404         }
6405         if (this.selModel) {
6406             this.selModel.initEvents();
6407         }
6408         
6409         
6410         //Roo.log('initEvents with ds!!!!');
6411         
6412         this.mainBody = this.el.select('tbody', true).first();
6413         this.mainHead = this.el.select('thead', true).first();
6414         this.mainFoot = this.el.select('tfoot', true).first();
6415         
6416         
6417         
6418         var _this = this;
6419         
6420         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6421             e.on('click', _this.sort, _this);
6422         });
6423         
6424         this.mainBody.on("click", this.onClick, this);
6425         this.mainBody.on("dblclick", this.onDblClick, this);
6426         
6427         // why is this done????? = it breaks dialogs??
6428         //this.parent().el.setStyle('position', 'relative');
6429         
6430         
6431         if (this.footer) {
6432             this.footer.parentId = this.id;
6433             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6434             
6435             if(this.lazyLoad){
6436                 this.el.select('tfoot tr td').first().addClass('hide');
6437             }
6438         } 
6439         
6440         if(this.loadMask) {
6441             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6442         }
6443         
6444         this.store.on('load', this.onLoad, this);
6445         this.store.on('beforeload', this.onBeforeLoad, this);
6446         this.store.on('update', this.onUpdate, this);
6447         this.store.on('add', this.onAdd, this);
6448         this.store.on("clear", this.clear, this);
6449         
6450         this.el.on("contextmenu", this.onContextMenu, this);
6451         
6452         this.mainBody.on('scroll', this.onBodyScroll, this);
6453         
6454         this.cm.on("headerchange", this.onHeaderChange, this);
6455         
6456         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6457         
6458     },
6459     
6460     onContextMenu : function(e, t)
6461     {
6462         this.processEvent("contextmenu", e);
6463     },
6464     
6465     processEvent : function(name, e)
6466     {
6467         if (name != 'touchstart' ) {
6468             this.fireEvent(name, e);    
6469         }
6470         
6471         var t = e.getTarget();
6472         
6473         var cell = Roo.get(t);
6474         
6475         if(!cell){
6476             return;
6477         }
6478         
6479         if(cell.findParent('tfoot', false, true)){
6480             return;
6481         }
6482         
6483         if(cell.findParent('thead', false, true)){
6484             
6485             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6486                 cell = Roo.get(t).findParent('th', false, true);
6487                 if (!cell) {
6488                     Roo.log("failed to find th in thead?");
6489                     Roo.log(e.getTarget());
6490                     return;
6491                 }
6492             }
6493             
6494             var cellIndex = cell.dom.cellIndex;
6495             
6496             var ename = name == 'touchstart' ? 'click' : name;
6497             this.fireEvent("header" + ename, this, cellIndex, e);
6498             
6499             return;
6500         }
6501         
6502         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6503             cell = Roo.get(t).findParent('td', false, true);
6504             if (!cell) {
6505                 Roo.log("failed to find th in tbody?");
6506                 Roo.log(e.getTarget());
6507                 return;
6508             }
6509         }
6510         
6511         var row = cell.findParent('tr', false, true);
6512         var cellIndex = cell.dom.cellIndex;
6513         var rowIndex = row.dom.rowIndex - 1;
6514         
6515         if(row !== false){
6516             
6517             this.fireEvent("row" + name, this, rowIndex, e);
6518             
6519             if(cell !== false){
6520             
6521                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6522             }
6523         }
6524         
6525     },
6526     
6527     onMouseover : function(e, el)
6528     {
6529         var cell = Roo.get(el);
6530         
6531         if(!cell){
6532             return;
6533         }
6534         
6535         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6536             cell = cell.findParent('td', false, true);
6537         }
6538         
6539         var row = cell.findParent('tr', false, true);
6540         var cellIndex = cell.dom.cellIndex;
6541         var rowIndex = row.dom.rowIndex - 1; // start from 0
6542         
6543         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6544         
6545     },
6546     
6547     onMouseout : function(e, el)
6548     {
6549         var cell = Roo.get(el);
6550         
6551         if(!cell){
6552             return;
6553         }
6554         
6555         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6556             cell = cell.findParent('td', false, true);
6557         }
6558         
6559         var row = cell.findParent('tr', false, true);
6560         var cellIndex = cell.dom.cellIndex;
6561         var rowIndex = row.dom.rowIndex - 1; // start from 0
6562         
6563         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6564         
6565     },
6566     
6567     onClick : function(e, el)
6568     {
6569         var cell = Roo.get(el);
6570         
6571         if(!cell || (!this.cellSelection && !this.rowSelection)){
6572             return;
6573         }
6574         
6575         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6576             cell = cell.findParent('td', false, true);
6577         }
6578         
6579         if(!cell || typeof(cell) == 'undefined'){
6580             return;
6581         }
6582         
6583         var row = cell.findParent('tr', false, true);
6584         
6585         if(!row || typeof(row) == 'undefined'){
6586             return;
6587         }
6588         
6589         var cellIndex = cell.dom.cellIndex;
6590         var rowIndex = this.getRowIndex(row);
6591         
6592         // why??? - should these not be based on SelectionModel?
6593         if(this.cellSelection){
6594             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6595         }
6596         
6597         if(this.rowSelection){
6598             this.fireEvent('rowclick', this, row, rowIndex, e);
6599         }
6600         
6601         
6602     },
6603         
6604     onDblClick : function(e,el)
6605     {
6606         var cell = Roo.get(el);
6607         
6608         if(!cell || (!this.cellSelection && !this.rowSelection)){
6609             return;
6610         }
6611         
6612         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6613             cell = cell.findParent('td', false, true);
6614         }
6615         
6616         if(!cell || typeof(cell) == 'undefined'){
6617             return;
6618         }
6619         
6620         var row = cell.findParent('tr', false, true);
6621         
6622         if(!row || typeof(row) == 'undefined'){
6623             return;
6624         }
6625         
6626         var cellIndex = cell.dom.cellIndex;
6627         var rowIndex = this.getRowIndex(row);
6628         
6629         if(this.cellSelection){
6630             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6631         }
6632         
6633         if(this.rowSelection){
6634             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6635         }
6636     },
6637     
6638     sort : function(e,el)
6639     {
6640         var col = Roo.get(el);
6641         
6642         if(!col.hasClass('sortable')){
6643             return;
6644         }
6645         
6646         var sort = col.attr('sort');
6647         var dir = 'ASC';
6648         
6649         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6650             dir = 'DESC';
6651         }
6652         
6653         this.store.sortInfo = {field : sort, direction : dir};
6654         
6655         if (this.footer) {
6656             Roo.log("calling footer first");
6657             this.footer.onClick('first');
6658         } else {
6659         
6660             this.store.load({ params : { start : 0 } });
6661         }
6662     },
6663     
6664     renderHeader : function()
6665     {
6666         var header = {
6667             tag: 'thead',
6668             cn : []
6669         };
6670         
6671         var cm = this.cm;
6672         this.totalWidth = 0;
6673         
6674         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6675             
6676             var config = cm.config[i];
6677             
6678             var c = {
6679                 tag: 'th',
6680                 cls : 'x-hcol-' + i,
6681                 style : '',
6682                 html: cm.getColumnHeader(i)
6683             };
6684             
6685             var hh = '';
6686             
6687             if(typeof(config.sortable) != 'undefined' && config.sortable){
6688                 c.cls = 'sortable';
6689                 c.html = '<i class="glyphicon"></i>' + c.html;
6690             }
6691             
6692             if(typeof(config.lgHeader) != 'undefined'){
6693                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6694             }
6695             
6696             if(typeof(config.mdHeader) != 'undefined'){
6697                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6698             }
6699             
6700             if(typeof(config.smHeader) != 'undefined'){
6701                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6702             }
6703             
6704             if(typeof(config.xsHeader) != 'undefined'){
6705                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6706             }
6707             
6708             if(hh.length){
6709                 c.html = hh;
6710             }
6711             
6712             if(typeof(config.tooltip) != 'undefined'){
6713                 c.tooltip = config.tooltip;
6714             }
6715             
6716             if(typeof(config.colspan) != 'undefined'){
6717                 c.colspan = config.colspan;
6718             }
6719             
6720             if(typeof(config.hidden) != 'undefined' && config.hidden){
6721                 c.style += ' display:none;';
6722             }
6723             
6724             if(typeof(config.dataIndex) != 'undefined'){
6725                 c.sort = config.dataIndex;
6726             }
6727             
6728            
6729             
6730             if(typeof(config.align) != 'undefined' && config.align.length){
6731                 c.style += ' text-align:' + config.align + ';';
6732             }
6733             
6734             if(typeof(config.width) != 'undefined'){
6735                 c.style += ' width:' + config.width + 'px;';
6736                 this.totalWidth += config.width;
6737             } else {
6738                 this.totalWidth += 100; // assume minimum of 100 per column?
6739             }
6740             
6741             if(typeof(config.cls) != 'undefined'){
6742                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6743             }
6744             
6745             ['xs','sm','md','lg'].map(function(size){
6746                 
6747                 if(typeof(config[size]) == 'undefined'){
6748                     return;
6749                 }
6750                 
6751                 if (!config[size]) { // 0 = hidden
6752                     c.cls += ' hidden-' + size;
6753                     return;
6754                 }
6755                 
6756                 c.cls += ' col-' + size + '-' + config[size];
6757
6758             });
6759             
6760             header.cn.push(c)
6761         }
6762         
6763         return header;
6764     },
6765     
6766     renderBody : function()
6767     {
6768         var body = {
6769             tag: 'tbody',
6770             cn : [
6771                 {
6772                     tag: 'tr',
6773                     cn : [
6774                         {
6775                             tag : 'td',
6776                             colspan :  this.cm.getColumnCount()
6777                         }
6778                     ]
6779                 }
6780             ]
6781         };
6782         
6783         return body;
6784     },
6785     
6786     renderFooter : function()
6787     {
6788         var footer = {
6789             tag: 'tfoot',
6790             cn : [
6791                 {
6792                     tag: 'tr',
6793                     cn : [
6794                         {
6795                             tag : 'td',
6796                             colspan :  this.cm.getColumnCount()
6797                         }
6798                     ]
6799                 }
6800             ]
6801         };
6802         
6803         return footer;
6804     },
6805     
6806     
6807     
6808     onLoad : function()
6809     {
6810 //        Roo.log('ds onload');
6811         this.clear();
6812         
6813         var _this = this;
6814         var cm = this.cm;
6815         var ds = this.store;
6816         
6817         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6818             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6819             if (_this.store.sortInfo) {
6820                     
6821                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6822                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6823                 }
6824                 
6825                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6826                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6827                 }
6828             }
6829         });
6830         
6831         var tbody =  this.mainBody;
6832               
6833         if(ds.getCount() > 0){
6834             ds.data.each(function(d,rowIndex){
6835                 var row =  this.renderRow(cm, ds, rowIndex);
6836                 
6837                 tbody.createChild(row);
6838                 
6839                 var _this = this;
6840                 
6841                 if(row.cellObjects.length){
6842                     Roo.each(row.cellObjects, function(r){
6843                         _this.renderCellObject(r);
6844                     })
6845                 }
6846                 
6847             }, this);
6848         }
6849         
6850         var tfoot = this.el.select('tfoot', true).first();
6851         
6852         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6853             
6854             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6855             
6856             var total = this.ds.getTotalCount();
6857             
6858             if(this.footer.pageSize < total){
6859                 this.mainFoot.show();
6860             }
6861         }
6862         
6863         Roo.each(this.el.select('tbody td', true).elements, function(e){
6864             e.on('mouseover', _this.onMouseover, _this);
6865         });
6866         
6867         Roo.each(this.el.select('tbody td', true).elements, function(e){
6868             e.on('mouseout', _this.onMouseout, _this);
6869         });
6870         this.fireEvent('rowsrendered', this);
6871         
6872         this.autoSize();
6873     },
6874     
6875     
6876     onUpdate : function(ds,record)
6877     {
6878         this.refreshRow(record);
6879         this.autoSize();
6880     },
6881     
6882     onRemove : function(ds, record, index, isUpdate){
6883         if(isUpdate !== true){
6884             this.fireEvent("beforerowremoved", this, index, record);
6885         }
6886         var bt = this.mainBody.dom;
6887         
6888         var rows = this.el.select('tbody > tr', true).elements;
6889         
6890         if(typeof(rows[index]) != 'undefined'){
6891             bt.removeChild(rows[index].dom);
6892         }
6893         
6894 //        if(bt.rows[index]){
6895 //            bt.removeChild(bt.rows[index]);
6896 //        }
6897         
6898         if(isUpdate !== true){
6899             //this.stripeRows(index);
6900             //this.syncRowHeights(index, index);
6901             //this.layout();
6902             this.fireEvent("rowremoved", this, index, record);
6903         }
6904     },
6905     
6906     onAdd : function(ds, records, rowIndex)
6907     {
6908         //Roo.log('on Add called');
6909         // - note this does not handle multiple adding very well..
6910         var bt = this.mainBody.dom;
6911         for (var i =0 ; i < records.length;i++) {
6912             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6913             //Roo.log(records[i]);
6914             //Roo.log(this.store.getAt(rowIndex+i));
6915             this.insertRow(this.store, rowIndex + i, false);
6916             return;
6917         }
6918         
6919     },
6920     
6921     
6922     refreshRow : function(record){
6923         var ds = this.store, index;
6924         if(typeof record == 'number'){
6925             index = record;
6926             record = ds.getAt(index);
6927         }else{
6928             index = ds.indexOf(record);
6929         }
6930         this.insertRow(ds, index, true);
6931         this.autoSize();
6932         this.onRemove(ds, record, index+1, true);
6933         this.autoSize();
6934         //this.syncRowHeights(index, index);
6935         //this.layout();
6936         this.fireEvent("rowupdated", this, index, record);
6937     },
6938     
6939     insertRow : function(dm, rowIndex, isUpdate){
6940         
6941         if(!isUpdate){
6942             this.fireEvent("beforerowsinserted", this, rowIndex);
6943         }
6944             //var s = this.getScrollState();
6945         var row = this.renderRow(this.cm, this.store, rowIndex);
6946         // insert before rowIndex..
6947         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6948         
6949         var _this = this;
6950                 
6951         if(row.cellObjects.length){
6952             Roo.each(row.cellObjects, function(r){
6953                 _this.renderCellObject(r);
6954             })
6955         }
6956             
6957         if(!isUpdate){
6958             this.fireEvent("rowsinserted", this, rowIndex);
6959             //this.syncRowHeights(firstRow, lastRow);
6960             //this.stripeRows(firstRow);
6961             //this.layout();
6962         }
6963         
6964     },
6965     
6966     
6967     getRowDom : function(rowIndex)
6968     {
6969         var rows = this.el.select('tbody > tr', true).elements;
6970         
6971         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6972         
6973     },
6974     // returns the object tree for a tr..
6975   
6976     
6977     renderRow : function(cm, ds, rowIndex) 
6978     {
6979         var d = ds.getAt(rowIndex);
6980         
6981         var row = {
6982             tag : 'tr',
6983             cls : 'x-row-' + rowIndex,
6984             cn : []
6985         };
6986             
6987         var cellObjects = [];
6988         
6989         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6990             var config = cm.config[i];
6991             
6992             var renderer = cm.getRenderer(i);
6993             var value = '';
6994             var id = false;
6995             
6996             if(typeof(renderer) !== 'undefined'){
6997                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6998             }
6999             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7000             // and are rendered into the cells after the row is rendered - using the id for the element.
7001             
7002             if(typeof(value) === 'object'){
7003                 id = Roo.id();
7004                 cellObjects.push({
7005                     container : id,
7006                     cfg : value 
7007                 })
7008             }
7009             
7010             var rowcfg = {
7011                 record: d,
7012                 rowIndex : rowIndex,
7013                 colIndex : i,
7014                 rowClass : ''
7015             };
7016
7017             this.fireEvent('rowclass', this, rowcfg);
7018             
7019             var td = {
7020                 tag: 'td',
7021                 cls : rowcfg.rowClass + ' x-col-' + i,
7022                 style: '',
7023                 html: (typeof(value) === 'object') ? '' : value
7024             };
7025             
7026             if (id) {
7027                 td.id = id;
7028             }
7029             
7030             if(typeof(config.colspan) != 'undefined'){
7031                 td.colspan = config.colspan;
7032             }
7033             
7034             if(typeof(config.hidden) != 'undefined' && config.hidden){
7035                 td.style += ' display:none;';
7036             }
7037             
7038             if(typeof(config.align) != 'undefined' && config.align.length){
7039                 td.style += ' text-align:' + config.align + ';';
7040             }
7041             if(typeof(config.valign) != 'undefined' && config.valign.length){
7042                 td.style += ' vertical-align:' + config.valign + ';';
7043             }
7044             
7045             if(typeof(config.width) != 'undefined'){
7046                 td.style += ' width:' +  config.width + 'px;';
7047             }
7048             
7049             if(typeof(config.cursor) != 'undefined'){
7050                 td.style += ' cursor:' +  config.cursor + ';';
7051             }
7052             
7053             if(typeof(config.cls) != 'undefined'){
7054                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7055             }
7056             
7057             ['xs','sm','md','lg'].map(function(size){
7058                 
7059                 if(typeof(config[size]) == 'undefined'){
7060                     return;
7061                 }
7062                 
7063                 if (!config[size]) { // 0 = hidden
7064                     td.cls += ' hidden-' + size;
7065                     return;
7066                 }
7067                 
7068                 td.cls += ' col-' + size + '-' + config[size];
7069
7070             });
7071             
7072             row.cn.push(td);
7073            
7074         }
7075         
7076         row.cellObjects = cellObjects;
7077         
7078         return row;
7079           
7080     },
7081     
7082     
7083     
7084     onBeforeLoad : function()
7085     {
7086         
7087     },
7088      /**
7089      * Remove all rows
7090      */
7091     clear : function()
7092     {
7093         this.el.select('tbody', true).first().dom.innerHTML = '';
7094     },
7095     /**
7096      * Show or hide a row.
7097      * @param {Number} rowIndex to show or hide
7098      * @param {Boolean} state hide
7099      */
7100     setRowVisibility : function(rowIndex, state)
7101     {
7102         var bt = this.mainBody.dom;
7103         
7104         var rows = this.el.select('tbody > tr', true).elements;
7105         
7106         if(typeof(rows[rowIndex]) == 'undefined'){
7107             return;
7108         }
7109         rows[rowIndex].dom.style.display = state ? '' : 'none';
7110     },
7111     
7112     
7113     getSelectionModel : function(){
7114         if(!this.selModel){
7115             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7116         }
7117         return this.selModel;
7118     },
7119     /*
7120      * Render the Roo.bootstrap object from renderder
7121      */
7122     renderCellObject : function(r)
7123     {
7124         var _this = this;
7125         
7126         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7127         
7128         var t = r.cfg.render(r.container);
7129         
7130         if(r.cfg.cn){
7131             Roo.each(r.cfg.cn, function(c){
7132                 var child = {
7133                     container: t.getChildContainer(),
7134                     cfg: c
7135                 };
7136                 _this.renderCellObject(child);
7137             })
7138         }
7139     },
7140     
7141     getRowIndex : function(row)
7142     {
7143         var rowIndex = -1;
7144         
7145         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7146             if(el != row){
7147                 return;
7148             }
7149             
7150             rowIndex = index;
7151         });
7152         
7153         return rowIndex;
7154     },
7155      /**
7156      * Returns the grid's underlying element = used by panel.Grid
7157      * @return {Element} The element
7158      */
7159     getGridEl : function(){
7160         return this.el;
7161     },
7162      /**
7163      * Forces a resize - used by panel.Grid
7164      * @return {Element} The element
7165      */
7166     autoSize : function()
7167     {
7168         //var ctr = Roo.get(this.container.dom.parentElement);
7169         var ctr = Roo.get(this.el.dom);
7170         
7171         var thd = this.getGridEl().select('thead',true).first();
7172         var tbd = this.getGridEl().select('tbody', true).first();
7173         var tfd = this.getGridEl().select('tfoot', true).first();
7174         
7175         var cw = ctr.getWidth();
7176         
7177         if (tbd) {
7178             
7179             tbd.setSize(ctr.getWidth(),
7180                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7181             );
7182             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7183             cw -= barsize;
7184         }
7185         cw = Math.max(cw, this.totalWidth);
7186         this.getGridEl().select('tr',true).setWidth(cw);
7187         // resize 'expandable coloumn?
7188         
7189         return; // we doe not have a view in this design..
7190         
7191     },
7192     onBodyScroll: function()
7193     {
7194         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7195         if(this.mainHead){
7196             this.mainHead.setStyle({
7197                 'position' : 'relative',
7198                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7199             });
7200         }
7201         
7202         if(this.lazyLoad){
7203             
7204             var scrollHeight = this.mainBody.dom.scrollHeight;
7205             
7206             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7207             
7208             var height = this.mainBody.getHeight();
7209             
7210             if(scrollHeight - height == scrollTop) {
7211                 
7212                 var total = this.ds.getTotalCount();
7213                 
7214                 if(this.footer.cursor + this.footer.pageSize < total){
7215                     
7216                     this.footer.ds.load({
7217                         params : {
7218                             start : this.footer.cursor + this.footer.pageSize,
7219                             limit : this.footer.pageSize
7220                         },
7221                         add : true
7222                     });
7223                 }
7224             }
7225             
7226         }
7227     },
7228     
7229     onHeaderChange : function()
7230     {
7231         var header = this.renderHeader();
7232         var table = this.el.select('table', true).first();
7233         
7234         this.mainHead.remove();
7235         this.mainHead = table.createChild(header, this.mainBody, false);
7236     },
7237     
7238     onHiddenChange : function(colModel, colIndex, hidden)
7239     {
7240         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7241         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7242         
7243         this.CSS.updateRule(thSelector, "display", "");
7244         this.CSS.updateRule(tdSelector, "display", "");
7245         
7246         if(hidden){
7247             this.CSS.updateRule(thSelector, "display", "none");
7248             this.CSS.updateRule(tdSelector, "display", "none");
7249         }
7250         
7251         this.onHeaderChange();
7252         this.onLoad();
7253     },
7254     
7255     setColumnWidth: function(col_index, width)
7256     {
7257         // width = "md-2 xs-2..."
7258         if(!this.colModel.config[col_index]) {
7259             return;
7260         }
7261         
7262         var w = width.split(" ");
7263         
7264         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7265         
7266         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7267         
7268         
7269         for(var j = 0; j < w.length; j++) {
7270             
7271             if(!w[j]) {
7272                 continue;
7273             }
7274             
7275             var size_cls = w[j].split("-");
7276             
7277             if(!Number.isInteger(size_cls[1] * 1)) {
7278                 continue;
7279             }
7280             
7281             if(!this.colModel.config[col_index][size_cls[0]]) {
7282                 continue;
7283             }
7284             
7285             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7286                 continue;
7287             }
7288             
7289             h_row[0].classList.replace(
7290                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7291                 "col-"+size_cls[0]+"-"+size_cls[1]
7292             );
7293             
7294             for(var i = 0; i < rows.length; i++) {
7295                 
7296                 var size_cls = w[j].split("-");
7297                 
7298                 if(!Number.isInteger(size_cls[1] * 1)) {
7299                     continue;
7300                 }
7301                 
7302                 if(!this.colModel.config[col_index][size_cls[0]]) {
7303                     continue;
7304                 }
7305                 
7306                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7307                     continue;
7308                 }
7309                 
7310                 rows[i].classList.replace(
7311                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7312                     "col-"+size_cls[0]+"-"+size_cls[1]
7313                 );
7314             }
7315             
7316             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7317         }
7318     }
7319 });
7320
7321  
7322
7323  /*
7324  * - LGPL
7325  *
7326  * table cell
7327  * 
7328  */
7329
7330 /**
7331  * @class Roo.bootstrap.TableCell
7332  * @extends Roo.bootstrap.Component
7333  * Bootstrap TableCell class
7334  * @cfg {String} html cell contain text
7335  * @cfg {String} cls cell class
7336  * @cfg {String} tag cell tag (td|th) default td
7337  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7338  * @cfg {String} align Aligns the content in a cell
7339  * @cfg {String} axis Categorizes cells
7340  * @cfg {String} bgcolor Specifies the background color of a cell
7341  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7342  * @cfg {Number} colspan Specifies the number of columns a cell should span
7343  * @cfg {String} headers Specifies one or more header cells a cell is related to
7344  * @cfg {Number} height Sets the height of a cell
7345  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7346  * @cfg {Number} rowspan Sets the number of rows a cell should span
7347  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7348  * @cfg {String} valign Vertical aligns the content in a cell
7349  * @cfg {Number} width Specifies the width of a cell
7350  * 
7351  * @constructor
7352  * Create a new TableCell
7353  * @param {Object} config The config object
7354  */
7355
7356 Roo.bootstrap.TableCell = function(config){
7357     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7358 };
7359
7360 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7361     
7362     html: false,
7363     cls: false,
7364     tag: false,
7365     abbr: false,
7366     align: false,
7367     axis: false,
7368     bgcolor: false,
7369     charoff: false,
7370     colspan: false,
7371     headers: false,
7372     height: false,
7373     nowrap: false,
7374     rowspan: false,
7375     scope: false,
7376     valign: false,
7377     width: false,
7378     
7379     
7380     getAutoCreate : function(){
7381         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7382         
7383         cfg = {
7384             tag: 'td'
7385         };
7386         
7387         if(this.tag){
7388             cfg.tag = this.tag;
7389         }
7390         
7391         if (this.html) {
7392             cfg.html=this.html
7393         }
7394         if (this.cls) {
7395             cfg.cls=this.cls
7396         }
7397         if (this.abbr) {
7398             cfg.abbr=this.abbr
7399         }
7400         if (this.align) {
7401             cfg.align=this.align
7402         }
7403         if (this.axis) {
7404             cfg.axis=this.axis
7405         }
7406         if (this.bgcolor) {
7407             cfg.bgcolor=this.bgcolor
7408         }
7409         if (this.charoff) {
7410             cfg.charoff=this.charoff
7411         }
7412         if (this.colspan) {
7413             cfg.colspan=this.colspan
7414         }
7415         if (this.headers) {
7416             cfg.headers=this.headers
7417         }
7418         if (this.height) {
7419             cfg.height=this.height
7420         }
7421         if (this.nowrap) {
7422             cfg.nowrap=this.nowrap
7423         }
7424         if (this.rowspan) {
7425             cfg.rowspan=this.rowspan
7426         }
7427         if (this.scope) {
7428             cfg.scope=this.scope
7429         }
7430         if (this.valign) {
7431             cfg.valign=this.valign
7432         }
7433         if (this.width) {
7434             cfg.width=this.width
7435         }
7436         
7437         
7438         return cfg;
7439     }
7440    
7441 });
7442
7443  
7444
7445  /*
7446  * - LGPL
7447  *
7448  * table row
7449  * 
7450  */
7451
7452 /**
7453  * @class Roo.bootstrap.TableRow
7454  * @extends Roo.bootstrap.Component
7455  * Bootstrap TableRow class
7456  * @cfg {String} cls row class
7457  * @cfg {String} align Aligns the content in a table row
7458  * @cfg {String} bgcolor Specifies a background color for a table row
7459  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7460  * @cfg {String} valign Vertical aligns the content in a table row
7461  * 
7462  * @constructor
7463  * Create a new TableRow
7464  * @param {Object} config The config object
7465  */
7466
7467 Roo.bootstrap.TableRow = function(config){
7468     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7469 };
7470
7471 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7472     
7473     cls: false,
7474     align: false,
7475     bgcolor: false,
7476     charoff: false,
7477     valign: false,
7478     
7479     getAutoCreate : function(){
7480         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7481         
7482         cfg = {
7483             tag: 'tr'
7484         };
7485             
7486         if(this.cls){
7487             cfg.cls = this.cls;
7488         }
7489         if(this.align){
7490             cfg.align = this.align;
7491         }
7492         if(this.bgcolor){
7493             cfg.bgcolor = this.bgcolor;
7494         }
7495         if(this.charoff){
7496             cfg.charoff = this.charoff;
7497         }
7498         if(this.valign){
7499             cfg.valign = this.valign;
7500         }
7501         
7502         return cfg;
7503     }
7504    
7505 });
7506
7507  
7508
7509  /*
7510  * - LGPL
7511  *
7512  * table body
7513  * 
7514  */
7515
7516 /**
7517  * @class Roo.bootstrap.TableBody
7518  * @extends Roo.bootstrap.Component
7519  * Bootstrap TableBody class
7520  * @cfg {String} cls element class
7521  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7522  * @cfg {String} align Aligns the content inside the element
7523  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7524  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7525  * 
7526  * @constructor
7527  * Create a new TableBody
7528  * @param {Object} config The config object
7529  */
7530
7531 Roo.bootstrap.TableBody = function(config){
7532     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7533 };
7534
7535 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7536     
7537     cls: false,
7538     tag: false,
7539     align: false,
7540     charoff: false,
7541     valign: false,
7542     
7543     getAutoCreate : function(){
7544         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7545         
7546         cfg = {
7547             tag: 'tbody'
7548         };
7549             
7550         if (this.cls) {
7551             cfg.cls=this.cls
7552         }
7553         if(this.tag){
7554             cfg.tag = this.tag;
7555         }
7556         
7557         if(this.align){
7558             cfg.align = this.align;
7559         }
7560         if(this.charoff){
7561             cfg.charoff = this.charoff;
7562         }
7563         if(this.valign){
7564             cfg.valign = this.valign;
7565         }
7566         
7567         return cfg;
7568     }
7569     
7570     
7571 //    initEvents : function()
7572 //    {
7573 //        
7574 //        if(!this.store){
7575 //            return;
7576 //        }
7577 //        
7578 //        this.store = Roo.factory(this.store, Roo.data);
7579 //        this.store.on('load', this.onLoad, this);
7580 //        
7581 //        this.store.load();
7582 //        
7583 //    },
7584 //    
7585 //    onLoad: function () 
7586 //    {   
7587 //        this.fireEvent('load', this);
7588 //    }
7589 //    
7590 //   
7591 });
7592
7593  
7594
7595  /*
7596  * Based on:
7597  * Ext JS Library 1.1.1
7598  * Copyright(c) 2006-2007, Ext JS, LLC.
7599  *
7600  * Originally Released Under LGPL - original licence link has changed is not relivant.
7601  *
7602  * Fork - LGPL
7603  * <script type="text/javascript">
7604  */
7605
7606 // as we use this in bootstrap.
7607 Roo.namespace('Roo.form');
7608  /**
7609  * @class Roo.form.Action
7610  * Internal Class used to handle form actions
7611  * @constructor
7612  * @param {Roo.form.BasicForm} el The form element or its id
7613  * @param {Object} config Configuration options
7614  */
7615
7616  
7617  
7618 // define the action interface
7619 Roo.form.Action = function(form, options){
7620     this.form = form;
7621     this.options = options || {};
7622 };
7623 /**
7624  * Client Validation Failed
7625  * @const 
7626  */
7627 Roo.form.Action.CLIENT_INVALID = 'client';
7628 /**
7629  * Server Validation Failed
7630  * @const 
7631  */
7632 Roo.form.Action.SERVER_INVALID = 'server';
7633  /**
7634  * Connect to Server Failed
7635  * @const 
7636  */
7637 Roo.form.Action.CONNECT_FAILURE = 'connect';
7638 /**
7639  * Reading Data from Server Failed
7640  * @const 
7641  */
7642 Roo.form.Action.LOAD_FAILURE = 'load';
7643
7644 Roo.form.Action.prototype = {
7645     type : 'default',
7646     failureType : undefined,
7647     response : undefined,
7648     result : undefined,
7649
7650     // interface method
7651     run : function(options){
7652
7653     },
7654
7655     // interface method
7656     success : function(response){
7657
7658     },
7659
7660     // interface method
7661     handleResponse : function(response){
7662
7663     },
7664
7665     // default connection failure
7666     failure : function(response){
7667         
7668         this.response = response;
7669         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7670         this.form.afterAction(this, false);
7671     },
7672
7673     processResponse : function(response){
7674         this.response = response;
7675         if(!response.responseText){
7676             return true;
7677         }
7678         this.result = this.handleResponse(response);
7679         return this.result;
7680     },
7681
7682     // utility functions used internally
7683     getUrl : function(appendParams){
7684         var url = this.options.url || this.form.url || this.form.el.dom.action;
7685         if(appendParams){
7686             var p = this.getParams();
7687             if(p){
7688                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7689             }
7690         }
7691         return url;
7692     },
7693
7694     getMethod : function(){
7695         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7696     },
7697
7698     getParams : function(){
7699         var bp = this.form.baseParams;
7700         var p = this.options.params;
7701         if(p){
7702             if(typeof p == "object"){
7703                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7704             }else if(typeof p == 'string' && bp){
7705                 p += '&' + Roo.urlEncode(bp);
7706             }
7707         }else if(bp){
7708             p = Roo.urlEncode(bp);
7709         }
7710         return p;
7711     },
7712
7713     createCallback : function(){
7714         return {
7715             success: this.success,
7716             failure: this.failure,
7717             scope: this,
7718             timeout: (this.form.timeout*1000),
7719             upload: this.form.fileUpload ? this.success : undefined
7720         };
7721     }
7722 };
7723
7724 Roo.form.Action.Submit = function(form, options){
7725     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7726 };
7727
7728 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7729     type : 'submit',
7730
7731     haveProgress : false,
7732     uploadComplete : false,
7733     
7734     // uploadProgress indicator.
7735     uploadProgress : function()
7736     {
7737         if (!this.form.progressUrl) {
7738             return;
7739         }
7740         
7741         if (!this.haveProgress) {
7742             Roo.MessageBox.progress("Uploading", "Uploading");
7743         }
7744         if (this.uploadComplete) {
7745            Roo.MessageBox.hide();
7746            return;
7747         }
7748         
7749         this.haveProgress = true;
7750    
7751         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7752         
7753         var c = new Roo.data.Connection();
7754         c.request({
7755             url : this.form.progressUrl,
7756             params: {
7757                 id : uid
7758             },
7759             method: 'GET',
7760             success : function(req){
7761                //console.log(data);
7762                 var rdata = false;
7763                 var edata;
7764                 try  {
7765                    rdata = Roo.decode(req.responseText)
7766                 } catch (e) {
7767                     Roo.log("Invalid data from server..");
7768                     Roo.log(edata);
7769                     return;
7770                 }
7771                 if (!rdata || !rdata.success) {
7772                     Roo.log(rdata);
7773                     Roo.MessageBox.alert(Roo.encode(rdata));
7774                     return;
7775                 }
7776                 var data = rdata.data;
7777                 
7778                 if (this.uploadComplete) {
7779                    Roo.MessageBox.hide();
7780                    return;
7781                 }
7782                    
7783                 if (data){
7784                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7785                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7786                     );
7787                 }
7788                 this.uploadProgress.defer(2000,this);
7789             },
7790        
7791             failure: function(data) {
7792                 Roo.log('progress url failed ');
7793                 Roo.log(data);
7794             },
7795             scope : this
7796         });
7797            
7798     },
7799     
7800     
7801     run : function()
7802     {
7803         // run get Values on the form, so it syncs any secondary forms.
7804         this.form.getValues();
7805         
7806         var o = this.options;
7807         var method = this.getMethod();
7808         var isPost = method == 'POST';
7809         if(o.clientValidation === false || this.form.isValid()){
7810             
7811             if (this.form.progressUrl) {
7812                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7813                     (new Date() * 1) + '' + Math.random());
7814                     
7815             } 
7816             
7817             
7818             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7819                 form:this.form.el.dom,
7820                 url:this.getUrl(!isPost),
7821                 method: method,
7822                 params:isPost ? this.getParams() : null,
7823                 isUpload: this.form.fileUpload
7824             }));
7825             
7826             this.uploadProgress();
7827
7828         }else if (o.clientValidation !== false){ // client validation failed
7829             this.failureType = Roo.form.Action.CLIENT_INVALID;
7830             this.form.afterAction(this, false);
7831         }
7832     },
7833
7834     success : function(response)
7835     {
7836         this.uploadComplete= true;
7837         if (this.haveProgress) {
7838             Roo.MessageBox.hide();
7839         }
7840         
7841         
7842         var result = this.processResponse(response);
7843         if(result === true || result.success){
7844             this.form.afterAction(this, true);
7845             return;
7846         }
7847         if(result.errors){
7848             this.form.markInvalid(result.errors);
7849             this.failureType = Roo.form.Action.SERVER_INVALID;
7850         }
7851         this.form.afterAction(this, false);
7852     },
7853     failure : function(response)
7854     {
7855         this.uploadComplete= true;
7856         if (this.haveProgress) {
7857             Roo.MessageBox.hide();
7858         }
7859         
7860         this.response = response;
7861         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7862         this.form.afterAction(this, false);
7863     },
7864     
7865     handleResponse : function(response){
7866         if(this.form.errorReader){
7867             var rs = this.form.errorReader.read(response);
7868             var errors = [];
7869             if(rs.records){
7870                 for(var i = 0, len = rs.records.length; i < len; i++) {
7871                     var r = rs.records[i];
7872                     errors[i] = r.data;
7873                 }
7874             }
7875             if(errors.length < 1){
7876                 errors = null;
7877             }
7878             return {
7879                 success : rs.success,
7880                 errors : errors
7881             };
7882         }
7883         var ret = false;
7884         try {
7885             ret = Roo.decode(response.responseText);
7886         } catch (e) {
7887             ret = {
7888                 success: false,
7889                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7890                 errors : []
7891             };
7892         }
7893         return ret;
7894         
7895     }
7896 });
7897
7898
7899 Roo.form.Action.Load = function(form, options){
7900     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7901     this.reader = this.form.reader;
7902 };
7903
7904 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7905     type : 'load',
7906
7907     run : function(){
7908         
7909         Roo.Ajax.request(Roo.apply(
7910                 this.createCallback(), {
7911                     method:this.getMethod(),
7912                     url:this.getUrl(false),
7913                     params:this.getParams()
7914         }));
7915     },
7916
7917     success : function(response){
7918         
7919         var result = this.processResponse(response);
7920         if(result === true || !result.success || !result.data){
7921             this.failureType = Roo.form.Action.LOAD_FAILURE;
7922             this.form.afterAction(this, false);
7923             return;
7924         }
7925         this.form.clearInvalid();
7926         this.form.setValues(result.data);
7927         this.form.afterAction(this, true);
7928     },
7929
7930     handleResponse : function(response){
7931         if(this.form.reader){
7932             var rs = this.form.reader.read(response);
7933             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7934             return {
7935                 success : rs.success,
7936                 data : data
7937             };
7938         }
7939         return Roo.decode(response.responseText);
7940     }
7941 });
7942
7943 Roo.form.Action.ACTION_TYPES = {
7944     'load' : Roo.form.Action.Load,
7945     'submit' : Roo.form.Action.Submit
7946 };/*
7947  * - LGPL
7948  *
7949  * form
7950  *
7951  */
7952
7953 /**
7954  * @class Roo.bootstrap.Form
7955  * @extends Roo.bootstrap.Component
7956  * Bootstrap Form class
7957  * @cfg {String} method  GET | POST (default POST)
7958  * @cfg {String} labelAlign top | left (default top)
7959  * @cfg {String} align left  | right - for navbars
7960  * @cfg {Boolean} loadMask load mask when submit (default true)
7961
7962  *
7963  * @constructor
7964  * Create a new Form
7965  * @param {Object} config The config object
7966  */
7967
7968
7969 Roo.bootstrap.Form = function(config){
7970     
7971     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7972     
7973     Roo.bootstrap.Form.popover.apply();
7974     
7975     this.addEvents({
7976         /**
7977          * @event clientvalidation
7978          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7979          * @param {Form} this
7980          * @param {Boolean} valid true if the form has passed client-side validation
7981          */
7982         clientvalidation: true,
7983         /**
7984          * @event beforeaction
7985          * Fires before any action is performed. Return false to cancel the action.
7986          * @param {Form} this
7987          * @param {Action} action The action to be performed
7988          */
7989         beforeaction: true,
7990         /**
7991          * @event actionfailed
7992          * Fires when an action fails.
7993          * @param {Form} this
7994          * @param {Action} action The action that failed
7995          */
7996         actionfailed : true,
7997         /**
7998          * @event actioncomplete
7999          * Fires when an action is completed.
8000          * @param {Form} this
8001          * @param {Action} action The action that completed
8002          */
8003         actioncomplete : true
8004     });
8005 };
8006
8007 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8008
8009      /**
8010      * @cfg {String} method
8011      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8012      */
8013     method : 'POST',
8014     /**
8015      * @cfg {String} url
8016      * The URL to use for form actions if one isn't supplied in the action options.
8017      */
8018     /**
8019      * @cfg {Boolean} fileUpload
8020      * Set to true if this form is a file upload.
8021      */
8022
8023     /**
8024      * @cfg {Object} baseParams
8025      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8026      */
8027
8028     /**
8029      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8030      */
8031     timeout: 30,
8032     /**
8033      * @cfg {Sting} align (left|right) for navbar forms
8034      */
8035     align : 'left',
8036
8037     // private
8038     activeAction : null,
8039
8040     /**
8041      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8042      * element by passing it or its id or mask the form itself by passing in true.
8043      * @type Mixed
8044      */
8045     waitMsgTarget : false,
8046
8047     loadMask : true,
8048     
8049     /**
8050      * @cfg {Boolean} errorMask (true|false) default false
8051      */
8052     errorMask : false,
8053     
8054     /**
8055      * @cfg {Number} maskOffset Default 100
8056      */
8057     maskOffset : 100,
8058     
8059     /**
8060      * @cfg {Boolean} maskBody
8061      */
8062     maskBody : false,
8063
8064     getAutoCreate : function(){
8065
8066         var cfg = {
8067             tag: 'form',
8068             method : this.method || 'POST',
8069             id : this.id || Roo.id(),
8070             cls : ''
8071         };
8072         if (this.parent().xtype.match(/^Nav/)) {
8073             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8074
8075         }
8076
8077         if (this.labelAlign == 'left' ) {
8078             cfg.cls += ' form-horizontal';
8079         }
8080
8081
8082         return cfg;
8083     },
8084     initEvents : function()
8085     {
8086         this.el.on('submit', this.onSubmit, this);
8087         // this was added as random key presses on the form where triggering form submit.
8088         this.el.on('keypress', function(e) {
8089             if (e.getCharCode() != 13) {
8090                 return true;
8091             }
8092             // we might need to allow it for textareas.. and some other items.
8093             // check e.getTarget().
8094
8095             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8096                 return true;
8097             }
8098
8099             Roo.log("keypress blocked");
8100
8101             e.preventDefault();
8102             return false;
8103         });
8104         
8105     },
8106     // private
8107     onSubmit : function(e){
8108         e.stopEvent();
8109     },
8110
8111      /**
8112      * Returns true if client-side validation on the form is successful.
8113      * @return Boolean
8114      */
8115     isValid : function(){
8116         var items = this.getItems();
8117         var valid = true;
8118         var target = false;
8119         
8120         items.each(function(f){
8121             
8122             if(f.validate()){
8123                 return;
8124             }
8125             
8126             Roo.log('invalid field: ' + f.name);
8127             
8128             valid = false;
8129
8130             if(!target && f.el.isVisible(true)){
8131                 target = f;
8132             }
8133            
8134         });
8135         
8136         if(this.errorMask && !valid){
8137             Roo.bootstrap.Form.popover.mask(this, target);
8138         }
8139         
8140         return valid;
8141     },
8142     
8143     /**
8144      * Returns true if any fields in this form have changed since their original load.
8145      * @return Boolean
8146      */
8147     isDirty : function(){
8148         var dirty = false;
8149         var items = this.getItems();
8150         items.each(function(f){
8151            if(f.isDirty()){
8152                dirty = true;
8153                return false;
8154            }
8155            return true;
8156         });
8157         return dirty;
8158     },
8159      /**
8160      * Performs a predefined action (submit or load) or custom actions you define on this form.
8161      * @param {String} actionName The name of the action type
8162      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8163      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8164      * accept other config options):
8165      * <pre>
8166 Property          Type             Description
8167 ----------------  ---------------  ----------------------------------------------------------------------------------
8168 url               String           The url for the action (defaults to the form's url)
8169 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8170 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8171 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8172                                    validate the form on the client (defaults to false)
8173      * </pre>
8174      * @return {BasicForm} this
8175      */
8176     doAction : function(action, options){
8177         if(typeof action == 'string'){
8178             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8179         }
8180         if(this.fireEvent('beforeaction', this, action) !== false){
8181             this.beforeAction(action);
8182             action.run.defer(100, action);
8183         }
8184         return this;
8185     },
8186
8187     // private
8188     beforeAction : function(action){
8189         var o = action.options;
8190         
8191         if(this.loadMask){
8192             
8193             if(this.maskBody){
8194                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8195             } else {
8196                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8197             }
8198         }
8199         // not really supported yet.. ??
8200
8201         //if(this.waitMsgTarget === true){
8202         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8203         //}else if(this.waitMsgTarget){
8204         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8205         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8206         //}else {
8207         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8208        // }
8209
8210     },
8211
8212     // private
8213     afterAction : function(action, success){
8214         this.activeAction = null;
8215         var o = action.options;
8216
8217         if(this.loadMask){
8218             
8219             if(this.maskBody){
8220                 Roo.get(document.body).unmask();
8221             } else {
8222                 this.el.unmask();
8223             }
8224         }
8225         
8226         //if(this.waitMsgTarget === true){
8227 //            this.el.unmask();
8228         //}else if(this.waitMsgTarget){
8229         //    this.waitMsgTarget.unmask();
8230         //}else{
8231         //    Roo.MessageBox.updateProgress(1);
8232         //    Roo.MessageBox.hide();
8233        // }
8234         //
8235         if(success){
8236             if(o.reset){
8237                 this.reset();
8238             }
8239             Roo.callback(o.success, o.scope, [this, action]);
8240             this.fireEvent('actioncomplete', this, action);
8241
8242         }else{
8243
8244             // failure condition..
8245             // we have a scenario where updates need confirming.
8246             // eg. if a locking scenario exists..
8247             // we look for { errors : { needs_confirm : true }} in the response.
8248             if (
8249                 (typeof(action.result) != 'undefined')  &&
8250                 (typeof(action.result.errors) != 'undefined')  &&
8251                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8252            ){
8253                 var _t = this;
8254                 Roo.log("not supported yet");
8255                  /*
8256
8257                 Roo.MessageBox.confirm(
8258                     "Change requires confirmation",
8259                     action.result.errorMsg,
8260                     function(r) {
8261                         if (r != 'yes') {
8262                             return;
8263                         }
8264                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8265                     }
8266
8267                 );
8268                 */
8269
8270
8271                 return;
8272             }
8273
8274             Roo.callback(o.failure, o.scope, [this, action]);
8275             // show an error message if no failed handler is set..
8276             if (!this.hasListener('actionfailed')) {
8277                 Roo.log("need to add dialog support");
8278                 /*
8279                 Roo.MessageBox.alert("Error",
8280                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8281                         action.result.errorMsg :
8282                         "Saving Failed, please check your entries or try again"
8283                 );
8284                 */
8285             }
8286
8287             this.fireEvent('actionfailed', this, action);
8288         }
8289
8290     },
8291     /**
8292      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8293      * @param {String} id The value to search for
8294      * @return Field
8295      */
8296     findField : function(id){
8297         var items = this.getItems();
8298         var field = items.get(id);
8299         if(!field){
8300              items.each(function(f){
8301                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8302                     field = f;
8303                     return false;
8304                 }
8305                 return true;
8306             });
8307         }
8308         return field || null;
8309     },
8310      /**
8311      * Mark fields in this form invalid in bulk.
8312      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8313      * @return {BasicForm} this
8314      */
8315     markInvalid : function(errors){
8316         if(errors instanceof Array){
8317             for(var i = 0, len = errors.length; i < len; i++){
8318                 var fieldError = errors[i];
8319                 var f = this.findField(fieldError.id);
8320                 if(f){
8321                     f.markInvalid(fieldError.msg);
8322                 }
8323             }
8324         }else{
8325             var field, id;
8326             for(id in errors){
8327                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8328                     field.markInvalid(errors[id]);
8329                 }
8330             }
8331         }
8332         //Roo.each(this.childForms || [], function (f) {
8333         //    f.markInvalid(errors);
8334         //});
8335
8336         return this;
8337     },
8338
8339     /**
8340      * Set values for fields in this form in bulk.
8341      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8342      * @return {BasicForm} this
8343      */
8344     setValues : function(values){
8345         if(values instanceof Array){ // array of objects
8346             for(var i = 0, len = values.length; i < len; i++){
8347                 var v = values[i];
8348                 var f = this.findField(v.id);
8349                 if(f){
8350                     f.setValue(v.value);
8351                     if(this.trackResetOnLoad){
8352                         f.originalValue = f.getValue();
8353                     }
8354                 }
8355             }
8356         }else{ // object hash
8357             var field, id;
8358             for(id in values){
8359                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8360
8361                     if (field.setFromData &&
8362                         field.valueField &&
8363                         field.displayField &&
8364                         // combos' with local stores can
8365                         // be queried via setValue()
8366                         // to set their value..
8367                         (field.store && !field.store.isLocal)
8368                         ) {
8369                         // it's a combo
8370                         var sd = { };
8371                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8372                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8373                         field.setFromData(sd);
8374
8375                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8376                         
8377                         field.setFromData(values);
8378                         
8379                     } else {
8380                         field.setValue(values[id]);
8381                     }
8382
8383
8384                     if(this.trackResetOnLoad){
8385                         field.originalValue = field.getValue();
8386                     }
8387                 }
8388             }
8389         }
8390
8391         //Roo.each(this.childForms || [], function (f) {
8392         //    f.setValues(values);
8393         //});
8394
8395         return this;
8396     },
8397
8398     /**
8399      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8400      * they are returned as an array.
8401      * @param {Boolean} asString
8402      * @return {Object}
8403      */
8404     getValues : function(asString){
8405         //if (this.childForms) {
8406             // copy values from the child forms
8407         //    Roo.each(this.childForms, function (f) {
8408         //        this.setValues(f.getValues());
8409         //    }, this);
8410         //}
8411
8412
8413
8414         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8415         if(asString === true){
8416             return fs;
8417         }
8418         return Roo.urlDecode(fs);
8419     },
8420
8421     /**
8422      * Returns the fields in this form as an object with key/value pairs.
8423      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8424      * @return {Object}
8425      */
8426     getFieldValues : function(with_hidden)
8427     {
8428         var items = this.getItems();
8429         var ret = {};
8430         items.each(function(f){
8431             
8432             if (!f.getName()) {
8433                 return;
8434             }
8435             
8436             var v = f.getValue();
8437             
8438             if (f.inputType =='radio') {
8439                 if (typeof(ret[f.getName()]) == 'undefined') {
8440                     ret[f.getName()] = ''; // empty..
8441                 }
8442
8443                 if (!f.el.dom.checked) {
8444                     return;
8445
8446                 }
8447                 v = f.el.dom.value;
8448
8449             }
8450             
8451             if(f.xtype == 'MoneyField'){
8452                 ret[f.currencyName] = f.getCurrency();
8453             }
8454
8455             // not sure if this supported any more..
8456             if ((typeof(v) == 'object') && f.getRawValue) {
8457                 v = f.getRawValue() ; // dates..
8458             }
8459             // combo boxes where name != hiddenName...
8460             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8461                 ret[f.name] = f.getRawValue();
8462             }
8463             ret[f.getName()] = v;
8464         });
8465
8466         return ret;
8467     },
8468
8469     /**
8470      * Clears all invalid messages in this form.
8471      * @return {BasicForm} this
8472      */
8473     clearInvalid : function(){
8474         var items = this.getItems();
8475
8476         items.each(function(f){
8477            f.clearInvalid();
8478         });
8479
8480         return this;
8481     },
8482
8483     /**
8484      * Resets this form.
8485      * @return {BasicForm} this
8486      */
8487     reset : function(){
8488         var items = this.getItems();
8489         items.each(function(f){
8490             f.reset();
8491         });
8492
8493         Roo.each(this.childForms || [], function (f) {
8494             f.reset();
8495         });
8496
8497
8498         return this;
8499     },
8500     
8501     getItems : function()
8502     {
8503         var r=new Roo.util.MixedCollection(false, function(o){
8504             return o.id || (o.id = Roo.id());
8505         });
8506         var iter = function(el) {
8507             if (el.inputEl) {
8508                 r.add(el);
8509             }
8510             if (!el.items) {
8511                 return;
8512             }
8513             Roo.each(el.items,function(e) {
8514                 iter(e);
8515             });
8516         };
8517
8518         iter(this);
8519         return r;
8520     },
8521     
8522     hideFields : function(items)
8523     {
8524         Roo.each(items, function(i){
8525             
8526             var f = this.findField(i);
8527             
8528             if(!f){
8529                 return;
8530             }
8531             
8532             f.hide();
8533             
8534         }, this);
8535     },
8536     
8537     showFields : function(items)
8538     {
8539         Roo.each(items, function(i){
8540             
8541             var f = this.findField(i);
8542             
8543             if(!f){
8544                 return;
8545             }
8546             
8547             f.show();
8548             
8549         }, this);
8550     }
8551
8552 });
8553
8554 Roo.apply(Roo.bootstrap.Form, {
8555     
8556     popover : {
8557         
8558         padding : 5,
8559         
8560         isApplied : false,
8561         
8562         isMasked : false,
8563         
8564         form : false,
8565         
8566         target : false,
8567         
8568         toolTip : false,
8569         
8570         intervalID : false,
8571         
8572         maskEl : false,
8573         
8574         apply : function()
8575         {
8576             if(this.isApplied){
8577                 return;
8578             }
8579             
8580             this.maskEl = {
8581                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8582                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8583                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8584                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8585             };
8586             
8587             this.maskEl.top.enableDisplayMode("block");
8588             this.maskEl.left.enableDisplayMode("block");
8589             this.maskEl.bottom.enableDisplayMode("block");
8590             this.maskEl.right.enableDisplayMode("block");
8591             
8592             this.toolTip = new Roo.bootstrap.Tooltip({
8593                 cls : 'roo-form-error-popover',
8594                 alignment : {
8595                     'left' : ['r-l', [-2,0], 'right'],
8596                     'right' : ['l-r', [2,0], 'left'],
8597                     'bottom' : ['tl-bl', [0,2], 'top'],
8598                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8599                 }
8600             });
8601             
8602             this.toolTip.render(Roo.get(document.body));
8603
8604             this.toolTip.el.enableDisplayMode("block");
8605             
8606             Roo.get(document.body).on('click', function(){
8607                 this.unmask();
8608             }, this);
8609             
8610             Roo.get(document.body).on('touchstart', function(){
8611                 this.unmask();
8612             }, this);
8613             
8614             this.isApplied = true
8615         },
8616         
8617         mask : function(form, target)
8618         {
8619             this.form = form;
8620             
8621             this.target = target;
8622             
8623             if(!this.form.errorMask || !target.el){
8624                 return;
8625             }
8626             
8627             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8628             
8629             Roo.log(scrollable);
8630             
8631             var ot = this.target.el.calcOffsetsTo(scrollable);
8632             
8633             var scrollTo = ot[1] - this.form.maskOffset;
8634             
8635             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8636             
8637             scrollable.scrollTo('top', scrollTo);
8638             
8639             var box = this.target.el.getBox();
8640             Roo.log(box);
8641             var zIndex = Roo.bootstrap.Modal.zIndex++;
8642
8643             
8644             this.maskEl.top.setStyle('position', 'absolute');
8645             this.maskEl.top.setStyle('z-index', zIndex);
8646             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8647             this.maskEl.top.setLeft(0);
8648             this.maskEl.top.setTop(0);
8649             this.maskEl.top.show();
8650             
8651             this.maskEl.left.setStyle('position', 'absolute');
8652             this.maskEl.left.setStyle('z-index', zIndex);
8653             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8654             this.maskEl.left.setLeft(0);
8655             this.maskEl.left.setTop(box.y - this.padding);
8656             this.maskEl.left.show();
8657
8658             this.maskEl.bottom.setStyle('position', 'absolute');
8659             this.maskEl.bottom.setStyle('z-index', zIndex);
8660             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8661             this.maskEl.bottom.setLeft(0);
8662             this.maskEl.bottom.setTop(box.bottom + this.padding);
8663             this.maskEl.bottom.show();
8664
8665             this.maskEl.right.setStyle('position', 'absolute');
8666             this.maskEl.right.setStyle('z-index', zIndex);
8667             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8668             this.maskEl.right.setLeft(box.right + this.padding);
8669             this.maskEl.right.setTop(box.y - this.padding);
8670             this.maskEl.right.show();
8671
8672             this.toolTip.bindEl = this.target.el;
8673
8674             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8675
8676             var tip = this.target.blankText;
8677
8678             if(this.target.getValue() !== '' ) {
8679                 
8680                 if (this.target.invalidText.length) {
8681                     tip = this.target.invalidText;
8682                 } else if (this.target.regexText.length){
8683                     tip = this.target.regexText;
8684                 }
8685             }
8686
8687             this.toolTip.show(tip);
8688
8689             this.intervalID = window.setInterval(function() {
8690                 Roo.bootstrap.Form.popover.unmask();
8691             }, 10000);
8692
8693             window.onwheel = function(){ return false;};
8694             
8695             (function(){ this.isMasked = true; }).defer(500, this);
8696             
8697         },
8698         
8699         unmask : function()
8700         {
8701             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8702                 return;
8703             }
8704             
8705             this.maskEl.top.setStyle('position', 'absolute');
8706             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8707             this.maskEl.top.hide();
8708
8709             this.maskEl.left.setStyle('position', 'absolute');
8710             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8711             this.maskEl.left.hide();
8712
8713             this.maskEl.bottom.setStyle('position', 'absolute');
8714             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8715             this.maskEl.bottom.hide();
8716
8717             this.maskEl.right.setStyle('position', 'absolute');
8718             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8719             this.maskEl.right.hide();
8720             
8721             this.toolTip.hide();
8722             
8723             this.toolTip.el.hide();
8724             
8725             window.onwheel = function(){ return true;};
8726             
8727             if(this.intervalID){
8728                 window.clearInterval(this.intervalID);
8729                 this.intervalID = false;
8730             }
8731             
8732             this.isMasked = false;
8733             
8734         }
8735         
8736     }
8737     
8738 });
8739
8740 /*
8741  * Based on:
8742  * Ext JS Library 1.1.1
8743  * Copyright(c) 2006-2007, Ext JS, LLC.
8744  *
8745  * Originally Released Under LGPL - original licence link has changed is not relivant.
8746  *
8747  * Fork - LGPL
8748  * <script type="text/javascript">
8749  */
8750 /**
8751  * @class Roo.form.VTypes
8752  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8753  * @singleton
8754  */
8755 Roo.form.VTypes = function(){
8756     // closure these in so they are only created once.
8757     var alpha = /^[a-zA-Z_]+$/;
8758     var alphanum = /^[a-zA-Z0-9_]+$/;
8759     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8760     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8761
8762     // All these messages and functions are configurable
8763     return {
8764         /**
8765          * The function used to validate email addresses
8766          * @param {String} value The email address
8767          */
8768         'email' : function(v){
8769             return email.test(v);
8770         },
8771         /**
8772          * The error text to display when the email validation function returns false
8773          * @type String
8774          */
8775         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8776         /**
8777          * The keystroke filter mask to be applied on email input
8778          * @type RegExp
8779          */
8780         'emailMask' : /[a-z0-9_\.\-@]/i,
8781
8782         /**
8783          * The function used to validate URLs
8784          * @param {String} value The URL
8785          */
8786         'url' : function(v){
8787             return url.test(v);
8788         },
8789         /**
8790          * The error text to display when the url validation function returns false
8791          * @type String
8792          */
8793         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8794         
8795         /**
8796          * The function used to validate alpha values
8797          * @param {String} value The value
8798          */
8799         'alpha' : function(v){
8800             return alpha.test(v);
8801         },
8802         /**
8803          * The error text to display when the alpha validation function returns false
8804          * @type String
8805          */
8806         'alphaText' : 'This field should only contain letters and _',
8807         /**
8808          * The keystroke filter mask to be applied on alpha input
8809          * @type RegExp
8810          */
8811         'alphaMask' : /[a-z_]/i,
8812
8813         /**
8814          * The function used to validate alphanumeric values
8815          * @param {String} value The value
8816          */
8817         'alphanum' : function(v){
8818             return alphanum.test(v);
8819         },
8820         /**
8821          * The error text to display when the alphanumeric validation function returns false
8822          * @type String
8823          */
8824         'alphanumText' : 'This field should only contain letters, numbers and _',
8825         /**
8826          * The keystroke filter mask to be applied on alphanumeric input
8827          * @type RegExp
8828          */
8829         'alphanumMask' : /[a-z0-9_]/i
8830     };
8831 }();/*
8832  * - LGPL
8833  *
8834  * Input
8835  * 
8836  */
8837
8838 /**
8839  * @class Roo.bootstrap.Input
8840  * @extends Roo.bootstrap.Component
8841  * Bootstrap Input class
8842  * @cfg {Boolean} disabled is it disabled
8843  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8844  * @cfg {String} name name of the input
8845  * @cfg {string} fieldLabel - the label associated
8846  * @cfg {string} placeholder - placeholder to put in text.
8847  * @cfg {string}  before - input group add on before
8848  * @cfg {string} after - input group add on after
8849  * @cfg {string} size - (lg|sm) or leave empty..
8850  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8851  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8852  * @cfg {Number} md colspan out of 12 for computer-sized screens
8853  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8854  * @cfg {string} value default value of the input
8855  * @cfg {Number} labelWidth set the width of label 
8856  * @cfg {Number} labellg set the width of label (1-12)
8857  * @cfg {Number} labelmd set the width of label (1-12)
8858  * @cfg {Number} labelsm set the width of label (1-12)
8859  * @cfg {Number} labelxs set the width of label (1-12)
8860  * @cfg {String} labelAlign (top|left)
8861  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8862  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8863  * @cfg {String} indicatorpos (left|right) default left
8864  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8865  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8866
8867  * @cfg {String} align (left|center|right) Default left
8868  * @cfg {Boolean} forceFeedback (true|false) Default false
8869  * 
8870  * @constructor
8871  * Create a new Input
8872  * @param {Object} config The config object
8873  */
8874
8875 Roo.bootstrap.Input = function(config){
8876     
8877     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8878     
8879     this.addEvents({
8880         /**
8881          * @event focus
8882          * Fires when this field receives input focus.
8883          * @param {Roo.form.Field} this
8884          */
8885         focus : true,
8886         /**
8887          * @event blur
8888          * Fires when this field loses input focus.
8889          * @param {Roo.form.Field} this
8890          */
8891         blur : true,
8892         /**
8893          * @event specialkey
8894          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8895          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8896          * @param {Roo.form.Field} this
8897          * @param {Roo.EventObject} e The event object
8898          */
8899         specialkey : true,
8900         /**
8901          * @event change
8902          * Fires just before the field blurs if the field value has changed.
8903          * @param {Roo.form.Field} this
8904          * @param {Mixed} newValue The new value
8905          * @param {Mixed} oldValue The original value
8906          */
8907         change : true,
8908         /**
8909          * @event invalid
8910          * Fires after the field has been marked as invalid.
8911          * @param {Roo.form.Field} this
8912          * @param {String} msg The validation message
8913          */
8914         invalid : true,
8915         /**
8916          * @event valid
8917          * Fires after the field has been validated with no errors.
8918          * @param {Roo.form.Field} this
8919          */
8920         valid : true,
8921          /**
8922          * @event keyup
8923          * Fires after the key up
8924          * @param {Roo.form.Field} this
8925          * @param {Roo.EventObject}  e The event Object
8926          */
8927         keyup : true
8928     });
8929 };
8930
8931 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8932      /**
8933      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8934       automatic validation (defaults to "keyup").
8935      */
8936     validationEvent : "keyup",
8937      /**
8938      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8939      */
8940     validateOnBlur : true,
8941     /**
8942      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8943      */
8944     validationDelay : 250,
8945      /**
8946      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8947      */
8948     focusClass : "x-form-focus",  // not needed???
8949     
8950        
8951     /**
8952      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8953      */
8954     invalidClass : "has-warning",
8955     
8956     /**
8957      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8958      */
8959     validClass : "has-success",
8960     
8961     /**
8962      * @cfg {Boolean} hasFeedback (true|false) default true
8963      */
8964     hasFeedback : true,
8965     
8966     /**
8967      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8968      */
8969     invalidFeedbackClass : "glyphicon-warning-sign",
8970     
8971     /**
8972      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8973      */
8974     validFeedbackClass : "glyphicon-ok",
8975     
8976     /**
8977      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8978      */
8979     selectOnFocus : false,
8980     
8981      /**
8982      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8983      */
8984     maskRe : null,
8985        /**
8986      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8987      */
8988     vtype : null,
8989     
8990       /**
8991      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8992      */
8993     disableKeyFilter : false,
8994     
8995        /**
8996      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8997      */
8998     disabled : false,
8999      /**
9000      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9001      */
9002     allowBlank : true,
9003     /**
9004      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9005      */
9006     blankText : "Please complete this mandatory field",
9007     
9008      /**
9009      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9010      */
9011     minLength : 0,
9012     /**
9013      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9014      */
9015     maxLength : Number.MAX_VALUE,
9016     /**
9017      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9018      */
9019     minLengthText : "The minimum length for this field is {0}",
9020     /**
9021      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9022      */
9023     maxLengthText : "The maximum length for this field is {0}",
9024   
9025     
9026     /**
9027      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9028      * If available, this function will be called only after the basic validators all return true, and will be passed the
9029      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9030      */
9031     validator : null,
9032     /**
9033      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9034      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9035      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9036      */
9037     regex : null,
9038     /**
9039      * @cfg {String} regexText -- Depricated - use Invalid Text
9040      */
9041     regexText : "",
9042     
9043     /**
9044      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9045      */
9046     invalidText : "",
9047     
9048     
9049     
9050     autocomplete: false,
9051     
9052     
9053     fieldLabel : '',
9054     inputType : 'text',
9055     
9056     name : false,
9057     placeholder: false,
9058     before : false,
9059     after : false,
9060     size : false,
9061     hasFocus : false,
9062     preventMark: false,
9063     isFormField : true,
9064     value : '',
9065     labelWidth : 2,
9066     labelAlign : false,
9067     readOnly : false,
9068     align : false,
9069     formatedValue : false,
9070     forceFeedback : false,
9071     
9072     indicatorpos : 'left',
9073     
9074     labellg : 0,
9075     labelmd : 0,
9076     labelsm : 0,
9077     labelxs : 0,
9078     
9079     capture : '',
9080     accept : '',
9081     
9082     parentLabelAlign : function()
9083     {
9084         var parent = this;
9085         while (parent.parent()) {
9086             parent = parent.parent();
9087             if (typeof(parent.labelAlign) !='undefined') {
9088                 return parent.labelAlign;
9089             }
9090         }
9091         return 'left';
9092         
9093     },
9094     
9095     getAutoCreate : function()
9096     {
9097         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9098         
9099         var id = Roo.id();
9100         
9101         var cfg = {};
9102         
9103         if(this.inputType != 'hidden'){
9104             cfg.cls = 'form-group' //input-group
9105         }
9106         
9107         var input =  {
9108             tag: 'input',
9109             id : id,
9110             type : this.inputType,
9111             value : this.value,
9112             cls : 'form-control',
9113             placeholder : this.placeholder || '',
9114             autocomplete : this.autocomplete || 'new-password'
9115         };
9116         
9117         if(this.capture.length){
9118             input.capture = this.capture;
9119         }
9120         
9121         if(this.accept.length){
9122             input.accept = this.accept + "/*";
9123         }
9124         
9125         if(this.align){
9126             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9127         }
9128         
9129         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9130             input.maxLength = this.maxLength;
9131         }
9132         
9133         if (this.disabled) {
9134             input.disabled=true;
9135         }
9136         
9137         if (this.readOnly) {
9138             input.readonly=true;
9139         }
9140         
9141         if (this.name) {
9142             input.name = this.name;
9143         }
9144         
9145         if (this.size) {
9146             input.cls += ' input-' + this.size;
9147         }
9148         
9149         var settings=this;
9150         ['xs','sm','md','lg'].map(function(size){
9151             if (settings[size]) {
9152                 cfg.cls += ' col-' + size + '-' + settings[size];
9153             }
9154         });
9155         
9156         var inputblock = input;
9157         
9158         var feedback = {
9159             tag: 'span',
9160             cls: 'glyphicon form-control-feedback'
9161         };
9162             
9163         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9164             
9165             inputblock = {
9166                 cls : 'has-feedback',
9167                 cn :  [
9168                     input,
9169                     feedback
9170                 ] 
9171             };  
9172         }
9173         
9174         if (this.before || this.after) {
9175             
9176             inputblock = {
9177                 cls : 'input-group',
9178                 cn :  [] 
9179             };
9180             
9181             if (this.before && typeof(this.before) == 'string') {
9182                 
9183                 inputblock.cn.push({
9184                     tag :'span',
9185                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9186                     html : this.before
9187                 });
9188             }
9189             if (this.before && typeof(this.before) == 'object') {
9190                 this.before = Roo.factory(this.before);
9191                 
9192                 inputblock.cn.push({
9193                     tag :'span',
9194                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9195                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9196                 });
9197             }
9198             
9199             inputblock.cn.push(input);
9200             
9201             if (this.after && typeof(this.after) == 'string') {
9202                 inputblock.cn.push({
9203                     tag :'span',
9204                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9205                     html : this.after
9206                 });
9207             }
9208             if (this.after && typeof(this.after) == 'object') {
9209                 this.after = Roo.factory(this.after);
9210                 
9211                 inputblock.cn.push({
9212                     tag :'span',
9213                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9214                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9215                 });
9216             }
9217             
9218             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9219                 inputblock.cls += ' has-feedback';
9220                 inputblock.cn.push(feedback);
9221             }
9222         };
9223         var indicator = {
9224             tag : 'i',
9225             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9226             tooltip : 'This field is required'
9227         };
9228         if (Roo.bootstrap.version == 4) {
9229             indicator = {
9230                 tag : 'i',
9231                 style : 'display-none'
9232             };
9233         }
9234         if (align ==='left' && this.fieldLabel.length) {
9235             
9236             cfg.cls += ' roo-form-group-label-left row';
9237             
9238             cfg.cn = [
9239                 indicator,
9240                 {
9241                     tag: 'label',
9242                     'for' :  id,
9243                     cls : 'control-label col-form-label',
9244                     html : this.fieldLabel
9245
9246                 },
9247                 {
9248                     cls : "", 
9249                     cn: [
9250                         inputblock
9251                     ]
9252                 }
9253             ];
9254             
9255             var labelCfg = cfg.cn[1];
9256             var contentCfg = cfg.cn[2];
9257             
9258             if(this.indicatorpos == 'right'){
9259                 cfg.cn = [
9260                     {
9261                         tag: 'label',
9262                         'for' :  id,
9263                         cls : 'control-label col-form-label',
9264                         cn : [
9265                             {
9266                                 tag : 'span',
9267                                 html : this.fieldLabel
9268                             },
9269                             indicator
9270                         ]
9271                     },
9272                     {
9273                         cls : "",
9274                         cn: [
9275                             inputblock
9276                         ]
9277                     }
9278
9279                 ];
9280                 
9281                 labelCfg = cfg.cn[0];
9282                 contentCfg = cfg.cn[1];
9283             
9284             }
9285             
9286             if(this.labelWidth > 12){
9287                 labelCfg.style = "width: " + this.labelWidth + 'px';
9288             }
9289             
9290             if(this.labelWidth < 13 && this.labelmd == 0){
9291                 this.labelmd = this.labelWidth;
9292             }
9293             
9294             if(this.labellg > 0){
9295                 labelCfg.cls += ' col-lg-' + this.labellg;
9296                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9297             }
9298             
9299             if(this.labelmd > 0){
9300                 labelCfg.cls += ' col-md-' + this.labelmd;
9301                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9302             }
9303             
9304             if(this.labelsm > 0){
9305                 labelCfg.cls += ' col-sm-' + this.labelsm;
9306                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9307             }
9308             
9309             if(this.labelxs > 0){
9310                 labelCfg.cls += ' col-xs-' + this.labelxs;
9311                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9312             }
9313             
9314             
9315         } else if ( this.fieldLabel.length) {
9316                 
9317             cfg.cn = [
9318                 {
9319                     tag : 'i',
9320                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9321                     tooltip : 'This field is required'
9322                 },
9323                 {
9324                     tag: 'label',
9325                    //cls : 'input-group-addon',
9326                     html : this.fieldLabel
9327
9328                 },
9329
9330                inputblock
9331
9332            ];
9333            
9334            if(this.indicatorpos == 'right'){
9335                 
9336                 cfg.cn = [
9337                     {
9338                         tag: 'label',
9339                        //cls : 'input-group-addon',
9340                         html : this.fieldLabel
9341
9342                     },
9343                     {
9344                         tag : 'i',
9345                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9346                         tooltip : 'This field is required'
9347                     },
9348
9349                    inputblock
9350
9351                ];
9352
9353             }
9354
9355         } else {
9356             
9357             cfg.cn = [
9358
9359                     inputblock
9360
9361             ];
9362                 
9363                 
9364         };
9365         
9366         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9367            cfg.cls += ' navbar-form';
9368         }
9369         
9370         if (this.parentType === 'NavGroup') {
9371            cfg.cls += ' navbar-form';
9372            cfg.tag = 'li';
9373         }
9374         
9375         return cfg;
9376         
9377     },
9378     /**
9379      * return the real input element.
9380      */
9381     inputEl: function ()
9382     {
9383         return this.el.select('input.form-control',true).first();
9384     },
9385     
9386     tooltipEl : function()
9387     {
9388         return this.inputEl();
9389     },
9390     
9391     indicatorEl : function()
9392     {
9393         if (Roo.bootstrap.version == 4) {
9394             return false; // not enabled in v4 yet.
9395         }
9396         
9397         var indicator = this.el.select('i.roo-required-indicator',true).first();
9398         
9399         if(!indicator){
9400             return false;
9401         }
9402         
9403         return indicator;
9404         
9405     },
9406     
9407     setDisabled : function(v)
9408     {
9409         var i  = this.inputEl().dom;
9410         if (!v) {
9411             i.removeAttribute('disabled');
9412             return;
9413             
9414         }
9415         i.setAttribute('disabled','true');
9416     },
9417     initEvents : function()
9418     {
9419           
9420         this.inputEl().on("keydown" , this.fireKey,  this);
9421         this.inputEl().on("focus", this.onFocus,  this);
9422         this.inputEl().on("blur", this.onBlur,  this);
9423         
9424         this.inputEl().relayEvent('keyup', this);
9425         
9426         this.indicator = this.indicatorEl();
9427         
9428         if(this.indicator){
9429             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9430         }
9431  
9432         // reference to original value for reset
9433         this.originalValue = this.getValue();
9434         //Roo.form.TextField.superclass.initEvents.call(this);
9435         if(this.validationEvent == 'keyup'){
9436             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9437             this.inputEl().on('keyup', this.filterValidation, this);
9438         }
9439         else if(this.validationEvent !== false){
9440             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9441         }
9442         
9443         if(this.selectOnFocus){
9444             this.on("focus", this.preFocus, this);
9445             
9446         }
9447         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9448             this.inputEl().on("keypress", this.filterKeys, this);
9449         } else {
9450             this.inputEl().relayEvent('keypress', this);
9451         }
9452        /* if(this.grow){
9453             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9454             this.el.on("click", this.autoSize,  this);
9455         }
9456         */
9457         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9458             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9459         }
9460         
9461         if (typeof(this.before) == 'object') {
9462             this.before.render(this.el.select('.roo-input-before',true).first());
9463         }
9464         if (typeof(this.after) == 'object') {
9465             this.after.render(this.el.select('.roo-input-after',true).first());
9466         }
9467         
9468         this.inputEl().on('change', this.onChange, this);
9469         
9470     },
9471     filterValidation : function(e){
9472         if(!e.isNavKeyPress()){
9473             this.validationTask.delay(this.validationDelay);
9474         }
9475     },
9476      /**
9477      * Validates the field value
9478      * @return {Boolean} True if the value is valid, else false
9479      */
9480     validate : function(){
9481         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9482         if(this.disabled || this.validateValue(this.getRawValue())){
9483             this.markValid();
9484             return true;
9485         }
9486         
9487         this.markInvalid();
9488         return false;
9489     },
9490     
9491     
9492     /**
9493      * Validates a value according to the field's validation rules and marks the field as invalid
9494      * if the validation fails
9495      * @param {Mixed} value The value to validate
9496      * @return {Boolean} True if the value is valid, else false
9497      */
9498     validateValue : function(value)
9499     {
9500         if(this.getVisibilityEl().hasClass('hidden')){
9501             return true;
9502         }
9503         
9504         if(value.length < 1)  { // if it's blank
9505             if(this.allowBlank){
9506                 return true;
9507             }
9508             return false;
9509         }
9510         
9511         if(value.length < this.minLength){
9512             return false;
9513         }
9514         if(value.length > this.maxLength){
9515             return false;
9516         }
9517         if(this.vtype){
9518             var vt = Roo.form.VTypes;
9519             if(!vt[this.vtype](value, this)){
9520                 return false;
9521             }
9522         }
9523         if(typeof this.validator == "function"){
9524             var msg = this.validator(value);
9525             if(msg !== true){
9526                 return false;
9527             }
9528             if (typeof(msg) == 'string') {
9529                 this.invalidText = msg;
9530             }
9531         }
9532         
9533         if(this.regex && !this.regex.test(value)){
9534             return false;
9535         }
9536         
9537         return true;
9538     },
9539     
9540      // private
9541     fireKey : function(e){
9542         //Roo.log('field ' + e.getKey());
9543         if(e.isNavKeyPress()){
9544             this.fireEvent("specialkey", this, e);
9545         }
9546     },
9547     focus : function (selectText){
9548         if(this.rendered){
9549             this.inputEl().focus();
9550             if(selectText === true){
9551                 this.inputEl().dom.select();
9552             }
9553         }
9554         return this;
9555     } ,
9556     
9557     onFocus : function(){
9558         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9559            // this.el.addClass(this.focusClass);
9560         }
9561         if(!this.hasFocus){
9562             this.hasFocus = true;
9563             this.startValue = this.getValue();
9564             this.fireEvent("focus", this);
9565         }
9566     },
9567     
9568     beforeBlur : Roo.emptyFn,
9569
9570     
9571     // private
9572     onBlur : function(){
9573         this.beforeBlur();
9574         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9575             //this.el.removeClass(this.focusClass);
9576         }
9577         this.hasFocus = false;
9578         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9579             this.validate();
9580         }
9581         var v = this.getValue();
9582         if(String(v) !== String(this.startValue)){
9583             this.fireEvent('change', this, v, this.startValue);
9584         }
9585         this.fireEvent("blur", this);
9586     },
9587     
9588     onChange : function(e)
9589     {
9590         var v = this.getValue();
9591         if(String(v) !== String(this.startValue)){
9592             this.fireEvent('change', this, v, this.startValue);
9593         }
9594         
9595     },
9596     
9597     /**
9598      * Resets the current field value to the originally loaded value and clears any validation messages
9599      */
9600     reset : function(){
9601         this.setValue(this.originalValue);
9602         this.validate();
9603     },
9604      /**
9605      * Returns the name of the field
9606      * @return {Mixed} name The name field
9607      */
9608     getName: function(){
9609         return this.name;
9610     },
9611      /**
9612      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9613      * @return {Mixed} value The field value
9614      */
9615     getValue : function(){
9616         
9617         var v = this.inputEl().getValue();
9618         
9619         return v;
9620     },
9621     /**
9622      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9623      * @return {Mixed} value The field value
9624      */
9625     getRawValue : function(){
9626         var v = this.inputEl().getValue();
9627         
9628         return v;
9629     },
9630     
9631     /**
9632      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9633      * @param {Mixed} value The value to set
9634      */
9635     setRawValue : function(v){
9636         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9637     },
9638     
9639     selectText : function(start, end){
9640         var v = this.getRawValue();
9641         if(v.length > 0){
9642             start = start === undefined ? 0 : start;
9643             end = end === undefined ? v.length : end;
9644             var d = this.inputEl().dom;
9645             if(d.setSelectionRange){
9646                 d.setSelectionRange(start, end);
9647             }else if(d.createTextRange){
9648                 var range = d.createTextRange();
9649                 range.moveStart("character", start);
9650                 range.moveEnd("character", v.length-end);
9651                 range.select();
9652             }
9653         }
9654     },
9655     
9656     /**
9657      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9658      * @param {Mixed} value The value to set
9659      */
9660     setValue : function(v){
9661         this.value = v;
9662         if(this.rendered){
9663             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9664             this.validate();
9665         }
9666     },
9667     
9668     /*
9669     processValue : function(value){
9670         if(this.stripCharsRe){
9671             var newValue = value.replace(this.stripCharsRe, '');
9672             if(newValue !== value){
9673                 this.setRawValue(newValue);
9674                 return newValue;
9675             }
9676         }
9677         return value;
9678     },
9679   */
9680     preFocus : function(){
9681         
9682         if(this.selectOnFocus){
9683             this.inputEl().dom.select();
9684         }
9685     },
9686     filterKeys : function(e){
9687         var k = e.getKey();
9688         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9689             return;
9690         }
9691         var c = e.getCharCode(), cc = String.fromCharCode(c);
9692         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9693             return;
9694         }
9695         if(!this.maskRe.test(cc)){
9696             e.stopEvent();
9697         }
9698     },
9699      /**
9700      * Clear any invalid styles/messages for this field
9701      */
9702     clearInvalid : function(){
9703         
9704         if(!this.el || this.preventMark){ // not rendered
9705             return;
9706         }
9707         
9708      
9709         this.el.removeClass(this.invalidClass);
9710         
9711         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9712             
9713             var feedback = this.el.select('.form-control-feedback', true).first();
9714             
9715             if(feedback){
9716                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9717             }
9718             
9719         }
9720         
9721         if(this.indicator){
9722             this.indicator.removeClass('visible');
9723             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9724         }
9725         
9726         this.fireEvent('valid', this);
9727     },
9728     
9729      /**
9730      * Mark this field as valid
9731      */
9732     markValid : function()
9733     {
9734         if(!this.el  || this.preventMark){ // not rendered...
9735             return;
9736         }
9737         
9738         this.el.removeClass([this.invalidClass, this.validClass]);
9739         
9740         var feedback = this.el.select('.form-control-feedback', true).first();
9741             
9742         if(feedback){
9743             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9744         }
9745         
9746         if(this.indicator){
9747             this.indicator.removeClass('visible');
9748             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9749         }
9750         
9751         if(this.disabled){
9752             return;
9753         }
9754         
9755         if(this.allowBlank && !this.getRawValue().length){
9756             return;
9757         }
9758         
9759         this.el.addClass(this.validClass);
9760         
9761         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9762             
9763             var feedback = this.el.select('.form-control-feedback', true).first();
9764             
9765             if(feedback){
9766                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9767                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9768             }
9769             
9770         }
9771         
9772         this.fireEvent('valid', this);
9773     },
9774     
9775      /**
9776      * Mark this field as invalid
9777      * @param {String} msg The validation message
9778      */
9779     markInvalid : function(msg)
9780     {
9781         if(!this.el  || this.preventMark){ // not rendered
9782             return;
9783         }
9784         
9785         this.el.removeClass([this.invalidClass, this.validClass]);
9786         
9787         var feedback = this.el.select('.form-control-feedback', true).first();
9788             
9789         if(feedback){
9790             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9791         }
9792
9793         if(this.disabled){
9794             return;
9795         }
9796         
9797         if(this.allowBlank && !this.getRawValue().length){
9798             return;
9799         }
9800         
9801         if(this.indicator){
9802             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9803             this.indicator.addClass('visible');
9804         }
9805         
9806         this.el.addClass(this.invalidClass);
9807         
9808         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9809             
9810             var feedback = this.el.select('.form-control-feedback', true).first();
9811             
9812             if(feedback){
9813                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9814                 
9815                 if(this.getValue().length || this.forceFeedback){
9816                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9817                 }
9818                 
9819             }
9820             
9821         }
9822         
9823         this.fireEvent('invalid', this, msg);
9824     },
9825     // private
9826     SafariOnKeyDown : function(event)
9827     {
9828         // this is a workaround for a password hang bug on chrome/ webkit.
9829         if (this.inputEl().dom.type != 'password') {
9830             return;
9831         }
9832         
9833         var isSelectAll = false;
9834         
9835         if(this.inputEl().dom.selectionEnd > 0){
9836             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9837         }
9838         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9839             event.preventDefault();
9840             this.setValue('');
9841             return;
9842         }
9843         
9844         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9845             
9846             event.preventDefault();
9847             // this is very hacky as keydown always get's upper case.
9848             //
9849             var cc = String.fromCharCode(event.getCharCode());
9850             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9851             
9852         }
9853     },
9854     adjustWidth : function(tag, w){
9855         tag = tag.toLowerCase();
9856         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9857             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9858                 if(tag == 'input'){
9859                     return w + 2;
9860                 }
9861                 if(tag == 'textarea'){
9862                     return w-2;
9863                 }
9864             }else if(Roo.isOpera){
9865                 if(tag == 'input'){
9866                     return w + 2;
9867                 }
9868                 if(tag == 'textarea'){
9869                     return w-2;
9870                 }
9871             }
9872         }
9873         return w;
9874     },
9875     
9876     setFieldLabel : function(v)
9877     {
9878         if(!this.rendered){
9879             return;
9880         }
9881         
9882         if(this.indicatorEl()){
9883             var ar = this.el.select('label > span',true);
9884             
9885             if (ar.elements.length) {
9886                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9887                 this.fieldLabel = v;
9888                 return;
9889             }
9890             
9891             var br = this.el.select('label',true);
9892             
9893             if(br.elements.length) {
9894                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9895                 this.fieldLabel = v;
9896                 return;
9897             }
9898             
9899             Roo.log('Cannot Found any of label > span || label in input');
9900             return;
9901         }
9902         
9903         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9904         this.fieldLabel = v;
9905         
9906         
9907     }
9908 });
9909
9910  
9911 /*
9912  * - LGPL
9913  *
9914  * Input
9915  * 
9916  */
9917
9918 /**
9919  * @class Roo.bootstrap.TextArea
9920  * @extends Roo.bootstrap.Input
9921  * Bootstrap TextArea class
9922  * @cfg {Number} cols Specifies the visible width of a text area
9923  * @cfg {Number} rows Specifies the visible number of lines in a text area
9924  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9925  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9926  * @cfg {string} html text
9927  * 
9928  * @constructor
9929  * Create a new TextArea
9930  * @param {Object} config The config object
9931  */
9932
9933 Roo.bootstrap.TextArea = function(config){
9934     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9935    
9936 };
9937
9938 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9939      
9940     cols : false,
9941     rows : 5,
9942     readOnly : false,
9943     warp : 'soft',
9944     resize : false,
9945     value: false,
9946     html: false,
9947     
9948     getAutoCreate : function(){
9949         
9950         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9951         
9952         var id = Roo.id();
9953         
9954         var cfg = {};
9955         
9956         if(this.inputType != 'hidden'){
9957             cfg.cls = 'form-group' //input-group
9958         }
9959         
9960         var input =  {
9961             tag: 'textarea',
9962             id : id,
9963             warp : this.warp,
9964             rows : this.rows,
9965             value : this.value || '',
9966             html: this.html || '',
9967             cls : 'form-control',
9968             placeholder : this.placeholder || '' 
9969             
9970         };
9971         
9972         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9973             input.maxLength = this.maxLength;
9974         }
9975         
9976         if(this.resize){
9977             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9978         }
9979         
9980         if(this.cols){
9981             input.cols = this.cols;
9982         }
9983         
9984         if (this.readOnly) {
9985             input.readonly = true;
9986         }
9987         
9988         if (this.name) {
9989             input.name = this.name;
9990         }
9991         
9992         if (this.size) {
9993             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9994         }
9995         
9996         var settings=this;
9997         ['xs','sm','md','lg'].map(function(size){
9998             if (settings[size]) {
9999                 cfg.cls += ' col-' + size + '-' + settings[size];
10000             }
10001         });
10002         
10003         var inputblock = input;
10004         
10005         if(this.hasFeedback && !this.allowBlank){
10006             
10007             var feedback = {
10008                 tag: 'span',
10009                 cls: 'glyphicon form-control-feedback'
10010             };
10011
10012             inputblock = {
10013                 cls : 'has-feedback',
10014                 cn :  [
10015                     input,
10016                     feedback
10017                 ] 
10018             };  
10019         }
10020         
10021         
10022         if (this.before || this.after) {
10023             
10024             inputblock = {
10025                 cls : 'input-group',
10026                 cn :  [] 
10027             };
10028             if (this.before) {
10029                 inputblock.cn.push({
10030                     tag :'span',
10031                     cls : 'input-group-addon',
10032                     html : this.before
10033                 });
10034             }
10035             
10036             inputblock.cn.push(input);
10037             
10038             if(this.hasFeedback && !this.allowBlank){
10039                 inputblock.cls += ' has-feedback';
10040                 inputblock.cn.push(feedback);
10041             }
10042             
10043             if (this.after) {
10044                 inputblock.cn.push({
10045                     tag :'span',
10046                     cls : 'input-group-addon',
10047                     html : this.after
10048                 });
10049             }
10050             
10051         }
10052         
10053         if (align ==='left' && this.fieldLabel.length) {
10054             cfg.cn = [
10055                 {
10056                     tag: 'label',
10057                     'for' :  id,
10058                     cls : 'control-label',
10059                     html : this.fieldLabel
10060                 },
10061                 {
10062                     cls : "",
10063                     cn: [
10064                         inputblock
10065                     ]
10066                 }
10067
10068             ];
10069             
10070             if(this.labelWidth > 12){
10071                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10072             }
10073
10074             if(this.labelWidth < 13 && this.labelmd == 0){
10075                 this.labelmd = this.labelWidth;
10076             }
10077
10078             if(this.labellg > 0){
10079                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10080                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10081             }
10082
10083             if(this.labelmd > 0){
10084                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10085                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10086             }
10087
10088             if(this.labelsm > 0){
10089                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10090                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10091             }
10092
10093             if(this.labelxs > 0){
10094                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10095                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10096             }
10097             
10098         } else if ( this.fieldLabel.length) {
10099             cfg.cn = [
10100
10101                {
10102                    tag: 'label',
10103                    //cls : 'input-group-addon',
10104                    html : this.fieldLabel
10105
10106                },
10107
10108                inputblock
10109
10110            ];
10111
10112         } else {
10113
10114             cfg.cn = [
10115
10116                 inputblock
10117
10118             ];
10119                 
10120         }
10121         
10122         if (this.disabled) {
10123             input.disabled=true;
10124         }
10125         
10126         return cfg;
10127         
10128     },
10129     /**
10130      * return the real textarea element.
10131      */
10132     inputEl: function ()
10133     {
10134         return this.el.select('textarea.form-control',true).first();
10135     },
10136     
10137     /**
10138      * Clear any invalid styles/messages for this field
10139      */
10140     clearInvalid : function()
10141     {
10142         
10143         if(!this.el || this.preventMark){ // not rendered
10144             return;
10145         }
10146         
10147         var label = this.el.select('label', true).first();
10148         var icon = this.el.select('i.fa-star', true).first();
10149         
10150         if(label && icon){
10151             icon.remove();
10152         }
10153         
10154         this.el.removeClass(this.invalidClass);
10155         
10156         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10157             
10158             var feedback = this.el.select('.form-control-feedback', true).first();
10159             
10160             if(feedback){
10161                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10162             }
10163             
10164         }
10165         
10166         this.fireEvent('valid', this);
10167     },
10168     
10169      /**
10170      * Mark this field as valid
10171      */
10172     markValid : function()
10173     {
10174         if(!this.el  || this.preventMark){ // not rendered
10175             return;
10176         }
10177         
10178         this.el.removeClass([this.invalidClass, this.validClass]);
10179         
10180         var feedback = this.el.select('.form-control-feedback', true).first();
10181             
10182         if(feedback){
10183             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10184         }
10185
10186         if(this.disabled || this.allowBlank){
10187             return;
10188         }
10189         
10190         var label = this.el.select('label', true).first();
10191         var icon = this.el.select('i.fa-star', true).first();
10192         
10193         if(label && icon){
10194             icon.remove();
10195         }
10196         
10197         this.el.addClass(this.validClass);
10198         
10199         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10200             
10201             var feedback = this.el.select('.form-control-feedback', true).first();
10202             
10203             if(feedback){
10204                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10205                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10206             }
10207             
10208         }
10209         
10210         this.fireEvent('valid', this);
10211     },
10212     
10213      /**
10214      * Mark this field as invalid
10215      * @param {String} msg The validation message
10216      */
10217     markInvalid : function(msg)
10218     {
10219         if(!this.el  || this.preventMark){ // not rendered
10220             return;
10221         }
10222         
10223         this.el.removeClass([this.invalidClass, this.validClass]);
10224         
10225         var feedback = this.el.select('.form-control-feedback', true).first();
10226             
10227         if(feedback){
10228             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10229         }
10230
10231         if(this.disabled || this.allowBlank){
10232             return;
10233         }
10234         
10235         var label = this.el.select('label', true).first();
10236         var icon = this.el.select('i.fa-star', true).first();
10237         
10238         if(!this.getValue().length && label && !icon){
10239             this.el.createChild({
10240                 tag : 'i',
10241                 cls : 'text-danger fa fa-lg fa-star',
10242                 tooltip : 'This field is required',
10243                 style : 'margin-right:5px;'
10244             }, label, true);
10245         }
10246
10247         this.el.addClass(this.invalidClass);
10248         
10249         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10250             
10251             var feedback = this.el.select('.form-control-feedback', true).first();
10252             
10253             if(feedback){
10254                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10255                 
10256                 if(this.getValue().length || this.forceFeedback){
10257                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10258                 }
10259                 
10260             }
10261             
10262         }
10263         
10264         this.fireEvent('invalid', this, msg);
10265     }
10266 });
10267
10268  
10269 /*
10270  * - LGPL
10271  *
10272  * trigger field - base class for combo..
10273  * 
10274  */
10275  
10276 /**
10277  * @class Roo.bootstrap.TriggerField
10278  * @extends Roo.bootstrap.Input
10279  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10280  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10281  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10282  * for which you can provide a custom implementation.  For example:
10283  * <pre><code>
10284 var trigger = new Roo.bootstrap.TriggerField();
10285 trigger.onTriggerClick = myTriggerFn;
10286 trigger.applyTo('my-field');
10287 </code></pre>
10288  *
10289  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10290  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10291  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10292  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10293  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10294
10295  * @constructor
10296  * Create a new TriggerField.
10297  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10298  * to the base TextField)
10299  */
10300 Roo.bootstrap.TriggerField = function(config){
10301     this.mimicing = false;
10302     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10303 };
10304
10305 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10306     /**
10307      * @cfg {String} triggerClass A CSS class to apply to the trigger
10308      */
10309      /**
10310      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10311      */
10312     hideTrigger:false,
10313
10314     /**
10315      * @cfg {Boolean} removable (true|false) special filter default false
10316      */
10317     removable : false,
10318     
10319     /** @cfg {Boolean} grow @hide */
10320     /** @cfg {Number} growMin @hide */
10321     /** @cfg {Number} growMax @hide */
10322
10323     /**
10324      * @hide 
10325      * @method
10326      */
10327     autoSize: Roo.emptyFn,
10328     // private
10329     monitorTab : true,
10330     // private
10331     deferHeight : true,
10332
10333     
10334     actionMode : 'wrap',
10335     
10336     caret : false,
10337     
10338     
10339     getAutoCreate : function(){
10340        
10341         var align = this.labelAlign || this.parentLabelAlign();
10342         
10343         var id = Roo.id();
10344         
10345         var cfg = {
10346             cls: 'form-group' //input-group
10347         };
10348         
10349         
10350         var input =  {
10351             tag: 'input',
10352             id : id,
10353             type : this.inputType,
10354             cls : 'form-control',
10355             autocomplete: 'new-password',
10356             placeholder : this.placeholder || '' 
10357             
10358         };
10359         if (this.name) {
10360             input.name = this.name;
10361         }
10362         if (this.size) {
10363             input.cls += ' input-' + this.size;
10364         }
10365         
10366         if (this.disabled) {
10367             input.disabled=true;
10368         }
10369         
10370         var inputblock = input;
10371         
10372         if(this.hasFeedback && !this.allowBlank){
10373             
10374             var feedback = {
10375                 tag: 'span',
10376                 cls: 'glyphicon form-control-feedback'
10377             };
10378             
10379             if(this.removable && !this.editable && !this.tickable){
10380                 inputblock = {
10381                     cls : 'has-feedback',
10382                     cn :  [
10383                         inputblock,
10384                         {
10385                             tag: 'button',
10386                             html : 'x',
10387                             cls : 'roo-combo-removable-btn close'
10388                         },
10389                         feedback
10390                     ] 
10391                 };
10392             } else {
10393                 inputblock = {
10394                     cls : 'has-feedback',
10395                     cn :  [
10396                         inputblock,
10397                         feedback
10398                     ] 
10399                 };
10400             }
10401
10402         } else {
10403             if(this.removable && !this.editable && !this.tickable){
10404                 inputblock = {
10405                     cls : 'roo-removable',
10406                     cn :  [
10407                         inputblock,
10408                         {
10409                             tag: 'button',
10410                             html : 'x',
10411                             cls : 'roo-combo-removable-btn close'
10412                         }
10413                     ] 
10414                 };
10415             }
10416         }
10417         
10418         if (this.before || this.after) {
10419             
10420             inputblock = {
10421                 cls : 'input-group',
10422                 cn :  [] 
10423             };
10424             if (this.before) {
10425                 inputblock.cn.push({
10426                     tag :'span',
10427                     cls : 'input-group-addon input-group-prepend input-group-text',
10428                     html : this.before
10429                 });
10430             }
10431             
10432             inputblock.cn.push(input);
10433             
10434             if(this.hasFeedback && !this.allowBlank){
10435                 inputblock.cls += ' has-feedback';
10436                 inputblock.cn.push(feedback);
10437             }
10438             
10439             if (this.after) {
10440                 inputblock.cn.push({
10441                     tag :'span',
10442                     cls : 'input-group-addon input-group-append input-group-text',
10443                     html : this.after
10444                 });
10445             }
10446             
10447         };
10448         
10449       
10450         
10451         var ibwrap = inputblock;
10452         
10453         if(this.multiple){
10454             ibwrap = {
10455                 tag: 'ul',
10456                 cls: 'roo-select2-choices',
10457                 cn:[
10458                     {
10459                         tag: 'li',
10460                         cls: 'roo-select2-search-field',
10461                         cn: [
10462
10463                             inputblock
10464                         ]
10465                     }
10466                 ]
10467             };
10468                 
10469         }
10470         
10471         var combobox = {
10472             cls: 'roo-select2-container input-group',
10473             cn: [
10474                  {
10475                     tag: 'input',
10476                     type : 'hidden',
10477                     cls: 'form-hidden-field'
10478                 },
10479                 ibwrap
10480             ]
10481         };
10482         
10483         if(!this.multiple && this.showToggleBtn){
10484             
10485             var caret = {
10486                         tag: 'span',
10487                         cls: 'caret'
10488              };
10489             if (this.caret != false) {
10490                 caret = {
10491                      tag: 'i',
10492                      cls: 'fa fa-' + this.caret
10493                 };
10494                 
10495             }
10496             
10497             combobox.cn.push({
10498                 tag :'span',
10499                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10500                 cn : [
10501                     caret,
10502                     {
10503                         tag: 'span',
10504                         cls: 'combobox-clear',
10505                         cn  : [
10506                             {
10507                                 tag : 'i',
10508                                 cls: 'icon-remove'
10509                             }
10510                         ]
10511                     }
10512                 ]
10513
10514             })
10515         }
10516         
10517         if(this.multiple){
10518             combobox.cls += ' roo-select2-container-multi';
10519         }
10520          var indicator = {
10521             tag : 'i',
10522             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10523             tooltip : 'This field is required'
10524         };
10525         if (Roo.bootstrap.version == 4) {
10526             indicator = {
10527                 tag : 'i',
10528                 style : 'display:none'
10529             };
10530         }
10531         
10532         
10533         if (align ==='left' && this.fieldLabel.length) {
10534             
10535             cfg.cls += ' roo-form-group-label-left row';
10536
10537             cfg.cn = [
10538                 indicator,
10539                 {
10540                     tag: 'label',
10541                     'for' :  id,
10542                     cls : 'control-label',
10543                     html : this.fieldLabel
10544
10545                 },
10546                 {
10547                     cls : "", 
10548                     cn: [
10549                         combobox
10550                     ]
10551                 }
10552
10553             ];
10554             
10555             var labelCfg = cfg.cn[1];
10556             var contentCfg = cfg.cn[2];
10557             
10558             if(this.indicatorpos == 'right'){
10559                 cfg.cn = [
10560                     {
10561                         tag: 'label',
10562                         'for' :  id,
10563                         cls : 'control-label',
10564                         cn : [
10565                             {
10566                                 tag : 'span',
10567                                 html : this.fieldLabel
10568                             },
10569                             indicator
10570                         ]
10571                     },
10572                     {
10573                         cls : "", 
10574                         cn: [
10575                             combobox
10576                         ]
10577                     }
10578
10579                 ];
10580                 
10581                 labelCfg = cfg.cn[0];
10582                 contentCfg = cfg.cn[1];
10583             }
10584             
10585             if(this.labelWidth > 12){
10586                 labelCfg.style = "width: " + this.labelWidth + 'px';
10587             }
10588             
10589             if(this.labelWidth < 13 && this.labelmd == 0){
10590                 this.labelmd = this.labelWidth;
10591             }
10592             
10593             if(this.labellg > 0){
10594                 labelCfg.cls += ' col-lg-' + this.labellg;
10595                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10596             }
10597             
10598             if(this.labelmd > 0){
10599                 labelCfg.cls += ' col-md-' + this.labelmd;
10600                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10601             }
10602             
10603             if(this.labelsm > 0){
10604                 labelCfg.cls += ' col-sm-' + this.labelsm;
10605                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10606             }
10607             
10608             if(this.labelxs > 0){
10609                 labelCfg.cls += ' col-xs-' + this.labelxs;
10610                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10611             }
10612             
10613         } else if ( this.fieldLabel.length) {
10614 //                Roo.log(" label");
10615             cfg.cn = [
10616                 indicator,
10617                {
10618                    tag: 'label',
10619                    //cls : 'input-group-addon',
10620                    html : this.fieldLabel
10621
10622                },
10623
10624                combobox
10625
10626             ];
10627             
10628             if(this.indicatorpos == 'right'){
10629                 
10630                 cfg.cn = [
10631                     {
10632                        tag: 'label',
10633                        cn : [
10634                            {
10635                                tag : 'span',
10636                                html : this.fieldLabel
10637                            },
10638                            indicator
10639                        ]
10640
10641                     },
10642                     combobox
10643
10644                 ];
10645
10646             }
10647
10648         } else {
10649             
10650 //                Roo.log(" no label && no align");
10651                 cfg = combobox
10652                      
10653                 
10654         }
10655         
10656         var settings=this;
10657         ['xs','sm','md','lg'].map(function(size){
10658             if (settings[size]) {
10659                 cfg.cls += ' col-' + size + '-' + settings[size];
10660             }
10661         });
10662         
10663         return cfg;
10664         
10665     },
10666     
10667     
10668     
10669     // private
10670     onResize : function(w, h){
10671 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10672 //        if(typeof w == 'number'){
10673 //            var x = w - this.trigger.getWidth();
10674 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10675 //            this.trigger.setStyle('left', x+'px');
10676 //        }
10677     },
10678
10679     // private
10680     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10681
10682     // private
10683     getResizeEl : function(){
10684         return this.inputEl();
10685     },
10686
10687     // private
10688     getPositionEl : function(){
10689         return this.inputEl();
10690     },
10691
10692     // private
10693     alignErrorIcon : function(){
10694         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10695     },
10696
10697     // private
10698     initEvents : function(){
10699         
10700         this.createList();
10701         
10702         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10703         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10704         if(!this.multiple && this.showToggleBtn){
10705             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10706             if(this.hideTrigger){
10707                 this.trigger.setDisplayed(false);
10708             }
10709             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10710         }
10711         
10712         if(this.multiple){
10713             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10714         }
10715         
10716         if(this.removable && !this.editable && !this.tickable){
10717             var close = this.closeTriggerEl();
10718             
10719             if(close){
10720                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10721                 close.on('click', this.removeBtnClick, this, close);
10722             }
10723         }
10724         
10725         //this.trigger.addClassOnOver('x-form-trigger-over');
10726         //this.trigger.addClassOnClick('x-form-trigger-click');
10727         
10728         //if(!this.width){
10729         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10730         //}
10731     },
10732     
10733     closeTriggerEl : function()
10734     {
10735         var close = this.el.select('.roo-combo-removable-btn', true).first();
10736         return close ? close : false;
10737     },
10738     
10739     removeBtnClick : function(e, h, el)
10740     {
10741         e.preventDefault();
10742         
10743         if(this.fireEvent("remove", this) !== false){
10744             this.reset();
10745             this.fireEvent("afterremove", this)
10746         }
10747     },
10748     
10749     createList : function()
10750     {
10751         this.list = Roo.get(document.body).createChild({
10752             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10753             cls: 'typeahead typeahead-long dropdown-menu',
10754             style: 'display:none'
10755         });
10756         
10757         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10758         
10759     },
10760
10761     // private
10762     initTrigger : function(){
10763        
10764     },
10765
10766     // private
10767     onDestroy : function(){
10768         if(this.trigger){
10769             this.trigger.removeAllListeners();
10770           //  this.trigger.remove();
10771         }
10772         //if(this.wrap){
10773         //    this.wrap.remove();
10774         //}
10775         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10776     },
10777
10778     // private
10779     onFocus : function(){
10780         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10781         /*
10782         if(!this.mimicing){
10783             this.wrap.addClass('x-trigger-wrap-focus');
10784             this.mimicing = true;
10785             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10786             if(this.monitorTab){
10787                 this.el.on("keydown", this.checkTab, this);
10788             }
10789         }
10790         */
10791     },
10792
10793     // private
10794     checkTab : function(e){
10795         if(e.getKey() == e.TAB){
10796             this.triggerBlur();
10797         }
10798     },
10799
10800     // private
10801     onBlur : function(){
10802         // do nothing
10803     },
10804
10805     // private
10806     mimicBlur : function(e, t){
10807         /*
10808         if(!this.wrap.contains(t) && this.validateBlur()){
10809             this.triggerBlur();
10810         }
10811         */
10812     },
10813
10814     // private
10815     triggerBlur : function(){
10816         this.mimicing = false;
10817         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10818         if(this.monitorTab){
10819             this.el.un("keydown", this.checkTab, this);
10820         }
10821         //this.wrap.removeClass('x-trigger-wrap-focus');
10822         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10823     },
10824
10825     // private
10826     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10827     validateBlur : function(e, t){
10828         return true;
10829     },
10830
10831     // private
10832     onDisable : function(){
10833         this.inputEl().dom.disabled = true;
10834         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10835         //if(this.wrap){
10836         //    this.wrap.addClass('x-item-disabled');
10837         //}
10838     },
10839
10840     // private
10841     onEnable : function(){
10842         this.inputEl().dom.disabled = false;
10843         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10844         //if(this.wrap){
10845         //    this.el.removeClass('x-item-disabled');
10846         //}
10847     },
10848
10849     // private
10850     onShow : function(){
10851         var ae = this.getActionEl();
10852         
10853         if(ae){
10854             ae.dom.style.display = '';
10855             ae.dom.style.visibility = 'visible';
10856         }
10857     },
10858
10859     // private
10860     
10861     onHide : function(){
10862         var ae = this.getActionEl();
10863         ae.dom.style.display = 'none';
10864     },
10865
10866     /**
10867      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10868      * by an implementing function.
10869      * @method
10870      * @param {EventObject} e
10871      */
10872     onTriggerClick : Roo.emptyFn
10873 });
10874  /*
10875  * Based on:
10876  * Ext JS Library 1.1.1
10877  * Copyright(c) 2006-2007, Ext JS, LLC.
10878  *
10879  * Originally Released Under LGPL - original licence link has changed is not relivant.
10880  *
10881  * Fork - LGPL
10882  * <script type="text/javascript">
10883  */
10884
10885
10886 /**
10887  * @class Roo.data.SortTypes
10888  * @singleton
10889  * Defines the default sorting (casting?) comparison functions used when sorting data.
10890  */
10891 Roo.data.SortTypes = {
10892     /**
10893      * Default sort that does nothing
10894      * @param {Mixed} s The value being converted
10895      * @return {Mixed} The comparison value
10896      */
10897     none : function(s){
10898         return s;
10899     },
10900     
10901     /**
10902      * The regular expression used to strip tags
10903      * @type {RegExp}
10904      * @property
10905      */
10906     stripTagsRE : /<\/?[^>]+>/gi,
10907     
10908     /**
10909      * Strips all HTML tags to sort on text only
10910      * @param {Mixed} s The value being converted
10911      * @return {String} The comparison value
10912      */
10913     asText : function(s){
10914         return String(s).replace(this.stripTagsRE, "");
10915     },
10916     
10917     /**
10918      * Strips all HTML tags to sort on text only - Case insensitive
10919      * @param {Mixed} s The value being converted
10920      * @return {String} The comparison value
10921      */
10922     asUCText : function(s){
10923         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10924     },
10925     
10926     /**
10927      * Case insensitive string
10928      * @param {Mixed} s The value being converted
10929      * @return {String} The comparison value
10930      */
10931     asUCString : function(s) {
10932         return String(s).toUpperCase();
10933     },
10934     
10935     /**
10936      * Date sorting
10937      * @param {Mixed} s The value being converted
10938      * @return {Number} The comparison value
10939      */
10940     asDate : function(s) {
10941         if(!s){
10942             return 0;
10943         }
10944         if(s instanceof Date){
10945             return s.getTime();
10946         }
10947         return Date.parse(String(s));
10948     },
10949     
10950     /**
10951      * Float sorting
10952      * @param {Mixed} s The value being converted
10953      * @return {Float} The comparison value
10954      */
10955     asFloat : function(s) {
10956         var val = parseFloat(String(s).replace(/,/g, ""));
10957         if(isNaN(val)) {
10958             val = 0;
10959         }
10960         return val;
10961     },
10962     
10963     /**
10964      * Integer sorting
10965      * @param {Mixed} s The value being converted
10966      * @return {Number} The comparison value
10967      */
10968     asInt : function(s) {
10969         var val = parseInt(String(s).replace(/,/g, ""));
10970         if(isNaN(val)) {
10971             val = 0;
10972         }
10973         return val;
10974     }
10975 };/*
10976  * Based on:
10977  * Ext JS Library 1.1.1
10978  * Copyright(c) 2006-2007, Ext JS, LLC.
10979  *
10980  * Originally Released Under LGPL - original licence link has changed is not relivant.
10981  *
10982  * Fork - LGPL
10983  * <script type="text/javascript">
10984  */
10985
10986 /**
10987 * @class Roo.data.Record
10988  * Instances of this class encapsulate both record <em>definition</em> information, and record
10989  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10990  * to access Records cached in an {@link Roo.data.Store} object.<br>
10991  * <p>
10992  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10993  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10994  * objects.<br>
10995  * <p>
10996  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10997  * @constructor
10998  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10999  * {@link #create}. The parameters are the same.
11000  * @param {Array} data An associative Array of data values keyed by the field name.
11001  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11002  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11003  * not specified an integer id is generated.
11004  */
11005 Roo.data.Record = function(data, id){
11006     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11007     this.data = data;
11008 };
11009
11010 /**
11011  * Generate a constructor for a specific record layout.
11012  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11013  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11014  * Each field definition object may contain the following properties: <ul>
11015  * <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,
11016  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11017  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11018  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11019  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11020  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11021  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11022  * this may be omitted.</p></li>
11023  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11024  * <ul><li>auto (Default, implies no conversion)</li>
11025  * <li>string</li>
11026  * <li>int</li>
11027  * <li>float</li>
11028  * <li>boolean</li>
11029  * <li>date</li></ul></p></li>
11030  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11031  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11032  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11033  * by the Reader into an object that will be stored in the Record. It is passed the
11034  * following parameters:<ul>
11035  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11036  * </ul></p></li>
11037  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11038  * </ul>
11039  * <br>usage:<br><pre><code>
11040 var TopicRecord = Roo.data.Record.create(
11041     {name: 'title', mapping: 'topic_title'},
11042     {name: 'author', mapping: 'username'},
11043     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11044     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11045     {name: 'lastPoster', mapping: 'user2'},
11046     {name: 'excerpt', mapping: 'post_text'}
11047 );
11048
11049 var myNewRecord = new TopicRecord({
11050     title: 'Do my job please',
11051     author: 'noobie',
11052     totalPosts: 1,
11053     lastPost: new Date(),
11054     lastPoster: 'Animal',
11055     excerpt: 'No way dude!'
11056 });
11057 myStore.add(myNewRecord);
11058 </code></pre>
11059  * @method create
11060  * @static
11061  */
11062 Roo.data.Record.create = function(o){
11063     var f = function(){
11064         f.superclass.constructor.apply(this, arguments);
11065     };
11066     Roo.extend(f, Roo.data.Record);
11067     var p = f.prototype;
11068     p.fields = new Roo.util.MixedCollection(false, function(field){
11069         return field.name;
11070     });
11071     for(var i = 0, len = o.length; i < len; i++){
11072         p.fields.add(new Roo.data.Field(o[i]));
11073     }
11074     f.getField = function(name){
11075         return p.fields.get(name);  
11076     };
11077     return f;
11078 };
11079
11080 Roo.data.Record.AUTO_ID = 1000;
11081 Roo.data.Record.EDIT = 'edit';
11082 Roo.data.Record.REJECT = 'reject';
11083 Roo.data.Record.COMMIT = 'commit';
11084
11085 Roo.data.Record.prototype = {
11086     /**
11087      * Readonly flag - true if this record has been modified.
11088      * @type Boolean
11089      */
11090     dirty : false,
11091     editing : false,
11092     error: null,
11093     modified: null,
11094
11095     // private
11096     join : function(store){
11097         this.store = store;
11098     },
11099
11100     /**
11101      * Set the named field to the specified value.
11102      * @param {String} name The name of the field to set.
11103      * @param {Object} value The value to set the field to.
11104      */
11105     set : function(name, value){
11106         if(this.data[name] == value){
11107             return;
11108         }
11109         this.dirty = true;
11110         if(!this.modified){
11111             this.modified = {};
11112         }
11113         if(typeof this.modified[name] == 'undefined'){
11114             this.modified[name] = this.data[name];
11115         }
11116         this.data[name] = value;
11117         if(!this.editing && this.store){
11118             this.store.afterEdit(this);
11119         }       
11120     },
11121
11122     /**
11123      * Get the value of the named field.
11124      * @param {String} name The name of the field to get the value of.
11125      * @return {Object} The value of the field.
11126      */
11127     get : function(name){
11128         return this.data[name]; 
11129     },
11130
11131     // private
11132     beginEdit : function(){
11133         this.editing = true;
11134         this.modified = {}; 
11135     },
11136
11137     // private
11138     cancelEdit : function(){
11139         this.editing = false;
11140         delete this.modified;
11141     },
11142
11143     // private
11144     endEdit : function(){
11145         this.editing = false;
11146         if(this.dirty && this.store){
11147             this.store.afterEdit(this);
11148         }
11149     },
11150
11151     /**
11152      * Usually called by the {@link Roo.data.Store} which owns the Record.
11153      * Rejects all changes made to the Record since either creation, or the last commit operation.
11154      * Modified fields are reverted to their original values.
11155      * <p>
11156      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11157      * of reject operations.
11158      */
11159     reject : function(){
11160         var m = this.modified;
11161         for(var n in m){
11162             if(typeof m[n] != "function"){
11163                 this.data[n] = m[n];
11164             }
11165         }
11166         this.dirty = false;
11167         delete this.modified;
11168         this.editing = false;
11169         if(this.store){
11170             this.store.afterReject(this);
11171         }
11172     },
11173
11174     /**
11175      * Usually called by the {@link Roo.data.Store} which owns the Record.
11176      * Commits all changes made to the Record since either creation, or the last commit operation.
11177      * <p>
11178      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11179      * of commit operations.
11180      */
11181     commit : function(){
11182         this.dirty = false;
11183         delete this.modified;
11184         this.editing = false;
11185         if(this.store){
11186             this.store.afterCommit(this);
11187         }
11188     },
11189
11190     // private
11191     hasError : function(){
11192         return this.error != null;
11193     },
11194
11195     // private
11196     clearError : function(){
11197         this.error = null;
11198     },
11199
11200     /**
11201      * Creates a copy of this record.
11202      * @param {String} id (optional) A new record id if you don't want to use this record's id
11203      * @return {Record}
11204      */
11205     copy : function(newId) {
11206         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11207     }
11208 };/*
11209  * Based on:
11210  * Ext JS Library 1.1.1
11211  * Copyright(c) 2006-2007, Ext JS, LLC.
11212  *
11213  * Originally Released Under LGPL - original licence link has changed is not relivant.
11214  *
11215  * Fork - LGPL
11216  * <script type="text/javascript">
11217  */
11218
11219
11220
11221 /**
11222  * @class Roo.data.Store
11223  * @extends Roo.util.Observable
11224  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11225  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11226  * <p>
11227  * 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
11228  * has no knowledge of the format of the data returned by the Proxy.<br>
11229  * <p>
11230  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11231  * instances from the data object. These records are cached and made available through accessor functions.
11232  * @constructor
11233  * Creates a new Store.
11234  * @param {Object} config A config object containing the objects needed for the Store to access data,
11235  * and read the data into Records.
11236  */
11237 Roo.data.Store = function(config){
11238     this.data = new Roo.util.MixedCollection(false);
11239     this.data.getKey = function(o){
11240         return o.id;
11241     };
11242     this.baseParams = {};
11243     // private
11244     this.paramNames = {
11245         "start" : "start",
11246         "limit" : "limit",
11247         "sort" : "sort",
11248         "dir" : "dir",
11249         "multisort" : "_multisort"
11250     };
11251
11252     if(config && config.data){
11253         this.inlineData = config.data;
11254         delete config.data;
11255     }
11256
11257     Roo.apply(this, config);
11258     
11259     if(this.reader){ // reader passed
11260         this.reader = Roo.factory(this.reader, Roo.data);
11261         this.reader.xmodule = this.xmodule || false;
11262         if(!this.recordType){
11263             this.recordType = this.reader.recordType;
11264         }
11265         if(this.reader.onMetaChange){
11266             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11267         }
11268     }
11269
11270     if(this.recordType){
11271         this.fields = this.recordType.prototype.fields;
11272     }
11273     this.modified = [];
11274
11275     this.addEvents({
11276         /**
11277          * @event datachanged
11278          * Fires when the data cache has changed, and a widget which is using this Store
11279          * as a Record cache should refresh its view.
11280          * @param {Store} this
11281          */
11282         datachanged : true,
11283         /**
11284          * @event metachange
11285          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11286          * @param {Store} this
11287          * @param {Object} meta The JSON metadata
11288          */
11289         metachange : true,
11290         /**
11291          * @event add
11292          * Fires when Records have been added to the Store
11293          * @param {Store} this
11294          * @param {Roo.data.Record[]} records The array of Records added
11295          * @param {Number} index The index at which the record(s) were added
11296          */
11297         add : true,
11298         /**
11299          * @event remove
11300          * Fires when a Record has been removed from the Store
11301          * @param {Store} this
11302          * @param {Roo.data.Record} record The Record that was removed
11303          * @param {Number} index The index at which the record was removed
11304          */
11305         remove : true,
11306         /**
11307          * @event update
11308          * Fires when a Record has been updated
11309          * @param {Store} this
11310          * @param {Roo.data.Record} record The Record that was updated
11311          * @param {String} operation The update operation being performed.  Value may be one of:
11312          * <pre><code>
11313  Roo.data.Record.EDIT
11314  Roo.data.Record.REJECT
11315  Roo.data.Record.COMMIT
11316          * </code></pre>
11317          */
11318         update : true,
11319         /**
11320          * @event clear
11321          * Fires when the data cache has been cleared.
11322          * @param {Store} this
11323          */
11324         clear : true,
11325         /**
11326          * @event beforeload
11327          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11328          * the load action will be canceled.
11329          * @param {Store} this
11330          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11331          */
11332         beforeload : true,
11333         /**
11334          * @event beforeloadadd
11335          * Fires after a new set of Records has been loaded.
11336          * @param {Store} this
11337          * @param {Roo.data.Record[]} records The Records that were loaded
11338          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11339          */
11340         beforeloadadd : true,
11341         /**
11342          * @event load
11343          * Fires after a new set of Records has been loaded, before they are added to the store.
11344          * @param {Store} this
11345          * @param {Roo.data.Record[]} records The Records that were loaded
11346          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11347          * @params {Object} return from reader
11348          */
11349         load : true,
11350         /**
11351          * @event loadexception
11352          * Fires if an exception occurs in the Proxy during loading.
11353          * Called with the signature of the Proxy's "loadexception" event.
11354          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11355          * 
11356          * @param {Proxy} 
11357          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11358          * @param {Object} load options 
11359          * @param {Object} jsonData from your request (normally this contains the Exception)
11360          */
11361         loadexception : true
11362     });
11363     
11364     if(this.proxy){
11365         this.proxy = Roo.factory(this.proxy, Roo.data);
11366         this.proxy.xmodule = this.xmodule || false;
11367         this.relayEvents(this.proxy,  ["loadexception"]);
11368     }
11369     this.sortToggle = {};
11370     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11371
11372     Roo.data.Store.superclass.constructor.call(this);
11373
11374     if(this.inlineData){
11375         this.loadData(this.inlineData);
11376         delete this.inlineData;
11377     }
11378 };
11379
11380 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11381      /**
11382     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11383     * without a remote query - used by combo/forms at present.
11384     */
11385     
11386     /**
11387     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11388     */
11389     /**
11390     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11391     */
11392     /**
11393     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11394     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11395     */
11396     /**
11397     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11398     * on any HTTP request
11399     */
11400     /**
11401     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11402     */
11403     /**
11404     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11405     */
11406     multiSort: false,
11407     /**
11408     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11409     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11410     */
11411     remoteSort : false,
11412
11413     /**
11414     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11415      * loaded or when a record is removed. (defaults to false).
11416     */
11417     pruneModifiedRecords : false,
11418
11419     // private
11420     lastOptions : null,
11421
11422     /**
11423      * Add Records to the Store and fires the add event.
11424      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11425      */
11426     add : function(records){
11427         records = [].concat(records);
11428         for(var i = 0, len = records.length; i < len; i++){
11429             records[i].join(this);
11430         }
11431         var index = this.data.length;
11432         this.data.addAll(records);
11433         this.fireEvent("add", this, records, index);
11434     },
11435
11436     /**
11437      * Remove a Record from the Store and fires the remove event.
11438      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11439      */
11440     remove : function(record){
11441         var index = this.data.indexOf(record);
11442         this.data.removeAt(index);
11443  
11444         if(this.pruneModifiedRecords){
11445             this.modified.remove(record);
11446         }
11447         this.fireEvent("remove", this, record, index);
11448     },
11449
11450     /**
11451      * Remove all Records from the Store and fires the clear event.
11452      */
11453     removeAll : function(){
11454         this.data.clear();
11455         if(this.pruneModifiedRecords){
11456             this.modified = [];
11457         }
11458         this.fireEvent("clear", this);
11459     },
11460
11461     /**
11462      * Inserts Records to the Store at the given index and fires the add event.
11463      * @param {Number} index The start index at which to insert the passed Records.
11464      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11465      */
11466     insert : function(index, records){
11467         records = [].concat(records);
11468         for(var i = 0, len = records.length; i < len; i++){
11469             this.data.insert(index, records[i]);
11470             records[i].join(this);
11471         }
11472         this.fireEvent("add", this, records, index);
11473     },
11474
11475     /**
11476      * Get the index within the cache of the passed Record.
11477      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11478      * @return {Number} The index of the passed Record. Returns -1 if not found.
11479      */
11480     indexOf : function(record){
11481         return this.data.indexOf(record);
11482     },
11483
11484     /**
11485      * Get the index within the cache of the Record with the passed id.
11486      * @param {String} id The id of the Record to find.
11487      * @return {Number} The index of the Record. Returns -1 if not found.
11488      */
11489     indexOfId : function(id){
11490         return this.data.indexOfKey(id);
11491     },
11492
11493     /**
11494      * Get the Record with the specified id.
11495      * @param {String} id The id of the Record to find.
11496      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11497      */
11498     getById : function(id){
11499         return this.data.key(id);
11500     },
11501
11502     /**
11503      * Get the Record at the specified index.
11504      * @param {Number} index The index of the Record to find.
11505      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11506      */
11507     getAt : function(index){
11508         return this.data.itemAt(index);
11509     },
11510
11511     /**
11512      * Returns a range of Records between specified indices.
11513      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11514      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11515      * @return {Roo.data.Record[]} An array of Records
11516      */
11517     getRange : function(start, end){
11518         return this.data.getRange(start, end);
11519     },
11520
11521     // private
11522     storeOptions : function(o){
11523         o = Roo.apply({}, o);
11524         delete o.callback;
11525         delete o.scope;
11526         this.lastOptions = o;
11527     },
11528
11529     /**
11530      * Loads the Record cache from the configured Proxy using the configured Reader.
11531      * <p>
11532      * If using remote paging, then the first load call must specify the <em>start</em>
11533      * and <em>limit</em> properties in the options.params property to establish the initial
11534      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11535      * <p>
11536      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11537      * and this call will return before the new data has been loaded. Perform any post-processing
11538      * in a callback function, or in a "load" event handler.</strong>
11539      * <p>
11540      * @param {Object} options An object containing properties which control loading options:<ul>
11541      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11542      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11543      * passed the following arguments:<ul>
11544      * <li>r : Roo.data.Record[]</li>
11545      * <li>options: Options object from the load call</li>
11546      * <li>success: Boolean success indicator</li></ul></li>
11547      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11548      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11549      * </ul>
11550      */
11551     load : function(options){
11552         options = options || {};
11553         if(this.fireEvent("beforeload", this, options) !== false){
11554             this.storeOptions(options);
11555             var p = Roo.apply(options.params || {}, this.baseParams);
11556             // if meta was not loaded from remote source.. try requesting it.
11557             if (!this.reader.metaFromRemote) {
11558                 p._requestMeta = 1;
11559             }
11560             if(this.sortInfo && this.remoteSort){
11561                 var pn = this.paramNames;
11562                 p[pn["sort"]] = this.sortInfo.field;
11563                 p[pn["dir"]] = this.sortInfo.direction;
11564             }
11565             if (this.multiSort) {
11566                 var pn = this.paramNames;
11567                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11568             }
11569             
11570             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11571         }
11572     },
11573
11574     /**
11575      * Reloads the Record cache from the configured Proxy using the configured Reader and
11576      * the options from the last load operation performed.
11577      * @param {Object} options (optional) An object containing properties which may override the options
11578      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11579      * the most recently used options are reused).
11580      */
11581     reload : function(options){
11582         this.load(Roo.applyIf(options||{}, this.lastOptions));
11583     },
11584
11585     // private
11586     // Called as a callback by the Reader during a load operation.
11587     loadRecords : function(o, options, success){
11588         if(!o || success === false){
11589             if(success !== false){
11590                 this.fireEvent("load", this, [], options, o);
11591             }
11592             if(options.callback){
11593                 options.callback.call(options.scope || this, [], options, false);
11594             }
11595             return;
11596         }
11597         // if data returned failure - throw an exception.
11598         if (o.success === false) {
11599             // show a message if no listener is registered.
11600             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11601                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11602             }
11603             // loadmask wil be hooked into this..
11604             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11605             return;
11606         }
11607         var r = o.records, t = o.totalRecords || r.length;
11608         
11609         this.fireEvent("beforeloadadd", this, r, options, o);
11610         
11611         if(!options || options.add !== true){
11612             if(this.pruneModifiedRecords){
11613                 this.modified = [];
11614             }
11615             for(var i = 0, len = r.length; i < len; i++){
11616                 r[i].join(this);
11617             }
11618             if(this.snapshot){
11619                 this.data = this.snapshot;
11620                 delete this.snapshot;
11621             }
11622             this.data.clear();
11623             this.data.addAll(r);
11624             this.totalLength = t;
11625             this.applySort();
11626             this.fireEvent("datachanged", this);
11627         }else{
11628             this.totalLength = Math.max(t, this.data.length+r.length);
11629             this.add(r);
11630         }
11631         
11632         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11633                 
11634             var e = new Roo.data.Record({});
11635
11636             e.set(this.parent.displayField, this.parent.emptyTitle);
11637             e.set(this.parent.valueField, '');
11638
11639             this.insert(0, e);
11640         }
11641             
11642         this.fireEvent("load", this, r, options, o);
11643         if(options.callback){
11644             options.callback.call(options.scope || this, r, options, true);
11645         }
11646     },
11647
11648
11649     /**
11650      * Loads data from a passed data block. A Reader which understands the format of the data
11651      * must have been configured in the constructor.
11652      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11653      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11654      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11655      */
11656     loadData : function(o, append){
11657         var r = this.reader.readRecords(o);
11658         this.loadRecords(r, {add: append}, true);
11659     },
11660
11661     /**
11662      * Gets the number of cached records.
11663      * <p>
11664      * <em>If using paging, this may not be the total size of the dataset. If the data object
11665      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11666      * the data set size</em>
11667      */
11668     getCount : function(){
11669         return this.data.length || 0;
11670     },
11671
11672     /**
11673      * Gets the total number of records in the dataset as returned by the server.
11674      * <p>
11675      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11676      * the dataset size</em>
11677      */
11678     getTotalCount : function(){
11679         return this.totalLength || 0;
11680     },
11681
11682     /**
11683      * Returns the sort state of the Store as an object with two properties:
11684      * <pre><code>
11685  field {String} The name of the field by which the Records are sorted
11686  direction {String} The sort order, "ASC" or "DESC"
11687      * </code></pre>
11688      */
11689     getSortState : function(){
11690         return this.sortInfo;
11691     },
11692
11693     // private
11694     applySort : function(){
11695         if(this.sortInfo && !this.remoteSort){
11696             var s = this.sortInfo, f = s.field;
11697             var st = this.fields.get(f).sortType;
11698             var fn = function(r1, r2){
11699                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11700                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11701             };
11702             this.data.sort(s.direction, fn);
11703             if(this.snapshot && this.snapshot != this.data){
11704                 this.snapshot.sort(s.direction, fn);
11705             }
11706         }
11707     },
11708
11709     /**
11710      * Sets the default sort column and order to be used by the next load operation.
11711      * @param {String} fieldName The name of the field to sort by.
11712      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11713      */
11714     setDefaultSort : function(field, dir){
11715         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11716     },
11717
11718     /**
11719      * Sort the Records.
11720      * If remote sorting is used, the sort is performed on the server, and the cache is
11721      * reloaded. If local sorting is used, the cache is sorted internally.
11722      * @param {String} fieldName The name of the field to sort by.
11723      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11724      */
11725     sort : function(fieldName, dir){
11726         var f = this.fields.get(fieldName);
11727         if(!dir){
11728             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11729             
11730             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11731                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11732             }else{
11733                 dir = f.sortDir;
11734             }
11735         }
11736         this.sortToggle[f.name] = dir;
11737         this.sortInfo = {field: f.name, direction: dir};
11738         if(!this.remoteSort){
11739             this.applySort();
11740             this.fireEvent("datachanged", this);
11741         }else{
11742             this.load(this.lastOptions);
11743         }
11744     },
11745
11746     /**
11747      * Calls the specified function for each of the Records in the cache.
11748      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11749      * Returning <em>false</em> aborts and exits the iteration.
11750      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11751      */
11752     each : function(fn, scope){
11753         this.data.each(fn, scope);
11754     },
11755
11756     /**
11757      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11758      * (e.g., during paging).
11759      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11760      */
11761     getModifiedRecords : function(){
11762         return this.modified;
11763     },
11764
11765     // private
11766     createFilterFn : function(property, value, anyMatch){
11767         if(!value.exec){ // not a regex
11768             value = String(value);
11769             if(value.length == 0){
11770                 return false;
11771             }
11772             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11773         }
11774         return function(r){
11775             return value.test(r.data[property]);
11776         };
11777     },
11778
11779     /**
11780      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11781      * @param {String} property A field on your records
11782      * @param {Number} start The record index to start at (defaults to 0)
11783      * @param {Number} end The last record index to include (defaults to length - 1)
11784      * @return {Number} The sum
11785      */
11786     sum : function(property, start, end){
11787         var rs = this.data.items, v = 0;
11788         start = start || 0;
11789         end = (end || end === 0) ? end : rs.length-1;
11790
11791         for(var i = start; i <= end; i++){
11792             v += (rs[i].data[property] || 0);
11793         }
11794         return v;
11795     },
11796
11797     /**
11798      * Filter the records by a specified property.
11799      * @param {String} field A field on your records
11800      * @param {String/RegExp} value Either a string that the field
11801      * should start with or a RegExp to test against the field
11802      * @param {Boolean} anyMatch True to match any part not just the beginning
11803      */
11804     filter : function(property, value, anyMatch){
11805         var fn = this.createFilterFn(property, value, anyMatch);
11806         return fn ? this.filterBy(fn) : this.clearFilter();
11807     },
11808
11809     /**
11810      * Filter by a function. The specified function will be called with each
11811      * record in this data source. If the function returns true the record is included,
11812      * otherwise it is filtered.
11813      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11814      * @param {Object} scope (optional) The scope of the function (defaults to this)
11815      */
11816     filterBy : function(fn, scope){
11817         this.snapshot = this.snapshot || this.data;
11818         this.data = this.queryBy(fn, scope||this);
11819         this.fireEvent("datachanged", this);
11820     },
11821
11822     /**
11823      * Query the records by a specified property.
11824      * @param {String} field A field on your records
11825      * @param {String/RegExp} value Either a string that the field
11826      * should start with or a RegExp to test against the field
11827      * @param {Boolean} anyMatch True to match any part not just the beginning
11828      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11829      */
11830     query : function(property, value, anyMatch){
11831         var fn = this.createFilterFn(property, value, anyMatch);
11832         return fn ? this.queryBy(fn) : this.data.clone();
11833     },
11834
11835     /**
11836      * Query by a function. The specified function will be called with each
11837      * record in this data source. If the function returns true the record is included
11838      * in the results.
11839      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11840      * @param {Object} scope (optional) The scope of the function (defaults to this)
11841       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11842      **/
11843     queryBy : function(fn, scope){
11844         var data = this.snapshot || this.data;
11845         return data.filterBy(fn, scope||this);
11846     },
11847
11848     /**
11849      * Collects unique values for a particular dataIndex from this store.
11850      * @param {String} dataIndex The property to collect
11851      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11852      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11853      * @return {Array} An array of the unique values
11854      **/
11855     collect : function(dataIndex, allowNull, bypassFilter){
11856         var d = (bypassFilter === true && this.snapshot) ?
11857                 this.snapshot.items : this.data.items;
11858         var v, sv, r = [], l = {};
11859         for(var i = 0, len = d.length; i < len; i++){
11860             v = d[i].data[dataIndex];
11861             sv = String(v);
11862             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11863                 l[sv] = true;
11864                 r[r.length] = v;
11865             }
11866         }
11867         return r;
11868     },
11869
11870     /**
11871      * Revert to a view of the Record cache with no filtering applied.
11872      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11873      */
11874     clearFilter : function(suppressEvent){
11875         if(this.snapshot && this.snapshot != this.data){
11876             this.data = this.snapshot;
11877             delete this.snapshot;
11878             if(suppressEvent !== true){
11879                 this.fireEvent("datachanged", this);
11880             }
11881         }
11882     },
11883
11884     // private
11885     afterEdit : function(record){
11886         if(this.modified.indexOf(record) == -1){
11887             this.modified.push(record);
11888         }
11889         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11890     },
11891     
11892     // private
11893     afterReject : function(record){
11894         this.modified.remove(record);
11895         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11896     },
11897
11898     // private
11899     afterCommit : function(record){
11900         this.modified.remove(record);
11901         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11902     },
11903
11904     /**
11905      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11906      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11907      */
11908     commitChanges : function(){
11909         var m = this.modified.slice(0);
11910         this.modified = [];
11911         for(var i = 0, len = m.length; i < len; i++){
11912             m[i].commit();
11913         }
11914     },
11915
11916     /**
11917      * Cancel outstanding changes on all changed records.
11918      */
11919     rejectChanges : function(){
11920         var m = this.modified.slice(0);
11921         this.modified = [];
11922         for(var i = 0, len = m.length; i < len; i++){
11923             m[i].reject();
11924         }
11925     },
11926
11927     onMetaChange : function(meta, rtype, o){
11928         this.recordType = rtype;
11929         this.fields = rtype.prototype.fields;
11930         delete this.snapshot;
11931         this.sortInfo = meta.sortInfo || this.sortInfo;
11932         this.modified = [];
11933         this.fireEvent('metachange', this, this.reader.meta);
11934     },
11935     
11936     moveIndex : function(data, type)
11937     {
11938         var index = this.indexOf(data);
11939         
11940         var newIndex = index + type;
11941         
11942         this.remove(data);
11943         
11944         this.insert(newIndex, data);
11945         
11946     }
11947 });/*
11948  * Based on:
11949  * Ext JS Library 1.1.1
11950  * Copyright(c) 2006-2007, Ext JS, LLC.
11951  *
11952  * Originally Released Under LGPL - original licence link has changed is not relivant.
11953  *
11954  * Fork - LGPL
11955  * <script type="text/javascript">
11956  */
11957
11958 /**
11959  * @class Roo.data.SimpleStore
11960  * @extends Roo.data.Store
11961  * Small helper class to make creating Stores from Array data easier.
11962  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11963  * @cfg {Array} fields An array of field definition objects, or field name strings.
11964  * @cfg {Array} data The multi-dimensional array of data
11965  * @constructor
11966  * @param {Object} config
11967  */
11968 Roo.data.SimpleStore = function(config){
11969     Roo.data.SimpleStore.superclass.constructor.call(this, {
11970         isLocal : true,
11971         reader: new Roo.data.ArrayReader({
11972                 id: config.id
11973             },
11974             Roo.data.Record.create(config.fields)
11975         ),
11976         proxy : new Roo.data.MemoryProxy(config.data)
11977     });
11978     this.load();
11979 };
11980 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11981  * Based on:
11982  * Ext JS Library 1.1.1
11983  * Copyright(c) 2006-2007, Ext JS, LLC.
11984  *
11985  * Originally Released Under LGPL - original licence link has changed is not relivant.
11986  *
11987  * Fork - LGPL
11988  * <script type="text/javascript">
11989  */
11990
11991 /**
11992 /**
11993  * @extends Roo.data.Store
11994  * @class Roo.data.JsonStore
11995  * Small helper class to make creating Stores for JSON data easier. <br/>
11996 <pre><code>
11997 var store = new Roo.data.JsonStore({
11998     url: 'get-images.php',
11999     root: 'images',
12000     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12001 });
12002 </code></pre>
12003  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12004  * JsonReader and HttpProxy (unless inline data is provided).</b>
12005  * @cfg {Array} fields An array of field definition objects, or field name strings.
12006  * @constructor
12007  * @param {Object} config
12008  */
12009 Roo.data.JsonStore = function(c){
12010     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12011         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12012         reader: new Roo.data.JsonReader(c, c.fields)
12013     }));
12014 };
12015 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12016  * Based on:
12017  * Ext JS Library 1.1.1
12018  * Copyright(c) 2006-2007, Ext JS, LLC.
12019  *
12020  * Originally Released Under LGPL - original licence link has changed is not relivant.
12021  *
12022  * Fork - LGPL
12023  * <script type="text/javascript">
12024  */
12025
12026  
12027 Roo.data.Field = function(config){
12028     if(typeof config == "string"){
12029         config = {name: config};
12030     }
12031     Roo.apply(this, config);
12032     
12033     if(!this.type){
12034         this.type = "auto";
12035     }
12036     
12037     var st = Roo.data.SortTypes;
12038     // named sortTypes are supported, here we look them up
12039     if(typeof this.sortType == "string"){
12040         this.sortType = st[this.sortType];
12041     }
12042     
12043     // set default sortType for strings and dates
12044     if(!this.sortType){
12045         switch(this.type){
12046             case "string":
12047                 this.sortType = st.asUCString;
12048                 break;
12049             case "date":
12050                 this.sortType = st.asDate;
12051                 break;
12052             default:
12053                 this.sortType = st.none;
12054         }
12055     }
12056
12057     // define once
12058     var stripRe = /[\$,%]/g;
12059
12060     // prebuilt conversion function for this field, instead of
12061     // switching every time we're reading a value
12062     if(!this.convert){
12063         var cv, dateFormat = this.dateFormat;
12064         switch(this.type){
12065             case "":
12066             case "auto":
12067             case undefined:
12068                 cv = function(v){ return v; };
12069                 break;
12070             case "string":
12071                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12072                 break;
12073             case "int":
12074                 cv = function(v){
12075                     return v !== undefined && v !== null && v !== '' ?
12076                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12077                     };
12078                 break;
12079             case "float":
12080                 cv = function(v){
12081                     return v !== undefined && v !== null && v !== '' ?
12082                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12083                     };
12084                 break;
12085             case "bool":
12086             case "boolean":
12087                 cv = function(v){ return v === true || v === "true" || v == 1; };
12088                 break;
12089             case "date":
12090                 cv = function(v){
12091                     if(!v){
12092                         return '';
12093                     }
12094                     if(v instanceof Date){
12095                         return v;
12096                     }
12097                     if(dateFormat){
12098                         if(dateFormat == "timestamp"){
12099                             return new Date(v*1000);
12100                         }
12101                         return Date.parseDate(v, dateFormat);
12102                     }
12103                     var parsed = Date.parse(v);
12104                     return parsed ? new Date(parsed) : null;
12105                 };
12106              break;
12107             
12108         }
12109         this.convert = cv;
12110     }
12111 };
12112
12113 Roo.data.Field.prototype = {
12114     dateFormat: null,
12115     defaultValue: "",
12116     mapping: null,
12117     sortType : null,
12118     sortDir : "ASC"
12119 };/*
12120  * Based on:
12121  * Ext JS Library 1.1.1
12122  * Copyright(c) 2006-2007, Ext JS, LLC.
12123  *
12124  * Originally Released Under LGPL - original licence link has changed is not relivant.
12125  *
12126  * Fork - LGPL
12127  * <script type="text/javascript">
12128  */
12129  
12130 // Base class for reading structured data from a data source.  This class is intended to be
12131 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12132
12133 /**
12134  * @class Roo.data.DataReader
12135  * Base class for reading structured data from a data source.  This class is intended to be
12136  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12137  */
12138
12139 Roo.data.DataReader = function(meta, recordType){
12140     
12141     this.meta = meta;
12142     
12143     this.recordType = recordType instanceof Array ? 
12144         Roo.data.Record.create(recordType) : recordType;
12145 };
12146
12147 Roo.data.DataReader.prototype = {
12148      /**
12149      * Create an empty record
12150      * @param {Object} data (optional) - overlay some values
12151      * @return {Roo.data.Record} record created.
12152      */
12153     newRow :  function(d) {
12154         var da =  {};
12155         this.recordType.prototype.fields.each(function(c) {
12156             switch( c.type) {
12157                 case 'int' : da[c.name] = 0; break;
12158                 case 'date' : da[c.name] = new Date(); break;
12159                 case 'float' : da[c.name] = 0.0; break;
12160                 case 'boolean' : da[c.name] = false; break;
12161                 default : da[c.name] = ""; break;
12162             }
12163             
12164         });
12165         return new this.recordType(Roo.apply(da, d));
12166     }
12167     
12168 };/*
12169  * Based on:
12170  * Ext JS Library 1.1.1
12171  * Copyright(c) 2006-2007, Ext JS, LLC.
12172  *
12173  * Originally Released Under LGPL - original licence link has changed is not relivant.
12174  *
12175  * Fork - LGPL
12176  * <script type="text/javascript">
12177  */
12178
12179 /**
12180  * @class Roo.data.DataProxy
12181  * @extends Roo.data.Observable
12182  * This class is an abstract base class for implementations which provide retrieval of
12183  * unformatted data objects.<br>
12184  * <p>
12185  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12186  * (of the appropriate type which knows how to parse the data object) to provide a block of
12187  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12188  * <p>
12189  * Custom implementations must implement the load method as described in
12190  * {@link Roo.data.HttpProxy#load}.
12191  */
12192 Roo.data.DataProxy = function(){
12193     this.addEvents({
12194         /**
12195          * @event beforeload
12196          * Fires before a network request is made to retrieve a data object.
12197          * @param {Object} This DataProxy object.
12198          * @param {Object} params The params parameter to the load function.
12199          */
12200         beforeload : true,
12201         /**
12202          * @event load
12203          * Fires before the load method's callback is called.
12204          * @param {Object} This DataProxy object.
12205          * @param {Object} o The data object.
12206          * @param {Object} arg The callback argument object passed to the load function.
12207          */
12208         load : true,
12209         /**
12210          * @event loadexception
12211          * Fires if an Exception occurs during data retrieval.
12212          * @param {Object} This DataProxy object.
12213          * @param {Object} o The data object.
12214          * @param {Object} arg The callback argument object passed to the load function.
12215          * @param {Object} e The Exception.
12216          */
12217         loadexception : true
12218     });
12219     Roo.data.DataProxy.superclass.constructor.call(this);
12220 };
12221
12222 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12223
12224     /**
12225      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12226      */
12227 /*
12228  * Based on:
12229  * Ext JS Library 1.1.1
12230  * Copyright(c) 2006-2007, Ext JS, LLC.
12231  *
12232  * Originally Released Under LGPL - original licence link has changed is not relivant.
12233  *
12234  * Fork - LGPL
12235  * <script type="text/javascript">
12236  */
12237 /**
12238  * @class Roo.data.MemoryProxy
12239  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12240  * to the Reader when its load method is called.
12241  * @constructor
12242  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12243  */
12244 Roo.data.MemoryProxy = function(data){
12245     if (data.data) {
12246         data = data.data;
12247     }
12248     Roo.data.MemoryProxy.superclass.constructor.call(this);
12249     this.data = data;
12250 };
12251
12252 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12253     
12254     /**
12255      * Load data from the requested source (in this case an in-memory
12256      * data object passed to the constructor), read the data object into
12257      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12258      * process that block using the passed callback.
12259      * @param {Object} params This parameter is not used by the MemoryProxy class.
12260      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12261      * object into a block of Roo.data.Records.
12262      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12263      * The function must be passed <ul>
12264      * <li>The Record block object</li>
12265      * <li>The "arg" argument from the load function</li>
12266      * <li>A boolean success indicator</li>
12267      * </ul>
12268      * @param {Object} scope The scope in which to call the callback
12269      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12270      */
12271     load : function(params, reader, callback, scope, arg){
12272         params = params || {};
12273         var result;
12274         try {
12275             result = reader.readRecords(this.data);
12276         }catch(e){
12277             this.fireEvent("loadexception", this, arg, null, e);
12278             callback.call(scope, null, arg, false);
12279             return;
12280         }
12281         callback.call(scope, result, arg, true);
12282     },
12283     
12284     // private
12285     update : function(params, records){
12286         
12287     }
12288 });/*
12289  * Based on:
12290  * Ext JS Library 1.1.1
12291  * Copyright(c) 2006-2007, Ext JS, LLC.
12292  *
12293  * Originally Released Under LGPL - original licence link has changed is not relivant.
12294  *
12295  * Fork - LGPL
12296  * <script type="text/javascript">
12297  */
12298 /**
12299  * @class Roo.data.HttpProxy
12300  * @extends Roo.data.DataProxy
12301  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12302  * configured to reference a certain URL.<br><br>
12303  * <p>
12304  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12305  * from which the running page was served.<br><br>
12306  * <p>
12307  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12308  * <p>
12309  * Be aware that to enable the browser to parse an XML document, the server must set
12310  * the Content-Type header in the HTTP response to "text/xml".
12311  * @constructor
12312  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12313  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12314  * will be used to make the request.
12315  */
12316 Roo.data.HttpProxy = function(conn){
12317     Roo.data.HttpProxy.superclass.constructor.call(this);
12318     // is conn a conn config or a real conn?
12319     this.conn = conn;
12320     this.useAjax = !conn || !conn.events;
12321   
12322 };
12323
12324 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12325     // thse are take from connection...
12326     
12327     /**
12328      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12329      */
12330     /**
12331      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12332      * extra parameters to each request made by this object. (defaults to undefined)
12333      */
12334     /**
12335      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12336      *  to each request made by this object. (defaults to undefined)
12337      */
12338     /**
12339      * @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)
12340      */
12341     /**
12342      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12343      */
12344      /**
12345      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12346      * @type Boolean
12347      */
12348   
12349
12350     /**
12351      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12352      * @type Boolean
12353      */
12354     /**
12355      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12356      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12357      * a finer-grained basis than the DataProxy events.
12358      */
12359     getConnection : function(){
12360         return this.useAjax ? Roo.Ajax : this.conn;
12361     },
12362
12363     /**
12364      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12365      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12366      * process that block using the passed callback.
12367      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12368      * for the request to the remote server.
12369      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12370      * object into a block of Roo.data.Records.
12371      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12372      * The function must be passed <ul>
12373      * <li>The Record block object</li>
12374      * <li>The "arg" argument from the load function</li>
12375      * <li>A boolean success indicator</li>
12376      * </ul>
12377      * @param {Object} scope The scope in which to call the callback
12378      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12379      */
12380     load : function(params, reader, callback, scope, arg){
12381         if(this.fireEvent("beforeload", this, params) !== false){
12382             var  o = {
12383                 params : params || {},
12384                 request: {
12385                     callback : callback,
12386                     scope : scope,
12387                     arg : arg
12388                 },
12389                 reader: reader,
12390                 callback : this.loadResponse,
12391                 scope: this
12392             };
12393             if(this.useAjax){
12394                 Roo.applyIf(o, this.conn);
12395                 if(this.activeRequest){
12396                     Roo.Ajax.abort(this.activeRequest);
12397                 }
12398                 this.activeRequest = Roo.Ajax.request(o);
12399             }else{
12400                 this.conn.request(o);
12401             }
12402         }else{
12403             callback.call(scope||this, null, arg, false);
12404         }
12405     },
12406
12407     // private
12408     loadResponse : function(o, success, response){
12409         delete this.activeRequest;
12410         if(!success){
12411             this.fireEvent("loadexception", this, o, response);
12412             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12413             return;
12414         }
12415         var result;
12416         try {
12417             result = o.reader.read(response);
12418         }catch(e){
12419             this.fireEvent("loadexception", this, o, response, e);
12420             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12421             return;
12422         }
12423         
12424         this.fireEvent("load", this, o, o.request.arg);
12425         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12426     },
12427
12428     // private
12429     update : function(dataSet){
12430
12431     },
12432
12433     // private
12434     updateResponse : function(dataSet){
12435
12436     }
12437 });/*
12438  * Based on:
12439  * Ext JS Library 1.1.1
12440  * Copyright(c) 2006-2007, Ext JS, LLC.
12441  *
12442  * Originally Released Under LGPL - original licence link has changed is not relivant.
12443  *
12444  * Fork - LGPL
12445  * <script type="text/javascript">
12446  */
12447
12448 /**
12449  * @class Roo.data.ScriptTagProxy
12450  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12451  * other than the originating domain of the running page.<br><br>
12452  * <p>
12453  * <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
12454  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12455  * <p>
12456  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12457  * source code that is used as the source inside a &lt;script> tag.<br><br>
12458  * <p>
12459  * In order for the browser to process the returned data, the server must wrap the data object
12460  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12461  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12462  * depending on whether the callback name was passed:
12463  * <p>
12464  * <pre><code>
12465 boolean scriptTag = false;
12466 String cb = request.getParameter("callback");
12467 if (cb != null) {
12468     scriptTag = true;
12469     response.setContentType("text/javascript");
12470 } else {
12471     response.setContentType("application/x-json");
12472 }
12473 Writer out = response.getWriter();
12474 if (scriptTag) {
12475     out.write(cb + "(");
12476 }
12477 out.print(dataBlock.toJsonString());
12478 if (scriptTag) {
12479     out.write(");");
12480 }
12481 </pre></code>
12482  *
12483  * @constructor
12484  * @param {Object} config A configuration object.
12485  */
12486 Roo.data.ScriptTagProxy = function(config){
12487     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12488     Roo.apply(this, config);
12489     this.head = document.getElementsByTagName("head")[0];
12490 };
12491
12492 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12493
12494 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12495     /**
12496      * @cfg {String} url The URL from which to request the data object.
12497      */
12498     /**
12499      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12500      */
12501     timeout : 30000,
12502     /**
12503      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12504      * the server the name of the callback function set up by the load call to process the returned data object.
12505      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12506      * javascript output which calls this named function passing the data object as its only parameter.
12507      */
12508     callbackParam : "callback",
12509     /**
12510      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12511      * name to the request.
12512      */
12513     nocache : true,
12514
12515     /**
12516      * Load data from the configured URL, read the data object into
12517      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12518      * process that block using the passed callback.
12519      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12520      * for the request to the remote server.
12521      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12522      * object into a block of Roo.data.Records.
12523      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12524      * The function must be passed <ul>
12525      * <li>The Record block object</li>
12526      * <li>The "arg" argument from the load function</li>
12527      * <li>A boolean success indicator</li>
12528      * </ul>
12529      * @param {Object} scope The scope in which to call the callback
12530      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12531      */
12532     load : function(params, reader, callback, scope, arg){
12533         if(this.fireEvent("beforeload", this, params) !== false){
12534
12535             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12536
12537             var url = this.url;
12538             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12539             if(this.nocache){
12540                 url += "&_dc=" + (new Date().getTime());
12541             }
12542             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12543             var trans = {
12544                 id : transId,
12545                 cb : "stcCallback"+transId,
12546                 scriptId : "stcScript"+transId,
12547                 params : params,
12548                 arg : arg,
12549                 url : url,
12550                 callback : callback,
12551                 scope : scope,
12552                 reader : reader
12553             };
12554             var conn = this;
12555
12556             window[trans.cb] = function(o){
12557                 conn.handleResponse(o, trans);
12558             };
12559
12560             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12561
12562             if(this.autoAbort !== false){
12563                 this.abort();
12564             }
12565
12566             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12567
12568             var script = document.createElement("script");
12569             script.setAttribute("src", url);
12570             script.setAttribute("type", "text/javascript");
12571             script.setAttribute("id", trans.scriptId);
12572             this.head.appendChild(script);
12573
12574             this.trans = trans;
12575         }else{
12576             callback.call(scope||this, null, arg, false);
12577         }
12578     },
12579
12580     // private
12581     isLoading : function(){
12582         return this.trans ? true : false;
12583     },
12584
12585     /**
12586      * Abort the current server request.
12587      */
12588     abort : function(){
12589         if(this.isLoading()){
12590             this.destroyTrans(this.trans);
12591         }
12592     },
12593
12594     // private
12595     destroyTrans : function(trans, isLoaded){
12596         this.head.removeChild(document.getElementById(trans.scriptId));
12597         clearTimeout(trans.timeoutId);
12598         if(isLoaded){
12599             window[trans.cb] = undefined;
12600             try{
12601                 delete window[trans.cb];
12602             }catch(e){}
12603         }else{
12604             // if hasn't been loaded, wait for load to remove it to prevent script error
12605             window[trans.cb] = function(){
12606                 window[trans.cb] = undefined;
12607                 try{
12608                     delete window[trans.cb];
12609                 }catch(e){}
12610             };
12611         }
12612     },
12613
12614     // private
12615     handleResponse : function(o, trans){
12616         this.trans = false;
12617         this.destroyTrans(trans, true);
12618         var result;
12619         try {
12620             result = trans.reader.readRecords(o);
12621         }catch(e){
12622             this.fireEvent("loadexception", this, o, trans.arg, e);
12623             trans.callback.call(trans.scope||window, null, trans.arg, false);
12624             return;
12625         }
12626         this.fireEvent("load", this, o, trans.arg);
12627         trans.callback.call(trans.scope||window, result, trans.arg, true);
12628     },
12629
12630     // private
12631     handleFailure : function(trans){
12632         this.trans = false;
12633         this.destroyTrans(trans, false);
12634         this.fireEvent("loadexception", this, null, trans.arg);
12635         trans.callback.call(trans.scope||window, null, trans.arg, false);
12636     }
12637 });/*
12638  * Based on:
12639  * Ext JS Library 1.1.1
12640  * Copyright(c) 2006-2007, Ext JS, LLC.
12641  *
12642  * Originally Released Under LGPL - original licence link has changed is not relivant.
12643  *
12644  * Fork - LGPL
12645  * <script type="text/javascript">
12646  */
12647
12648 /**
12649  * @class Roo.data.JsonReader
12650  * @extends Roo.data.DataReader
12651  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12652  * based on mappings in a provided Roo.data.Record constructor.
12653  * 
12654  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12655  * in the reply previously. 
12656  * 
12657  * <p>
12658  * Example code:
12659  * <pre><code>
12660 var RecordDef = Roo.data.Record.create([
12661     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12662     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12663 ]);
12664 var myReader = new Roo.data.JsonReader({
12665     totalProperty: "results",    // The property which contains the total dataset size (optional)
12666     root: "rows",                // The property which contains an Array of row objects
12667     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12668 }, RecordDef);
12669 </code></pre>
12670  * <p>
12671  * This would consume a JSON file like this:
12672  * <pre><code>
12673 { 'results': 2, 'rows': [
12674     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12675     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12676 }
12677 </code></pre>
12678  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12679  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12680  * paged from the remote server.
12681  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12682  * @cfg {String} root name of the property which contains the Array of row objects.
12683  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12684  * @cfg {Array} fields Array of field definition objects
12685  * @constructor
12686  * Create a new JsonReader
12687  * @param {Object} meta Metadata configuration options
12688  * @param {Object} recordType Either an Array of field definition objects,
12689  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12690  */
12691 Roo.data.JsonReader = function(meta, recordType){
12692     
12693     meta = meta || {};
12694     // set some defaults:
12695     Roo.applyIf(meta, {
12696         totalProperty: 'total',
12697         successProperty : 'success',
12698         root : 'data',
12699         id : 'id'
12700     });
12701     
12702     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12703 };
12704 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12705     
12706     /**
12707      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12708      * Used by Store query builder to append _requestMeta to params.
12709      * 
12710      */
12711     metaFromRemote : false,
12712     /**
12713      * This method is only used by a DataProxy which has retrieved data from a remote server.
12714      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12715      * @return {Object} data A data block which is used by an Roo.data.Store object as
12716      * a cache of Roo.data.Records.
12717      */
12718     read : function(response){
12719         var json = response.responseText;
12720        
12721         var o = /* eval:var:o */ eval("("+json+")");
12722         if(!o) {
12723             throw {message: "JsonReader.read: Json object not found"};
12724         }
12725         
12726         if(o.metaData){
12727             
12728             delete this.ef;
12729             this.metaFromRemote = true;
12730             this.meta = o.metaData;
12731             this.recordType = Roo.data.Record.create(o.metaData.fields);
12732             this.onMetaChange(this.meta, this.recordType, o);
12733         }
12734         return this.readRecords(o);
12735     },
12736
12737     // private function a store will implement
12738     onMetaChange : function(meta, recordType, o){
12739
12740     },
12741
12742     /**
12743          * @ignore
12744          */
12745     simpleAccess: function(obj, subsc) {
12746         return obj[subsc];
12747     },
12748
12749         /**
12750          * @ignore
12751          */
12752     getJsonAccessor: function(){
12753         var re = /[\[\.]/;
12754         return function(expr) {
12755             try {
12756                 return(re.test(expr))
12757                     ? new Function("obj", "return obj." + expr)
12758                     : function(obj){
12759                         return obj[expr];
12760                     };
12761             } catch(e){}
12762             return Roo.emptyFn;
12763         };
12764     }(),
12765
12766     /**
12767      * Create a data block containing Roo.data.Records from an XML document.
12768      * @param {Object} o An object which contains an Array of row objects in the property specified
12769      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12770      * which contains the total size of the dataset.
12771      * @return {Object} data A data block which is used by an Roo.data.Store object as
12772      * a cache of Roo.data.Records.
12773      */
12774     readRecords : function(o){
12775         /**
12776          * After any data loads, the raw JSON data is available for further custom processing.
12777          * @type Object
12778          */
12779         this.o = o;
12780         var s = this.meta, Record = this.recordType,
12781             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12782
12783 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12784         if (!this.ef) {
12785             if(s.totalProperty) {
12786                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12787                 }
12788                 if(s.successProperty) {
12789                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12790                 }
12791                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12792                 if (s.id) {
12793                         var g = this.getJsonAccessor(s.id);
12794                         this.getId = function(rec) {
12795                                 var r = g(rec);  
12796                                 return (r === undefined || r === "") ? null : r;
12797                         };
12798                 } else {
12799                         this.getId = function(){return null;};
12800                 }
12801             this.ef = [];
12802             for(var jj = 0; jj < fl; jj++){
12803                 f = fi[jj];
12804                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12805                 this.ef[jj] = this.getJsonAccessor(map);
12806             }
12807         }
12808
12809         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12810         if(s.totalProperty){
12811             var vt = parseInt(this.getTotal(o), 10);
12812             if(!isNaN(vt)){
12813                 totalRecords = vt;
12814             }
12815         }
12816         if(s.successProperty){
12817             var vs = this.getSuccess(o);
12818             if(vs === false || vs === 'false'){
12819                 success = false;
12820             }
12821         }
12822         var records = [];
12823         for(var i = 0; i < c; i++){
12824                 var n = root[i];
12825             var values = {};
12826             var id = this.getId(n);
12827             for(var j = 0; j < fl; j++){
12828                 f = fi[j];
12829             var v = this.ef[j](n);
12830             if (!f.convert) {
12831                 Roo.log('missing convert for ' + f.name);
12832                 Roo.log(f);
12833                 continue;
12834             }
12835             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12836             }
12837             var record = new Record(values, id);
12838             record.json = n;
12839             records[i] = record;
12840         }
12841         return {
12842             raw : o,
12843             success : success,
12844             records : records,
12845             totalRecords : totalRecords
12846         };
12847     }
12848 });/*
12849  * Based on:
12850  * Ext JS Library 1.1.1
12851  * Copyright(c) 2006-2007, Ext JS, LLC.
12852  *
12853  * Originally Released Under LGPL - original licence link has changed is not relivant.
12854  *
12855  * Fork - LGPL
12856  * <script type="text/javascript">
12857  */
12858
12859 /**
12860  * @class Roo.data.ArrayReader
12861  * @extends Roo.data.DataReader
12862  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12863  * Each element of that Array represents a row of data fields. The
12864  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12865  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12866  * <p>
12867  * Example code:.
12868  * <pre><code>
12869 var RecordDef = Roo.data.Record.create([
12870     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12871     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12872 ]);
12873 var myReader = new Roo.data.ArrayReader({
12874     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12875 }, RecordDef);
12876 </code></pre>
12877  * <p>
12878  * This would consume an Array like this:
12879  * <pre><code>
12880 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12881   </code></pre>
12882  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12883  * @constructor
12884  * Create a new JsonReader
12885  * @param {Object} meta Metadata configuration options.
12886  * @param {Object} recordType Either an Array of field definition objects
12887  * as specified to {@link Roo.data.Record#create},
12888  * or an {@link Roo.data.Record} object
12889  * created using {@link Roo.data.Record#create}.
12890  */
12891 Roo.data.ArrayReader = function(meta, recordType){
12892     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12893 };
12894
12895 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12896     /**
12897      * Create a data block containing Roo.data.Records from an XML document.
12898      * @param {Object} o An Array of row objects which represents the dataset.
12899      * @return {Object} data A data block which is used by an Roo.data.Store object as
12900      * a cache of Roo.data.Records.
12901      */
12902     readRecords : function(o){
12903         var sid = this.meta ? this.meta.id : null;
12904         var recordType = this.recordType, fields = recordType.prototype.fields;
12905         var records = [];
12906         var root = o;
12907             for(var i = 0; i < root.length; i++){
12908                     var n = root[i];
12909                 var values = {};
12910                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12911                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12912                 var f = fields.items[j];
12913                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12914                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12915                 v = f.convert(v);
12916                 values[f.name] = v;
12917             }
12918                 var record = new recordType(values, id);
12919                 record.json = n;
12920                 records[records.length] = record;
12921             }
12922             return {
12923                 records : records,
12924                 totalRecords : records.length
12925             };
12926     }
12927 });/*
12928  * - LGPL
12929  * * 
12930  */
12931
12932 /**
12933  * @class Roo.bootstrap.ComboBox
12934  * @extends Roo.bootstrap.TriggerField
12935  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12936  * @cfg {Boolean} append (true|false) default false
12937  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12938  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12939  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12940  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12941  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12942  * @cfg {Boolean} animate default true
12943  * @cfg {Boolean} emptyResultText only for touch device
12944  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12945  * @cfg {String} emptyTitle default ''
12946  * @constructor
12947  * Create a new ComboBox.
12948  * @param {Object} config Configuration options
12949  */
12950 Roo.bootstrap.ComboBox = function(config){
12951     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12952     this.addEvents({
12953         /**
12954          * @event expand
12955          * Fires when the dropdown list is expanded
12956         * @param {Roo.bootstrap.ComboBox} combo This combo box
12957         */
12958         'expand' : true,
12959         /**
12960          * @event collapse
12961          * Fires when the dropdown list is collapsed
12962         * @param {Roo.bootstrap.ComboBox} combo This combo box
12963         */
12964         'collapse' : true,
12965         /**
12966          * @event beforeselect
12967          * Fires before a list item is selected. Return false to cancel the selection.
12968         * @param {Roo.bootstrap.ComboBox} combo This combo box
12969         * @param {Roo.data.Record} record The data record returned from the underlying store
12970         * @param {Number} index The index of the selected item in the dropdown list
12971         */
12972         'beforeselect' : true,
12973         /**
12974          * @event select
12975          * Fires when a list item is selected
12976         * @param {Roo.bootstrap.ComboBox} combo This combo box
12977         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12978         * @param {Number} index The index of the selected item in the dropdown list
12979         */
12980         'select' : true,
12981         /**
12982          * @event beforequery
12983          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12984          * The event object passed has these properties:
12985         * @param {Roo.bootstrap.ComboBox} combo This combo box
12986         * @param {String} query The query
12987         * @param {Boolean} forceAll true to force "all" query
12988         * @param {Boolean} cancel true to cancel the query
12989         * @param {Object} e The query event object
12990         */
12991         'beforequery': true,
12992          /**
12993          * @event add
12994          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12995         * @param {Roo.bootstrap.ComboBox} combo This combo box
12996         */
12997         'add' : true,
12998         /**
12999          * @event edit
13000          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13001         * @param {Roo.bootstrap.ComboBox} combo This combo box
13002         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13003         */
13004         'edit' : true,
13005         /**
13006          * @event remove
13007          * Fires when the remove value from the combobox array
13008         * @param {Roo.bootstrap.ComboBox} combo This combo box
13009         */
13010         'remove' : true,
13011         /**
13012          * @event afterremove
13013          * Fires when the remove value from the combobox array
13014         * @param {Roo.bootstrap.ComboBox} combo This combo box
13015         */
13016         'afterremove' : true,
13017         /**
13018          * @event specialfilter
13019          * Fires when specialfilter
13020             * @param {Roo.bootstrap.ComboBox} combo This combo box
13021             */
13022         'specialfilter' : true,
13023         /**
13024          * @event tick
13025          * Fires when tick the element
13026             * @param {Roo.bootstrap.ComboBox} combo This combo box
13027             */
13028         'tick' : true,
13029         /**
13030          * @event touchviewdisplay
13031          * Fires when touch view require special display (default is using displayField)
13032             * @param {Roo.bootstrap.ComboBox} combo This combo box
13033             * @param {Object} cfg set html .
13034             */
13035         'touchviewdisplay' : true
13036         
13037     });
13038     
13039     this.item = [];
13040     this.tickItems = [];
13041     
13042     this.selectedIndex = -1;
13043     if(this.mode == 'local'){
13044         if(config.queryDelay === undefined){
13045             this.queryDelay = 10;
13046         }
13047         if(config.minChars === undefined){
13048             this.minChars = 0;
13049         }
13050     }
13051 };
13052
13053 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13054      
13055     /**
13056      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13057      * rendering into an Roo.Editor, defaults to false)
13058      */
13059     /**
13060      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13061      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13062      */
13063     /**
13064      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13065      */
13066     /**
13067      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13068      * the dropdown list (defaults to undefined, with no header element)
13069      */
13070
13071      /**
13072      * @cfg {String/Roo.Template} tpl The template to use to render the output
13073      */
13074      
13075      /**
13076      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13077      */
13078     listWidth: undefined,
13079     /**
13080      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13081      * mode = 'remote' or 'text' if mode = 'local')
13082      */
13083     displayField: undefined,
13084     
13085     /**
13086      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13087      * mode = 'remote' or 'value' if mode = 'local'). 
13088      * Note: use of a valueField requires the user make a selection
13089      * in order for a value to be mapped.
13090      */
13091     valueField: undefined,
13092     /**
13093      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13094      */
13095     modalTitle : '',
13096     
13097     /**
13098      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13099      * field's data value (defaults to the underlying DOM element's name)
13100      */
13101     hiddenName: undefined,
13102     /**
13103      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13104      */
13105     listClass: '',
13106     /**
13107      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13108      */
13109     selectedClass: 'active',
13110     
13111     /**
13112      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13113      */
13114     shadow:'sides',
13115     /**
13116      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13117      * anchor positions (defaults to 'tl-bl')
13118      */
13119     listAlign: 'tl-bl?',
13120     /**
13121      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13122      */
13123     maxHeight: 300,
13124     /**
13125      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13126      * query specified by the allQuery config option (defaults to 'query')
13127      */
13128     triggerAction: 'query',
13129     /**
13130      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13131      * (defaults to 4, does not apply if editable = false)
13132      */
13133     minChars : 4,
13134     /**
13135      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13136      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13137      */
13138     typeAhead: false,
13139     /**
13140      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13141      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13142      */
13143     queryDelay: 500,
13144     /**
13145      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13146      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13147      */
13148     pageSize: 0,
13149     /**
13150      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13151      * when editable = true (defaults to false)
13152      */
13153     selectOnFocus:false,
13154     /**
13155      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13156      */
13157     queryParam: 'query',
13158     /**
13159      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13160      * when mode = 'remote' (defaults to 'Loading...')
13161      */
13162     loadingText: 'Loading...',
13163     /**
13164      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13165      */
13166     resizable: false,
13167     /**
13168      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13169      */
13170     handleHeight : 8,
13171     /**
13172      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13173      * traditional select (defaults to true)
13174      */
13175     editable: true,
13176     /**
13177      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13178      */
13179     allQuery: '',
13180     /**
13181      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13182      */
13183     mode: 'remote',
13184     /**
13185      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13186      * listWidth has a higher value)
13187      */
13188     minListWidth : 70,
13189     /**
13190      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13191      * allow the user to set arbitrary text into the field (defaults to false)
13192      */
13193     forceSelection:false,
13194     /**
13195      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13196      * if typeAhead = true (defaults to 250)
13197      */
13198     typeAheadDelay : 250,
13199     /**
13200      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13201      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13202      */
13203     valueNotFoundText : undefined,
13204     /**
13205      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13206      */
13207     blockFocus : false,
13208     
13209     /**
13210      * @cfg {Boolean} disableClear Disable showing of clear button.
13211      */
13212     disableClear : false,
13213     /**
13214      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13215      */
13216     alwaysQuery : false,
13217     
13218     /**
13219      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13220      */
13221     multiple : false,
13222     
13223     /**
13224      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13225      */
13226     invalidClass : "has-warning",
13227     
13228     /**
13229      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13230      */
13231     validClass : "has-success",
13232     
13233     /**
13234      * @cfg {Boolean} specialFilter (true|false) special filter default false
13235      */
13236     specialFilter : false,
13237     
13238     /**
13239      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13240      */
13241     mobileTouchView : true,
13242     
13243     /**
13244      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13245      */
13246     useNativeIOS : false,
13247     
13248     /**
13249      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13250      */
13251     mobile_restrict_height : false,
13252     
13253     ios_options : false,
13254     
13255     //private
13256     addicon : false,
13257     editicon: false,
13258     
13259     page: 0,
13260     hasQuery: false,
13261     append: false,
13262     loadNext: false,
13263     autoFocus : true,
13264     tickable : false,
13265     btnPosition : 'right',
13266     triggerList : true,
13267     showToggleBtn : true,
13268     animate : true,
13269     emptyResultText: 'Empty',
13270     triggerText : 'Select',
13271     emptyTitle : '',
13272     
13273     // element that contains real text value.. (when hidden is used..)
13274     
13275     getAutoCreate : function()
13276     {   
13277         var cfg = false;
13278         //render
13279         /*
13280          * Render classic select for iso
13281          */
13282         
13283         if(Roo.isIOS && this.useNativeIOS){
13284             cfg = this.getAutoCreateNativeIOS();
13285             return cfg;
13286         }
13287         
13288         /*
13289          * Touch Devices
13290          */
13291         
13292         if(Roo.isTouch && this.mobileTouchView){
13293             cfg = this.getAutoCreateTouchView();
13294             return cfg;;
13295         }
13296         
13297         /*
13298          *  Normal ComboBox
13299          */
13300         if(!this.tickable){
13301             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13302             return cfg;
13303         }
13304         
13305         /*
13306          *  ComboBox with tickable selections
13307          */
13308              
13309         var align = this.labelAlign || this.parentLabelAlign();
13310         
13311         cfg = {
13312             cls : 'form-group roo-combobox-tickable' //input-group
13313         };
13314         
13315         var btn_text_select = '';
13316         var btn_text_done = '';
13317         var btn_text_cancel = '';
13318         
13319         if (this.btn_text_show) {
13320             btn_text_select = 'Select';
13321             btn_text_done = 'Done';
13322             btn_text_cancel = 'Cancel'; 
13323         }
13324         
13325         var buttons = {
13326             tag : 'div',
13327             cls : 'tickable-buttons',
13328             cn : [
13329                 {
13330                     tag : 'button',
13331                     type : 'button',
13332                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13333                     //html : this.triggerText
13334                     html: btn_text_select
13335                 },
13336                 {
13337                     tag : 'button',
13338                     type : 'button',
13339                     name : 'ok',
13340                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13341                     //html : 'Done'
13342                     html: btn_text_done
13343                 },
13344                 {
13345                     tag : 'button',
13346                     type : 'button',
13347                     name : 'cancel',
13348                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13349                     //html : 'Cancel'
13350                     html: btn_text_cancel
13351                 }
13352             ]
13353         };
13354         
13355         if(this.editable){
13356             buttons.cn.unshift({
13357                 tag: 'input',
13358                 cls: 'roo-select2-search-field-input'
13359             });
13360         }
13361         
13362         var _this = this;
13363         
13364         Roo.each(buttons.cn, function(c){
13365             if (_this.size) {
13366                 c.cls += ' btn-' + _this.size;
13367             }
13368
13369             if (_this.disabled) {
13370                 c.disabled = true;
13371             }
13372         });
13373         
13374         var box = {
13375             tag: 'div',
13376             style : 'display: contents',
13377             cn: [
13378                 {
13379                     tag: 'input',
13380                     type : 'hidden',
13381                     cls: 'form-hidden-field'
13382                 },
13383                 {
13384                     tag: 'ul',
13385                     cls: 'roo-select2-choices',
13386                     cn:[
13387                         {
13388                             tag: 'li',
13389                             cls: 'roo-select2-search-field',
13390                             cn: [
13391                                 buttons
13392                             ]
13393                         }
13394                     ]
13395                 }
13396             ]
13397         };
13398         
13399         var combobox = {
13400             cls: 'roo-select2-container input-group roo-select2-container-multi',
13401             cn: [
13402                 
13403                 box
13404 //                {
13405 //                    tag: 'ul',
13406 //                    cls: 'typeahead typeahead-long dropdown-menu',
13407 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13408 //                }
13409             ]
13410         };
13411         
13412         if(this.hasFeedback && !this.allowBlank){
13413             
13414             var feedback = {
13415                 tag: 'span',
13416                 cls: 'glyphicon form-control-feedback'
13417             };
13418
13419             combobox.cn.push(feedback);
13420         }
13421         
13422         var indicator = {
13423             tag : 'i',
13424             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13425             tooltip : 'This field is required'
13426         };
13427         if (Roo.bootstrap.version == 4) {
13428             indicator = {
13429                 tag : 'i',
13430                 style : 'display:none'
13431             };
13432         }
13433         if (align ==='left' && this.fieldLabel.length) {
13434             
13435             cfg.cls += ' roo-form-group-label-left row';
13436             
13437             cfg.cn = [
13438                 indicator,
13439                 {
13440                     tag: 'label',
13441                     'for' :  id,
13442                     cls : 'control-label col-form-label',
13443                     html : this.fieldLabel
13444
13445                 },
13446                 {
13447                     cls : "", 
13448                     cn: [
13449                         combobox
13450                     ]
13451                 }
13452
13453             ];
13454             
13455             var labelCfg = cfg.cn[1];
13456             var contentCfg = cfg.cn[2];
13457             
13458
13459             if(this.indicatorpos == 'right'){
13460                 
13461                 cfg.cn = [
13462                     {
13463                         tag: 'label',
13464                         'for' :  id,
13465                         cls : 'control-label col-form-label',
13466                         cn : [
13467                             {
13468                                 tag : 'span',
13469                                 html : this.fieldLabel
13470                             },
13471                             indicator
13472                         ]
13473                     },
13474                     {
13475                         cls : "",
13476                         cn: [
13477                             combobox
13478                         ]
13479                     }
13480
13481                 ];
13482                 
13483                 
13484                 
13485                 labelCfg = cfg.cn[0];
13486                 contentCfg = cfg.cn[1];
13487             
13488             }
13489             
13490             if(this.labelWidth > 12){
13491                 labelCfg.style = "width: " + this.labelWidth + 'px';
13492             }
13493             
13494             if(this.labelWidth < 13 && this.labelmd == 0){
13495                 this.labelmd = this.labelWidth;
13496             }
13497             
13498             if(this.labellg > 0){
13499                 labelCfg.cls += ' col-lg-' + this.labellg;
13500                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13501             }
13502             
13503             if(this.labelmd > 0){
13504                 labelCfg.cls += ' col-md-' + this.labelmd;
13505                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13506             }
13507             
13508             if(this.labelsm > 0){
13509                 labelCfg.cls += ' col-sm-' + this.labelsm;
13510                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13511             }
13512             
13513             if(this.labelxs > 0){
13514                 labelCfg.cls += ' col-xs-' + this.labelxs;
13515                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13516             }
13517                 
13518                 
13519         } else if ( this.fieldLabel.length) {
13520 //                Roo.log(" label");
13521                  cfg.cn = [
13522                    indicator,
13523                     {
13524                         tag: 'label',
13525                         //cls : 'input-group-addon',
13526                         html : this.fieldLabel
13527                     },
13528                     combobox
13529                 ];
13530                 
13531                 if(this.indicatorpos == 'right'){
13532                     cfg.cn = [
13533                         {
13534                             tag: 'label',
13535                             //cls : 'input-group-addon',
13536                             html : this.fieldLabel
13537                         },
13538                         indicator,
13539                         combobox
13540                     ];
13541                     
13542                 }
13543
13544         } else {
13545             
13546 //                Roo.log(" no label && no align");
13547                 cfg = combobox
13548                      
13549                 
13550         }
13551          
13552         var settings=this;
13553         ['xs','sm','md','lg'].map(function(size){
13554             if (settings[size]) {
13555                 cfg.cls += ' col-' + size + '-' + settings[size];
13556             }
13557         });
13558         
13559         return cfg;
13560         
13561     },
13562     
13563     _initEventsCalled : false,
13564     
13565     // private
13566     initEvents: function()
13567     {   
13568         if (this._initEventsCalled) { // as we call render... prevent looping...
13569             return;
13570         }
13571         this._initEventsCalled = true;
13572         
13573         if (!this.store) {
13574             throw "can not find store for combo";
13575         }
13576         
13577         this.indicator = this.indicatorEl();
13578         
13579         this.store = Roo.factory(this.store, Roo.data);
13580         this.store.parent = this;
13581         
13582         // if we are building from html. then this element is so complex, that we can not really
13583         // use the rendered HTML.
13584         // so we have to trash and replace the previous code.
13585         if (Roo.XComponent.build_from_html) {
13586             // remove this element....
13587             var e = this.el.dom, k=0;
13588             while (e ) { e = e.previousSibling;  ++k;}
13589
13590             this.el.remove();
13591             
13592             this.el=false;
13593             this.rendered = false;
13594             
13595             this.render(this.parent().getChildContainer(true), k);
13596         }
13597         
13598         if(Roo.isIOS && this.useNativeIOS){
13599             this.initIOSView();
13600             return;
13601         }
13602         
13603         /*
13604          * Touch Devices
13605          */
13606         
13607         if(Roo.isTouch && this.mobileTouchView){
13608             this.initTouchView();
13609             return;
13610         }
13611         
13612         if(this.tickable){
13613             this.initTickableEvents();
13614             return;
13615         }
13616         
13617         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13618         
13619         if(this.hiddenName){
13620             
13621             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13622             
13623             this.hiddenField.dom.value =
13624                 this.hiddenValue !== undefined ? this.hiddenValue :
13625                 this.value !== undefined ? this.value : '';
13626
13627             // prevent input submission
13628             this.el.dom.removeAttribute('name');
13629             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13630              
13631              
13632         }
13633         //if(Roo.isGecko){
13634         //    this.el.dom.setAttribute('autocomplete', 'off');
13635         //}
13636         
13637         var cls = 'x-combo-list';
13638         
13639         //this.list = new Roo.Layer({
13640         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13641         //});
13642         
13643         var _this = this;
13644         
13645         (function(){
13646             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13647             _this.list.setWidth(lw);
13648         }).defer(100);
13649         
13650         this.list.on('mouseover', this.onViewOver, this);
13651         this.list.on('mousemove', this.onViewMove, this);
13652         this.list.on('scroll', this.onViewScroll, this);
13653         
13654         /*
13655         this.list.swallowEvent('mousewheel');
13656         this.assetHeight = 0;
13657
13658         if(this.title){
13659             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13660             this.assetHeight += this.header.getHeight();
13661         }
13662
13663         this.innerList = this.list.createChild({cls:cls+'-inner'});
13664         this.innerList.on('mouseover', this.onViewOver, this);
13665         this.innerList.on('mousemove', this.onViewMove, this);
13666         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13667         
13668         if(this.allowBlank && !this.pageSize && !this.disableClear){
13669             this.footer = this.list.createChild({cls:cls+'-ft'});
13670             this.pageTb = new Roo.Toolbar(this.footer);
13671            
13672         }
13673         if(this.pageSize){
13674             this.footer = this.list.createChild({cls:cls+'-ft'});
13675             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13676                     {pageSize: this.pageSize});
13677             
13678         }
13679         
13680         if (this.pageTb && this.allowBlank && !this.disableClear) {
13681             var _this = this;
13682             this.pageTb.add(new Roo.Toolbar.Fill(), {
13683                 cls: 'x-btn-icon x-btn-clear',
13684                 text: '&#160;',
13685                 handler: function()
13686                 {
13687                     _this.collapse();
13688                     _this.clearValue();
13689                     _this.onSelect(false, -1);
13690                 }
13691             });
13692         }
13693         if (this.footer) {
13694             this.assetHeight += this.footer.getHeight();
13695         }
13696         */
13697             
13698         if(!this.tpl){
13699             this.tpl = Roo.bootstrap.version == 4 ?
13700                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13701                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13702         }
13703
13704         this.view = new Roo.View(this.list, this.tpl, {
13705             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13706         });
13707         //this.view.wrapEl.setDisplayed(false);
13708         this.view.on('click', this.onViewClick, this);
13709         
13710         
13711         this.store.on('beforeload', this.onBeforeLoad, this);
13712         this.store.on('load', this.onLoad, this);
13713         this.store.on('loadexception', this.onLoadException, this);
13714         /*
13715         if(this.resizable){
13716             this.resizer = new Roo.Resizable(this.list,  {
13717                pinned:true, handles:'se'
13718             });
13719             this.resizer.on('resize', function(r, w, h){
13720                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13721                 this.listWidth = w;
13722                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13723                 this.restrictHeight();
13724             }, this);
13725             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13726         }
13727         */
13728         if(!this.editable){
13729             this.editable = true;
13730             this.setEditable(false);
13731         }
13732         
13733         /*
13734         
13735         if (typeof(this.events.add.listeners) != 'undefined') {
13736             
13737             this.addicon = this.wrap.createChild(
13738                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13739        
13740             this.addicon.on('click', function(e) {
13741                 this.fireEvent('add', this);
13742             }, this);
13743         }
13744         if (typeof(this.events.edit.listeners) != 'undefined') {
13745             
13746             this.editicon = this.wrap.createChild(
13747                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13748             if (this.addicon) {
13749                 this.editicon.setStyle('margin-left', '40px');
13750             }
13751             this.editicon.on('click', function(e) {
13752                 
13753                 // we fire even  if inothing is selected..
13754                 this.fireEvent('edit', this, this.lastData );
13755                 
13756             }, this);
13757         }
13758         */
13759         
13760         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13761             "up" : function(e){
13762                 this.inKeyMode = true;
13763                 this.selectPrev();
13764             },
13765
13766             "down" : function(e){
13767                 if(!this.isExpanded()){
13768                     this.onTriggerClick();
13769                 }else{
13770                     this.inKeyMode = true;
13771                     this.selectNext();
13772                 }
13773             },
13774
13775             "enter" : function(e){
13776 //                this.onViewClick();
13777                 //return true;
13778                 this.collapse();
13779                 
13780                 if(this.fireEvent("specialkey", this, e)){
13781                     this.onViewClick(false);
13782                 }
13783                 
13784                 return true;
13785             },
13786
13787             "esc" : function(e){
13788                 this.collapse();
13789             },
13790
13791             "tab" : function(e){
13792                 this.collapse();
13793                 
13794                 if(this.fireEvent("specialkey", this, e)){
13795                     this.onViewClick(false);
13796                 }
13797                 
13798                 return true;
13799             },
13800
13801             scope : this,
13802
13803             doRelay : function(foo, bar, hname){
13804                 if(hname == 'down' || this.scope.isExpanded()){
13805                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13806                 }
13807                 return true;
13808             },
13809
13810             forceKeyDown: true
13811         });
13812         
13813         
13814         this.queryDelay = Math.max(this.queryDelay || 10,
13815                 this.mode == 'local' ? 10 : 250);
13816         
13817         
13818         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13819         
13820         if(this.typeAhead){
13821             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13822         }
13823         if(this.editable !== false){
13824             this.inputEl().on("keyup", this.onKeyUp, this);
13825         }
13826         if(this.forceSelection){
13827             this.inputEl().on('blur', this.doForce, this);
13828         }
13829         
13830         if(this.multiple){
13831             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13832             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13833         }
13834     },
13835     
13836     initTickableEvents: function()
13837     {   
13838         this.createList();
13839         
13840         if(this.hiddenName){
13841             
13842             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13843             
13844             this.hiddenField.dom.value =
13845                 this.hiddenValue !== undefined ? this.hiddenValue :
13846                 this.value !== undefined ? this.value : '';
13847
13848             // prevent input submission
13849             this.el.dom.removeAttribute('name');
13850             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13851              
13852              
13853         }
13854         
13855 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13856         
13857         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13858         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13859         if(this.triggerList){
13860             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13861         }
13862          
13863         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13864         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13865         
13866         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13867         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13868         
13869         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13870         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13871         
13872         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13873         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13874         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13875         
13876         this.okBtn.hide();
13877         this.cancelBtn.hide();
13878         
13879         var _this = this;
13880         
13881         (function(){
13882             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13883             _this.list.setWidth(lw);
13884         }).defer(100);
13885         
13886         this.list.on('mouseover', this.onViewOver, this);
13887         this.list.on('mousemove', this.onViewMove, this);
13888         
13889         this.list.on('scroll', this.onViewScroll, this);
13890         
13891         if(!this.tpl){
13892             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13893                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13894         }
13895
13896         this.view = new Roo.View(this.list, this.tpl, {
13897             singleSelect:true,
13898             tickable:true,
13899             parent:this,
13900             store: this.store,
13901             selectedClass: this.selectedClass
13902         });
13903         
13904         //this.view.wrapEl.setDisplayed(false);
13905         this.view.on('click', this.onViewClick, this);
13906         
13907         
13908         
13909         this.store.on('beforeload', this.onBeforeLoad, this);
13910         this.store.on('load', this.onLoad, this);
13911         this.store.on('loadexception', this.onLoadException, this);
13912         
13913         if(this.editable){
13914             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13915                 "up" : function(e){
13916                     this.inKeyMode = true;
13917                     this.selectPrev();
13918                 },
13919
13920                 "down" : function(e){
13921                     this.inKeyMode = true;
13922                     this.selectNext();
13923                 },
13924
13925                 "enter" : function(e){
13926                     if(this.fireEvent("specialkey", this, e)){
13927                         this.onViewClick(false);
13928                     }
13929                     
13930                     return true;
13931                 },
13932
13933                 "esc" : function(e){
13934                     this.onTickableFooterButtonClick(e, false, false);
13935                 },
13936
13937                 "tab" : function(e){
13938                     this.fireEvent("specialkey", this, e);
13939                     
13940                     this.onTickableFooterButtonClick(e, false, false);
13941                     
13942                     return true;
13943                 },
13944
13945                 scope : this,
13946
13947                 doRelay : function(e, fn, key){
13948                     if(this.scope.isExpanded()){
13949                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13950                     }
13951                     return true;
13952                 },
13953
13954                 forceKeyDown: true
13955             });
13956         }
13957         
13958         this.queryDelay = Math.max(this.queryDelay || 10,
13959                 this.mode == 'local' ? 10 : 250);
13960         
13961         
13962         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13963         
13964         if(this.typeAhead){
13965             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13966         }
13967         
13968         if(this.editable !== false){
13969             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13970         }
13971         
13972         this.indicator = this.indicatorEl();
13973         
13974         if(this.indicator){
13975             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13976             this.indicator.hide();
13977         }
13978         
13979     },
13980
13981     onDestroy : function(){
13982         if(this.view){
13983             this.view.setStore(null);
13984             this.view.el.removeAllListeners();
13985             this.view.el.remove();
13986             this.view.purgeListeners();
13987         }
13988         if(this.list){
13989             this.list.dom.innerHTML  = '';
13990         }
13991         
13992         if(this.store){
13993             this.store.un('beforeload', this.onBeforeLoad, this);
13994             this.store.un('load', this.onLoad, this);
13995             this.store.un('loadexception', this.onLoadException, this);
13996         }
13997         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13998     },
13999
14000     // private
14001     fireKey : function(e){
14002         if(e.isNavKeyPress() && !this.list.isVisible()){
14003             this.fireEvent("specialkey", this, e);
14004         }
14005     },
14006
14007     // private
14008     onResize: function(w, h){
14009 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14010 //        
14011 //        if(typeof w != 'number'){
14012 //            // we do not handle it!?!?
14013 //            return;
14014 //        }
14015 //        var tw = this.trigger.getWidth();
14016 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14017 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14018 //        var x = w - tw;
14019 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14020 //            
14021 //        //this.trigger.setStyle('left', x+'px');
14022 //        
14023 //        if(this.list && this.listWidth === undefined){
14024 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14025 //            this.list.setWidth(lw);
14026 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14027 //        }
14028         
14029     
14030         
14031     },
14032
14033     /**
14034      * Allow or prevent the user from directly editing the field text.  If false is passed,
14035      * the user will only be able to select from the items defined in the dropdown list.  This method
14036      * is the runtime equivalent of setting the 'editable' config option at config time.
14037      * @param {Boolean} value True to allow the user to directly edit the field text
14038      */
14039     setEditable : function(value){
14040         if(value == this.editable){
14041             return;
14042         }
14043         this.editable = value;
14044         if(!value){
14045             this.inputEl().dom.setAttribute('readOnly', true);
14046             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14047             this.inputEl().addClass('x-combo-noedit');
14048         }else{
14049             this.inputEl().dom.setAttribute('readOnly', false);
14050             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14051             this.inputEl().removeClass('x-combo-noedit');
14052         }
14053     },
14054
14055     // private
14056     
14057     onBeforeLoad : function(combo,opts){
14058         if(!this.hasFocus){
14059             return;
14060         }
14061          if (!opts.add) {
14062             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14063          }
14064         this.restrictHeight();
14065         this.selectedIndex = -1;
14066     },
14067
14068     // private
14069     onLoad : function(){
14070         
14071         this.hasQuery = false;
14072         
14073         if(!this.hasFocus){
14074             return;
14075         }
14076         
14077         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14078             this.loading.hide();
14079         }
14080         
14081         if(this.store.getCount() > 0){
14082             
14083             this.expand();
14084             this.restrictHeight();
14085             if(this.lastQuery == this.allQuery){
14086                 if(this.editable && !this.tickable){
14087                     this.inputEl().dom.select();
14088                 }
14089                 
14090                 if(
14091                     !this.selectByValue(this.value, true) &&
14092                     this.autoFocus && 
14093                     (
14094                         !this.store.lastOptions ||
14095                         typeof(this.store.lastOptions.add) == 'undefined' || 
14096                         this.store.lastOptions.add != true
14097                     )
14098                 ){
14099                     this.select(0, true);
14100                 }
14101             }else{
14102                 if(this.autoFocus){
14103                     this.selectNext();
14104                 }
14105                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14106                     this.taTask.delay(this.typeAheadDelay);
14107                 }
14108             }
14109         }else{
14110             this.onEmptyResults();
14111         }
14112         
14113         //this.el.focus();
14114     },
14115     // private
14116     onLoadException : function()
14117     {
14118         this.hasQuery = false;
14119         
14120         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14121             this.loading.hide();
14122         }
14123         
14124         if(this.tickable && this.editable){
14125             return;
14126         }
14127         
14128         this.collapse();
14129         // only causes errors at present
14130         //Roo.log(this.store.reader.jsonData);
14131         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14132             // fixme
14133             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14134         //}
14135         
14136         
14137     },
14138     // private
14139     onTypeAhead : function(){
14140         if(this.store.getCount() > 0){
14141             var r = this.store.getAt(0);
14142             var newValue = r.data[this.displayField];
14143             var len = newValue.length;
14144             var selStart = this.getRawValue().length;
14145             
14146             if(selStart != len){
14147                 this.setRawValue(newValue);
14148                 this.selectText(selStart, newValue.length);
14149             }
14150         }
14151     },
14152
14153     // private
14154     onSelect : function(record, index){
14155         
14156         if(this.fireEvent('beforeselect', this, record, index) !== false){
14157         
14158             this.setFromData(index > -1 ? record.data : false);
14159             
14160             this.collapse();
14161             this.fireEvent('select', this, record, index);
14162         }
14163     },
14164
14165     /**
14166      * Returns the currently selected field value or empty string if no value is set.
14167      * @return {String} value The selected value
14168      */
14169     getValue : function()
14170     {
14171         if(Roo.isIOS && this.useNativeIOS){
14172             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14173         }
14174         
14175         if(this.multiple){
14176             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14177         }
14178         
14179         if(this.valueField){
14180             return typeof this.value != 'undefined' ? this.value : '';
14181         }else{
14182             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14183         }
14184     },
14185     
14186     getRawValue : function()
14187     {
14188         if(Roo.isIOS && this.useNativeIOS){
14189             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14190         }
14191         
14192         var v = this.inputEl().getValue();
14193         
14194         return v;
14195     },
14196
14197     /**
14198      * Clears any text/value currently set in the field
14199      */
14200     clearValue : function(){
14201         
14202         if(this.hiddenField){
14203             this.hiddenField.dom.value = '';
14204         }
14205         this.value = '';
14206         this.setRawValue('');
14207         this.lastSelectionText = '';
14208         this.lastData = false;
14209         
14210         var close = this.closeTriggerEl();
14211         
14212         if(close){
14213             close.hide();
14214         }
14215         
14216         this.validate();
14217         
14218     },
14219
14220     /**
14221      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14222      * will be displayed in the field.  If the value does not match the data value of an existing item,
14223      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14224      * Otherwise the field will be blank (although the value will still be set).
14225      * @param {String} value The value to match
14226      */
14227     setValue : function(v)
14228     {
14229         if(Roo.isIOS && this.useNativeIOS){
14230             this.setIOSValue(v);
14231             return;
14232         }
14233         
14234         if(this.multiple){
14235             this.syncValue();
14236             return;
14237         }
14238         
14239         var text = v;
14240         if(this.valueField){
14241             var r = this.findRecord(this.valueField, v);
14242             if(r){
14243                 text = r.data[this.displayField];
14244             }else if(this.valueNotFoundText !== undefined){
14245                 text = this.valueNotFoundText;
14246             }
14247         }
14248         this.lastSelectionText = text;
14249         if(this.hiddenField){
14250             this.hiddenField.dom.value = v;
14251         }
14252         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14253         this.value = v;
14254         
14255         var close = this.closeTriggerEl();
14256         
14257         if(close){
14258             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14259         }
14260         
14261         this.validate();
14262     },
14263     /**
14264      * @property {Object} the last set data for the element
14265      */
14266     
14267     lastData : false,
14268     /**
14269      * Sets the value of the field based on a object which is related to the record format for the store.
14270      * @param {Object} value the value to set as. or false on reset?
14271      */
14272     setFromData : function(o){
14273         
14274         if(this.multiple){
14275             this.addItem(o);
14276             return;
14277         }
14278             
14279         var dv = ''; // display value
14280         var vv = ''; // value value..
14281         this.lastData = o;
14282         if (this.displayField) {
14283             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14284         } else {
14285             // this is an error condition!!!
14286             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14287         }
14288         
14289         if(this.valueField){
14290             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14291         }
14292         
14293         var close = this.closeTriggerEl();
14294         
14295         if(close){
14296             if(dv.length || vv * 1 > 0){
14297                 close.show() ;
14298                 this.blockFocus=true;
14299             } else {
14300                 close.hide();
14301             }             
14302         }
14303         
14304         if(this.hiddenField){
14305             this.hiddenField.dom.value = vv;
14306             
14307             this.lastSelectionText = dv;
14308             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14309             this.value = vv;
14310             return;
14311         }
14312         // no hidden field.. - we store the value in 'value', but still display
14313         // display field!!!!
14314         this.lastSelectionText = dv;
14315         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14316         this.value = vv;
14317         
14318         
14319         
14320     },
14321     // private
14322     reset : function(){
14323         // overridden so that last data is reset..
14324         
14325         if(this.multiple){
14326             this.clearItem();
14327             return;
14328         }
14329         
14330         this.setValue(this.originalValue);
14331         //this.clearInvalid();
14332         this.lastData = false;
14333         if (this.view) {
14334             this.view.clearSelections();
14335         }
14336         
14337         this.validate();
14338     },
14339     // private
14340     findRecord : function(prop, value){
14341         var record;
14342         if(this.store.getCount() > 0){
14343             this.store.each(function(r){
14344                 if(r.data[prop] == value){
14345                     record = r;
14346                     return false;
14347                 }
14348                 return true;
14349             });
14350         }
14351         return record;
14352     },
14353     
14354     getName: function()
14355     {
14356         // returns hidden if it's set..
14357         if (!this.rendered) {return ''};
14358         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14359         
14360     },
14361     // private
14362     onViewMove : function(e, t){
14363         this.inKeyMode = false;
14364     },
14365
14366     // private
14367     onViewOver : function(e, t){
14368         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14369             return;
14370         }
14371         var item = this.view.findItemFromChild(t);
14372         
14373         if(item){
14374             var index = this.view.indexOf(item);
14375             this.select(index, false);
14376         }
14377     },
14378
14379     // private
14380     onViewClick : function(view, doFocus, el, e)
14381     {
14382         var index = this.view.getSelectedIndexes()[0];
14383         
14384         var r = this.store.getAt(index);
14385         
14386         if(this.tickable){
14387             
14388             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14389                 return;
14390             }
14391             
14392             var rm = false;
14393             var _this = this;
14394             
14395             Roo.each(this.tickItems, function(v,k){
14396                 
14397                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14398                     Roo.log(v);
14399                     _this.tickItems.splice(k, 1);
14400                     
14401                     if(typeof(e) == 'undefined' && view == false){
14402                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14403                     }
14404                     
14405                     rm = true;
14406                     return;
14407                 }
14408             });
14409             
14410             if(rm){
14411                 return;
14412             }
14413             
14414             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14415                 this.tickItems.push(r.data);
14416             }
14417             
14418             if(typeof(e) == 'undefined' && view == false){
14419                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14420             }
14421                     
14422             return;
14423         }
14424         
14425         if(r){
14426             this.onSelect(r, index);
14427         }
14428         if(doFocus !== false && !this.blockFocus){
14429             this.inputEl().focus();
14430         }
14431     },
14432
14433     // private
14434     restrictHeight : function(){
14435         //this.innerList.dom.style.height = '';
14436         //var inner = this.innerList.dom;
14437         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14438         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14439         //this.list.beginUpdate();
14440         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14441         this.list.alignTo(this.inputEl(), this.listAlign);
14442         this.list.alignTo(this.inputEl(), this.listAlign);
14443         //this.list.endUpdate();
14444     },
14445
14446     // private
14447     onEmptyResults : function(){
14448         
14449         if(this.tickable && this.editable){
14450             this.hasFocus = false;
14451             this.restrictHeight();
14452             return;
14453         }
14454         
14455         this.collapse();
14456     },
14457
14458     /**
14459      * Returns true if the dropdown list is expanded, else false.
14460      */
14461     isExpanded : function(){
14462         return this.list.isVisible();
14463     },
14464
14465     /**
14466      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14467      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14468      * @param {String} value The data value of the item to select
14469      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14470      * selected item if it is not currently in view (defaults to true)
14471      * @return {Boolean} True if the value matched an item in the list, else false
14472      */
14473     selectByValue : function(v, scrollIntoView){
14474         if(v !== undefined && v !== null){
14475             var r = this.findRecord(this.valueField || this.displayField, v);
14476             if(r){
14477                 this.select(this.store.indexOf(r), scrollIntoView);
14478                 return true;
14479             }
14480         }
14481         return false;
14482     },
14483
14484     /**
14485      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14486      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14487      * @param {Number} index The zero-based index of the list item to select
14488      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14489      * selected item if it is not currently in view (defaults to true)
14490      */
14491     select : function(index, scrollIntoView){
14492         this.selectedIndex = index;
14493         this.view.select(index);
14494         if(scrollIntoView !== false){
14495             var el = this.view.getNode(index);
14496             /*
14497              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14498              */
14499             if(el){
14500                 this.list.scrollChildIntoView(el, false);
14501             }
14502         }
14503     },
14504
14505     // private
14506     selectNext : function(){
14507         var ct = this.store.getCount();
14508         if(ct > 0){
14509             if(this.selectedIndex == -1){
14510                 this.select(0);
14511             }else if(this.selectedIndex < ct-1){
14512                 this.select(this.selectedIndex+1);
14513             }
14514         }
14515     },
14516
14517     // private
14518     selectPrev : function(){
14519         var ct = this.store.getCount();
14520         if(ct > 0){
14521             if(this.selectedIndex == -1){
14522                 this.select(0);
14523             }else if(this.selectedIndex != 0){
14524                 this.select(this.selectedIndex-1);
14525             }
14526         }
14527     },
14528
14529     // private
14530     onKeyUp : function(e){
14531         if(this.editable !== false && !e.isSpecialKey()){
14532             this.lastKey = e.getKey();
14533             this.dqTask.delay(this.queryDelay);
14534         }
14535     },
14536
14537     // private
14538     validateBlur : function(){
14539         return !this.list || !this.list.isVisible();   
14540     },
14541
14542     // private
14543     initQuery : function(){
14544         
14545         var v = this.getRawValue();
14546         
14547         if(this.tickable && this.editable){
14548             v = this.tickableInputEl().getValue();
14549         }
14550         
14551         this.doQuery(v);
14552     },
14553
14554     // private
14555     doForce : function(){
14556         if(this.inputEl().dom.value.length > 0){
14557             this.inputEl().dom.value =
14558                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14559              
14560         }
14561     },
14562
14563     /**
14564      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14565      * query allowing the query action to be canceled if needed.
14566      * @param {String} query The SQL query to execute
14567      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14568      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14569      * saved in the current store (defaults to false)
14570      */
14571     doQuery : function(q, forceAll){
14572         
14573         if(q === undefined || q === null){
14574             q = '';
14575         }
14576         var qe = {
14577             query: q,
14578             forceAll: forceAll,
14579             combo: this,
14580             cancel:false
14581         };
14582         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14583             return false;
14584         }
14585         q = qe.query;
14586         
14587         forceAll = qe.forceAll;
14588         if(forceAll === true || (q.length >= this.minChars)){
14589             
14590             this.hasQuery = true;
14591             
14592             if(this.lastQuery != q || this.alwaysQuery){
14593                 this.lastQuery = q;
14594                 if(this.mode == 'local'){
14595                     this.selectedIndex = -1;
14596                     if(forceAll){
14597                         this.store.clearFilter();
14598                     }else{
14599                         
14600                         if(this.specialFilter){
14601                             this.fireEvent('specialfilter', this);
14602                             this.onLoad();
14603                             return;
14604                         }
14605                         
14606                         this.store.filter(this.displayField, q);
14607                     }
14608                     
14609                     this.store.fireEvent("datachanged", this.store);
14610                     
14611                     this.onLoad();
14612                     
14613                     
14614                 }else{
14615                     
14616                     this.store.baseParams[this.queryParam] = q;
14617                     
14618                     var options = {params : this.getParams(q)};
14619                     
14620                     if(this.loadNext){
14621                         options.add = true;
14622                         options.params.start = this.page * this.pageSize;
14623                     }
14624                     
14625                     this.store.load(options);
14626                     
14627                     /*
14628                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14629                      *  we should expand the list on onLoad
14630                      *  so command out it
14631                      */
14632 //                    this.expand();
14633                 }
14634             }else{
14635                 this.selectedIndex = -1;
14636                 this.onLoad();   
14637             }
14638         }
14639         
14640         this.loadNext = false;
14641     },
14642     
14643     // private
14644     getParams : function(q){
14645         var p = {};
14646         //p[this.queryParam] = q;
14647         
14648         if(this.pageSize){
14649             p.start = 0;
14650             p.limit = this.pageSize;
14651         }
14652         return p;
14653     },
14654
14655     /**
14656      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14657      */
14658     collapse : function(){
14659         if(!this.isExpanded()){
14660             return;
14661         }
14662         
14663         this.list.hide();
14664         
14665         this.hasFocus = false;
14666         
14667         if(this.tickable){
14668             this.okBtn.hide();
14669             this.cancelBtn.hide();
14670             this.trigger.show();
14671             
14672             if(this.editable){
14673                 this.tickableInputEl().dom.value = '';
14674                 this.tickableInputEl().blur();
14675             }
14676             
14677         }
14678         
14679         Roo.get(document).un('mousedown', this.collapseIf, this);
14680         Roo.get(document).un('mousewheel', this.collapseIf, this);
14681         if (!this.editable) {
14682             Roo.get(document).un('keydown', this.listKeyPress, this);
14683         }
14684         this.fireEvent('collapse', this);
14685         
14686         this.validate();
14687     },
14688
14689     // private
14690     collapseIf : function(e){
14691         var in_combo  = e.within(this.el);
14692         var in_list =  e.within(this.list);
14693         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14694         
14695         if (in_combo || in_list || is_list) {
14696             //e.stopPropagation();
14697             return;
14698         }
14699         
14700         if(this.tickable){
14701             this.onTickableFooterButtonClick(e, false, false);
14702         }
14703
14704         this.collapse();
14705         
14706     },
14707
14708     /**
14709      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14710      */
14711     expand : function(){
14712        
14713         if(this.isExpanded() || !this.hasFocus){
14714             return;
14715         }
14716         
14717         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14718         this.list.setWidth(lw);
14719         
14720         Roo.log('expand');
14721         
14722         this.list.show();
14723         
14724         this.restrictHeight();
14725         
14726         if(this.tickable){
14727             
14728             this.tickItems = Roo.apply([], this.item);
14729             
14730             this.okBtn.show();
14731             this.cancelBtn.show();
14732             this.trigger.hide();
14733             
14734             if(this.editable){
14735                 this.tickableInputEl().focus();
14736             }
14737             
14738         }
14739         
14740         Roo.get(document).on('mousedown', this.collapseIf, this);
14741         Roo.get(document).on('mousewheel', this.collapseIf, this);
14742         if (!this.editable) {
14743             Roo.get(document).on('keydown', this.listKeyPress, this);
14744         }
14745         
14746         this.fireEvent('expand', this);
14747     },
14748
14749     // private
14750     // Implements the default empty TriggerField.onTriggerClick function
14751     onTriggerClick : function(e)
14752     {
14753         Roo.log('trigger click');
14754         
14755         if(this.disabled || !this.triggerList){
14756             return;
14757         }
14758         
14759         this.page = 0;
14760         this.loadNext = false;
14761         
14762         if(this.isExpanded()){
14763             this.collapse();
14764             if (!this.blockFocus) {
14765                 this.inputEl().focus();
14766             }
14767             
14768         }else {
14769             this.hasFocus = true;
14770             if(this.triggerAction == 'all') {
14771                 this.doQuery(this.allQuery, true);
14772             } else {
14773                 this.doQuery(this.getRawValue());
14774             }
14775             if (!this.blockFocus) {
14776                 this.inputEl().focus();
14777             }
14778         }
14779     },
14780     
14781     onTickableTriggerClick : function(e)
14782     {
14783         if(this.disabled){
14784             return;
14785         }
14786         
14787         this.page = 0;
14788         this.loadNext = false;
14789         this.hasFocus = true;
14790         
14791         if(this.triggerAction == 'all') {
14792             this.doQuery(this.allQuery, true);
14793         } else {
14794             this.doQuery(this.getRawValue());
14795         }
14796     },
14797     
14798     onSearchFieldClick : function(e)
14799     {
14800         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14801             this.onTickableFooterButtonClick(e, false, false);
14802             return;
14803         }
14804         
14805         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14806             return;
14807         }
14808         
14809         this.page = 0;
14810         this.loadNext = false;
14811         this.hasFocus = true;
14812         
14813         if(this.triggerAction == 'all') {
14814             this.doQuery(this.allQuery, true);
14815         } else {
14816             this.doQuery(this.getRawValue());
14817         }
14818     },
14819     
14820     listKeyPress : function(e)
14821     {
14822         //Roo.log('listkeypress');
14823         // scroll to first matching element based on key pres..
14824         if (e.isSpecialKey()) {
14825             return false;
14826         }
14827         var k = String.fromCharCode(e.getKey()).toUpperCase();
14828         //Roo.log(k);
14829         var match  = false;
14830         var csel = this.view.getSelectedNodes();
14831         var cselitem = false;
14832         if (csel.length) {
14833             var ix = this.view.indexOf(csel[0]);
14834             cselitem  = this.store.getAt(ix);
14835             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14836                 cselitem = false;
14837             }
14838             
14839         }
14840         
14841         this.store.each(function(v) { 
14842             if (cselitem) {
14843                 // start at existing selection.
14844                 if (cselitem.id == v.id) {
14845                     cselitem = false;
14846                 }
14847                 return true;
14848             }
14849                 
14850             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14851                 match = this.store.indexOf(v);
14852                 return false;
14853             }
14854             return true;
14855         }, this);
14856         
14857         if (match === false) {
14858             return true; // no more action?
14859         }
14860         // scroll to?
14861         this.view.select(match);
14862         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14863         sn.scrollIntoView(sn.dom.parentNode, false);
14864     },
14865     
14866     onViewScroll : function(e, t){
14867         
14868         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){
14869             return;
14870         }
14871         
14872         this.hasQuery = true;
14873         
14874         this.loading = this.list.select('.loading', true).first();
14875         
14876         if(this.loading === null){
14877             this.list.createChild({
14878                 tag: 'div',
14879                 cls: 'loading roo-select2-more-results roo-select2-active',
14880                 html: 'Loading more results...'
14881             });
14882             
14883             this.loading = this.list.select('.loading', true).first();
14884             
14885             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14886             
14887             this.loading.hide();
14888         }
14889         
14890         this.loading.show();
14891         
14892         var _combo = this;
14893         
14894         this.page++;
14895         this.loadNext = true;
14896         
14897         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14898         
14899         return;
14900     },
14901     
14902     addItem : function(o)
14903     {   
14904         var dv = ''; // display value
14905         
14906         if (this.displayField) {
14907             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14908         } else {
14909             // this is an error condition!!!
14910             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14911         }
14912         
14913         if(!dv.length){
14914             return;
14915         }
14916         
14917         var choice = this.choices.createChild({
14918             tag: 'li',
14919             cls: 'roo-select2-search-choice',
14920             cn: [
14921                 {
14922                     tag: 'div',
14923                     html: dv
14924                 },
14925                 {
14926                     tag: 'a',
14927                     href: '#',
14928                     cls: 'roo-select2-search-choice-close fa fa-times',
14929                     tabindex: '-1'
14930                 }
14931             ]
14932             
14933         }, this.searchField);
14934         
14935         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14936         
14937         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14938         
14939         this.item.push(o);
14940         
14941         this.lastData = o;
14942         
14943         this.syncValue();
14944         
14945         this.inputEl().dom.value = '';
14946         
14947         this.validate();
14948     },
14949     
14950     onRemoveItem : function(e, _self, o)
14951     {
14952         e.preventDefault();
14953         
14954         this.lastItem = Roo.apply([], this.item);
14955         
14956         var index = this.item.indexOf(o.data) * 1;
14957         
14958         if( index < 0){
14959             Roo.log('not this item?!');
14960             return;
14961         }
14962         
14963         this.item.splice(index, 1);
14964         o.item.remove();
14965         
14966         this.syncValue();
14967         
14968         this.fireEvent('remove', this, e);
14969         
14970         this.validate();
14971         
14972     },
14973     
14974     syncValue : function()
14975     {
14976         if(!this.item.length){
14977             this.clearValue();
14978             return;
14979         }
14980             
14981         var value = [];
14982         var _this = this;
14983         Roo.each(this.item, function(i){
14984             if(_this.valueField){
14985                 value.push(i[_this.valueField]);
14986                 return;
14987             }
14988
14989             value.push(i);
14990         });
14991
14992         this.value = value.join(',');
14993
14994         if(this.hiddenField){
14995             this.hiddenField.dom.value = this.value;
14996         }
14997         
14998         this.store.fireEvent("datachanged", this.store);
14999         
15000         this.validate();
15001     },
15002     
15003     clearItem : function()
15004     {
15005         if(!this.multiple){
15006             return;
15007         }
15008         
15009         this.item = [];
15010         
15011         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15012            c.remove();
15013         });
15014         
15015         this.syncValue();
15016         
15017         this.validate();
15018         
15019         if(this.tickable && !Roo.isTouch){
15020             this.view.refresh();
15021         }
15022     },
15023     
15024     inputEl: function ()
15025     {
15026         if(Roo.isIOS && this.useNativeIOS){
15027             return this.el.select('select.roo-ios-select', true).first();
15028         }
15029         
15030         if(Roo.isTouch && this.mobileTouchView){
15031             return this.el.select('input.form-control',true).first();
15032         }
15033         
15034         if(this.tickable){
15035             return this.searchField;
15036         }
15037         
15038         return this.el.select('input.form-control',true).first();
15039     },
15040     
15041     onTickableFooterButtonClick : function(e, btn, el)
15042     {
15043         e.preventDefault();
15044         
15045         this.lastItem = Roo.apply([], this.item);
15046         
15047         if(btn && btn.name == 'cancel'){
15048             this.tickItems = Roo.apply([], this.item);
15049             this.collapse();
15050             return;
15051         }
15052         
15053         this.clearItem();
15054         
15055         var _this = this;
15056         
15057         Roo.each(this.tickItems, function(o){
15058             _this.addItem(o);
15059         });
15060         
15061         this.collapse();
15062         
15063     },
15064     
15065     validate : function()
15066     {
15067         if(this.getVisibilityEl().hasClass('hidden')){
15068             return true;
15069         }
15070         
15071         var v = this.getRawValue();
15072         
15073         if(this.multiple){
15074             v = this.getValue();
15075         }
15076         
15077         if(this.disabled || this.allowBlank || v.length){
15078             this.markValid();
15079             return true;
15080         }
15081         
15082         this.markInvalid();
15083         return false;
15084     },
15085     
15086     tickableInputEl : function()
15087     {
15088         if(!this.tickable || !this.editable){
15089             return this.inputEl();
15090         }
15091         
15092         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15093     },
15094     
15095     
15096     getAutoCreateTouchView : function()
15097     {
15098         var id = Roo.id();
15099         
15100         var cfg = {
15101             cls: 'form-group' //input-group
15102         };
15103         
15104         var input =  {
15105             tag: 'input',
15106             id : id,
15107             type : this.inputType,
15108             cls : 'form-control x-combo-noedit',
15109             autocomplete: 'new-password',
15110             placeholder : this.placeholder || '',
15111             readonly : true
15112         };
15113         
15114         if (this.name) {
15115             input.name = this.name;
15116         }
15117         
15118         if (this.size) {
15119             input.cls += ' input-' + this.size;
15120         }
15121         
15122         if (this.disabled) {
15123             input.disabled = true;
15124         }
15125         
15126         var inputblock = {
15127             cls : '',
15128             cn : [
15129                 input
15130             ]
15131         };
15132         
15133         if(this.before){
15134             inputblock.cls += ' input-group';
15135             
15136             inputblock.cn.unshift({
15137                 tag :'span',
15138                 cls : 'input-group-addon input-group-prepend input-group-text',
15139                 html : this.before
15140             });
15141         }
15142         
15143         if(this.removable && !this.multiple){
15144             inputblock.cls += ' roo-removable';
15145             
15146             inputblock.cn.push({
15147                 tag: 'button',
15148                 html : 'x',
15149                 cls : 'roo-combo-removable-btn close'
15150             });
15151         }
15152
15153         if(this.hasFeedback && !this.allowBlank){
15154             
15155             inputblock.cls += ' has-feedback';
15156             
15157             inputblock.cn.push({
15158                 tag: 'span',
15159                 cls: 'glyphicon form-control-feedback'
15160             });
15161             
15162         }
15163         
15164         if (this.after) {
15165             
15166             inputblock.cls += (this.before) ? '' : ' input-group';
15167             
15168             inputblock.cn.push({
15169                 tag :'span',
15170                 cls : 'input-group-addon input-group-append input-group-text',
15171                 html : this.after
15172             });
15173         }
15174
15175         
15176         var ibwrap = inputblock;
15177         
15178         if(this.multiple){
15179             ibwrap = {
15180                 tag: 'ul',
15181                 cls: 'roo-select2-choices',
15182                 cn:[
15183                     {
15184                         tag: 'li',
15185                         cls: 'roo-select2-search-field',
15186                         cn: [
15187
15188                             inputblock
15189                         ]
15190                     }
15191                 ]
15192             };
15193         
15194             
15195         }
15196         
15197         var combobox = {
15198             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15199             cn: [
15200                 {
15201                     tag: 'input',
15202                     type : 'hidden',
15203                     cls: 'form-hidden-field'
15204                 },
15205                 ibwrap
15206             ]
15207         };
15208         
15209         if(!this.multiple && this.showToggleBtn){
15210             
15211             var caret = {
15212                         tag: 'span',
15213                         cls: 'caret'
15214             };
15215             
15216             if (this.caret != false) {
15217                 caret = {
15218                      tag: 'i',
15219                      cls: 'fa fa-' + this.caret
15220                 };
15221                 
15222             }
15223             
15224             combobox.cn.push({
15225                 tag :'span',
15226                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15227                 cn : [
15228                     caret,
15229                     {
15230                         tag: 'span',
15231                         cls: 'combobox-clear',
15232                         cn  : [
15233                             {
15234                                 tag : 'i',
15235                                 cls: 'icon-remove'
15236                             }
15237                         ]
15238                     }
15239                 ]
15240
15241             })
15242         }
15243         
15244         if(this.multiple){
15245             combobox.cls += ' roo-select2-container-multi';
15246         }
15247         
15248         var align = this.labelAlign || this.parentLabelAlign();
15249         
15250         if (align ==='left' && this.fieldLabel.length) {
15251
15252             cfg.cn = [
15253                 {
15254                    tag : 'i',
15255                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15256                    tooltip : 'This field is required'
15257                 },
15258                 {
15259                     tag: 'label',
15260                     cls : 'control-label col-form-label',
15261                     html : this.fieldLabel
15262
15263                 },
15264                 {
15265                     cls : '', 
15266                     cn: [
15267                         combobox
15268                     ]
15269                 }
15270             ];
15271             
15272             var labelCfg = cfg.cn[1];
15273             var contentCfg = cfg.cn[2];
15274             
15275
15276             if(this.indicatorpos == 'right'){
15277                 cfg.cn = [
15278                     {
15279                         tag: 'label',
15280                         'for' :  id,
15281                         cls : 'control-label col-form-label',
15282                         cn : [
15283                             {
15284                                 tag : 'span',
15285                                 html : this.fieldLabel
15286                             },
15287                             {
15288                                 tag : 'i',
15289                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15290                                 tooltip : 'This field is required'
15291                             }
15292                         ]
15293                     },
15294                     {
15295                         cls : "",
15296                         cn: [
15297                             combobox
15298                         ]
15299                     }
15300
15301                 ];
15302                 
15303                 labelCfg = cfg.cn[0];
15304                 contentCfg = cfg.cn[1];
15305             }
15306             
15307            
15308             
15309             if(this.labelWidth > 12){
15310                 labelCfg.style = "width: " + this.labelWidth + 'px';
15311             }
15312             
15313             if(this.labelWidth < 13 && this.labelmd == 0){
15314                 this.labelmd = this.labelWidth;
15315             }
15316             
15317             if(this.labellg > 0){
15318                 labelCfg.cls += ' col-lg-' + this.labellg;
15319                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15320             }
15321             
15322             if(this.labelmd > 0){
15323                 labelCfg.cls += ' col-md-' + this.labelmd;
15324                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15325             }
15326             
15327             if(this.labelsm > 0){
15328                 labelCfg.cls += ' col-sm-' + this.labelsm;
15329                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15330             }
15331             
15332             if(this.labelxs > 0){
15333                 labelCfg.cls += ' col-xs-' + this.labelxs;
15334                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15335             }
15336                 
15337                 
15338         } else if ( this.fieldLabel.length) {
15339             cfg.cn = [
15340                 {
15341                    tag : 'i',
15342                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15343                    tooltip : 'This field is required'
15344                 },
15345                 {
15346                     tag: 'label',
15347                     cls : 'control-label',
15348                     html : this.fieldLabel
15349
15350                 },
15351                 {
15352                     cls : '', 
15353                     cn: [
15354                         combobox
15355                     ]
15356                 }
15357             ];
15358             
15359             if(this.indicatorpos == 'right'){
15360                 cfg.cn = [
15361                     {
15362                         tag: 'label',
15363                         cls : 'control-label',
15364                         html : this.fieldLabel,
15365                         cn : [
15366                             {
15367                                tag : 'i',
15368                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15369                                tooltip : 'This field is required'
15370                             }
15371                         ]
15372                     },
15373                     {
15374                         cls : '', 
15375                         cn: [
15376                             combobox
15377                         ]
15378                     }
15379                 ];
15380             }
15381         } else {
15382             cfg.cn = combobox;    
15383         }
15384         
15385         
15386         var settings = this;
15387         
15388         ['xs','sm','md','lg'].map(function(size){
15389             if (settings[size]) {
15390                 cfg.cls += ' col-' + size + '-' + settings[size];
15391             }
15392         });
15393         
15394         return cfg;
15395     },
15396     
15397     initTouchView : function()
15398     {
15399         this.renderTouchView();
15400         
15401         this.touchViewEl.on('scroll', function(){
15402             this.el.dom.scrollTop = 0;
15403         }, this);
15404         
15405         this.originalValue = this.getValue();
15406         
15407         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15408         
15409         this.inputEl().on("click", this.showTouchView, this);
15410         if (this.triggerEl) {
15411             this.triggerEl.on("click", this.showTouchView, this);
15412         }
15413         
15414         
15415         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15416         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15417         
15418         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15419         
15420         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15421         this.store.on('load', this.onTouchViewLoad, this);
15422         this.store.on('loadexception', this.onTouchViewLoadException, this);
15423         
15424         if(this.hiddenName){
15425             
15426             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15427             
15428             this.hiddenField.dom.value =
15429                 this.hiddenValue !== undefined ? this.hiddenValue :
15430                 this.value !== undefined ? this.value : '';
15431         
15432             this.el.dom.removeAttribute('name');
15433             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15434         }
15435         
15436         if(this.multiple){
15437             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15438             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15439         }
15440         
15441         if(this.removable && !this.multiple){
15442             var close = this.closeTriggerEl();
15443             if(close){
15444                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15445                 close.on('click', this.removeBtnClick, this, close);
15446             }
15447         }
15448         /*
15449          * fix the bug in Safari iOS8
15450          */
15451         this.inputEl().on("focus", function(e){
15452             document.activeElement.blur();
15453         }, this);
15454         
15455         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15456         
15457         return;
15458         
15459         
15460     },
15461     
15462     renderTouchView : function()
15463     {
15464         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15465         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15466         
15467         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15468         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15469         
15470         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15471         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15472         this.touchViewBodyEl.setStyle('overflow', 'auto');
15473         
15474         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15475         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15476         
15477         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15478         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15479         
15480     },
15481     
15482     showTouchView : function()
15483     {
15484         if(this.disabled){
15485             return;
15486         }
15487         
15488         this.touchViewHeaderEl.hide();
15489
15490         if(this.modalTitle.length){
15491             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15492             this.touchViewHeaderEl.show();
15493         }
15494
15495         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15496         this.touchViewEl.show();
15497
15498         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15499         
15500         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15501         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15502
15503         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15504
15505         if(this.modalTitle.length){
15506             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15507         }
15508         
15509         this.touchViewBodyEl.setHeight(bodyHeight);
15510
15511         if(this.animate){
15512             var _this = this;
15513             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15514         }else{
15515             this.touchViewEl.addClass('in');
15516         }
15517         
15518         if(this._touchViewMask){
15519             Roo.get(document.body).addClass("x-body-masked");
15520             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15521             this._touchViewMask.setStyle('z-index', 10000);
15522             this._touchViewMask.addClass('show');
15523         }
15524         
15525         this.doTouchViewQuery();
15526         
15527     },
15528     
15529     hideTouchView : function()
15530     {
15531         this.touchViewEl.removeClass('in');
15532
15533         if(this.animate){
15534             var _this = this;
15535             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15536         }else{
15537             this.touchViewEl.setStyle('display', 'none');
15538         }
15539         
15540         if(this._touchViewMask){
15541             this._touchViewMask.removeClass('show');
15542             Roo.get(document.body).removeClass("x-body-masked");
15543         }
15544     },
15545     
15546     setTouchViewValue : function()
15547     {
15548         if(this.multiple){
15549             this.clearItem();
15550         
15551             var _this = this;
15552
15553             Roo.each(this.tickItems, function(o){
15554                 this.addItem(o);
15555             }, this);
15556         }
15557         
15558         this.hideTouchView();
15559     },
15560     
15561     doTouchViewQuery : function()
15562     {
15563         var qe = {
15564             query: '',
15565             forceAll: true,
15566             combo: this,
15567             cancel:false
15568         };
15569         
15570         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15571             return false;
15572         }
15573         
15574         if(!this.alwaysQuery || this.mode == 'local'){
15575             this.onTouchViewLoad();
15576             return;
15577         }
15578         
15579         this.store.load();
15580     },
15581     
15582     onTouchViewBeforeLoad : function(combo,opts)
15583     {
15584         return;
15585     },
15586
15587     // private
15588     onTouchViewLoad : function()
15589     {
15590         if(this.store.getCount() < 1){
15591             this.onTouchViewEmptyResults();
15592             return;
15593         }
15594         
15595         this.clearTouchView();
15596         
15597         var rawValue = this.getRawValue();
15598         
15599         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15600         
15601         this.tickItems = [];
15602         
15603         this.store.data.each(function(d, rowIndex){
15604             var row = this.touchViewListGroup.createChild(template);
15605             
15606             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15607                 row.addClass(d.data.cls);
15608             }
15609             
15610             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15611                 var cfg = {
15612                     data : d.data,
15613                     html : d.data[this.displayField]
15614                 };
15615                 
15616                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15617                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15618                 }
15619             }
15620             row.removeClass('selected');
15621             if(!this.multiple && this.valueField &&
15622                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15623             {
15624                 // radio buttons..
15625                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15626                 row.addClass('selected');
15627             }
15628             
15629             if(this.multiple && this.valueField &&
15630                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15631             {
15632                 
15633                 // checkboxes...
15634                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15635                 this.tickItems.push(d.data);
15636             }
15637             
15638             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15639             
15640         }, this);
15641         
15642         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15643         
15644         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15645
15646         if(this.modalTitle.length){
15647             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15648         }
15649
15650         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15651         
15652         if(this.mobile_restrict_height && listHeight < bodyHeight){
15653             this.touchViewBodyEl.setHeight(listHeight);
15654         }
15655         
15656         var _this = this;
15657         
15658         if(firstChecked && listHeight > bodyHeight){
15659             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15660         }
15661         
15662     },
15663     
15664     onTouchViewLoadException : function()
15665     {
15666         this.hideTouchView();
15667     },
15668     
15669     onTouchViewEmptyResults : function()
15670     {
15671         this.clearTouchView();
15672         
15673         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15674         
15675         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15676         
15677     },
15678     
15679     clearTouchView : function()
15680     {
15681         this.touchViewListGroup.dom.innerHTML = '';
15682     },
15683     
15684     onTouchViewClick : function(e, el, o)
15685     {
15686         e.preventDefault();
15687         
15688         var row = o.row;
15689         var rowIndex = o.rowIndex;
15690         
15691         var r = this.store.getAt(rowIndex);
15692         
15693         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15694             
15695             if(!this.multiple){
15696                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15697                     c.dom.removeAttribute('checked');
15698                 }, this);
15699
15700                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15701
15702                 this.setFromData(r.data);
15703
15704                 var close = this.closeTriggerEl();
15705
15706                 if(close){
15707                     close.show();
15708                 }
15709
15710                 this.hideTouchView();
15711
15712                 this.fireEvent('select', this, r, rowIndex);
15713
15714                 return;
15715             }
15716
15717             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15718                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15719                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15720                 return;
15721             }
15722
15723             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15724             this.addItem(r.data);
15725             this.tickItems.push(r.data);
15726         }
15727     },
15728     
15729     getAutoCreateNativeIOS : function()
15730     {
15731         var cfg = {
15732             cls: 'form-group' //input-group,
15733         };
15734         
15735         var combobox =  {
15736             tag: 'select',
15737             cls : 'roo-ios-select'
15738         };
15739         
15740         if (this.name) {
15741             combobox.name = this.name;
15742         }
15743         
15744         if (this.disabled) {
15745             combobox.disabled = true;
15746         }
15747         
15748         var settings = this;
15749         
15750         ['xs','sm','md','lg'].map(function(size){
15751             if (settings[size]) {
15752                 cfg.cls += ' col-' + size + '-' + settings[size];
15753             }
15754         });
15755         
15756         cfg.cn = combobox;
15757         
15758         return cfg;
15759         
15760     },
15761     
15762     initIOSView : function()
15763     {
15764         this.store.on('load', this.onIOSViewLoad, this);
15765         
15766         return;
15767     },
15768     
15769     onIOSViewLoad : function()
15770     {
15771         if(this.store.getCount() < 1){
15772             return;
15773         }
15774         
15775         this.clearIOSView();
15776         
15777         if(this.allowBlank) {
15778             
15779             var default_text = '-- SELECT --';
15780             
15781             if(this.placeholder.length){
15782                 default_text = this.placeholder;
15783             }
15784             
15785             if(this.emptyTitle.length){
15786                 default_text += ' - ' + this.emptyTitle + ' -';
15787             }
15788             
15789             var opt = this.inputEl().createChild({
15790                 tag: 'option',
15791                 value : 0,
15792                 html : default_text
15793             });
15794             
15795             var o = {};
15796             o[this.valueField] = 0;
15797             o[this.displayField] = default_text;
15798             
15799             this.ios_options.push({
15800                 data : o,
15801                 el : opt
15802             });
15803             
15804         }
15805         
15806         this.store.data.each(function(d, rowIndex){
15807             
15808             var html = '';
15809             
15810             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15811                 html = d.data[this.displayField];
15812             }
15813             
15814             var value = '';
15815             
15816             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15817                 value = d.data[this.valueField];
15818             }
15819             
15820             var option = {
15821                 tag: 'option',
15822                 value : value,
15823                 html : html
15824             };
15825             
15826             if(this.value == d.data[this.valueField]){
15827                 option['selected'] = true;
15828             }
15829             
15830             var opt = this.inputEl().createChild(option);
15831             
15832             this.ios_options.push({
15833                 data : d.data,
15834                 el : opt
15835             });
15836             
15837         }, this);
15838         
15839         this.inputEl().on('change', function(){
15840            this.fireEvent('select', this);
15841         }, this);
15842         
15843     },
15844     
15845     clearIOSView: function()
15846     {
15847         this.inputEl().dom.innerHTML = '';
15848         
15849         this.ios_options = [];
15850     },
15851     
15852     setIOSValue: function(v)
15853     {
15854         this.value = v;
15855         
15856         if(!this.ios_options){
15857             return;
15858         }
15859         
15860         Roo.each(this.ios_options, function(opts){
15861            
15862            opts.el.dom.removeAttribute('selected');
15863            
15864            if(opts.data[this.valueField] != v){
15865                return;
15866            }
15867            
15868            opts.el.dom.setAttribute('selected', true);
15869            
15870         }, this);
15871     }
15872
15873     /** 
15874     * @cfg {Boolean} grow 
15875     * @hide 
15876     */
15877     /** 
15878     * @cfg {Number} growMin 
15879     * @hide 
15880     */
15881     /** 
15882     * @cfg {Number} growMax 
15883     * @hide 
15884     */
15885     /**
15886      * @hide
15887      * @method autoSize
15888      */
15889 });
15890
15891 Roo.apply(Roo.bootstrap.ComboBox,  {
15892     
15893     header : {
15894         tag: 'div',
15895         cls: 'modal-header',
15896         cn: [
15897             {
15898                 tag: 'h4',
15899                 cls: 'modal-title'
15900             }
15901         ]
15902     },
15903     
15904     body : {
15905         tag: 'div',
15906         cls: 'modal-body',
15907         cn: [
15908             {
15909                 tag: 'ul',
15910                 cls: 'list-group'
15911             }
15912         ]
15913     },
15914     
15915     listItemRadio : {
15916         tag: 'li',
15917         cls: 'list-group-item',
15918         cn: [
15919             {
15920                 tag: 'span',
15921                 cls: 'roo-combobox-list-group-item-value'
15922             },
15923             {
15924                 tag: 'div',
15925                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15926                 cn: [
15927                     {
15928                         tag: 'input',
15929                         type: 'radio'
15930                     },
15931                     {
15932                         tag: 'label'
15933                     }
15934                 ]
15935             }
15936         ]
15937     },
15938     
15939     listItemCheckbox : {
15940         tag: 'li',
15941         cls: 'list-group-item',
15942         cn: [
15943             {
15944                 tag: 'span',
15945                 cls: 'roo-combobox-list-group-item-value'
15946             },
15947             {
15948                 tag: 'div',
15949                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15950                 cn: [
15951                     {
15952                         tag: 'input',
15953                         type: 'checkbox'
15954                     },
15955                     {
15956                         tag: 'label'
15957                     }
15958                 ]
15959             }
15960         ]
15961     },
15962     
15963     emptyResult : {
15964         tag: 'div',
15965         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15966     },
15967     
15968     footer : {
15969         tag: 'div',
15970         cls: 'modal-footer',
15971         cn: [
15972             {
15973                 tag: 'div',
15974                 cls: 'row',
15975                 cn: [
15976                     {
15977                         tag: 'div',
15978                         cls: 'col-xs-6 text-left',
15979                         cn: {
15980                             tag: 'button',
15981                             cls: 'btn btn-danger roo-touch-view-cancel',
15982                             html: 'Cancel'
15983                         }
15984                     },
15985                     {
15986                         tag: 'div',
15987                         cls: 'col-xs-6 text-right',
15988                         cn: {
15989                             tag: 'button',
15990                             cls: 'btn btn-success roo-touch-view-ok',
15991                             html: 'OK'
15992                         }
15993                     }
15994                 ]
15995             }
15996         ]
15997         
15998     }
15999 });
16000
16001 Roo.apply(Roo.bootstrap.ComboBox,  {
16002     
16003     touchViewTemplate : {
16004         tag: 'div',
16005         cls: 'modal fade roo-combobox-touch-view',
16006         cn: [
16007             {
16008                 tag: 'div',
16009                 cls: 'modal-dialog',
16010                 style : 'position:fixed', // we have to fix position....
16011                 cn: [
16012                     {
16013                         tag: 'div',
16014                         cls: 'modal-content',
16015                         cn: [
16016                             Roo.bootstrap.ComboBox.header,
16017                             Roo.bootstrap.ComboBox.body,
16018                             Roo.bootstrap.ComboBox.footer
16019                         ]
16020                     }
16021                 ]
16022             }
16023         ]
16024     }
16025 });/*
16026  * Based on:
16027  * Ext JS Library 1.1.1
16028  * Copyright(c) 2006-2007, Ext JS, LLC.
16029  *
16030  * Originally Released Under LGPL - original licence link has changed is not relivant.
16031  *
16032  * Fork - LGPL
16033  * <script type="text/javascript">
16034  */
16035
16036 /**
16037  * @class Roo.View
16038  * @extends Roo.util.Observable
16039  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16040  * This class also supports single and multi selection modes. <br>
16041  * Create a data model bound view:
16042  <pre><code>
16043  var store = new Roo.data.Store(...);
16044
16045  var view = new Roo.View({
16046     el : "my-element",
16047     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16048  
16049     singleSelect: true,
16050     selectedClass: "ydataview-selected",
16051     store: store
16052  });
16053
16054  // listen for node click?
16055  view.on("click", function(vw, index, node, e){
16056  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16057  });
16058
16059  // load XML data
16060  dataModel.load("foobar.xml");
16061  </code></pre>
16062  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16063  * <br><br>
16064  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16065  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16066  * 
16067  * Note: old style constructor is still suported (container, template, config)
16068  * 
16069  * @constructor
16070  * Create a new View
16071  * @param {Object} config The config object
16072  * 
16073  */
16074 Roo.View = function(config, depreciated_tpl, depreciated_config){
16075     
16076     this.parent = false;
16077     
16078     if (typeof(depreciated_tpl) == 'undefined') {
16079         // new way.. - universal constructor.
16080         Roo.apply(this, config);
16081         this.el  = Roo.get(this.el);
16082     } else {
16083         // old format..
16084         this.el  = Roo.get(config);
16085         this.tpl = depreciated_tpl;
16086         Roo.apply(this, depreciated_config);
16087     }
16088     this.wrapEl  = this.el.wrap().wrap();
16089     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16090     
16091     
16092     if(typeof(this.tpl) == "string"){
16093         this.tpl = new Roo.Template(this.tpl);
16094     } else {
16095         // support xtype ctors..
16096         this.tpl = new Roo.factory(this.tpl, Roo);
16097     }
16098     
16099     
16100     this.tpl.compile();
16101     
16102     /** @private */
16103     this.addEvents({
16104         /**
16105          * @event beforeclick
16106          * Fires before a click is processed. Returns false to cancel the default action.
16107          * @param {Roo.View} this
16108          * @param {Number} index The index of the target node
16109          * @param {HTMLElement} node The target node
16110          * @param {Roo.EventObject} e The raw event object
16111          */
16112             "beforeclick" : true,
16113         /**
16114          * @event click
16115          * Fires when a template node is clicked.
16116          * @param {Roo.View} this
16117          * @param {Number} index The index of the target node
16118          * @param {HTMLElement} node The target node
16119          * @param {Roo.EventObject} e The raw event object
16120          */
16121             "click" : true,
16122         /**
16123          * @event dblclick
16124          * Fires when a template node is double clicked.
16125          * @param {Roo.View} this
16126          * @param {Number} index The index of the target node
16127          * @param {HTMLElement} node The target node
16128          * @param {Roo.EventObject} e The raw event object
16129          */
16130             "dblclick" : true,
16131         /**
16132          * @event contextmenu
16133          * Fires when a template node is right clicked.
16134          * @param {Roo.View} this
16135          * @param {Number} index The index of the target node
16136          * @param {HTMLElement} node The target node
16137          * @param {Roo.EventObject} e The raw event object
16138          */
16139             "contextmenu" : true,
16140         /**
16141          * @event selectionchange
16142          * Fires when the selected nodes change.
16143          * @param {Roo.View} this
16144          * @param {Array} selections Array of the selected nodes
16145          */
16146             "selectionchange" : true,
16147     
16148         /**
16149          * @event beforeselect
16150          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16151          * @param {Roo.View} this
16152          * @param {HTMLElement} node The node to be selected
16153          * @param {Array} selections Array of currently selected nodes
16154          */
16155             "beforeselect" : true,
16156         /**
16157          * @event preparedata
16158          * Fires on every row to render, to allow you to change the data.
16159          * @param {Roo.View} this
16160          * @param {Object} data to be rendered (change this)
16161          */
16162           "preparedata" : true
16163           
16164           
16165         });
16166
16167
16168
16169     this.el.on({
16170         "click": this.onClick,
16171         "dblclick": this.onDblClick,
16172         "contextmenu": this.onContextMenu,
16173         scope:this
16174     });
16175
16176     this.selections = [];
16177     this.nodes = [];
16178     this.cmp = new Roo.CompositeElementLite([]);
16179     if(this.store){
16180         this.store = Roo.factory(this.store, Roo.data);
16181         this.setStore(this.store, true);
16182     }
16183     
16184     if ( this.footer && this.footer.xtype) {
16185            
16186          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16187         
16188         this.footer.dataSource = this.store;
16189         this.footer.container = fctr;
16190         this.footer = Roo.factory(this.footer, Roo);
16191         fctr.insertFirst(this.el);
16192         
16193         // this is a bit insane - as the paging toolbar seems to detach the el..
16194 //        dom.parentNode.parentNode.parentNode
16195          // they get detached?
16196     }
16197     
16198     
16199     Roo.View.superclass.constructor.call(this);
16200     
16201     
16202 };
16203
16204 Roo.extend(Roo.View, Roo.util.Observable, {
16205     
16206      /**
16207      * @cfg {Roo.data.Store} store Data store to load data from.
16208      */
16209     store : false,
16210     
16211     /**
16212      * @cfg {String|Roo.Element} el The container element.
16213      */
16214     el : '',
16215     
16216     /**
16217      * @cfg {String|Roo.Template} tpl The template used by this View 
16218      */
16219     tpl : false,
16220     /**
16221      * @cfg {String} dataName the named area of the template to use as the data area
16222      *                          Works with domtemplates roo-name="name"
16223      */
16224     dataName: false,
16225     /**
16226      * @cfg {String} selectedClass The css class to add to selected nodes
16227      */
16228     selectedClass : "x-view-selected",
16229      /**
16230      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16231      */
16232     emptyText : "",
16233     
16234     /**
16235      * @cfg {String} text to display on mask (default Loading)
16236      */
16237     mask : false,
16238     /**
16239      * @cfg {Boolean} multiSelect Allow multiple selection
16240      */
16241     multiSelect : false,
16242     /**
16243      * @cfg {Boolean} singleSelect Allow single selection
16244      */
16245     singleSelect:  false,
16246     
16247     /**
16248      * @cfg {Boolean} toggleSelect - selecting 
16249      */
16250     toggleSelect : false,
16251     
16252     /**
16253      * @cfg {Boolean} tickable - selecting 
16254      */
16255     tickable : false,
16256     
16257     /**
16258      * Returns the element this view is bound to.
16259      * @return {Roo.Element}
16260      */
16261     getEl : function(){
16262         return this.wrapEl;
16263     },
16264     
16265     
16266
16267     /**
16268      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16269      */
16270     refresh : function(){
16271         //Roo.log('refresh');
16272         var t = this.tpl;
16273         
16274         // if we are using something like 'domtemplate', then
16275         // the what gets used is:
16276         // t.applySubtemplate(NAME, data, wrapping data..)
16277         // the outer template then get' applied with
16278         //     the store 'extra data'
16279         // and the body get's added to the
16280         //      roo-name="data" node?
16281         //      <span class='roo-tpl-{name}'></span> ?????
16282         
16283         
16284         
16285         this.clearSelections();
16286         this.el.update("");
16287         var html = [];
16288         var records = this.store.getRange();
16289         if(records.length < 1) {
16290             
16291             // is this valid??  = should it render a template??
16292             
16293             this.el.update(this.emptyText);
16294             return;
16295         }
16296         var el = this.el;
16297         if (this.dataName) {
16298             this.el.update(t.apply(this.store.meta)); //????
16299             el = this.el.child('.roo-tpl-' + this.dataName);
16300         }
16301         
16302         for(var i = 0, len = records.length; i < len; i++){
16303             var data = this.prepareData(records[i].data, i, records[i]);
16304             this.fireEvent("preparedata", this, data, i, records[i]);
16305             
16306             var d = Roo.apply({}, data);
16307             
16308             if(this.tickable){
16309                 Roo.apply(d, {'roo-id' : Roo.id()});
16310                 
16311                 var _this = this;
16312             
16313                 Roo.each(this.parent.item, function(item){
16314                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16315                         return;
16316                     }
16317                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16318                 });
16319             }
16320             
16321             html[html.length] = Roo.util.Format.trim(
16322                 this.dataName ?
16323                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16324                     t.apply(d)
16325             );
16326         }
16327         
16328         
16329         
16330         el.update(html.join(""));
16331         this.nodes = el.dom.childNodes;
16332         this.updateIndexes(0);
16333     },
16334     
16335
16336     /**
16337      * Function to override to reformat the data that is sent to
16338      * the template for each node.
16339      * DEPRICATED - use the preparedata event handler.
16340      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16341      * a JSON object for an UpdateManager bound view).
16342      */
16343     prepareData : function(data, index, record)
16344     {
16345         this.fireEvent("preparedata", this, data, index, record);
16346         return data;
16347     },
16348
16349     onUpdate : function(ds, record){
16350         // Roo.log('on update');   
16351         this.clearSelections();
16352         var index = this.store.indexOf(record);
16353         var n = this.nodes[index];
16354         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16355         n.parentNode.removeChild(n);
16356         this.updateIndexes(index, index);
16357     },
16358
16359     
16360     
16361 // --------- FIXME     
16362     onAdd : function(ds, records, index)
16363     {
16364         //Roo.log(['on Add', ds, records, index] );        
16365         this.clearSelections();
16366         if(this.nodes.length == 0){
16367             this.refresh();
16368             return;
16369         }
16370         var n = this.nodes[index];
16371         for(var i = 0, len = records.length; i < len; i++){
16372             var d = this.prepareData(records[i].data, i, records[i]);
16373             if(n){
16374                 this.tpl.insertBefore(n, d);
16375             }else{
16376                 
16377                 this.tpl.append(this.el, d);
16378             }
16379         }
16380         this.updateIndexes(index);
16381     },
16382
16383     onRemove : function(ds, record, index){
16384        // Roo.log('onRemove');
16385         this.clearSelections();
16386         var el = this.dataName  ?
16387             this.el.child('.roo-tpl-' + this.dataName) :
16388             this.el; 
16389         
16390         el.dom.removeChild(this.nodes[index]);
16391         this.updateIndexes(index);
16392     },
16393
16394     /**
16395      * Refresh an individual node.
16396      * @param {Number} index
16397      */
16398     refreshNode : function(index){
16399         this.onUpdate(this.store, this.store.getAt(index));
16400     },
16401
16402     updateIndexes : function(startIndex, endIndex){
16403         var ns = this.nodes;
16404         startIndex = startIndex || 0;
16405         endIndex = endIndex || ns.length - 1;
16406         for(var i = startIndex; i <= endIndex; i++){
16407             ns[i].nodeIndex = i;
16408         }
16409     },
16410
16411     /**
16412      * Changes the data store this view uses and refresh the view.
16413      * @param {Store} store
16414      */
16415     setStore : function(store, initial){
16416         if(!initial && this.store){
16417             this.store.un("datachanged", this.refresh);
16418             this.store.un("add", this.onAdd);
16419             this.store.un("remove", this.onRemove);
16420             this.store.un("update", this.onUpdate);
16421             this.store.un("clear", this.refresh);
16422             this.store.un("beforeload", this.onBeforeLoad);
16423             this.store.un("load", this.onLoad);
16424             this.store.un("loadexception", this.onLoad);
16425         }
16426         if(store){
16427           
16428             store.on("datachanged", this.refresh, this);
16429             store.on("add", this.onAdd, this);
16430             store.on("remove", this.onRemove, this);
16431             store.on("update", this.onUpdate, this);
16432             store.on("clear", this.refresh, this);
16433             store.on("beforeload", this.onBeforeLoad, this);
16434             store.on("load", this.onLoad, this);
16435             store.on("loadexception", this.onLoad, this);
16436         }
16437         
16438         if(store){
16439             this.refresh();
16440         }
16441     },
16442     /**
16443      * onbeforeLoad - masks the loading area.
16444      *
16445      */
16446     onBeforeLoad : function(store,opts)
16447     {
16448          //Roo.log('onBeforeLoad');   
16449         if (!opts.add) {
16450             this.el.update("");
16451         }
16452         this.el.mask(this.mask ? this.mask : "Loading" ); 
16453     },
16454     onLoad : function ()
16455     {
16456         this.el.unmask();
16457     },
16458     
16459
16460     /**
16461      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16462      * @param {HTMLElement} node
16463      * @return {HTMLElement} The template node
16464      */
16465     findItemFromChild : function(node){
16466         var el = this.dataName  ?
16467             this.el.child('.roo-tpl-' + this.dataName,true) :
16468             this.el.dom; 
16469         
16470         if(!node || node.parentNode == el){
16471                     return node;
16472             }
16473             var p = node.parentNode;
16474             while(p && p != el){
16475             if(p.parentNode == el){
16476                 return p;
16477             }
16478             p = p.parentNode;
16479         }
16480             return null;
16481     },
16482
16483     /** @ignore */
16484     onClick : function(e){
16485         var item = this.findItemFromChild(e.getTarget());
16486         if(item){
16487             var index = this.indexOf(item);
16488             if(this.onItemClick(item, index, e) !== false){
16489                 this.fireEvent("click", this, index, item, e);
16490             }
16491         }else{
16492             this.clearSelections();
16493         }
16494     },
16495
16496     /** @ignore */
16497     onContextMenu : function(e){
16498         var item = this.findItemFromChild(e.getTarget());
16499         if(item){
16500             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16501         }
16502     },
16503
16504     /** @ignore */
16505     onDblClick : function(e){
16506         var item = this.findItemFromChild(e.getTarget());
16507         if(item){
16508             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16509         }
16510     },
16511
16512     onItemClick : function(item, index, e)
16513     {
16514         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16515             return false;
16516         }
16517         if (this.toggleSelect) {
16518             var m = this.isSelected(item) ? 'unselect' : 'select';
16519             //Roo.log(m);
16520             var _t = this;
16521             _t[m](item, true, false);
16522             return true;
16523         }
16524         if(this.multiSelect || this.singleSelect){
16525             if(this.multiSelect && e.shiftKey && this.lastSelection){
16526                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16527             }else{
16528                 this.select(item, this.multiSelect && e.ctrlKey);
16529                 this.lastSelection = item;
16530             }
16531             
16532             if(!this.tickable){
16533                 e.preventDefault();
16534             }
16535             
16536         }
16537         return true;
16538     },
16539
16540     /**
16541      * Get the number of selected nodes.
16542      * @return {Number}
16543      */
16544     getSelectionCount : function(){
16545         return this.selections.length;
16546     },
16547
16548     /**
16549      * Get the currently selected nodes.
16550      * @return {Array} An array of HTMLElements
16551      */
16552     getSelectedNodes : function(){
16553         return this.selections;
16554     },
16555
16556     /**
16557      * Get the indexes of the selected nodes.
16558      * @return {Array}
16559      */
16560     getSelectedIndexes : function(){
16561         var indexes = [], s = this.selections;
16562         for(var i = 0, len = s.length; i < len; i++){
16563             indexes.push(s[i].nodeIndex);
16564         }
16565         return indexes;
16566     },
16567
16568     /**
16569      * Clear all selections
16570      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16571      */
16572     clearSelections : function(suppressEvent){
16573         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16574             this.cmp.elements = this.selections;
16575             this.cmp.removeClass(this.selectedClass);
16576             this.selections = [];
16577             if(!suppressEvent){
16578                 this.fireEvent("selectionchange", this, this.selections);
16579             }
16580         }
16581     },
16582
16583     /**
16584      * Returns true if the passed node is selected
16585      * @param {HTMLElement/Number} node The node or node index
16586      * @return {Boolean}
16587      */
16588     isSelected : function(node){
16589         var s = this.selections;
16590         if(s.length < 1){
16591             return false;
16592         }
16593         node = this.getNode(node);
16594         return s.indexOf(node) !== -1;
16595     },
16596
16597     /**
16598      * Selects nodes.
16599      * @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
16600      * @param {Boolean} keepExisting (optional) true to keep existing selections
16601      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16602      */
16603     select : function(nodeInfo, keepExisting, suppressEvent){
16604         if(nodeInfo instanceof Array){
16605             if(!keepExisting){
16606                 this.clearSelections(true);
16607             }
16608             for(var i = 0, len = nodeInfo.length; i < len; i++){
16609                 this.select(nodeInfo[i], true, true);
16610             }
16611             return;
16612         } 
16613         var node = this.getNode(nodeInfo);
16614         if(!node || this.isSelected(node)){
16615             return; // already selected.
16616         }
16617         if(!keepExisting){
16618             this.clearSelections(true);
16619         }
16620         
16621         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16622             Roo.fly(node).addClass(this.selectedClass);
16623             this.selections.push(node);
16624             if(!suppressEvent){
16625                 this.fireEvent("selectionchange", this, this.selections);
16626             }
16627         }
16628         
16629         
16630     },
16631       /**
16632      * Unselects nodes.
16633      * @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
16634      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16635      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16636      */
16637     unselect : function(nodeInfo, keepExisting, suppressEvent)
16638     {
16639         if(nodeInfo instanceof Array){
16640             Roo.each(this.selections, function(s) {
16641                 this.unselect(s, nodeInfo);
16642             }, this);
16643             return;
16644         }
16645         var node = this.getNode(nodeInfo);
16646         if(!node || !this.isSelected(node)){
16647             //Roo.log("not selected");
16648             return; // not selected.
16649         }
16650         // fireevent???
16651         var ns = [];
16652         Roo.each(this.selections, function(s) {
16653             if (s == node ) {
16654                 Roo.fly(node).removeClass(this.selectedClass);
16655
16656                 return;
16657             }
16658             ns.push(s);
16659         },this);
16660         
16661         this.selections= ns;
16662         this.fireEvent("selectionchange", this, this.selections);
16663     },
16664
16665     /**
16666      * Gets a template node.
16667      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16668      * @return {HTMLElement} The node or null if it wasn't found
16669      */
16670     getNode : function(nodeInfo){
16671         if(typeof nodeInfo == "string"){
16672             return document.getElementById(nodeInfo);
16673         }else if(typeof nodeInfo == "number"){
16674             return this.nodes[nodeInfo];
16675         }
16676         return nodeInfo;
16677     },
16678
16679     /**
16680      * Gets a range template nodes.
16681      * @param {Number} startIndex
16682      * @param {Number} endIndex
16683      * @return {Array} An array of nodes
16684      */
16685     getNodes : function(start, end){
16686         var ns = this.nodes;
16687         start = start || 0;
16688         end = typeof end == "undefined" ? ns.length - 1 : end;
16689         var nodes = [];
16690         if(start <= end){
16691             for(var i = start; i <= end; i++){
16692                 nodes.push(ns[i]);
16693             }
16694         } else{
16695             for(var i = start; i >= end; i--){
16696                 nodes.push(ns[i]);
16697             }
16698         }
16699         return nodes;
16700     },
16701
16702     /**
16703      * Finds the index of the passed node
16704      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16705      * @return {Number} The index of the node or -1
16706      */
16707     indexOf : function(node){
16708         node = this.getNode(node);
16709         if(typeof node.nodeIndex == "number"){
16710             return node.nodeIndex;
16711         }
16712         var ns = this.nodes;
16713         for(var i = 0, len = ns.length; i < len; i++){
16714             if(ns[i] == node){
16715                 return i;
16716             }
16717         }
16718         return -1;
16719     }
16720 });
16721 /*
16722  * - LGPL
16723  *
16724  * based on jquery fullcalendar
16725  * 
16726  */
16727
16728 Roo.bootstrap = Roo.bootstrap || {};
16729 /**
16730  * @class Roo.bootstrap.Calendar
16731  * @extends Roo.bootstrap.Component
16732  * Bootstrap Calendar class
16733  * @cfg {Boolean} loadMask (true|false) default false
16734  * @cfg {Object} header generate the user specific header of the calendar, default false
16735
16736  * @constructor
16737  * Create a new Container
16738  * @param {Object} config The config object
16739  */
16740
16741
16742
16743 Roo.bootstrap.Calendar = function(config){
16744     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16745      this.addEvents({
16746         /**
16747              * @event select
16748              * Fires when a date is selected
16749              * @param {DatePicker} this
16750              * @param {Date} date The selected date
16751              */
16752         'select': true,
16753         /**
16754              * @event monthchange
16755              * Fires when the displayed month changes 
16756              * @param {DatePicker} this
16757              * @param {Date} date The selected month
16758              */
16759         'monthchange': true,
16760         /**
16761              * @event evententer
16762              * Fires when mouse over an event
16763              * @param {Calendar} this
16764              * @param {event} Event
16765              */
16766         'evententer': true,
16767         /**
16768              * @event eventleave
16769              * Fires when the mouse leaves an
16770              * @param {Calendar} this
16771              * @param {event}
16772              */
16773         'eventleave': true,
16774         /**
16775              * @event eventclick
16776              * Fires when the mouse click an
16777              * @param {Calendar} this
16778              * @param {event}
16779              */
16780         'eventclick': true
16781         
16782     });
16783
16784 };
16785
16786 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16787     
16788      /**
16789      * @cfg {Number} startDay
16790      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16791      */
16792     startDay : 0,
16793     
16794     loadMask : false,
16795     
16796     header : false,
16797       
16798     getAutoCreate : function(){
16799         
16800         
16801         var fc_button = function(name, corner, style, content ) {
16802             return Roo.apply({},{
16803                 tag : 'span',
16804                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16805                          (corner.length ?
16806                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16807                             ''
16808                         ),
16809                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16810                 unselectable: 'on'
16811             });
16812         };
16813         
16814         var header = {};
16815         
16816         if(!this.header){
16817             header = {
16818                 tag : 'table',
16819                 cls : 'fc-header',
16820                 style : 'width:100%',
16821                 cn : [
16822                     {
16823                         tag: 'tr',
16824                         cn : [
16825                             {
16826                                 tag : 'td',
16827                                 cls : 'fc-header-left',
16828                                 cn : [
16829                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16830                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16831                                     { tag: 'span', cls: 'fc-header-space' },
16832                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16833
16834
16835                                 ]
16836                             },
16837
16838                             {
16839                                 tag : 'td',
16840                                 cls : 'fc-header-center',
16841                                 cn : [
16842                                     {
16843                                         tag: 'span',
16844                                         cls: 'fc-header-title',
16845                                         cn : {
16846                                             tag: 'H2',
16847                                             html : 'month / year'
16848                                         }
16849                                     }
16850
16851                                 ]
16852                             },
16853                             {
16854                                 tag : 'td',
16855                                 cls : 'fc-header-right',
16856                                 cn : [
16857                               /*      fc_button('month', 'left', '', 'month' ),
16858                                     fc_button('week', '', '', 'week' ),
16859                                     fc_button('day', 'right', '', 'day' )
16860                                 */    
16861
16862                                 ]
16863                             }
16864
16865                         ]
16866                     }
16867                 ]
16868             };
16869         }
16870         
16871         header = this.header;
16872         
16873        
16874         var cal_heads = function() {
16875             var ret = [];
16876             // fixme - handle this.
16877             
16878             for (var i =0; i < Date.dayNames.length; i++) {
16879                 var d = Date.dayNames[i];
16880                 ret.push({
16881                     tag: 'th',
16882                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16883                     html : d.substring(0,3)
16884                 });
16885                 
16886             }
16887             ret[0].cls += ' fc-first';
16888             ret[6].cls += ' fc-last';
16889             return ret;
16890         };
16891         var cal_cell = function(n) {
16892             return  {
16893                 tag: 'td',
16894                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16895                 cn : [
16896                     {
16897                         cn : [
16898                             {
16899                                 cls: 'fc-day-number',
16900                                 html: 'D'
16901                             },
16902                             {
16903                                 cls: 'fc-day-content',
16904                              
16905                                 cn : [
16906                                      {
16907                                         style: 'position: relative;' // height: 17px;
16908                                     }
16909                                 ]
16910                             }
16911                             
16912                             
16913                         ]
16914                     }
16915                 ]
16916                 
16917             }
16918         };
16919         var cal_rows = function() {
16920             
16921             var ret = [];
16922             for (var r = 0; r < 6; r++) {
16923                 var row= {
16924                     tag : 'tr',
16925                     cls : 'fc-week',
16926                     cn : []
16927                 };
16928                 
16929                 for (var i =0; i < Date.dayNames.length; i++) {
16930                     var d = Date.dayNames[i];
16931                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16932
16933                 }
16934                 row.cn[0].cls+=' fc-first';
16935                 row.cn[0].cn[0].style = 'min-height:90px';
16936                 row.cn[6].cls+=' fc-last';
16937                 ret.push(row);
16938                 
16939             }
16940             ret[0].cls += ' fc-first';
16941             ret[4].cls += ' fc-prev-last';
16942             ret[5].cls += ' fc-last';
16943             return ret;
16944             
16945         };
16946         
16947         var cal_table = {
16948             tag: 'table',
16949             cls: 'fc-border-separate',
16950             style : 'width:100%',
16951             cellspacing  : 0,
16952             cn : [
16953                 { 
16954                     tag: 'thead',
16955                     cn : [
16956                         { 
16957                             tag: 'tr',
16958                             cls : 'fc-first fc-last',
16959                             cn : cal_heads()
16960                         }
16961                     ]
16962                 },
16963                 { 
16964                     tag: 'tbody',
16965                     cn : cal_rows()
16966                 }
16967                   
16968             ]
16969         };
16970          
16971          var cfg = {
16972             cls : 'fc fc-ltr',
16973             cn : [
16974                 header,
16975                 {
16976                     cls : 'fc-content',
16977                     style : "position: relative;",
16978                     cn : [
16979                         {
16980                             cls : 'fc-view fc-view-month fc-grid',
16981                             style : 'position: relative',
16982                             unselectable : 'on',
16983                             cn : [
16984                                 {
16985                                     cls : 'fc-event-container',
16986                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16987                                 },
16988                                 cal_table
16989                             ]
16990                         }
16991                     ]
16992     
16993                 }
16994            ] 
16995             
16996         };
16997         
16998          
16999         
17000         return cfg;
17001     },
17002     
17003     
17004     initEvents : function()
17005     {
17006         if(!this.store){
17007             throw "can not find store for calendar";
17008         }
17009         
17010         var mark = {
17011             tag: "div",
17012             cls:"x-dlg-mask",
17013             style: "text-align:center",
17014             cn: [
17015                 {
17016                     tag: "div",
17017                     style: "background-color:white;width:50%;margin:250 auto",
17018                     cn: [
17019                         {
17020                             tag: "img",
17021                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17022                         },
17023                         {
17024                             tag: "span",
17025                             html: "Loading"
17026                         }
17027                         
17028                     ]
17029                 }
17030             ]
17031         };
17032         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17033         
17034         var size = this.el.select('.fc-content', true).first().getSize();
17035         this.maskEl.setSize(size.width, size.height);
17036         this.maskEl.enableDisplayMode("block");
17037         if(!this.loadMask){
17038             this.maskEl.hide();
17039         }
17040         
17041         this.store = Roo.factory(this.store, Roo.data);
17042         this.store.on('load', this.onLoad, this);
17043         this.store.on('beforeload', this.onBeforeLoad, this);
17044         
17045         this.resize();
17046         
17047         this.cells = this.el.select('.fc-day',true);
17048         //Roo.log(this.cells);
17049         this.textNodes = this.el.query('.fc-day-number');
17050         this.cells.addClassOnOver('fc-state-hover');
17051         
17052         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17053         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17054         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17055         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17056         
17057         this.on('monthchange', this.onMonthChange, this);
17058         
17059         this.update(new Date().clearTime());
17060     },
17061     
17062     resize : function() {
17063         var sz  = this.el.getSize();
17064         
17065         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17066         this.el.select('.fc-day-content div',true).setHeight(34);
17067     },
17068     
17069     
17070     // private
17071     showPrevMonth : function(e){
17072         this.update(this.activeDate.add("mo", -1));
17073     },
17074     showToday : function(e){
17075         this.update(new Date().clearTime());
17076     },
17077     // private
17078     showNextMonth : function(e){
17079         this.update(this.activeDate.add("mo", 1));
17080     },
17081
17082     // private
17083     showPrevYear : function(){
17084         this.update(this.activeDate.add("y", -1));
17085     },
17086
17087     // private
17088     showNextYear : function(){
17089         this.update(this.activeDate.add("y", 1));
17090     },
17091
17092     
17093    // private
17094     update : function(date)
17095     {
17096         var vd = this.activeDate;
17097         this.activeDate = date;
17098 //        if(vd && this.el){
17099 //            var t = date.getTime();
17100 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17101 //                Roo.log('using add remove');
17102 //                
17103 //                this.fireEvent('monthchange', this, date);
17104 //                
17105 //                this.cells.removeClass("fc-state-highlight");
17106 //                this.cells.each(function(c){
17107 //                   if(c.dateValue == t){
17108 //                       c.addClass("fc-state-highlight");
17109 //                       setTimeout(function(){
17110 //                            try{c.dom.firstChild.focus();}catch(e){}
17111 //                       }, 50);
17112 //                       return false;
17113 //                   }
17114 //                   return true;
17115 //                });
17116 //                return;
17117 //            }
17118 //        }
17119         
17120         var days = date.getDaysInMonth();
17121         
17122         var firstOfMonth = date.getFirstDateOfMonth();
17123         var startingPos = firstOfMonth.getDay()-this.startDay;
17124         
17125         if(startingPos < this.startDay){
17126             startingPos += 7;
17127         }
17128         
17129         var pm = date.add(Date.MONTH, -1);
17130         var prevStart = pm.getDaysInMonth()-startingPos;
17131 //        
17132         this.cells = this.el.select('.fc-day',true);
17133         this.textNodes = this.el.query('.fc-day-number');
17134         this.cells.addClassOnOver('fc-state-hover');
17135         
17136         var cells = this.cells.elements;
17137         var textEls = this.textNodes;
17138         
17139         Roo.each(cells, function(cell){
17140             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17141         });
17142         
17143         days += startingPos;
17144
17145         // convert everything to numbers so it's fast
17146         var day = 86400000;
17147         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17148         //Roo.log(d);
17149         //Roo.log(pm);
17150         //Roo.log(prevStart);
17151         
17152         var today = new Date().clearTime().getTime();
17153         var sel = date.clearTime().getTime();
17154         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17155         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17156         var ddMatch = this.disabledDatesRE;
17157         var ddText = this.disabledDatesText;
17158         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17159         var ddaysText = this.disabledDaysText;
17160         var format = this.format;
17161         
17162         var setCellClass = function(cal, cell){
17163             cell.row = 0;
17164             cell.events = [];
17165             cell.more = [];
17166             //Roo.log('set Cell Class');
17167             cell.title = "";
17168             var t = d.getTime();
17169             
17170             //Roo.log(d);
17171             
17172             cell.dateValue = t;
17173             if(t == today){
17174                 cell.className += " fc-today";
17175                 cell.className += " fc-state-highlight";
17176                 cell.title = cal.todayText;
17177             }
17178             if(t == sel){
17179                 // disable highlight in other month..
17180                 //cell.className += " fc-state-highlight";
17181                 
17182             }
17183             // disabling
17184             if(t < min) {
17185                 cell.className = " fc-state-disabled";
17186                 cell.title = cal.minText;
17187                 return;
17188             }
17189             if(t > max) {
17190                 cell.className = " fc-state-disabled";
17191                 cell.title = cal.maxText;
17192                 return;
17193             }
17194             if(ddays){
17195                 if(ddays.indexOf(d.getDay()) != -1){
17196                     cell.title = ddaysText;
17197                     cell.className = " fc-state-disabled";
17198                 }
17199             }
17200             if(ddMatch && format){
17201                 var fvalue = d.dateFormat(format);
17202                 if(ddMatch.test(fvalue)){
17203                     cell.title = ddText.replace("%0", fvalue);
17204                     cell.className = " fc-state-disabled";
17205                 }
17206             }
17207             
17208             if (!cell.initialClassName) {
17209                 cell.initialClassName = cell.dom.className;
17210             }
17211             
17212             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17213         };
17214
17215         var i = 0;
17216         
17217         for(; i < startingPos; i++) {
17218             textEls[i].innerHTML = (++prevStart);
17219             d.setDate(d.getDate()+1);
17220             
17221             cells[i].className = "fc-past fc-other-month";
17222             setCellClass(this, cells[i]);
17223         }
17224         
17225         var intDay = 0;
17226         
17227         for(; i < days; i++){
17228             intDay = i - startingPos + 1;
17229             textEls[i].innerHTML = (intDay);
17230             d.setDate(d.getDate()+1);
17231             
17232             cells[i].className = ''; // "x-date-active";
17233             setCellClass(this, cells[i]);
17234         }
17235         var extraDays = 0;
17236         
17237         for(; i < 42; i++) {
17238             textEls[i].innerHTML = (++extraDays);
17239             d.setDate(d.getDate()+1);
17240             
17241             cells[i].className = "fc-future fc-other-month";
17242             setCellClass(this, cells[i]);
17243         }
17244         
17245         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17246         
17247         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17248         
17249         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17250         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17251         
17252         if(totalRows != 6){
17253             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17254             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17255         }
17256         
17257         this.fireEvent('monthchange', this, date);
17258         
17259         
17260         /*
17261         if(!this.internalRender){
17262             var main = this.el.dom.firstChild;
17263             var w = main.offsetWidth;
17264             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17265             Roo.fly(main).setWidth(w);
17266             this.internalRender = true;
17267             // opera does not respect the auto grow header center column
17268             // then, after it gets a width opera refuses to recalculate
17269             // without a second pass
17270             if(Roo.isOpera && !this.secondPass){
17271                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17272                 this.secondPass = true;
17273                 this.update.defer(10, this, [date]);
17274             }
17275         }
17276         */
17277         
17278     },
17279     
17280     findCell : function(dt) {
17281         dt = dt.clearTime().getTime();
17282         var ret = false;
17283         this.cells.each(function(c){
17284             //Roo.log("check " +c.dateValue + '?=' + dt);
17285             if(c.dateValue == dt){
17286                 ret = c;
17287                 return false;
17288             }
17289             return true;
17290         });
17291         
17292         return ret;
17293     },
17294     
17295     findCells : function(ev) {
17296         var s = ev.start.clone().clearTime().getTime();
17297        // Roo.log(s);
17298         var e= ev.end.clone().clearTime().getTime();
17299        // Roo.log(e);
17300         var ret = [];
17301         this.cells.each(function(c){
17302              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17303             
17304             if(c.dateValue > e){
17305                 return ;
17306             }
17307             if(c.dateValue < s){
17308                 return ;
17309             }
17310             ret.push(c);
17311         });
17312         
17313         return ret;    
17314     },
17315     
17316 //    findBestRow: function(cells)
17317 //    {
17318 //        var ret = 0;
17319 //        
17320 //        for (var i =0 ; i < cells.length;i++) {
17321 //            ret  = Math.max(cells[i].rows || 0,ret);
17322 //        }
17323 //        return ret;
17324 //        
17325 //    },
17326     
17327     
17328     addItem : function(ev)
17329     {
17330         // look for vertical location slot in
17331         var cells = this.findCells(ev);
17332         
17333 //        ev.row = this.findBestRow(cells);
17334         
17335         // work out the location.
17336         
17337         var crow = false;
17338         var rows = [];
17339         for(var i =0; i < cells.length; i++) {
17340             
17341             cells[i].row = cells[0].row;
17342             
17343             if(i == 0){
17344                 cells[i].row = cells[i].row + 1;
17345             }
17346             
17347             if (!crow) {
17348                 crow = {
17349                     start : cells[i],
17350                     end :  cells[i]
17351                 };
17352                 continue;
17353             }
17354             if (crow.start.getY() == cells[i].getY()) {
17355                 // on same row.
17356                 crow.end = cells[i];
17357                 continue;
17358             }
17359             // different row.
17360             rows.push(crow);
17361             crow = {
17362                 start: cells[i],
17363                 end : cells[i]
17364             };
17365             
17366         }
17367         
17368         rows.push(crow);
17369         ev.els = [];
17370         ev.rows = rows;
17371         ev.cells = cells;
17372         
17373         cells[0].events.push(ev);
17374         
17375         this.calevents.push(ev);
17376     },
17377     
17378     clearEvents: function() {
17379         
17380         if(!this.calevents){
17381             return;
17382         }
17383         
17384         Roo.each(this.cells.elements, function(c){
17385             c.row = 0;
17386             c.events = [];
17387             c.more = [];
17388         });
17389         
17390         Roo.each(this.calevents, function(e) {
17391             Roo.each(e.els, function(el) {
17392                 el.un('mouseenter' ,this.onEventEnter, this);
17393                 el.un('mouseleave' ,this.onEventLeave, this);
17394                 el.remove();
17395             },this);
17396         },this);
17397         
17398         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17399             e.remove();
17400         });
17401         
17402     },
17403     
17404     renderEvents: function()
17405     {   
17406         var _this = this;
17407         
17408         this.cells.each(function(c) {
17409             
17410             if(c.row < 5){
17411                 return;
17412             }
17413             
17414             var ev = c.events;
17415             
17416             var r = 4;
17417             if(c.row != c.events.length){
17418                 r = 4 - (4 - (c.row - c.events.length));
17419             }
17420             
17421             c.events = ev.slice(0, r);
17422             c.more = ev.slice(r);
17423             
17424             if(c.more.length && c.more.length == 1){
17425                 c.events.push(c.more.pop());
17426             }
17427             
17428             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17429             
17430         });
17431             
17432         this.cells.each(function(c) {
17433             
17434             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17435             
17436             
17437             for (var e = 0; e < c.events.length; e++){
17438                 var ev = c.events[e];
17439                 var rows = ev.rows;
17440                 
17441                 for(var i = 0; i < rows.length; i++) {
17442                 
17443                     // how many rows should it span..
17444
17445                     var  cfg = {
17446                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17447                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17448
17449                         unselectable : "on",
17450                         cn : [
17451                             {
17452                                 cls: 'fc-event-inner',
17453                                 cn : [
17454     //                                {
17455     //                                  tag:'span',
17456     //                                  cls: 'fc-event-time',
17457     //                                  html : cells.length > 1 ? '' : ev.time
17458     //                                },
17459                                     {
17460                                       tag:'span',
17461                                       cls: 'fc-event-title',
17462                                       html : String.format('{0}', ev.title)
17463                                     }
17464
17465
17466                                 ]
17467                             },
17468                             {
17469                                 cls: 'ui-resizable-handle ui-resizable-e',
17470                                 html : '&nbsp;&nbsp;&nbsp'
17471                             }
17472
17473                         ]
17474                     };
17475
17476                     if (i == 0) {
17477                         cfg.cls += ' fc-event-start';
17478                     }
17479                     if ((i+1) == rows.length) {
17480                         cfg.cls += ' fc-event-end';
17481                     }
17482
17483                     var ctr = _this.el.select('.fc-event-container',true).first();
17484                     var cg = ctr.createChild(cfg);
17485
17486                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17487                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17488
17489                     var r = (c.more.length) ? 1 : 0;
17490                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17491                     cg.setWidth(ebox.right - sbox.x -2);
17492
17493                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17494                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17495                     cg.on('click', _this.onEventClick, _this, ev);
17496
17497                     ev.els.push(cg);
17498                     
17499                 }
17500                 
17501             }
17502             
17503             
17504             if(c.more.length){
17505                 var  cfg = {
17506                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17507                     style : 'position: absolute',
17508                     unselectable : "on",
17509                     cn : [
17510                         {
17511                             cls: 'fc-event-inner',
17512                             cn : [
17513                                 {
17514                                   tag:'span',
17515                                   cls: 'fc-event-title',
17516                                   html : 'More'
17517                                 }
17518
17519
17520                             ]
17521                         },
17522                         {
17523                             cls: 'ui-resizable-handle ui-resizable-e',
17524                             html : '&nbsp;&nbsp;&nbsp'
17525                         }
17526
17527                     ]
17528                 };
17529
17530                 var ctr = _this.el.select('.fc-event-container',true).first();
17531                 var cg = ctr.createChild(cfg);
17532
17533                 var sbox = c.select('.fc-day-content',true).first().getBox();
17534                 var ebox = c.select('.fc-day-content',true).first().getBox();
17535                 //Roo.log(cg);
17536                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17537                 cg.setWidth(ebox.right - sbox.x -2);
17538
17539                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17540                 
17541             }
17542             
17543         });
17544         
17545         
17546         
17547     },
17548     
17549     onEventEnter: function (e, el,event,d) {
17550         this.fireEvent('evententer', this, el, event);
17551     },
17552     
17553     onEventLeave: function (e, el,event,d) {
17554         this.fireEvent('eventleave', this, el, event);
17555     },
17556     
17557     onEventClick: function (e, el,event,d) {
17558         this.fireEvent('eventclick', this, el, event);
17559     },
17560     
17561     onMonthChange: function () {
17562         this.store.load();
17563     },
17564     
17565     onMoreEventClick: function(e, el, more)
17566     {
17567         var _this = this;
17568         
17569         this.calpopover.placement = 'right';
17570         this.calpopover.setTitle('More');
17571         
17572         this.calpopover.setContent('');
17573         
17574         var ctr = this.calpopover.el.select('.popover-content', true).first();
17575         
17576         Roo.each(more, function(m){
17577             var cfg = {
17578                 cls : 'fc-event-hori fc-event-draggable',
17579                 html : m.title
17580             };
17581             var cg = ctr.createChild(cfg);
17582             
17583             cg.on('click', _this.onEventClick, _this, m);
17584         });
17585         
17586         this.calpopover.show(el);
17587         
17588         
17589     },
17590     
17591     onLoad: function () 
17592     {   
17593         this.calevents = [];
17594         var cal = this;
17595         
17596         if(this.store.getCount() > 0){
17597             this.store.data.each(function(d){
17598                cal.addItem({
17599                     id : d.data.id,
17600                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17601                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17602                     time : d.data.start_time,
17603                     title : d.data.title,
17604                     description : d.data.description,
17605                     venue : d.data.venue
17606                 });
17607             });
17608         }
17609         
17610         this.renderEvents();
17611         
17612         if(this.calevents.length && this.loadMask){
17613             this.maskEl.hide();
17614         }
17615     },
17616     
17617     onBeforeLoad: function()
17618     {
17619         this.clearEvents();
17620         if(this.loadMask){
17621             this.maskEl.show();
17622         }
17623     }
17624 });
17625
17626  
17627  /*
17628  * - LGPL
17629  *
17630  * element
17631  * 
17632  */
17633
17634 /**
17635  * @class Roo.bootstrap.Popover
17636  * @extends Roo.bootstrap.Component
17637  * Bootstrap Popover class
17638  * @cfg {String} html contents of the popover   (or false to use children..)
17639  * @cfg {String} title of popover (or false to hide)
17640  * @cfg {String} placement how it is placed
17641  * @cfg {String} trigger click || hover (or false to trigger manually)
17642  * @cfg {String} over what (parent or false to trigger manually.)
17643  * @cfg {Number} delay - delay before showing
17644  
17645  * @constructor
17646  * Create a new Popover
17647  * @param {Object} config The config object
17648  */
17649
17650 Roo.bootstrap.Popover = function(config){
17651     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17652     
17653     this.addEvents({
17654         // raw events
17655          /**
17656          * @event show
17657          * After the popover show
17658          * 
17659          * @param {Roo.bootstrap.Popover} this
17660          */
17661         "show" : true,
17662         /**
17663          * @event hide
17664          * After the popover hide
17665          * 
17666          * @param {Roo.bootstrap.Popover} this
17667          */
17668         "hide" : true
17669     });
17670 };
17671
17672 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17673     
17674     title: 'Fill in a title',
17675     html: false,
17676     
17677     placement : 'right',
17678     trigger : 'hover', // hover
17679     
17680     delay : 0,
17681     
17682     over: 'parent',
17683     
17684     can_build_overlaid : false,
17685     
17686     getChildContainer : function()
17687     {
17688         return this.el.select('.popover-content',true).first();
17689     },
17690     
17691     getAutoCreate : function(){
17692          
17693         var cfg = {
17694            cls : 'popover roo-dynamic',
17695            style: 'display:block',
17696            cn : [
17697                 {
17698                     cls : 'arrow'
17699                 },
17700                 {
17701                     cls : 'popover-inner',
17702                     cn : [
17703                         {
17704                             tag: 'h3',
17705                             cls: 'popover-title popover-header',
17706                             html : this.title
17707                         },
17708                         {
17709                             cls : 'popover-content popover-body',
17710                             html : this.html
17711                         }
17712                     ]
17713                     
17714                 }
17715            ]
17716         };
17717         
17718         return cfg;
17719     },
17720     setTitle: function(str)
17721     {
17722         this.title = str;
17723         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17724     },
17725     setContent: function(str)
17726     {
17727         this.html = str;
17728         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17729     },
17730     // as it get's added to the bottom of the page.
17731     onRender : function(ct, position)
17732     {
17733         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17734         if(!this.el){
17735             var cfg = Roo.apply({},  this.getAutoCreate());
17736             cfg.id = Roo.id();
17737             
17738             if (this.cls) {
17739                 cfg.cls += ' ' + this.cls;
17740             }
17741             if (this.style) {
17742                 cfg.style = this.style;
17743             }
17744             //Roo.log("adding to ");
17745             this.el = Roo.get(document.body).createChild(cfg, position);
17746 //            Roo.log(this.el);
17747         }
17748         this.initEvents();
17749     },
17750     
17751     initEvents : function()
17752     {
17753         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17754         this.el.enableDisplayMode('block');
17755         this.el.hide();
17756         if (this.over === false) {
17757             return; 
17758         }
17759         if (this.triggers === false) {
17760             return;
17761         }
17762         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17763         var triggers = this.trigger ? this.trigger.split(' ') : [];
17764         Roo.each(triggers, function(trigger) {
17765         
17766             if (trigger == 'click') {
17767                 on_el.on('click', this.toggle, this);
17768             } else if (trigger != 'manual') {
17769                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17770                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17771       
17772                 on_el.on(eventIn  ,this.enter, this);
17773                 on_el.on(eventOut, this.leave, this);
17774             }
17775         }, this);
17776         
17777     },
17778     
17779     
17780     // private
17781     timeout : null,
17782     hoverState : null,
17783     
17784     toggle : function () {
17785         this.hoverState == 'in' ? this.leave() : this.enter();
17786     },
17787     
17788     enter : function () {
17789         
17790         clearTimeout(this.timeout);
17791     
17792         this.hoverState = 'in';
17793     
17794         if (!this.delay || !this.delay.show) {
17795             this.show();
17796             return;
17797         }
17798         var _t = this;
17799         this.timeout = setTimeout(function () {
17800             if (_t.hoverState == 'in') {
17801                 _t.show();
17802             }
17803         }, this.delay.show)
17804     },
17805     
17806     leave : function() {
17807         clearTimeout(this.timeout);
17808     
17809         this.hoverState = 'out';
17810     
17811         if (!this.delay || !this.delay.hide) {
17812             this.hide();
17813             return;
17814         }
17815         var _t = this;
17816         this.timeout = setTimeout(function () {
17817             if (_t.hoverState == 'out') {
17818                 _t.hide();
17819             }
17820         }, this.delay.hide)
17821     },
17822     
17823     show : function (on_el)
17824     {
17825         if (!on_el) {
17826             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17827         }
17828         
17829         // set content.
17830         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17831         if (this.html !== false) {
17832             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17833         }
17834         this.el.removeClass([
17835             'fade','top','bottom', 'left', 'right','in',
17836             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17837         ]);
17838         if (!this.title.length) {
17839             this.el.select('.popover-title',true).hide();
17840         }
17841         
17842         var placement = typeof this.placement == 'function' ?
17843             this.placement.call(this, this.el, on_el) :
17844             this.placement;
17845             
17846         var autoToken = /\s?auto?\s?/i;
17847         var autoPlace = autoToken.test(placement);
17848         if (autoPlace) {
17849             placement = placement.replace(autoToken, '') || 'top';
17850         }
17851         
17852         //this.el.detach()
17853         //this.el.setXY([0,0]);
17854         this.el.show();
17855         this.el.dom.style.display='block';
17856         this.el.addClass(placement);
17857         
17858         //this.el.appendTo(on_el);
17859         
17860         var p = this.getPosition();
17861         var box = this.el.getBox();
17862         
17863         if (autoPlace) {
17864             // fixme..
17865         }
17866         var align = Roo.bootstrap.Popover.alignment[placement];
17867         
17868 //        Roo.log(align);
17869         this.el.alignTo(on_el, align[0],align[1]);
17870         //var arrow = this.el.select('.arrow',true).first();
17871         //arrow.set(align[2], 
17872         
17873         this.el.addClass('in');
17874         
17875         
17876         if (this.el.hasClass('fade')) {
17877             // fade it?
17878         }
17879         
17880         this.hoverState = 'in';
17881         
17882         this.fireEvent('show', this);
17883         
17884     },
17885     hide : function()
17886     {
17887         this.el.setXY([0,0]);
17888         this.el.removeClass('in');
17889         this.el.hide();
17890         this.hoverState = null;
17891         
17892         this.fireEvent('hide', this);
17893     }
17894     
17895 });
17896
17897 Roo.bootstrap.Popover.alignment = {
17898     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17899     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17900     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17901     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17902 };
17903
17904  /*
17905  * - LGPL
17906  *
17907  * Progress
17908  * 
17909  */
17910
17911 /**
17912  * @class Roo.bootstrap.Progress
17913  * @extends Roo.bootstrap.Component
17914  * Bootstrap Progress class
17915  * @cfg {Boolean} striped striped of the progress bar
17916  * @cfg {Boolean} active animated of the progress bar
17917  * 
17918  * 
17919  * @constructor
17920  * Create a new Progress
17921  * @param {Object} config The config object
17922  */
17923
17924 Roo.bootstrap.Progress = function(config){
17925     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17926 };
17927
17928 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17929     
17930     striped : false,
17931     active: false,
17932     
17933     getAutoCreate : function(){
17934         var cfg = {
17935             tag: 'div',
17936             cls: 'progress'
17937         };
17938         
17939         
17940         if(this.striped){
17941             cfg.cls += ' progress-striped';
17942         }
17943       
17944         if(this.active){
17945             cfg.cls += ' active';
17946         }
17947         
17948         
17949         return cfg;
17950     }
17951    
17952 });
17953
17954  
17955
17956  /*
17957  * - LGPL
17958  *
17959  * ProgressBar
17960  * 
17961  */
17962
17963 /**
17964  * @class Roo.bootstrap.ProgressBar
17965  * @extends Roo.bootstrap.Component
17966  * Bootstrap ProgressBar class
17967  * @cfg {Number} aria_valuenow aria-value now
17968  * @cfg {Number} aria_valuemin aria-value min
17969  * @cfg {Number} aria_valuemax aria-value max
17970  * @cfg {String} label label for the progress bar
17971  * @cfg {String} panel (success | info | warning | danger )
17972  * @cfg {String} role role of the progress bar
17973  * @cfg {String} sr_only text
17974  * 
17975  * 
17976  * @constructor
17977  * Create a new ProgressBar
17978  * @param {Object} config The config object
17979  */
17980
17981 Roo.bootstrap.ProgressBar = function(config){
17982     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17983 };
17984
17985 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17986     
17987     aria_valuenow : 0,
17988     aria_valuemin : 0,
17989     aria_valuemax : 100,
17990     label : false,
17991     panel : false,
17992     role : false,
17993     sr_only: false,
17994     
17995     getAutoCreate : function()
17996     {
17997         
17998         var cfg = {
17999             tag: 'div',
18000             cls: 'progress-bar',
18001             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18002         };
18003         
18004         if(this.sr_only){
18005             cfg.cn = {
18006                 tag: 'span',
18007                 cls: 'sr-only',
18008                 html: this.sr_only
18009             }
18010         }
18011         
18012         if(this.role){
18013             cfg.role = this.role;
18014         }
18015         
18016         if(this.aria_valuenow){
18017             cfg['aria-valuenow'] = this.aria_valuenow;
18018         }
18019         
18020         if(this.aria_valuemin){
18021             cfg['aria-valuemin'] = this.aria_valuemin;
18022         }
18023         
18024         if(this.aria_valuemax){
18025             cfg['aria-valuemax'] = this.aria_valuemax;
18026         }
18027         
18028         if(this.label && !this.sr_only){
18029             cfg.html = this.label;
18030         }
18031         
18032         if(this.panel){
18033             cfg.cls += ' progress-bar-' + this.panel;
18034         }
18035         
18036         return cfg;
18037     },
18038     
18039     update : function(aria_valuenow)
18040     {
18041         this.aria_valuenow = aria_valuenow;
18042         
18043         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18044     }
18045    
18046 });
18047
18048  
18049
18050  /*
18051  * - LGPL
18052  *
18053  * column
18054  * 
18055  */
18056
18057 /**
18058  * @class Roo.bootstrap.TabGroup
18059  * @extends Roo.bootstrap.Column
18060  * Bootstrap Column class
18061  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18062  * @cfg {Boolean} carousel true to make the group behave like a carousel
18063  * @cfg {Boolean} bullets show bullets for the panels
18064  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18065  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18066  * @cfg {Boolean} showarrow (true|false) show arrow default true
18067  * 
18068  * @constructor
18069  * Create a new TabGroup
18070  * @param {Object} config The config object
18071  */
18072
18073 Roo.bootstrap.TabGroup = function(config){
18074     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18075     if (!this.navId) {
18076         this.navId = Roo.id();
18077     }
18078     this.tabs = [];
18079     Roo.bootstrap.TabGroup.register(this);
18080     
18081 };
18082
18083 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18084     
18085     carousel : false,
18086     transition : false,
18087     bullets : 0,
18088     timer : 0,
18089     autoslide : false,
18090     slideFn : false,
18091     slideOnTouch : false,
18092     showarrow : true,
18093     
18094     getAutoCreate : function()
18095     {
18096         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18097         
18098         cfg.cls += ' tab-content';
18099         
18100         if (this.carousel) {
18101             cfg.cls += ' carousel slide';
18102             
18103             cfg.cn = [{
18104                cls : 'carousel-inner',
18105                cn : []
18106             }];
18107         
18108             if(this.bullets  && !Roo.isTouch){
18109                 
18110                 var bullets = {
18111                     cls : 'carousel-bullets',
18112                     cn : []
18113                 };
18114                
18115                 if(this.bullets_cls){
18116                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18117                 }
18118                 
18119                 bullets.cn.push({
18120                     cls : 'clear'
18121                 });
18122                 
18123                 cfg.cn[0].cn.push(bullets);
18124             }
18125             
18126             if(this.showarrow){
18127                 cfg.cn[0].cn.push({
18128                     tag : 'div',
18129                     class : 'carousel-arrow',
18130                     cn : [
18131                         {
18132                             tag : 'div',
18133                             class : 'carousel-prev',
18134                             cn : [
18135                                 {
18136                                     tag : 'i',
18137                                     class : 'fa fa-chevron-left'
18138                                 }
18139                             ]
18140                         },
18141                         {
18142                             tag : 'div',
18143                             class : 'carousel-next',
18144                             cn : [
18145                                 {
18146                                     tag : 'i',
18147                                     class : 'fa fa-chevron-right'
18148                                 }
18149                             ]
18150                         }
18151                     ]
18152                 });
18153             }
18154             
18155         }
18156         
18157         return cfg;
18158     },
18159     
18160     initEvents:  function()
18161     {
18162 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18163 //            this.el.on("touchstart", this.onTouchStart, this);
18164 //        }
18165         
18166         if(this.autoslide){
18167             var _this = this;
18168             
18169             this.slideFn = window.setInterval(function() {
18170                 _this.showPanelNext();
18171             }, this.timer);
18172         }
18173         
18174         if(this.showarrow){
18175             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18176             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18177         }
18178         
18179         
18180     },
18181     
18182 //    onTouchStart : function(e, el, o)
18183 //    {
18184 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18185 //            return;
18186 //        }
18187 //        
18188 //        this.showPanelNext();
18189 //    },
18190     
18191     
18192     getChildContainer : function()
18193     {
18194         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18195     },
18196     
18197     /**
18198     * register a Navigation item
18199     * @param {Roo.bootstrap.NavItem} the navitem to add
18200     */
18201     register : function(item)
18202     {
18203         this.tabs.push( item);
18204         item.navId = this.navId; // not really needed..
18205         this.addBullet();
18206     
18207     },
18208     
18209     getActivePanel : function()
18210     {
18211         var r = false;
18212         Roo.each(this.tabs, function(t) {
18213             if (t.active) {
18214                 r = t;
18215                 return false;
18216             }
18217             return null;
18218         });
18219         return r;
18220         
18221     },
18222     getPanelByName : function(n)
18223     {
18224         var r = false;
18225         Roo.each(this.tabs, function(t) {
18226             if (t.tabId == n) {
18227                 r = t;
18228                 return false;
18229             }
18230             return null;
18231         });
18232         return r;
18233     },
18234     indexOfPanel : function(p)
18235     {
18236         var r = false;
18237         Roo.each(this.tabs, function(t,i) {
18238             if (t.tabId == p.tabId) {
18239                 r = i;
18240                 return false;
18241             }
18242             return null;
18243         });
18244         return r;
18245     },
18246     /**
18247      * show a specific panel
18248      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18249      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18250      */
18251     showPanel : function (pan)
18252     {
18253         if(this.transition || typeof(pan) == 'undefined'){
18254             Roo.log("waiting for the transitionend");
18255             return;
18256         }
18257         
18258         if (typeof(pan) == 'number') {
18259             pan = this.tabs[pan];
18260         }
18261         
18262         if (typeof(pan) == 'string') {
18263             pan = this.getPanelByName(pan);
18264         }
18265         
18266         var cur = this.getActivePanel();
18267         
18268         if(!pan || !cur){
18269             Roo.log('pan or acitve pan is undefined');
18270             return false;
18271         }
18272         
18273         if (pan.tabId == this.getActivePanel().tabId) {
18274             return true;
18275         }
18276         
18277         if (false === cur.fireEvent('beforedeactivate')) {
18278             return false;
18279         }
18280         
18281         if(this.bullets > 0 && !Roo.isTouch){
18282             this.setActiveBullet(this.indexOfPanel(pan));
18283         }
18284         
18285         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18286             
18287             this.transition = true;
18288             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18289             var lr = dir == 'next' ? 'left' : 'right';
18290             pan.el.addClass(dir); // or prev
18291             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18292             cur.el.addClass(lr); // or right
18293             pan.el.addClass(lr);
18294             
18295             var _this = this;
18296             cur.el.on('transitionend', function() {
18297                 Roo.log("trans end?");
18298                 
18299                 pan.el.removeClass([lr,dir]);
18300                 pan.setActive(true);
18301                 
18302                 cur.el.removeClass([lr]);
18303                 cur.setActive(false);
18304                 
18305                 _this.transition = false;
18306                 
18307             }, this, { single:  true } );
18308             
18309             return true;
18310         }
18311         
18312         cur.setActive(false);
18313         pan.setActive(true);
18314         
18315         return true;
18316         
18317     },
18318     showPanelNext : function()
18319     {
18320         var i = this.indexOfPanel(this.getActivePanel());
18321         
18322         if (i >= this.tabs.length - 1 && !this.autoslide) {
18323             return;
18324         }
18325         
18326         if (i >= this.tabs.length - 1 && this.autoslide) {
18327             i = -1;
18328         }
18329         
18330         this.showPanel(this.tabs[i+1]);
18331     },
18332     
18333     showPanelPrev : function()
18334     {
18335         var i = this.indexOfPanel(this.getActivePanel());
18336         
18337         if (i  < 1 && !this.autoslide) {
18338             return;
18339         }
18340         
18341         if (i < 1 && this.autoslide) {
18342             i = this.tabs.length;
18343         }
18344         
18345         this.showPanel(this.tabs[i-1]);
18346     },
18347     
18348     
18349     addBullet: function()
18350     {
18351         if(!this.bullets || Roo.isTouch){
18352             return;
18353         }
18354         var ctr = this.el.select('.carousel-bullets',true).first();
18355         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18356         var bullet = ctr.createChild({
18357             cls : 'bullet bullet-' + i
18358         },ctr.dom.lastChild);
18359         
18360         
18361         var _this = this;
18362         
18363         bullet.on('click', (function(e, el, o, ii, t){
18364
18365             e.preventDefault();
18366
18367             this.showPanel(ii);
18368
18369             if(this.autoslide && this.slideFn){
18370                 clearInterval(this.slideFn);
18371                 this.slideFn = window.setInterval(function() {
18372                     _this.showPanelNext();
18373                 }, this.timer);
18374             }
18375
18376         }).createDelegate(this, [i, bullet], true));
18377                 
18378         
18379     },
18380      
18381     setActiveBullet : function(i)
18382     {
18383         if(Roo.isTouch){
18384             return;
18385         }
18386         
18387         Roo.each(this.el.select('.bullet', true).elements, function(el){
18388             el.removeClass('selected');
18389         });
18390
18391         var bullet = this.el.select('.bullet-' + i, true).first();
18392         
18393         if(!bullet){
18394             return;
18395         }
18396         
18397         bullet.addClass('selected');
18398     }
18399     
18400     
18401   
18402 });
18403
18404  
18405
18406  
18407  
18408 Roo.apply(Roo.bootstrap.TabGroup, {
18409     
18410     groups: {},
18411      /**
18412     * register a Navigation Group
18413     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18414     */
18415     register : function(navgrp)
18416     {
18417         this.groups[navgrp.navId] = navgrp;
18418         
18419     },
18420     /**
18421     * fetch a Navigation Group based on the navigation ID
18422     * if one does not exist , it will get created.
18423     * @param {string} the navgroup to add
18424     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18425     */
18426     get: function(navId) {
18427         if (typeof(this.groups[navId]) == 'undefined') {
18428             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18429         }
18430         return this.groups[navId] ;
18431     }
18432     
18433     
18434     
18435 });
18436
18437  /*
18438  * - LGPL
18439  *
18440  * TabPanel
18441  * 
18442  */
18443
18444 /**
18445  * @class Roo.bootstrap.TabPanel
18446  * @extends Roo.bootstrap.Component
18447  * Bootstrap TabPanel class
18448  * @cfg {Boolean} active panel active
18449  * @cfg {String} html panel content
18450  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18451  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18452  * @cfg {String} href click to link..
18453  * 
18454  * 
18455  * @constructor
18456  * Create a new TabPanel
18457  * @param {Object} config The config object
18458  */
18459
18460 Roo.bootstrap.TabPanel = function(config){
18461     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18462     this.addEvents({
18463         /**
18464              * @event changed
18465              * Fires when the active status changes
18466              * @param {Roo.bootstrap.TabPanel} this
18467              * @param {Boolean} state the new state
18468             
18469          */
18470         'changed': true,
18471         /**
18472              * @event beforedeactivate
18473              * Fires before a tab is de-activated - can be used to do validation on a form.
18474              * @param {Roo.bootstrap.TabPanel} this
18475              * @return {Boolean} false if there is an error
18476             
18477          */
18478         'beforedeactivate': true
18479      });
18480     
18481     this.tabId = this.tabId || Roo.id();
18482   
18483 };
18484
18485 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18486     
18487     active: false,
18488     html: false,
18489     tabId: false,
18490     navId : false,
18491     href : '',
18492     
18493     getAutoCreate : function(){
18494         var cfg = {
18495             tag: 'div',
18496             // item is needed for carousel - not sure if it has any effect otherwise
18497             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18498             html: this.html || ''
18499         };
18500         
18501         if(this.active){
18502             cfg.cls += ' active';
18503         }
18504         
18505         if(this.tabId){
18506             cfg.tabId = this.tabId;
18507         }
18508         
18509         
18510         return cfg;
18511     },
18512     
18513     initEvents:  function()
18514     {
18515         var p = this.parent();
18516         
18517         this.navId = this.navId || p.navId;
18518         
18519         if (typeof(this.navId) != 'undefined') {
18520             // not really needed.. but just in case.. parent should be a NavGroup.
18521             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18522             
18523             tg.register(this);
18524             
18525             var i = tg.tabs.length - 1;
18526             
18527             if(this.active && tg.bullets > 0 && i < tg.bullets){
18528                 tg.setActiveBullet(i);
18529             }
18530         }
18531         
18532         this.el.on('click', this.onClick, this);
18533         
18534         if(Roo.isTouch){
18535             this.el.on("touchstart", this.onTouchStart, this);
18536             this.el.on("touchmove", this.onTouchMove, this);
18537             this.el.on("touchend", this.onTouchEnd, this);
18538         }
18539         
18540     },
18541     
18542     onRender : function(ct, position)
18543     {
18544         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18545     },
18546     
18547     setActive : function(state)
18548     {
18549         Roo.log("panel - set active " + this.tabId + "=" + state);
18550         
18551         this.active = state;
18552         if (!state) {
18553             this.el.removeClass('active');
18554             
18555         } else  if (!this.el.hasClass('active')) {
18556             this.el.addClass('active');
18557         }
18558         
18559         this.fireEvent('changed', this, state);
18560     },
18561     
18562     onClick : function(e)
18563     {
18564         e.preventDefault();
18565         
18566         if(!this.href.length){
18567             return;
18568         }
18569         
18570         window.location.href = this.href;
18571     },
18572     
18573     startX : 0,
18574     startY : 0,
18575     endX : 0,
18576     endY : 0,
18577     swiping : false,
18578     
18579     onTouchStart : function(e)
18580     {
18581         this.swiping = false;
18582         
18583         this.startX = e.browserEvent.touches[0].clientX;
18584         this.startY = e.browserEvent.touches[0].clientY;
18585     },
18586     
18587     onTouchMove : function(e)
18588     {
18589         this.swiping = true;
18590         
18591         this.endX = e.browserEvent.touches[0].clientX;
18592         this.endY = e.browserEvent.touches[0].clientY;
18593     },
18594     
18595     onTouchEnd : function(e)
18596     {
18597         if(!this.swiping){
18598             this.onClick(e);
18599             return;
18600         }
18601         
18602         var tabGroup = this.parent();
18603         
18604         if(this.endX > this.startX){ // swiping right
18605             tabGroup.showPanelPrev();
18606             return;
18607         }
18608         
18609         if(this.startX > this.endX){ // swiping left
18610             tabGroup.showPanelNext();
18611             return;
18612         }
18613     }
18614     
18615     
18616 });
18617  
18618
18619  
18620
18621  /*
18622  * - LGPL
18623  *
18624  * DateField
18625  * 
18626  */
18627
18628 /**
18629  * @class Roo.bootstrap.DateField
18630  * @extends Roo.bootstrap.Input
18631  * Bootstrap DateField class
18632  * @cfg {Number} weekStart default 0
18633  * @cfg {String} viewMode default empty, (months|years)
18634  * @cfg {String} minViewMode default empty, (months|years)
18635  * @cfg {Number} startDate default -Infinity
18636  * @cfg {Number} endDate default Infinity
18637  * @cfg {Boolean} todayHighlight default false
18638  * @cfg {Boolean} todayBtn default false
18639  * @cfg {Boolean} calendarWeeks default false
18640  * @cfg {Object} daysOfWeekDisabled default empty
18641  * @cfg {Boolean} singleMode default false (true | false)
18642  * 
18643  * @cfg {Boolean} keyboardNavigation default true
18644  * @cfg {String} language default en
18645  * 
18646  * @constructor
18647  * Create a new DateField
18648  * @param {Object} config The config object
18649  */
18650
18651 Roo.bootstrap.DateField = function(config){
18652     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18653      this.addEvents({
18654             /**
18655              * @event show
18656              * Fires when this field show.
18657              * @param {Roo.bootstrap.DateField} this
18658              * @param {Mixed} date The date value
18659              */
18660             show : true,
18661             /**
18662              * @event show
18663              * Fires when this field hide.
18664              * @param {Roo.bootstrap.DateField} this
18665              * @param {Mixed} date The date value
18666              */
18667             hide : true,
18668             /**
18669              * @event select
18670              * Fires when select a date.
18671              * @param {Roo.bootstrap.DateField} this
18672              * @param {Mixed} date The date value
18673              */
18674             select : true,
18675             /**
18676              * @event beforeselect
18677              * Fires when before select a date.
18678              * @param {Roo.bootstrap.DateField} this
18679              * @param {Mixed} date The date value
18680              */
18681             beforeselect : true
18682         });
18683 };
18684
18685 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18686     
18687     /**
18688      * @cfg {String} format
18689      * The default date format string which can be overriden for localization support.  The format must be
18690      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18691      */
18692     format : "m/d/y",
18693     /**
18694      * @cfg {String} altFormats
18695      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18696      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18697      */
18698     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18699     
18700     weekStart : 0,
18701     
18702     viewMode : '',
18703     
18704     minViewMode : '',
18705     
18706     todayHighlight : false,
18707     
18708     todayBtn: false,
18709     
18710     language: 'en',
18711     
18712     keyboardNavigation: true,
18713     
18714     calendarWeeks: false,
18715     
18716     startDate: -Infinity,
18717     
18718     endDate: Infinity,
18719     
18720     daysOfWeekDisabled: [],
18721     
18722     _events: [],
18723     
18724     singleMode : false,
18725     
18726     UTCDate: function()
18727     {
18728         return new Date(Date.UTC.apply(Date, arguments));
18729     },
18730     
18731     UTCToday: function()
18732     {
18733         var today = new Date();
18734         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18735     },
18736     
18737     getDate: function() {
18738             var d = this.getUTCDate();
18739             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18740     },
18741     
18742     getUTCDate: function() {
18743             return this.date;
18744     },
18745     
18746     setDate: function(d) {
18747             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18748     },
18749     
18750     setUTCDate: function(d) {
18751             this.date = d;
18752             this.setValue(this.formatDate(this.date));
18753     },
18754         
18755     onRender: function(ct, position)
18756     {
18757         
18758         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18759         
18760         this.language = this.language || 'en';
18761         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18762         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18763         
18764         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18765         this.format = this.format || 'm/d/y';
18766         this.isInline = false;
18767         this.isInput = true;
18768         this.component = this.el.select('.add-on', true).first() || false;
18769         this.component = (this.component && this.component.length === 0) ? false : this.component;
18770         this.hasInput = this.component && this.inputEl().length;
18771         
18772         if (typeof(this.minViewMode === 'string')) {
18773             switch (this.minViewMode) {
18774                 case 'months':
18775                     this.minViewMode = 1;
18776                     break;
18777                 case 'years':
18778                     this.minViewMode = 2;
18779                     break;
18780                 default:
18781                     this.minViewMode = 0;
18782                     break;
18783             }
18784         }
18785         
18786         if (typeof(this.viewMode === 'string')) {
18787             switch (this.viewMode) {
18788                 case 'months':
18789                     this.viewMode = 1;
18790                     break;
18791                 case 'years':
18792                     this.viewMode = 2;
18793                     break;
18794                 default:
18795                     this.viewMode = 0;
18796                     break;
18797             }
18798         }
18799                 
18800         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18801         
18802 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18803         
18804         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18805         
18806         this.picker().on('mousedown', this.onMousedown, this);
18807         this.picker().on('click', this.onClick, this);
18808         
18809         this.picker().addClass('datepicker-dropdown');
18810         
18811         this.startViewMode = this.viewMode;
18812         
18813         if(this.singleMode){
18814             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18815                 v.setVisibilityMode(Roo.Element.DISPLAY);
18816                 v.hide();
18817             });
18818             
18819             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18820                 v.setStyle('width', '189px');
18821             });
18822         }
18823         
18824         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18825             if(!this.calendarWeeks){
18826                 v.remove();
18827                 return;
18828             }
18829             
18830             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18831             v.attr('colspan', function(i, val){
18832                 return parseInt(val) + 1;
18833             });
18834         });
18835                         
18836         
18837         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18838         
18839         this.setStartDate(this.startDate);
18840         this.setEndDate(this.endDate);
18841         
18842         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18843         
18844         this.fillDow();
18845         this.fillMonths();
18846         this.update();
18847         this.showMode();
18848         
18849         if(this.isInline) {
18850             this.showPopup();
18851         }
18852     },
18853     
18854     picker : function()
18855     {
18856         return this.pickerEl;
18857 //        return this.el.select('.datepicker', true).first();
18858     },
18859     
18860     fillDow: function()
18861     {
18862         var dowCnt = this.weekStart;
18863         
18864         var dow = {
18865             tag: 'tr',
18866             cn: [
18867                 
18868             ]
18869         };
18870         
18871         if(this.calendarWeeks){
18872             dow.cn.push({
18873                 tag: 'th',
18874                 cls: 'cw',
18875                 html: '&nbsp;'
18876             })
18877         }
18878         
18879         while (dowCnt < this.weekStart + 7) {
18880             dow.cn.push({
18881                 tag: 'th',
18882                 cls: 'dow',
18883                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18884             });
18885         }
18886         
18887         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18888     },
18889     
18890     fillMonths: function()
18891     {    
18892         var i = 0;
18893         var months = this.picker().select('>.datepicker-months td', true).first();
18894         
18895         months.dom.innerHTML = '';
18896         
18897         while (i < 12) {
18898             var month = {
18899                 tag: 'span',
18900                 cls: 'month',
18901                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18902             };
18903             
18904             months.createChild(month);
18905         }
18906         
18907     },
18908     
18909     update: function()
18910     {
18911         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;
18912         
18913         if (this.date < this.startDate) {
18914             this.viewDate = new Date(this.startDate);
18915         } else if (this.date > this.endDate) {
18916             this.viewDate = new Date(this.endDate);
18917         } else {
18918             this.viewDate = new Date(this.date);
18919         }
18920         
18921         this.fill();
18922     },
18923     
18924     fill: function() 
18925     {
18926         var d = new Date(this.viewDate),
18927                 year = d.getUTCFullYear(),
18928                 month = d.getUTCMonth(),
18929                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18930                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18931                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18932                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18933                 currentDate = this.date && this.date.valueOf(),
18934                 today = this.UTCToday();
18935         
18936         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18937         
18938 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18939         
18940 //        this.picker.select('>tfoot th.today').
18941 //                                              .text(dates[this.language].today)
18942 //                                              .toggle(this.todayBtn !== false);
18943     
18944         this.updateNavArrows();
18945         this.fillMonths();
18946                                                 
18947         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18948         
18949         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18950          
18951         prevMonth.setUTCDate(day);
18952         
18953         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18954         
18955         var nextMonth = new Date(prevMonth);
18956         
18957         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18958         
18959         nextMonth = nextMonth.valueOf();
18960         
18961         var fillMonths = false;
18962         
18963         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18964         
18965         while(prevMonth.valueOf() <= nextMonth) {
18966             var clsName = '';
18967             
18968             if (prevMonth.getUTCDay() === this.weekStart) {
18969                 if(fillMonths){
18970                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18971                 }
18972                     
18973                 fillMonths = {
18974                     tag: 'tr',
18975                     cn: []
18976                 };
18977                 
18978                 if(this.calendarWeeks){
18979                     // ISO 8601: First week contains first thursday.
18980                     // ISO also states week starts on Monday, but we can be more abstract here.
18981                     var
18982                     // Start of current week: based on weekstart/current date
18983                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18984                     // Thursday of this week
18985                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18986                     // First Thursday of year, year from thursday
18987                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18988                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18989                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18990                     
18991                     fillMonths.cn.push({
18992                         tag: 'td',
18993                         cls: 'cw',
18994                         html: calWeek
18995                     });
18996                 }
18997             }
18998             
18999             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19000                 clsName += ' old';
19001             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19002                 clsName += ' new';
19003             }
19004             if (this.todayHighlight &&
19005                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19006                 prevMonth.getUTCMonth() == today.getMonth() &&
19007                 prevMonth.getUTCDate() == today.getDate()) {
19008                 clsName += ' today';
19009             }
19010             
19011             if (currentDate && prevMonth.valueOf() === currentDate) {
19012                 clsName += ' active';
19013             }
19014             
19015             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19016                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19017                     clsName += ' disabled';
19018             }
19019             
19020             fillMonths.cn.push({
19021                 tag: 'td',
19022                 cls: 'day ' + clsName,
19023                 html: prevMonth.getDate()
19024             });
19025             
19026             prevMonth.setDate(prevMonth.getDate()+1);
19027         }
19028           
19029         var currentYear = this.date && this.date.getUTCFullYear();
19030         var currentMonth = this.date && this.date.getUTCMonth();
19031         
19032         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19033         
19034         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19035             v.removeClass('active');
19036             
19037             if(currentYear === year && k === currentMonth){
19038                 v.addClass('active');
19039             }
19040             
19041             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19042                 v.addClass('disabled');
19043             }
19044             
19045         });
19046         
19047         
19048         year = parseInt(year/10, 10) * 10;
19049         
19050         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19051         
19052         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19053         
19054         year -= 1;
19055         for (var i = -1; i < 11; i++) {
19056             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19057                 tag: 'span',
19058                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19059                 html: year
19060             });
19061             
19062             year += 1;
19063         }
19064     },
19065     
19066     showMode: function(dir) 
19067     {
19068         if (dir) {
19069             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19070         }
19071         
19072         Roo.each(this.picker().select('>div',true).elements, function(v){
19073             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19074             v.hide();
19075         });
19076         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19077     },
19078     
19079     place: function()
19080     {
19081         if(this.isInline) {
19082             return;
19083         }
19084         
19085         this.picker().removeClass(['bottom', 'top']);
19086         
19087         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19088             /*
19089              * place to the top of element!
19090              *
19091              */
19092             
19093             this.picker().addClass('top');
19094             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19095             
19096             return;
19097         }
19098         
19099         this.picker().addClass('bottom');
19100         
19101         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19102     },
19103     
19104     parseDate : function(value)
19105     {
19106         if(!value || value instanceof Date){
19107             return value;
19108         }
19109         var v = Date.parseDate(value, this.format);
19110         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19111             v = Date.parseDate(value, 'Y-m-d');
19112         }
19113         if(!v && this.altFormats){
19114             if(!this.altFormatsArray){
19115                 this.altFormatsArray = this.altFormats.split("|");
19116             }
19117             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19118                 v = Date.parseDate(value, this.altFormatsArray[i]);
19119             }
19120         }
19121         return v;
19122     },
19123     
19124     formatDate : function(date, fmt)
19125     {   
19126         return (!date || !(date instanceof Date)) ?
19127         date : date.dateFormat(fmt || this.format);
19128     },
19129     
19130     onFocus : function()
19131     {
19132         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19133         this.showPopup();
19134     },
19135     
19136     onBlur : function()
19137     {
19138         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19139         
19140         var d = this.inputEl().getValue();
19141         
19142         this.setValue(d);
19143                 
19144         this.hidePopup();
19145     },
19146     
19147     showPopup : function()
19148     {
19149         this.picker().show();
19150         this.update();
19151         this.place();
19152         
19153         this.fireEvent('showpopup', this, this.date);
19154     },
19155     
19156     hidePopup : function()
19157     {
19158         if(this.isInline) {
19159             return;
19160         }
19161         this.picker().hide();
19162         this.viewMode = this.startViewMode;
19163         this.showMode();
19164         
19165         this.fireEvent('hidepopup', this, this.date);
19166         
19167     },
19168     
19169     onMousedown: function(e)
19170     {
19171         e.stopPropagation();
19172         e.preventDefault();
19173     },
19174     
19175     keyup: function(e)
19176     {
19177         Roo.bootstrap.DateField.superclass.keyup.call(this);
19178         this.update();
19179     },
19180
19181     setValue: function(v)
19182     {
19183         if(this.fireEvent('beforeselect', this, v) !== false){
19184             var d = new Date(this.parseDate(v) ).clearTime();
19185         
19186             if(isNaN(d.getTime())){
19187                 this.date = this.viewDate = '';
19188                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19189                 return;
19190             }
19191
19192             v = this.formatDate(d);
19193
19194             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19195
19196             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19197
19198             this.update();
19199
19200             this.fireEvent('select', this, this.date);
19201         }
19202     },
19203     
19204     getValue: function()
19205     {
19206         return this.formatDate(this.date);
19207     },
19208     
19209     fireKey: function(e)
19210     {
19211         if (!this.picker().isVisible()){
19212             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19213                 this.showPopup();
19214             }
19215             return;
19216         }
19217         
19218         var dateChanged = false,
19219         dir, day, month,
19220         newDate, newViewDate;
19221         
19222         switch(e.keyCode){
19223             case 27: // escape
19224                 this.hidePopup();
19225                 e.preventDefault();
19226                 break;
19227             case 37: // left
19228             case 39: // right
19229                 if (!this.keyboardNavigation) {
19230                     break;
19231                 }
19232                 dir = e.keyCode == 37 ? -1 : 1;
19233                 
19234                 if (e.ctrlKey){
19235                     newDate = this.moveYear(this.date, dir);
19236                     newViewDate = this.moveYear(this.viewDate, dir);
19237                 } else if (e.shiftKey){
19238                     newDate = this.moveMonth(this.date, dir);
19239                     newViewDate = this.moveMonth(this.viewDate, dir);
19240                 } else {
19241                     newDate = new Date(this.date);
19242                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19243                     newViewDate = new Date(this.viewDate);
19244                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19245                 }
19246                 if (this.dateWithinRange(newDate)){
19247                     this.date = newDate;
19248                     this.viewDate = newViewDate;
19249                     this.setValue(this.formatDate(this.date));
19250 //                    this.update();
19251                     e.preventDefault();
19252                     dateChanged = true;
19253                 }
19254                 break;
19255             case 38: // up
19256             case 40: // down
19257                 if (!this.keyboardNavigation) {
19258                     break;
19259                 }
19260                 dir = e.keyCode == 38 ? -1 : 1;
19261                 if (e.ctrlKey){
19262                     newDate = this.moveYear(this.date, dir);
19263                     newViewDate = this.moveYear(this.viewDate, dir);
19264                 } else if (e.shiftKey){
19265                     newDate = this.moveMonth(this.date, dir);
19266                     newViewDate = this.moveMonth(this.viewDate, dir);
19267                 } else {
19268                     newDate = new Date(this.date);
19269                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19270                     newViewDate = new Date(this.viewDate);
19271                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19272                 }
19273                 if (this.dateWithinRange(newDate)){
19274                     this.date = newDate;
19275                     this.viewDate = newViewDate;
19276                     this.setValue(this.formatDate(this.date));
19277 //                    this.update();
19278                     e.preventDefault();
19279                     dateChanged = true;
19280                 }
19281                 break;
19282             case 13: // enter
19283                 this.setValue(this.formatDate(this.date));
19284                 this.hidePopup();
19285                 e.preventDefault();
19286                 break;
19287             case 9: // tab
19288                 this.setValue(this.formatDate(this.date));
19289                 this.hidePopup();
19290                 break;
19291             case 16: // shift
19292             case 17: // ctrl
19293             case 18: // alt
19294                 break;
19295             default :
19296                 this.hidePopup();
19297                 
19298         }
19299     },
19300     
19301     
19302     onClick: function(e) 
19303     {
19304         e.stopPropagation();
19305         e.preventDefault();
19306         
19307         var target = e.getTarget();
19308         
19309         if(target.nodeName.toLowerCase() === 'i'){
19310             target = Roo.get(target).dom.parentNode;
19311         }
19312         
19313         var nodeName = target.nodeName;
19314         var className = target.className;
19315         var html = target.innerHTML;
19316         //Roo.log(nodeName);
19317         
19318         switch(nodeName.toLowerCase()) {
19319             case 'th':
19320                 switch(className) {
19321                     case 'switch':
19322                         this.showMode(1);
19323                         break;
19324                     case 'prev':
19325                     case 'next':
19326                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19327                         switch(this.viewMode){
19328                                 case 0:
19329                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19330                                         break;
19331                                 case 1:
19332                                 case 2:
19333                                         this.viewDate = this.moveYear(this.viewDate, dir);
19334                                         break;
19335                         }
19336                         this.fill();
19337                         break;
19338                     case 'today':
19339                         var date = new Date();
19340                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19341 //                        this.fill()
19342                         this.setValue(this.formatDate(this.date));
19343                         
19344                         this.hidePopup();
19345                         break;
19346                 }
19347                 break;
19348             case 'span':
19349                 if (className.indexOf('disabled') < 0) {
19350                     this.viewDate.setUTCDate(1);
19351                     if (className.indexOf('month') > -1) {
19352                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19353                     } else {
19354                         var year = parseInt(html, 10) || 0;
19355                         this.viewDate.setUTCFullYear(year);
19356                         
19357                     }
19358                     
19359                     if(this.singleMode){
19360                         this.setValue(this.formatDate(this.viewDate));
19361                         this.hidePopup();
19362                         return;
19363                     }
19364                     
19365                     this.showMode(-1);
19366                     this.fill();
19367                 }
19368                 break;
19369                 
19370             case 'td':
19371                 //Roo.log(className);
19372                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19373                     var day = parseInt(html, 10) || 1;
19374                     var year = this.viewDate.getUTCFullYear(),
19375                         month = this.viewDate.getUTCMonth();
19376
19377                     if (className.indexOf('old') > -1) {
19378                         if(month === 0 ){
19379                             month = 11;
19380                             year -= 1;
19381                         }else{
19382                             month -= 1;
19383                         }
19384                     } else if (className.indexOf('new') > -1) {
19385                         if (month == 11) {
19386                             month = 0;
19387                             year += 1;
19388                         } else {
19389                             month += 1;
19390                         }
19391                     }
19392                     //Roo.log([year,month,day]);
19393                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19394                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19395 //                    this.fill();
19396                     //Roo.log(this.formatDate(this.date));
19397                     this.setValue(this.formatDate(this.date));
19398                     this.hidePopup();
19399                 }
19400                 break;
19401         }
19402     },
19403     
19404     setStartDate: function(startDate)
19405     {
19406         this.startDate = startDate || -Infinity;
19407         if (this.startDate !== -Infinity) {
19408             this.startDate = this.parseDate(this.startDate);
19409         }
19410         this.update();
19411         this.updateNavArrows();
19412     },
19413
19414     setEndDate: function(endDate)
19415     {
19416         this.endDate = endDate || Infinity;
19417         if (this.endDate !== Infinity) {
19418             this.endDate = this.parseDate(this.endDate);
19419         }
19420         this.update();
19421         this.updateNavArrows();
19422     },
19423     
19424     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19425     {
19426         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19427         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19428             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19429         }
19430         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19431             return parseInt(d, 10);
19432         });
19433         this.update();
19434         this.updateNavArrows();
19435     },
19436     
19437     updateNavArrows: function() 
19438     {
19439         if(this.singleMode){
19440             return;
19441         }
19442         
19443         var d = new Date(this.viewDate),
19444         year = d.getUTCFullYear(),
19445         month = d.getUTCMonth();
19446         
19447         Roo.each(this.picker().select('.prev', true).elements, function(v){
19448             v.show();
19449             switch (this.viewMode) {
19450                 case 0:
19451
19452                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19453                         v.hide();
19454                     }
19455                     break;
19456                 case 1:
19457                 case 2:
19458                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19459                         v.hide();
19460                     }
19461                     break;
19462             }
19463         });
19464         
19465         Roo.each(this.picker().select('.next', true).elements, function(v){
19466             v.show();
19467             switch (this.viewMode) {
19468                 case 0:
19469
19470                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19471                         v.hide();
19472                     }
19473                     break;
19474                 case 1:
19475                 case 2:
19476                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19477                         v.hide();
19478                     }
19479                     break;
19480             }
19481         })
19482     },
19483     
19484     moveMonth: function(date, dir)
19485     {
19486         if (!dir) {
19487             return date;
19488         }
19489         var new_date = new Date(date.valueOf()),
19490         day = new_date.getUTCDate(),
19491         month = new_date.getUTCMonth(),
19492         mag = Math.abs(dir),
19493         new_month, test;
19494         dir = dir > 0 ? 1 : -1;
19495         if (mag == 1){
19496             test = dir == -1
19497             // If going back one month, make sure month is not current month
19498             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19499             ? function(){
19500                 return new_date.getUTCMonth() == month;
19501             }
19502             // If going forward one month, make sure month is as expected
19503             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19504             : function(){
19505                 return new_date.getUTCMonth() != new_month;
19506             };
19507             new_month = month + dir;
19508             new_date.setUTCMonth(new_month);
19509             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19510             if (new_month < 0 || new_month > 11) {
19511                 new_month = (new_month + 12) % 12;
19512             }
19513         } else {
19514             // For magnitudes >1, move one month at a time...
19515             for (var i=0; i<mag; i++) {
19516                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19517                 new_date = this.moveMonth(new_date, dir);
19518             }
19519             // ...then reset the day, keeping it in the new month
19520             new_month = new_date.getUTCMonth();
19521             new_date.setUTCDate(day);
19522             test = function(){
19523                 return new_month != new_date.getUTCMonth();
19524             };
19525         }
19526         // Common date-resetting loop -- if date is beyond end of month, make it
19527         // end of month
19528         while (test()){
19529             new_date.setUTCDate(--day);
19530             new_date.setUTCMonth(new_month);
19531         }
19532         return new_date;
19533     },
19534
19535     moveYear: function(date, dir)
19536     {
19537         return this.moveMonth(date, dir*12);
19538     },
19539
19540     dateWithinRange: function(date)
19541     {
19542         return date >= this.startDate && date <= this.endDate;
19543     },
19544
19545     
19546     remove: function() 
19547     {
19548         this.picker().remove();
19549     },
19550     
19551     validateValue : function(value)
19552     {
19553         if(this.getVisibilityEl().hasClass('hidden')){
19554             return true;
19555         }
19556         
19557         if(value.length < 1)  {
19558             if(this.allowBlank){
19559                 return true;
19560             }
19561             return false;
19562         }
19563         
19564         if(value.length < this.minLength){
19565             return false;
19566         }
19567         if(value.length > this.maxLength){
19568             return false;
19569         }
19570         if(this.vtype){
19571             var vt = Roo.form.VTypes;
19572             if(!vt[this.vtype](value, this)){
19573                 return false;
19574             }
19575         }
19576         if(typeof this.validator == "function"){
19577             var msg = this.validator(value);
19578             if(msg !== true){
19579                 return false;
19580             }
19581         }
19582         
19583         if(this.regex && !this.regex.test(value)){
19584             return false;
19585         }
19586         
19587         if(typeof(this.parseDate(value)) == 'undefined'){
19588             return false;
19589         }
19590         
19591         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19592             return false;
19593         }      
19594         
19595         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19596             return false;
19597         } 
19598         
19599         
19600         return true;
19601     },
19602     
19603     reset : function()
19604     {
19605         this.date = this.viewDate = '';
19606         
19607         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19608     }
19609    
19610 });
19611
19612 Roo.apply(Roo.bootstrap.DateField,  {
19613     
19614     head : {
19615         tag: 'thead',
19616         cn: [
19617         {
19618             tag: 'tr',
19619             cn: [
19620             {
19621                 tag: 'th',
19622                 cls: 'prev',
19623                 html: '<i class="fa fa-arrow-left"/>'
19624             },
19625             {
19626                 tag: 'th',
19627                 cls: 'switch',
19628                 colspan: '5'
19629             },
19630             {
19631                 tag: 'th',
19632                 cls: 'next',
19633                 html: '<i class="fa fa-arrow-right"/>'
19634             }
19635
19636             ]
19637         }
19638         ]
19639     },
19640     
19641     content : {
19642         tag: 'tbody',
19643         cn: [
19644         {
19645             tag: 'tr',
19646             cn: [
19647             {
19648                 tag: 'td',
19649                 colspan: '7'
19650             }
19651             ]
19652         }
19653         ]
19654     },
19655     
19656     footer : {
19657         tag: 'tfoot',
19658         cn: [
19659         {
19660             tag: 'tr',
19661             cn: [
19662             {
19663                 tag: 'th',
19664                 colspan: '7',
19665                 cls: 'today'
19666             }
19667                     
19668             ]
19669         }
19670         ]
19671     },
19672     
19673     dates:{
19674         en: {
19675             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19676             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19677             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19678             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19679             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19680             today: "Today"
19681         }
19682     },
19683     
19684     modes: [
19685     {
19686         clsName: 'days',
19687         navFnc: 'Month',
19688         navStep: 1
19689     },
19690     {
19691         clsName: 'months',
19692         navFnc: 'FullYear',
19693         navStep: 1
19694     },
19695     {
19696         clsName: 'years',
19697         navFnc: 'FullYear',
19698         navStep: 10
19699     }]
19700 });
19701
19702 Roo.apply(Roo.bootstrap.DateField,  {
19703   
19704     template : {
19705         tag: 'div',
19706         cls: 'datepicker dropdown-menu roo-dynamic',
19707         cn: [
19708         {
19709             tag: 'div',
19710             cls: 'datepicker-days',
19711             cn: [
19712             {
19713                 tag: 'table',
19714                 cls: 'table-condensed',
19715                 cn:[
19716                 Roo.bootstrap.DateField.head,
19717                 {
19718                     tag: 'tbody'
19719                 },
19720                 Roo.bootstrap.DateField.footer
19721                 ]
19722             }
19723             ]
19724         },
19725         {
19726             tag: 'div',
19727             cls: 'datepicker-months',
19728             cn: [
19729             {
19730                 tag: 'table',
19731                 cls: 'table-condensed',
19732                 cn:[
19733                 Roo.bootstrap.DateField.head,
19734                 Roo.bootstrap.DateField.content,
19735                 Roo.bootstrap.DateField.footer
19736                 ]
19737             }
19738             ]
19739         },
19740         {
19741             tag: 'div',
19742             cls: 'datepicker-years',
19743             cn: [
19744             {
19745                 tag: 'table',
19746                 cls: 'table-condensed',
19747                 cn:[
19748                 Roo.bootstrap.DateField.head,
19749                 Roo.bootstrap.DateField.content,
19750                 Roo.bootstrap.DateField.footer
19751                 ]
19752             }
19753             ]
19754         }
19755         ]
19756     }
19757 });
19758
19759  
19760
19761  /*
19762  * - LGPL
19763  *
19764  * TimeField
19765  * 
19766  */
19767
19768 /**
19769  * @class Roo.bootstrap.TimeField
19770  * @extends Roo.bootstrap.Input
19771  * Bootstrap DateField class
19772  * 
19773  * 
19774  * @constructor
19775  * Create a new TimeField
19776  * @param {Object} config The config object
19777  */
19778
19779 Roo.bootstrap.TimeField = function(config){
19780     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19781     this.addEvents({
19782             /**
19783              * @event show
19784              * Fires when this field show.
19785              * @param {Roo.bootstrap.DateField} thisthis
19786              * @param {Mixed} date The date value
19787              */
19788             show : true,
19789             /**
19790              * @event show
19791              * Fires when this field hide.
19792              * @param {Roo.bootstrap.DateField} this
19793              * @param {Mixed} date The date value
19794              */
19795             hide : true,
19796             /**
19797              * @event select
19798              * Fires when select a date.
19799              * @param {Roo.bootstrap.DateField} this
19800              * @param {Mixed} date The date value
19801              */
19802             select : true
19803         });
19804 };
19805
19806 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19807     
19808     /**
19809      * @cfg {String} format
19810      * The default time format string which can be overriden for localization support.  The format must be
19811      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19812      */
19813     format : "H:i",
19814        
19815     onRender: function(ct, position)
19816     {
19817         
19818         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19819                 
19820         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19821         
19822         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19823         
19824         this.pop = this.picker().select('>.datepicker-time',true).first();
19825         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19826         
19827         this.picker().on('mousedown', this.onMousedown, this);
19828         this.picker().on('click', this.onClick, this);
19829         
19830         this.picker().addClass('datepicker-dropdown');
19831     
19832         this.fillTime();
19833         this.update();
19834             
19835         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19836         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19837         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19838         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19839         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19840         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19841
19842     },
19843     
19844     fireKey: function(e){
19845         if (!this.picker().isVisible()){
19846             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19847                 this.show();
19848             }
19849             return;
19850         }
19851
19852         e.preventDefault();
19853         
19854         switch(e.keyCode){
19855             case 27: // escape
19856                 this.hide();
19857                 break;
19858             case 37: // left
19859             case 39: // right
19860                 this.onTogglePeriod();
19861                 break;
19862             case 38: // up
19863                 this.onIncrementMinutes();
19864                 break;
19865             case 40: // down
19866                 this.onDecrementMinutes();
19867                 break;
19868             case 13: // enter
19869             case 9: // tab
19870                 this.setTime();
19871                 break;
19872         }
19873     },
19874     
19875     onClick: function(e) {
19876         e.stopPropagation();
19877         e.preventDefault();
19878     },
19879     
19880     picker : function()
19881     {
19882         return this.el.select('.datepicker', true).first();
19883     },
19884     
19885     fillTime: function()
19886     {    
19887         var time = this.pop.select('tbody', true).first();
19888         
19889         time.dom.innerHTML = '';
19890         
19891         time.createChild({
19892             tag: 'tr',
19893             cn: [
19894                 {
19895                     tag: 'td',
19896                     cn: [
19897                         {
19898                             tag: 'a',
19899                             href: '#',
19900                             cls: 'btn',
19901                             cn: [
19902                                 {
19903                                     tag: 'span',
19904                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19905                                 }
19906                             ]
19907                         } 
19908                     ]
19909                 },
19910                 {
19911                     tag: 'td',
19912                     cls: 'separator'
19913                 },
19914                 {
19915                     tag: 'td',
19916                     cn: [
19917                         {
19918                             tag: 'a',
19919                             href: '#',
19920                             cls: 'btn',
19921                             cn: [
19922                                 {
19923                                     tag: 'span',
19924                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19925                                 }
19926                             ]
19927                         }
19928                     ]
19929                 },
19930                 {
19931                     tag: 'td',
19932                     cls: 'separator'
19933                 }
19934             ]
19935         });
19936         
19937         time.createChild({
19938             tag: 'tr',
19939             cn: [
19940                 {
19941                     tag: 'td',
19942                     cn: [
19943                         {
19944                             tag: 'span',
19945                             cls: 'timepicker-hour',
19946                             html: '00'
19947                         }  
19948                     ]
19949                 },
19950                 {
19951                     tag: 'td',
19952                     cls: 'separator',
19953                     html: ':'
19954                 },
19955                 {
19956                     tag: 'td',
19957                     cn: [
19958                         {
19959                             tag: 'span',
19960                             cls: 'timepicker-minute',
19961                             html: '00'
19962                         }  
19963                     ]
19964                 },
19965                 {
19966                     tag: 'td',
19967                     cls: 'separator'
19968                 },
19969                 {
19970                     tag: 'td',
19971                     cn: [
19972                         {
19973                             tag: 'button',
19974                             type: 'button',
19975                             cls: 'btn btn-primary period',
19976                             html: 'AM'
19977                             
19978                         }
19979                     ]
19980                 }
19981             ]
19982         });
19983         
19984         time.createChild({
19985             tag: 'tr',
19986             cn: [
19987                 {
19988                     tag: 'td',
19989                     cn: [
19990                         {
19991                             tag: 'a',
19992                             href: '#',
19993                             cls: 'btn',
19994                             cn: [
19995                                 {
19996                                     tag: 'span',
19997                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19998                                 }
19999                             ]
20000                         }
20001                     ]
20002                 },
20003                 {
20004                     tag: 'td',
20005                     cls: 'separator'
20006                 },
20007                 {
20008                     tag: 'td',
20009                     cn: [
20010                         {
20011                             tag: 'a',
20012                             href: '#',
20013                             cls: 'btn',
20014                             cn: [
20015                                 {
20016                                     tag: 'span',
20017                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20018                                 }
20019                             ]
20020                         }
20021                     ]
20022                 },
20023                 {
20024                     tag: 'td',
20025                     cls: 'separator'
20026                 }
20027             ]
20028         });
20029         
20030     },
20031     
20032     update: function()
20033     {
20034         
20035         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20036         
20037         this.fill();
20038     },
20039     
20040     fill: function() 
20041     {
20042         var hours = this.time.getHours();
20043         var minutes = this.time.getMinutes();
20044         var period = 'AM';
20045         
20046         if(hours > 11){
20047             period = 'PM';
20048         }
20049         
20050         if(hours == 0){
20051             hours = 12;
20052         }
20053         
20054         
20055         if(hours > 12){
20056             hours = hours - 12;
20057         }
20058         
20059         if(hours < 10){
20060             hours = '0' + hours;
20061         }
20062         
20063         if(minutes < 10){
20064             minutes = '0' + minutes;
20065         }
20066         
20067         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20068         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20069         this.pop.select('button', true).first().dom.innerHTML = period;
20070         
20071     },
20072     
20073     place: function()
20074     {   
20075         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20076         
20077         var cls = ['bottom'];
20078         
20079         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20080             cls.pop();
20081             cls.push('top');
20082         }
20083         
20084         cls.push('right');
20085         
20086         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20087             cls.pop();
20088             cls.push('left');
20089         }
20090         
20091         this.picker().addClass(cls.join('-'));
20092         
20093         var _this = this;
20094         
20095         Roo.each(cls, function(c){
20096             if(c == 'bottom'){
20097                 _this.picker().setTop(_this.inputEl().getHeight());
20098                 return;
20099             }
20100             if(c == 'top'){
20101                 _this.picker().setTop(0 - _this.picker().getHeight());
20102                 return;
20103             }
20104             
20105             if(c == 'left'){
20106                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20107                 return;
20108             }
20109             if(c == 'right'){
20110                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20111                 return;
20112             }
20113         });
20114         
20115     },
20116   
20117     onFocus : function()
20118     {
20119         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20120         this.show();
20121     },
20122     
20123     onBlur : function()
20124     {
20125         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20126         this.hide();
20127     },
20128     
20129     show : function()
20130     {
20131         this.picker().show();
20132         this.pop.show();
20133         this.update();
20134         this.place();
20135         
20136         this.fireEvent('show', this, this.date);
20137     },
20138     
20139     hide : function()
20140     {
20141         this.picker().hide();
20142         this.pop.hide();
20143         
20144         this.fireEvent('hide', this, this.date);
20145     },
20146     
20147     setTime : function()
20148     {
20149         this.hide();
20150         this.setValue(this.time.format(this.format));
20151         
20152         this.fireEvent('select', this, this.date);
20153         
20154         
20155     },
20156     
20157     onMousedown: function(e){
20158         e.stopPropagation();
20159         e.preventDefault();
20160     },
20161     
20162     onIncrementHours: function()
20163     {
20164         Roo.log('onIncrementHours');
20165         this.time = this.time.add(Date.HOUR, 1);
20166         this.update();
20167         
20168     },
20169     
20170     onDecrementHours: function()
20171     {
20172         Roo.log('onDecrementHours');
20173         this.time = this.time.add(Date.HOUR, -1);
20174         this.update();
20175     },
20176     
20177     onIncrementMinutes: function()
20178     {
20179         Roo.log('onIncrementMinutes');
20180         this.time = this.time.add(Date.MINUTE, 1);
20181         this.update();
20182     },
20183     
20184     onDecrementMinutes: function()
20185     {
20186         Roo.log('onDecrementMinutes');
20187         this.time = this.time.add(Date.MINUTE, -1);
20188         this.update();
20189     },
20190     
20191     onTogglePeriod: function()
20192     {
20193         Roo.log('onTogglePeriod');
20194         this.time = this.time.add(Date.HOUR, 12);
20195         this.update();
20196     }
20197     
20198    
20199 });
20200
20201 Roo.apply(Roo.bootstrap.TimeField,  {
20202     
20203     content : {
20204         tag: 'tbody',
20205         cn: [
20206             {
20207                 tag: 'tr',
20208                 cn: [
20209                 {
20210                     tag: 'td',
20211                     colspan: '7'
20212                 }
20213                 ]
20214             }
20215         ]
20216     },
20217     
20218     footer : {
20219         tag: 'tfoot',
20220         cn: [
20221             {
20222                 tag: 'tr',
20223                 cn: [
20224                 {
20225                     tag: 'th',
20226                     colspan: '7',
20227                     cls: '',
20228                     cn: [
20229                         {
20230                             tag: 'button',
20231                             cls: 'btn btn-info ok',
20232                             html: 'OK'
20233                         }
20234                     ]
20235                 }
20236
20237                 ]
20238             }
20239         ]
20240     }
20241 });
20242
20243 Roo.apply(Roo.bootstrap.TimeField,  {
20244   
20245     template : {
20246         tag: 'div',
20247         cls: 'datepicker dropdown-menu',
20248         cn: [
20249             {
20250                 tag: 'div',
20251                 cls: 'datepicker-time',
20252                 cn: [
20253                 {
20254                     tag: 'table',
20255                     cls: 'table-condensed',
20256                     cn:[
20257                     Roo.bootstrap.TimeField.content,
20258                     Roo.bootstrap.TimeField.footer
20259                     ]
20260                 }
20261                 ]
20262             }
20263         ]
20264     }
20265 });
20266
20267  
20268
20269  /*
20270  * - LGPL
20271  *
20272  * MonthField
20273  * 
20274  */
20275
20276 /**
20277  * @class Roo.bootstrap.MonthField
20278  * @extends Roo.bootstrap.Input
20279  * Bootstrap MonthField class
20280  * 
20281  * @cfg {String} language default en
20282  * 
20283  * @constructor
20284  * Create a new MonthField
20285  * @param {Object} config The config object
20286  */
20287
20288 Roo.bootstrap.MonthField = function(config){
20289     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20290     
20291     this.addEvents({
20292         /**
20293          * @event show
20294          * Fires when this field show.
20295          * @param {Roo.bootstrap.MonthField} this
20296          * @param {Mixed} date The date value
20297          */
20298         show : true,
20299         /**
20300          * @event show
20301          * Fires when this field hide.
20302          * @param {Roo.bootstrap.MonthField} this
20303          * @param {Mixed} date The date value
20304          */
20305         hide : true,
20306         /**
20307          * @event select
20308          * Fires when select a date.
20309          * @param {Roo.bootstrap.MonthField} this
20310          * @param {String} oldvalue The old value
20311          * @param {String} newvalue The new value
20312          */
20313         select : true
20314     });
20315 };
20316
20317 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20318     
20319     onRender: function(ct, position)
20320     {
20321         
20322         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20323         
20324         this.language = this.language || 'en';
20325         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20326         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20327         
20328         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20329         this.isInline = false;
20330         this.isInput = true;
20331         this.component = this.el.select('.add-on', true).first() || false;
20332         this.component = (this.component && this.component.length === 0) ? false : this.component;
20333         this.hasInput = this.component && this.inputEL().length;
20334         
20335         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20336         
20337         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20338         
20339         this.picker().on('mousedown', this.onMousedown, this);
20340         this.picker().on('click', this.onClick, this);
20341         
20342         this.picker().addClass('datepicker-dropdown');
20343         
20344         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20345             v.setStyle('width', '189px');
20346         });
20347         
20348         this.fillMonths();
20349         
20350         this.update();
20351         
20352         if(this.isInline) {
20353             this.show();
20354         }
20355         
20356     },
20357     
20358     setValue: function(v, suppressEvent)
20359     {   
20360         var o = this.getValue();
20361         
20362         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20363         
20364         this.update();
20365
20366         if(suppressEvent !== true){
20367             this.fireEvent('select', this, o, v);
20368         }
20369         
20370     },
20371     
20372     getValue: function()
20373     {
20374         return this.value;
20375     },
20376     
20377     onClick: function(e) 
20378     {
20379         e.stopPropagation();
20380         e.preventDefault();
20381         
20382         var target = e.getTarget();
20383         
20384         if(target.nodeName.toLowerCase() === 'i'){
20385             target = Roo.get(target).dom.parentNode;
20386         }
20387         
20388         var nodeName = target.nodeName;
20389         var className = target.className;
20390         var html = target.innerHTML;
20391         
20392         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20393             return;
20394         }
20395         
20396         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20397         
20398         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20399         
20400         this.hide();
20401                         
20402     },
20403     
20404     picker : function()
20405     {
20406         return this.pickerEl;
20407     },
20408     
20409     fillMonths: function()
20410     {    
20411         var i = 0;
20412         var months = this.picker().select('>.datepicker-months td', true).first();
20413         
20414         months.dom.innerHTML = '';
20415         
20416         while (i < 12) {
20417             var month = {
20418                 tag: 'span',
20419                 cls: 'month',
20420                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20421             };
20422             
20423             months.createChild(month);
20424         }
20425         
20426     },
20427     
20428     update: function()
20429     {
20430         var _this = this;
20431         
20432         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20433             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20434         }
20435         
20436         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20437             e.removeClass('active');
20438             
20439             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20440                 e.addClass('active');
20441             }
20442         })
20443     },
20444     
20445     place: function()
20446     {
20447         if(this.isInline) {
20448             return;
20449         }
20450         
20451         this.picker().removeClass(['bottom', 'top']);
20452         
20453         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20454             /*
20455              * place to the top of element!
20456              *
20457              */
20458             
20459             this.picker().addClass('top');
20460             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20461             
20462             return;
20463         }
20464         
20465         this.picker().addClass('bottom');
20466         
20467         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20468     },
20469     
20470     onFocus : function()
20471     {
20472         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20473         this.show();
20474     },
20475     
20476     onBlur : function()
20477     {
20478         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20479         
20480         var d = this.inputEl().getValue();
20481         
20482         this.setValue(d);
20483                 
20484         this.hide();
20485     },
20486     
20487     show : function()
20488     {
20489         this.picker().show();
20490         this.picker().select('>.datepicker-months', true).first().show();
20491         this.update();
20492         this.place();
20493         
20494         this.fireEvent('show', this, this.date);
20495     },
20496     
20497     hide : function()
20498     {
20499         if(this.isInline) {
20500             return;
20501         }
20502         this.picker().hide();
20503         this.fireEvent('hide', this, this.date);
20504         
20505     },
20506     
20507     onMousedown: function(e)
20508     {
20509         e.stopPropagation();
20510         e.preventDefault();
20511     },
20512     
20513     keyup: function(e)
20514     {
20515         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20516         this.update();
20517     },
20518
20519     fireKey: function(e)
20520     {
20521         if (!this.picker().isVisible()){
20522             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20523                 this.show();
20524             }
20525             return;
20526         }
20527         
20528         var dir;
20529         
20530         switch(e.keyCode){
20531             case 27: // escape
20532                 this.hide();
20533                 e.preventDefault();
20534                 break;
20535             case 37: // left
20536             case 39: // right
20537                 dir = e.keyCode == 37 ? -1 : 1;
20538                 
20539                 this.vIndex = this.vIndex + dir;
20540                 
20541                 if(this.vIndex < 0){
20542                     this.vIndex = 0;
20543                 }
20544                 
20545                 if(this.vIndex > 11){
20546                     this.vIndex = 11;
20547                 }
20548                 
20549                 if(isNaN(this.vIndex)){
20550                     this.vIndex = 0;
20551                 }
20552                 
20553                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20554                 
20555                 break;
20556             case 38: // up
20557             case 40: // down
20558                 
20559                 dir = e.keyCode == 38 ? -1 : 1;
20560                 
20561                 this.vIndex = this.vIndex + dir * 4;
20562                 
20563                 if(this.vIndex < 0){
20564                     this.vIndex = 0;
20565                 }
20566                 
20567                 if(this.vIndex > 11){
20568                     this.vIndex = 11;
20569                 }
20570                 
20571                 if(isNaN(this.vIndex)){
20572                     this.vIndex = 0;
20573                 }
20574                 
20575                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20576                 break;
20577                 
20578             case 13: // enter
20579                 
20580                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20581                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20582                 }
20583                 
20584                 this.hide();
20585                 e.preventDefault();
20586                 break;
20587             case 9: // tab
20588                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20589                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20590                 }
20591                 this.hide();
20592                 break;
20593             case 16: // shift
20594             case 17: // ctrl
20595             case 18: // alt
20596                 break;
20597             default :
20598                 this.hide();
20599                 
20600         }
20601     },
20602     
20603     remove: function() 
20604     {
20605         this.picker().remove();
20606     }
20607    
20608 });
20609
20610 Roo.apply(Roo.bootstrap.MonthField,  {
20611     
20612     content : {
20613         tag: 'tbody',
20614         cn: [
20615         {
20616             tag: 'tr',
20617             cn: [
20618             {
20619                 tag: 'td',
20620                 colspan: '7'
20621             }
20622             ]
20623         }
20624         ]
20625     },
20626     
20627     dates:{
20628         en: {
20629             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20630             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20631         }
20632     }
20633 });
20634
20635 Roo.apply(Roo.bootstrap.MonthField,  {
20636   
20637     template : {
20638         tag: 'div',
20639         cls: 'datepicker dropdown-menu roo-dynamic',
20640         cn: [
20641             {
20642                 tag: 'div',
20643                 cls: 'datepicker-months',
20644                 cn: [
20645                 {
20646                     tag: 'table',
20647                     cls: 'table-condensed',
20648                     cn:[
20649                         Roo.bootstrap.DateField.content
20650                     ]
20651                 }
20652                 ]
20653             }
20654         ]
20655     }
20656 });
20657
20658  
20659
20660  
20661  /*
20662  * - LGPL
20663  *
20664  * CheckBox
20665  * 
20666  */
20667
20668 /**
20669  * @class Roo.bootstrap.CheckBox
20670  * @extends Roo.bootstrap.Input
20671  * Bootstrap CheckBox class
20672  * 
20673  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20674  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20675  * @cfg {String} boxLabel The text that appears beside the checkbox
20676  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20677  * @cfg {Boolean} checked initnal the element
20678  * @cfg {Boolean} inline inline the element (default false)
20679  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20680  * @cfg {String} tooltip label tooltip
20681  * 
20682  * @constructor
20683  * Create a new CheckBox
20684  * @param {Object} config The config object
20685  */
20686
20687 Roo.bootstrap.CheckBox = function(config){
20688     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20689    
20690     this.addEvents({
20691         /**
20692         * @event check
20693         * Fires when the element is checked or unchecked.
20694         * @param {Roo.bootstrap.CheckBox} this This input
20695         * @param {Boolean} checked The new checked value
20696         */
20697        check : true,
20698        /**
20699         * @event click
20700         * Fires when the element is click.
20701         * @param {Roo.bootstrap.CheckBox} this This input
20702         */
20703        click : true
20704     });
20705     
20706 };
20707
20708 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20709   
20710     inputType: 'checkbox',
20711     inputValue: 1,
20712     valueOff: 0,
20713     boxLabel: false,
20714     checked: false,
20715     weight : false,
20716     inline: false,
20717     tooltip : '',
20718     
20719     getAutoCreate : function()
20720     {
20721         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20722         
20723         var id = Roo.id();
20724         
20725         var cfg = {};
20726         
20727         cfg.cls = 'form-group ' + this.inputType; //input-group
20728         
20729         if(this.inline){
20730             cfg.cls += ' ' + this.inputType + '-inline';
20731         }
20732         
20733         var input =  {
20734             tag: 'input',
20735             id : id,
20736             type : this.inputType,
20737             value : this.inputValue,
20738             cls : 'roo-' + this.inputType, //'form-box',
20739             placeholder : this.placeholder || ''
20740             
20741         };
20742         
20743         if(this.inputType != 'radio'){
20744             var hidden =  {
20745                 tag: 'input',
20746                 type : 'hidden',
20747                 cls : 'roo-hidden-value',
20748                 value : this.checked ? this.inputValue : this.valueOff
20749             };
20750         }
20751         
20752             
20753         if (this.weight) { // Validity check?
20754             cfg.cls += " " + this.inputType + "-" + this.weight;
20755         }
20756         
20757         if (this.disabled) {
20758             input.disabled=true;
20759         }
20760         
20761         if(this.checked){
20762             input.checked = this.checked;
20763         }
20764         
20765         if (this.name) {
20766             
20767             input.name = this.name;
20768             
20769             if(this.inputType != 'radio'){
20770                 hidden.name = this.name;
20771                 input.name = '_hidden_' + this.name;
20772             }
20773         }
20774         
20775         if (this.size) {
20776             input.cls += ' input-' + this.size;
20777         }
20778         
20779         var settings=this;
20780         
20781         ['xs','sm','md','lg'].map(function(size){
20782             if (settings[size]) {
20783                 cfg.cls += ' col-' + size + '-' + settings[size];
20784             }
20785         });
20786         
20787         var inputblock = input;
20788          
20789         if (this.before || this.after) {
20790             
20791             inputblock = {
20792                 cls : 'input-group',
20793                 cn :  [] 
20794             };
20795             
20796             if (this.before) {
20797                 inputblock.cn.push({
20798                     tag :'span',
20799                     cls : 'input-group-addon',
20800                     html : this.before
20801                 });
20802             }
20803             
20804             inputblock.cn.push(input);
20805             
20806             if(this.inputType != 'radio'){
20807                 inputblock.cn.push(hidden);
20808             }
20809             
20810             if (this.after) {
20811                 inputblock.cn.push({
20812                     tag :'span',
20813                     cls : 'input-group-addon',
20814                     html : this.after
20815                 });
20816             }
20817             
20818         }
20819         
20820         if (align ==='left' && this.fieldLabel.length) {
20821 //                Roo.log("left and has label");
20822             cfg.cn = [
20823                 {
20824                     tag: 'label',
20825                     'for' :  id,
20826                     cls : 'control-label',
20827                     html : this.fieldLabel
20828                 },
20829                 {
20830                     cls : "", 
20831                     cn: [
20832                         inputblock
20833                     ]
20834                 }
20835             ];
20836             
20837             if(this.labelWidth > 12){
20838                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20839             }
20840             
20841             if(this.labelWidth < 13 && this.labelmd == 0){
20842                 this.labelmd = this.labelWidth;
20843             }
20844             
20845             if(this.labellg > 0){
20846                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20847                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20848             }
20849             
20850             if(this.labelmd > 0){
20851                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20852                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20853             }
20854             
20855             if(this.labelsm > 0){
20856                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20857                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20858             }
20859             
20860             if(this.labelxs > 0){
20861                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20862                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20863             }
20864             
20865         } else if ( this.fieldLabel.length) {
20866 //                Roo.log(" label");
20867                 cfg.cn = [
20868                    
20869                     {
20870                         tag: this.boxLabel ? 'span' : 'label',
20871                         'for': id,
20872                         cls: 'control-label box-input-label',
20873                         //cls : 'input-group-addon',
20874                         html : this.fieldLabel
20875                     },
20876                     
20877                     inputblock
20878                     
20879                 ];
20880
20881         } else {
20882             
20883 //                Roo.log(" no label && no align");
20884                 cfg.cn = [  inputblock ] ;
20885                 
20886                 
20887         }
20888         
20889         if(this.boxLabel){
20890              var boxLabelCfg = {
20891                 tag: 'label',
20892                 //'for': id, // box label is handled by onclick - so no for...
20893                 cls: 'box-label',
20894                 html: this.boxLabel
20895             };
20896             
20897             if(this.tooltip){
20898                 boxLabelCfg.tooltip = this.tooltip;
20899             }
20900              
20901             cfg.cn.push(boxLabelCfg);
20902         }
20903         
20904         if(this.inputType != 'radio'){
20905             cfg.cn.push(hidden);
20906         }
20907         
20908         return cfg;
20909         
20910     },
20911     
20912     /**
20913      * return the real input element.
20914      */
20915     inputEl: function ()
20916     {
20917         return this.el.select('input.roo-' + this.inputType,true).first();
20918     },
20919     hiddenEl: function ()
20920     {
20921         return this.el.select('input.roo-hidden-value',true).first();
20922     },
20923     
20924     labelEl: function()
20925     {
20926         return this.el.select('label.control-label',true).first();
20927     },
20928     /* depricated... */
20929     
20930     label: function()
20931     {
20932         return this.labelEl();
20933     },
20934     
20935     boxLabelEl: function()
20936     {
20937         return this.el.select('label.box-label',true).first();
20938     },
20939     
20940     initEvents : function()
20941     {
20942 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20943         
20944         this.inputEl().on('click', this.onClick,  this);
20945         
20946         if (this.boxLabel) { 
20947             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20948         }
20949         
20950         this.startValue = this.getValue();
20951         
20952         if(this.groupId){
20953             Roo.bootstrap.CheckBox.register(this);
20954         }
20955     },
20956     
20957     onClick : function(e)
20958     {   
20959         if(this.fireEvent('click', this, e) !== false){
20960             this.setChecked(!this.checked);
20961         }
20962         
20963     },
20964     
20965     setChecked : function(state,suppressEvent)
20966     {
20967         this.startValue = this.getValue();
20968
20969         if(this.inputType == 'radio'){
20970             
20971             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20972                 e.dom.checked = false;
20973             });
20974             
20975             this.inputEl().dom.checked = true;
20976             
20977             this.inputEl().dom.value = this.inputValue;
20978             
20979             if(suppressEvent !== true){
20980                 this.fireEvent('check', this, true);
20981             }
20982             
20983             this.validate();
20984             
20985             return;
20986         }
20987         
20988         this.checked = state;
20989         
20990         this.inputEl().dom.checked = state;
20991         
20992         
20993         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20994         
20995         if(suppressEvent !== true){
20996             this.fireEvent('check', this, state);
20997         }
20998         
20999         this.validate();
21000     },
21001     
21002     getValue : function()
21003     {
21004         if(this.inputType == 'radio'){
21005             return this.getGroupValue();
21006         }
21007         
21008         return this.hiddenEl().dom.value;
21009         
21010     },
21011     
21012     getGroupValue : function()
21013     {
21014         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21015             return '';
21016         }
21017         
21018         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21019     },
21020     
21021     setValue : function(v,suppressEvent)
21022     {
21023         if(this.inputType == 'radio'){
21024             this.setGroupValue(v, suppressEvent);
21025             return;
21026         }
21027         
21028         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21029         
21030         this.validate();
21031     },
21032     
21033     setGroupValue : function(v, suppressEvent)
21034     {
21035         this.startValue = this.getValue();
21036         
21037         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21038             e.dom.checked = false;
21039             
21040             if(e.dom.value == v){
21041                 e.dom.checked = true;
21042             }
21043         });
21044         
21045         if(suppressEvent !== true){
21046             this.fireEvent('check', this, true);
21047         }
21048
21049         this.validate();
21050         
21051         return;
21052     },
21053     
21054     validate : function()
21055     {
21056         if(this.getVisibilityEl().hasClass('hidden')){
21057             return true;
21058         }
21059         
21060         if(
21061                 this.disabled || 
21062                 (this.inputType == 'radio' && this.validateRadio()) ||
21063                 (this.inputType == 'checkbox' && this.validateCheckbox())
21064         ){
21065             this.markValid();
21066             return true;
21067         }
21068         
21069         this.markInvalid();
21070         return false;
21071     },
21072     
21073     validateRadio : function()
21074     {
21075         if(this.getVisibilityEl().hasClass('hidden')){
21076             return true;
21077         }
21078         
21079         if(this.allowBlank){
21080             return true;
21081         }
21082         
21083         var valid = false;
21084         
21085         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21086             if(!e.dom.checked){
21087                 return;
21088             }
21089             
21090             valid = true;
21091             
21092             return false;
21093         });
21094         
21095         return valid;
21096     },
21097     
21098     validateCheckbox : function()
21099     {
21100         if(!this.groupId){
21101             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21102             //return (this.getValue() == this.inputValue) ? true : false;
21103         }
21104         
21105         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21106         
21107         if(!group){
21108             return false;
21109         }
21110         
21111         var r = false;
21112         
21113         for(var i in group){
21114             if(group[i].el.isVisible(true)){
21115                 r = false;
21116                 break;
21117             }
21118             
21119             r = true;
21120         }
21121         
21122         for(var i in group){
21123             if(r){
21124                 break;
21125             }
21126             
21127             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21128         }
21129         
21130         return r;
21131     },
21132     
21133     /**
21134      * Mark this field as valid
21135      */
21136     markValid : function()
21137     {
21138         var _this = this;
21139         
21140         this.fireEvent('valid', this);
21141         
21142         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21143         
21144         if(this.groupId){
21145             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21146         }
21147         
21148         if(label){
21149             label.markValid();
21150         }
21151
21152         if(this.inputType == 'radio'){
21153             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21154                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21155                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21156             });
21157             
21158             return;
21159         }
21160
21161         if(!this.groupId){
21162             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21163             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21164             return;
21165         }
21166         
21167         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21168         
21169         if(!group){
21170             return;
21171         }
21172         
21173         for(var i in group){
21174             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21175             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21176         }
21177     },
21178     
21179      /**
21180      * Mark this field as invalid
21181      * @param {String} msg The validation message
21182      */
21183     markInvalid : function(msg)
21184     {
21185         if(this.allowBlank){
21186             return;
21187         }
21188         
21189         var _this = this;
21190         
21191         this.fireEvent('invalid', this, msg);
21192         
21193         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21194         
21195         if(this.groupId){
21196             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21197         }
21198         
21199         if(label){
21200             label.markInvalid();
21201         }
21202             
21203         if(this.inputType == 'radio'){
21204             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21205                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21206                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21207             });
21208             
21209             return;
21210         }
21211         
21212         if(!this.groupId){
21213             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21214             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21215             return;
21216         }
21217         
21218         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21219         
21220         if(!group){
21221             return;
21222         }
21223         
21224         for(var i in group){
21225             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21226             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21227         }
21228         
21229     },
21230     
21231     clearInvalid : function()
21232     {
21233         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21234         
21235         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21236         
21237         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21238         
21239         if (label && label.iconEl) {
21240             label.iconEl.removeClass(label.validClass);
21241             label.iconEl.removeClass(label.invalidClass);
21242         }
21243     },
21244     
21245     disable : function()
21246     {
21247         if(this.inputType != 'radio'){
21248             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21249             return;
21250         }
21251         
21252         var _this = this;
21253         
21254         if(this.rendered){
21255             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21256                 _this.getActionEl().addClass(this.disabledClass);
21257                 e.dom.disabled = true;
21258             });
21259         }
21260         
21261         this.disabled = true;
21262         this.fireEvent("disable", this);
21263         return this;
21264     },
21265
21266     enable : function()
21267     {
21268         if(this.inputType != 'radio'){
21269             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21270             return;
21271         }
21272         
21273         var _this = this;
21274         
21275         if(this.rendered){
21276             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21277                 _this.getActionEl().removeClass(this.disabledClass);
21278                 e.dom.disabled = false;
21279             });
21280         }
21281         
21282         this.disabled = false;
21283         this.fireEvent("enable", this);
21284         return this;
21285     },
21286     
21287     setBoxLabel : function(v)
21288     {
21289         this.boxLabel = v;
21290         
21291         if(this.rendered){
21292             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21293         }
21294     }
21295
21296 });
21297
21298 Roo.apply(Roo.bootstrap.CheckBox, {
21299     
21300     groups: {},
21301     
21302      /**
21303     * register a CheckBox Group
21304     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21305     */
21306     register : function(checkbox)
21307     {
21308         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21309             this.groups[checkbox.groupId] = {};
21310         }
21311         
21312         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21313             return;
21314         }
21315         
21316         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21317         
21318     },
21319     /**
21320     * fetch a CheckBox Group based on the group ID
21321     * @param {string} the group ID
21322     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21323     */
21324     get: function(groupId) {
21325         if (typeof(this.groups[groupId]) == 'undefined') {
21326             return false;
21327         }
21328         
21329         return this.groups[groupId] ;
21330     }
21331     
21332     
21333 });
21334 /*
21335  * - LGPL
21336  *
21337  * RadioItem
21338  * 
21339  */
21340
21341 /**
21342  * @class Roo.bootstrap.Radio
21343  * @extends Roo.bootstrap.Component
21344  * Bootstrap Radio class
21345  * @cfg {String} boxLabel - the label associated
21346  * @cfg {String} value - the value of radio
21347  * 
21348  * @constructor
21349  * Create a new Radio
21350  * @param {Object} config The config object
21351  */
21352 Roo.bootstrap.Radio = function(config){
21353     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21354     
21355 };
21356
21357 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21358     
21359     boxLabel : '',
21360     
21361     value : '',
21362     
21363     getAutoCreate : function()
21364     {
21365         var cfg = {
21366             tag : 'div',
21367             cls : 'form-group radio',
21368             cn : [
21369                 {
21370                     tag : 'label',
21371                     cls : 'box-label',
21372                     html : this.boxLabel
21373                 }
21374             ]
21375         };
21376         
21377         return cfg;
21378     },
21379     
21380     initEvents : function() 
21381     {
21382         this.parent().register(this);
21383         
21384         this.el.on('click', this.onClick, this);
21385         
21386     },
21387     
21388     onClick : function(e)
21389     {
21390         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21391             this.setChecked(true);
21392         }
21393     },
21394     
21395     setChecked : function(state, suppressEvent)
21396     {
21397         this.parent().setValue(this.value, suppressEvent);
21398         
21399     },
21400     
21401     setBoxLabel : function(v)
21402     {
21403         this.boxLabel = v;
21404         
21405         if(this.rendered){
21406             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21407         }
21408     }
21409     
21410 });
21411  
21412
21413  /*
21414  * - LGPL
21415  *
21416  * Input
21417  * 
21418  */
21419
21420 /**
21421  * @class Roo.bootstrap.SecurePass
21422  * @extends Roo.bootstrap.Input
21423  * Bootstrap SecurePass class
21424  *
21425  * 
21426  * @constructor
21427  * Create a new SecurePass
21428  * @param {Object} config The config object
21429  */
21430  
21431 Roo.bootstrap.SecurePass = function (config) {
21432     // these go here, so the translation tool can replace them..
21433     this.errors = {
21434         PwdEmpty: "Please type a password, and then retype it to confirm.",
21435         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21436         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21437         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21438         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21439         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21440         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21441         TooWeak: "Your password is Too Weak."
21442     },
21443     this.meterLabel = "Password strength:";
21444     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21445     this.meterClass = [
21446         "roo-password-meter-tooweak", 
21447         "roo-password-meter-weak", 
21448         "roo-password-meter-medium", 
21449         "roo-password-meter-strong", 
21450         "roo-password-meter-grey"
21451     ];
21452     
21453     this.errors = {};
21454     
21455     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21456 }
21457
21458 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21459     /**
21460      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21461      * {
21462      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21463      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21464      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21465      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21466      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21467      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21468      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21469      * })
21470      */
21471     // private
21472     
21473     meterWidth: 300,
21474     errorMsg :'',    
21475     errors: false,
21476     imageRoot: '/',
21477     /**
21478      * @cfg {String/Object} Label for the strength meter (defaults to
21479      * 'Password strength:')
21480      */
21481     // private
21482     meterLabel: '',
21483     /**
21484      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21485      * ['Weak', 'Medium', 'Strong'])
21486      */
21487     // private    
21488     pwdStrengths: false,    
21489     // private
21490     strength: 0,
21491     // private
21492     _lastPwd: null,
21493     // private
21494     kCapitalLetter: 0,
21495     kSmallLetter: 1,
21496     kDigit: 2,
21497     kPunctuation: 3,
21498     
21499     insecure: false,
21500     // private
21501     initEvents: function ()
21502     {
21503         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21504
21505         if (this.el.is('input[type=password]') && Roo.isSafari) {
21506             this.el.on('keydown', this.SafariOnKeyDown, this);
21507         }
21508
21509         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21510     },
21511     // private
21512     onRender: function (ct, position)
21513     {
21514         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21515         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21516         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21517
21518         this.trigger.createChild({
21519                    cn: [
21520                     {
21521                     //id: 'PwdMeter',
21522                     tag: 'div',
21523                     cls: 'roo-password-meter-grey col-xs-12',
21524                     style: {
21525                         //width: 0,
21526                         //width: this.meterWidth + 'px'                                                
21527                         }
21528                     },
21529                     {                            
21530                          cls: 'roo-password-meter-text'                          
21531                     }
21532                 ]            
21533         });
21534
21535          
21536         if (this.hideTrigger) {
21537             this.trigger.setDisplayed(false);
21538         }
21539         this.setSize(this.width || '', this.height || '');
21540     },
21541     // private
21542     onDestroy: function ()
21543     {
21544         if (this.trigger) {
21545             this.trigger.removeAllListeners();
21546             this.trigger.remove();
21547         }
21548         if (this.wrap) {
21549             this.wrap.remove();
21550         }
21551         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21552     },
21553     // private
21554     checkStrength: function ()
21555     {
21556         var pwd = this.inputEl().getValue();
21557         if (pwd == this._lastPwd) {
21558             return;
21559         }
21560
21561         var strength;
21562         if (this.ClientSideStrongPassword(pwd)) {
21563             strength = 3;
21564         } else if (this.ClientSideMediumPassword(pwd)) {
21565             strength = 2;
21566         } else if (this.ClientSideWeakPassword(pwd)) {
21567             strength = 1;
21568         } else {
21569             strength = 0;
21570         }
21571         
21572         Roo.log('strength1: ' + strength);
21573         
21574         //var pm = this.trigger.child('div/div/div').dom;
21575         var pm = this.trigger.child('div/div');
21576         pm.removeClass(this.meterClass);
21577         pm.addClass(this.meterClass[strength]);
21578                 
21579         
21580         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21581                 
21582         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21583         
21584         this._lastPwd = pwd;
21585     },
21586     reset: function ()
21587     {
21588         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21589         
21590         this._lastPwd = '';
21591         
21592         var pm = this.trigger.child('div/div');
21593         pm.removeClass(this.meterClass);
21594         pm.addClass('roo-password-meter-grey');        
21595         
21596         
21597         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21598         
21599         pt.innerHTML = '';
21600         this.inputEl().dom.type='password';
21601     },
21602     // private
21603     validateValue: function (value)
21604     {
21605         
21606         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21607             return false;
21608         }
21609         if (value.length == 0) {
21610             if (this.allowBlank) {
21611                 this.clearInvalid();
21612                 return true;
21613             }
21614
21615             this.markInvalid(this.errors.PwdEmpty);
21616             this.errorMsg = this.errors.PwdEmpty;
21617             return false;
21618         }
21619         
21620         if(this.insecure){
21621             return true;
21622         }
21623         
21624         if ('[\x21-\x7e]*'.match(value)) {
21625             this.markInvalid(this.errors.PwdBadChar);
21626             this.errorMsg = this.errors.PwdBadChar;
21627             return false;
21628         }
21629         if (value.length < 6) {
21630             this.markInvalid(this.errors.PwdShort);
21631             this.errorMsg = this.errors.PwdShort;
21632             return false;
21633         }
21634         if (value.length > 16) {
21635             this.markInvalid(this.errors.PwdLong);
21636             this.errorMsg = this.errors.PwdLong;
21637             return false;
21638         }
21639         var strength;
21640         if (this.ClientSideStrongPassword(value)) {
21641             strength = 3;
21642         } else if (this.ClientSideMediumPassword(value)) {
21643             strength = 2;
21644         } else if (this.ClientSideWeakPassword(value)) {
21645             strength = 1;
21646         } else {
21647             strength = 0;
21648         }
21649
21650         
21651         if (strength < 2) {
21652             //this.markInvalid(this.errors.TooWeak);
21653             this.errorMsg = this.errors.TooWeak;
21654             //return false;
21655         }
21656         
21657         
21658         console.log('strength2: ' + strength);
21659         
21660         //var pm = this.trigger.child('div/div/div').dom;
21661         
21662         var pm = this.trigger.child('div/div');
21663         pm.removeClass(this.meterClass);
21664         pm.addClass(this.meterClass[strength]);
21665                 
21666         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21667                 
21668         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21669         
21670         this.errorMsg = ''; 
21671         return true;
21672     },
21673     // private
21674     CharacterSetChecks: function (type)
21675     {
21676         this.type = type;
21677         this.fResult = false;
21678     },
21679     // private
21680     isctype: function (character, type)
21681     {
21682         switch (type) {  
21683             case this.kCapitalLetter:
21684                 if (character >= 'A' && character <= 'Z') {
21685                     return true;
21686                 }
21687                 break;
21688             
21689             case this.kSmallLetter:
21690                 if (character >= 'a' && character <= 'z') {
21691                     return true;
21692                 }
21693                 break;
21694             
21695             case this.kDigit:
21696                 if (character >= '0' && character <= '9') {
21697                     return true;
21698                 }
21699                 break;
21700             
21701             case this.kPunctuation:
21702                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21703                     return true;
21704                 }
21705                 break;
21706             
21707             default:
21708                 return false;
21709         }
21710
21711     },
21712     // private
21713     IsLongEnough: function (pwd, size)
21714     {
21715         return !(pwd == null || isNaN(size) || pwd.length < size);
21716     },
21717     // private
21718     SpansEnoughCharacterSets: function (word, nb)
21719     {
21720         if (!this.IsLongEnough(word, nb))
21721         {
21722             return false;
21723         }
21724
21725         var characterSetChecks = new Array(
21726             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21727             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21728         );
21729         
21730         for (var index = 0; index < word.length; ++index) {
21731             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21732                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21733                     characterSetChecks[nCharSet].fResult = true;
21734                     break;
21735                 }
21736             }
21737         }
21738
21739         var nCharSets = 0;
21740         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21741             if (characterSetChecks[nCharSet].fResult) {
21742                 ++nCharSets;
21743             }
21744         }
21745
21746         if (nCharSets < nb) {
21747             return false;
21748         }
21749         return true;
21750     },
21751     // private
21752     ClientSideStrongPassword: function (pwd)
21753     {
21754         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21755     },
21756     // private
21757     ClientSideMediumPassword: function (pwd)
21758     {
21759         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21760     },
21761     // private
21762     ClientSideWeakPassword: function (pwd)
21763     {
21764         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21765     }
21766           
21767 })//<script type="text/javascript">
21768
21769 /*
21770  * Based  Ext JS Library 1.1.1
21771  * Copyright(c) 2006-2007, Ext JS, LLC.
21772  * LGPL
21773  *
21774  */
21775  
21776 /**
21777  * @class Roo.HtmlEditorCore
21778  * @extends Roo.Component
21779  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21780  *
21781  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21782  */
21783
21784 Roo.HtmlEditorCore = function(config){
21785     
21786     
21787     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21788     
21789     
21790     this.addEvents({
21791         /**
21792          * @event initialize
21793          * Fires when the editor is fully initialized (including the iframe)
21794          * @param {Roo.HtmlEditorCore} this
21795          */
21796         initialize: true,
21797         /**
21798          * @event activate
21799          * Fires when the editor is first receives the focus. Any insertion must wait
21800          * until after this event.
21801          * @param {Roo.HtmlEditorCore} this
21802          */
21803         activate: true,
21804          /**
21805          * @event beforesync
21806          * Fires before the textarea is updated with content from the editor iframe. Return false
21807          * to cancel the sync.
21808          * @param {Roo.HtmlEditorCore} this
21809          * @param {String} html
21810          */
21811         beforesync: true,
21812          /**
21813          * @event beforepush
21814          * Fires before the iframe editor is updated with content from the textarea. Return false
21815          * to cancel the push.
21816          * @param {Roo.HtmlEditorCore} this
21817          * @param {String} html
21818          */
21819         beforepush: true,
21820          /**
21821          * @event sync
21822          * Fires when the textarea is updated with content from the editor iframe.
21823          * @param {Roo.HtmlEditorCore} this
21824          * @param {String} html
21825          */
21826         sync: true,
21827          /**
21828          * @event push
21829          * Fires when the iframe editor is updated with content from the textarea.
21830          * @param {Roo.HtmlEditorCore} this
21831          * @param {String} html
21832          */
21833         push: true,
21834         
21835         /**
21836          * @event editorevent
21837          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21838          * @param {Roo.HtmlEditorCore} this
21839          */
21840         editorevent: true
21841         
21842     });
21843     
21844     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21845     
21846     // defaults : white / black...
21847     this.applyBlacklists();
21848     
21849     
21850     
21851 };
21852
21853
21854 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21855
21856
21857      /**
21858      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21859      */
21860     
21861     owner : false,
21862     
21863      /**
21864      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21865      *                        Roo.resizable.
21866      */
21867     resizable : false,
21868      /**
21869      * @cfg {Number} height (in pixels)
21870      */   
21871     height: 300,
21872    /**
21873      * @cfg {Number} width (in pixels)
21874      */   
21875     width: 500,
21876     
21877     /**
21878      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21879      * 
21880      */
21881     stylesheets: false,
21882     
21883     // id of frame..
21884     frameId: false,
21885     
21886     // private properties
21887     validationEvent : false,
21888     deferHeight: true,
21889     initialized : false,
21890     activated : false,
21891     sourceEditMode : false,
21892     onFocus : Roo.emptyFn,
21893     iframePad:3,
21894     hideMode:'offsets',
21895     
21896     clearUp: true,
21897     
21898     // blacklist + whitelisted elements..
21899     black: false,
21900     white: false,
21901      
21902     bodyCls : '',
21903
21904     /**
21905      * Protected method that will not generally be called directly. It
21906      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21907      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21908      */
21909     getDocMarkup : function(){
21910         // body styles..
21911         var st = '';
21912         
21913         // inherit styels from page...?? 
21914         if (this.stylesheets === false) {
21915             
21916             Roo.get(document.head).select('style').each(function(node) {
21917                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21918             });
21919             
21920             Roo.get(document.head).select('link').each(function(node) { 
21921                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21922             });
21923             
21924         } else if (!this.stylesheets.length) {
21925                 // simple..
21926                 st = '<style type="text/css">' +
21927                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21928                    '</style>';
21929         } else { 
21930             st = '<style type="text/css">' +
21931                     this.stylesheets +
21932                 '</style>';
21933         }
21934         
21935         st +=  '<style type="text/css">' +
21936             'IMG { cursor: pointer } ' +
21937         '</style>';
21938
21939         var cls = 'roo-htmleditor-body';
21940         
21941         if(this.bodyCls.length){
21942             cls += ' ' + this.bodyCls;
21943         }
21944         
21945         return '<html><head>' + st  +
21946             //<style type="text/css">' +
21947             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21948             //'</style>' +
21949             ' </head><body class="' +  cls + '"></body></html>';
21950     },
21951
21952     // private
21953     onRender : function(ct, position)
21954     {
21955         var _t = this;
21956         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21957         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21958         
21959         
21960         this.el.dom.style.border = '0 none';
21961         this.el.dom.setAttribute('tabIndex', -1);
21962         this.el.addClass('x-hidden hide');
21963         
21964         
21965         
21966         if(Roo.isIE){ // fix IE 1px bogus margin
21967             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21968         }
21969        
21970         
21971         this.frameId = Roo.id();
21972         
21973          
21974         
21975         var iframe = this.owner.wrap.createChild({
21976             tag: 'iframe',
21977             cls: 'form-control', // bootstrap..
21978             id: this.frameId,
21979             name: this.frameId,
21980             frameBorder : 'no',
21981             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21982         }, this.el
21983         );
21984         
21985         
21986         this.iframe = iframe.dom;
21987
21988          this.assignDocWin();
21989         
21990         this.doc.designMode = 'on';
21991        
21992         this.doc.open();
21993         this.doc.write(this.getDocMarkup());
21994         this.doc.close();
21995
21996         
21997         var task = { // must defer to wait for browser to be ready
21998             run : function(){
21999                 //console.log("run task?" + this.doc.readyState);
22000                 this.assignDocWin();
22001                 if(this.doc.body || this.doc.readyState == 'complete'){
22002                     try {
22003                         this.doc.designMode="on";
22004                     } catch (e) {
22005                         return;
22006                     }
22007                     Roo.TaskMgr.stop(task);
22008                     this.initEditor.defer(10, this);
22009                 }
22010             },
22011             interval : 10,
22012             duration: 10000,
22013             scope: this
22014         };
22015         Roo.TaskMgr.start(task);
22016
22017     },
22018
22019     // private
22020     onResize : function(w, h)
22021     {
22022          Roo.log('resize: ' +w + ',' + h );
22023         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22024         if(!this.iframe){
22025             return;
22026         }
22027         if(typeof w == 'number'){
22028             
22029             this.iframe.style.width = w + 'px';
22030         }
22031         if(typeof h == 'number'){
22032             
22033             this.iframe.style.height = h + 'px';
22034             if(this.doc){
22035                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22036             }
22037         }
22038         
22039     },
22040
22041     /**
22042      * Toggles the editor between standard and source edit mode.
22043      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22044      */
22045     toggleSourceEdit : function(sourceEditMode){
22046         
22047         this.sourceEditMode = sourceEditMode === true;
22048         
22049         if(this.sourceEditMode){
22050  
22051             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22052             
22053         }else{
22054             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22055             //this.iframe.className = '';
22056             this.deferFocus();
22057         }
22058         //this.setSize(this.owner.wrap.getSize());
22059         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22060     },
22061
22062     
22063   
22064
22065     /**
22066      * Protected method that will not generally be called directly. If you need/want
22067      * custom HTML cleanup, this is the method you should override.
22068      * @param {String} html The HTML to be cleaned
22069      * return {String} The cleaned HTML
22070      */
22071     cleanHtml : function(html){
22072         html = String(html);
22073         if(html.length > 5){
22074             if(Roo.isSafari){ // strip safari nonsense
22075                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22076             }
22077         }
22078         if(html == '&nbsp;'){
22079             html = '';
22080         }
22081         return html;
22082     },
22083
22084     /**
22085      * HTML Editor -> Textarea
22086      * Protected method that will not generally be called directly. Syncs the contents
22087      * of the editor iframe with the textarea.
22088      */
22089     syncValue : function(){
22090         if(this.initialized){
22091             var bd = (this.doc.body || this.doc.documentElement);
22092             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22093             var html = bd.innerHTML;
22094             if(Roo.isSafari){
22095                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22096                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22097                 if(m && m[1]){
22098                     html = '<div style="'+m[0]+'">' + html + '</div>';
22099                 }
22100             }
22101             html = this.cleanHtml(html);
22102             // fix up the special chars.. normaly like back quotes in word...
22103             // however we do not want to do this with chinese..
22104             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22105                 var cc = b.charCodeAt();
22106                 if (
22107                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22108                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22109                     (cc >= 0xf900 && cc < 0xfb00 )
22110                 ) {
22111                         return b;
22112                 }
22113                 return "&#"+cc+";" 
22114             });
22115             if(this.owner.fireEvent('beforesync', this, html) !== false){
22116                 this.el.dom.value = html;
22117                 this.owner.fireEvent('sync', this, html);
22118             }
22119         }
22120     },
22121
22122     /**
22123      * Protected method that will not generally be called directly. Pushes the value of the textarea
22124      * into the iframe editor.
22125      */
22126     pushValue : function(){
22127         if(this.initialized){
22128             var v = this.el.dom.value.trim();
22129             
22130 //            if(v.length < 1){
22131 //                v = '&#160;';
22132 //            }
22133             
22134             if(this.owner.fireEvent('beforepush', this, v) !== false){
22135                 var d = (this.doc.body || this.doc.documentElement);
22136                 d.innerHTML = v;
22137                 this.cleanUpPaste();
22138                 this.el.dom.value = d.innerHTML;
22139                 this.owner.fireEvent('push', this, v);
22140             }
22141         }
22142     },
22143
22144     // private
22145     deferFocus : function(){
22146         this.focus.defer(10, this);
22147     },
22148
22149     // doc'ed in Field
22150     focus : function(){
22151         if(this.win && !this.sourceEditMode){
22152             this.win.focus();
22153         }else{
22154             this.el.focus();
22155         }
22156     },
22157     
22158     assignDocWin: function()
22159     {
22160         var iframe = this.iframe;
22161         
22162          if(Roo.isIE){
22163             this.doc = iframe.contentWindow.document;
22164             this.win = iframe.contentWindow;
22165         } else {
22166 //            if (!Roo.get(this.frameId)) {
22167 //                return;
22168 //            }
22169 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22170 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22171             
22172             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22173                 return;
22174             }
22175             
22176             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22177             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22178         }
22179     },
22180     
22181     // private
22182     initEditor : function(){
22183         //console.log("INIT EDITOR");
22184         this.assignDocWin();
22185         
22186         
22187         
22188         this.doc.designMode="on";
22189         this.doc.open();
22190         this.doc.write(this.getDocMarkup());
22191         this.doc.close();
22192         
22193         var dbody = (this.doc.body || this.doc.documentElement);
22194         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22195         // this copies styles from the containing element into thsi one..
22196         // not sure why we need all of this..
22197         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22198         
22199         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22200         //ss['background-attachment'] = 'fixed'; // w3c
22201         dbody.bgProperties = 'fixed'; // ie
22202         //Roo.DomHelper.applyStyles(dbody, ss);
22203         Roo.EventManager.on(this.doc, {
22204             //'mousedown': this.onEditorEvent,
22205             'mouseup': this.onEditorEvent,
22206             'dblclick': this.onEditorEvent,
22207             'click': this.onEditorEvent,
22208             'keyup': this.onEditorEvent,
22209             buffer:100,
22210             scope: this
22211         });
22212         if(Roo.isGecko){
22213             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22214         }
22215         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22216             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22217         }
22218         this.initialized = true;
22219
22220         this.owner.fireEvent('initialize', this);
22221         this.pushValue();
22222     },
22223
22224     // private
22225     onDestroy : function(){
22226         
22227         
22228         
22229         if(this.rendered){
22230             
22231             //for (var i =0; i < this.toolbars.length;i++) {
22232             //    // fixme - ask toolbars for heights?
22233             //    this.toolbars[i].onDestroy();
22234            // }
22235             
22236             //this.wrap.dom.innerHTML = '';
22237             //this.wrap.remove();
22238         }
22239     },
22240
22241     // private
22242     onFirstFocus : function(){
22243         
22244         this.assignDocWin();
22245         
22246         
22247         this.activated = true;
22248          
22249     
22250         if(Roo.isGecko){ // prevent silly gecko errors
22251             this.win.focus();
22252             var s = this.win.getSelection();
22253             if(!s.focusNode || s.focusNode.nodeType != 3){
22254                 var r = s.getRangeAt(0);
22255                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22256                 r.collapse(true);
22257                 this.deferFocus();
22258             }
22259             try{
22260                 this.execCmd('useCSS', true);
22261                 this.execCmd('styleWithCSS', false);
22262             }catch(e){}
22263         }
22264         this.owner.fireEvent('activate', this);
22265     },
22266
22267     // private
22268     adjustFont: function(btn){
22269         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22270         //if(Roo.isSafari){ // safari
22271         //    adjust *= 2;
22272        // }
22273         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22274         if(Roo.isSafari){ // safari
22275             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22276             v =  (v < 10) ? 10 : v;
22277             v =  (v > 48) ? 48 : v;
22278             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22279             
22280         }
22281         
22282         
22283         v = Math.max(1, v+adjust);
22284         
22285         this.execCmd('FontSize', v  );
22286     },
22287
22288     onEditorEvent : function(e)
22289     {
22290         this.owner.fireEvent('editorevent', this, e);
22291       //  this.updateToolbar();
22292         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22293     },
22294
22295     insertTag : function(tg)
22296     {
22297         // could be a bit smarter... -> wrap the current selected tRoo..
22298         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22299             
22300             range = this.createRange(this.getSelection());
22301             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22302             wrappingNode.appendChild(range.extractContents());
22303             range.insertNode(wrappingNode);
22304
22305             return;
22306             
22307             
22308             
22309         }
22310         this.execCmd("formatblock",   tg);
22311         
22312     },
22313     
22314     insertText : function(txt)
22315     {
22316         
22317         
22318         var range = this.createRange();
22319         range.deleteContents();
22320                //alert(Sender.getAttribute('label'));
22321                
22322         range.insertNode(this.doc.createTextNode(txt));
22323     } ,
22324     
22325      
22326
22327     /**
22328      * Executes a Midas editor command on the editor document and performs necessary focus and
22329      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22330      * @param {String} cmd The Midas command
22331      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22332      */
22333     relayCmd : function(cmd, value){
22334         this.win.focus();
22335         this.execCmd(cmd, value);
22336         this.owner.fireEvent('editorevent', this);
22337         //this.updateToolbar();
22338         this.owner.deferFocus();
22339     },
22340
22341     /**
22342      * Executes a Midas editor command directly on the editor document.
22343      * For visual commands, you should use {@link #relayCmd} instead.
22344      * <b>This should only be called after the editor is initialized.</b>
22345      * @param {String} cmd The Midas command
22346      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22347      */
22348     execCmd : function(cmd, value){
22349         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22350         this.syncValue();
22351     },
22352  
22353  
22354    
22355     /**
22356      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22357      * to insert tRoo.
22358      * @param {String} text | dom node.. 
22359      */
22360     insertAtCursor : function(text)
22361     {
22362         
22363         if(!this.activated){
22364             return;
22365         }
22366         /*
22367         if(Roo.isIE){
22368             this.win.focus();
22369             var r = this.doc.selection.createRange();
22370             if(r){
22371                 r.collapse(true);
22372                 r.pasteHTML(text);
22373                 this.syncValue();
22374                 this.deferFocus();
22375             
22376             }
22377             return;
22378         }
22379         */
22380         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22381             this.win.focus();
22382             
22383             
22384             // from jquery ui (MIT licenced)
22385             var range, node;
22386             var win = this.win;
22387             
22388             if (win.getSelection && win.getSelection().getRangeAt) {
22389                 range = win.getSelection().getRangeAt(0);
22390                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22391                 range.insertNode(node);
22392             } else if (win.document.selection && win.document.selection.createRange) {
22393                 // no firefox support
22394                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22395                 win.document.selection.createRange().pasteHTML(txt);
22396             } else {
22397                 // no firefox support
22398                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22399                 this.execCmd('InsertHTML', txt);
22400             } 
22401             
22402             this.syncValue();
22403             
22404             this.deferFocus();
22405         }
22406     },
22407  // private
22408     mozKeyPress : function(e){
22409         if(e.ctrlKey){
22410             var c = e.getCharCode(), cmd;
22411           
22412             if(c > 0){
22413                 c = String.fromCharCode(c).toLowerCase();
22414                 switch(c){
22415                     case 'b':
22416                         cmd = 'bold';
22417                         break;
22418                     case 'i':
22419                         cmd = 'italic';
22420                         break;
22421                     
22422                     case 'u':
22423                         cmd = 'underline';
22424                         break;
22425                     
22426                     case 'v':
22427                         this.cleanUpPaste.defer(100, this);
22428                         return;
22429                         
22430                 }
22431                 if(cmd){
22432                     this.win.focus();
22433                     this.execCmd(cmd);
22434                     this.deferFocus();
22435                     e.preventDefault();
22436                 }
22437                 
22438             }
22439         }
22440     },
22441
22442     // private
22443     fixKeys : function(){ // load time branching for fastest keydown performance
22444         if(Roo.isIE){
22445             return function(e){
22446                 var k = e.getKey(), r;
22447                 if(k == e.TAB){
22448                     e.stopEvent();
22449                     r = this.doc.selection.createRange();
22450                     if(r){
22451                         r.collapse(true);
22452                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22453                         this.deferFocus();
22454                     }
22455                     return;
22456                 }
22457                 
22458                 if(k == e.ENTER){
22459                     r = this.doc.selection.createRange();
22460                     if(r){
22461                         var target = r.parentElement();
22462                         if(!target || target.tagName.toLowerCase() != 'li'){
22463                             e.stopEvent();
22464                             r.pasteHTML('<br />');
22465                             r.collapse(false);
22466                             r.select();
22467                         }
22468                     }
22469                 }
22470                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22471                     this.cleanUpPaste.defer(100, this);
22472                     return;
22473                 }
22474                 
22475                 
22476             };
22477         }else if(Roo.isOpera){
22478             return function(e){
22479                 var k = e.getKey();
22480                 if(k == e.TAB){
22481                     e.stopEvent();
22482                     this.win.focus();
22483                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22484                     this.deferFocus();
22485                 }
22486                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22487                     this.cleanUpPaste.defer(100, this);
22488                     return;
22489                 }
22490                 
22491             };
22492         }else if(Roo.isSafari){
22493             return function(e){
22494                 var k = e.getKey();
22495                 
22496                 if(k == e.TAB){
22497                     e.stopEvent();
22498                     this.execCmd('InsertText','\t');
22499                     this.deferFocus();
22500                     return;
22501                 }
22502                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22503                     this.cleanUpPaste.defer(100, this);
22504                     return;
22505                 }
22506                 
22507              };
22508         }
22509     }(),
22510     
22511     getAllAncestors: function()
22512     {
22513         var p = this.getSelectedNode();
22514         var a = [];
22515         if (!p) {
22516             a.push(p); // push blank onto stack..
22517             p = this.getParentElement();
22518         }
22519         
22520         
22521         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22522             a.push(p);
22523             p = p.parentNode;
22524         }
22525         a.push(this.doc.body);
22526         return a;
22527     },
22528     lastSel : false,
22529     lastSelNode : false,
22530     
22531     
22532     getSelection : function() 
22533     {
22534         this.assignDocWin();
22535         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22536     },
22537     
22538     getSelectedNode: function() 
22539     {
22540         // this may only work on Gecko!!!
22541         
22542         // should we cache this!!!!
22543         
22544         
22545         
22546          
22547         var range = this.createRange(this.getSelection()).cloneRange();
22548         
22549         if (Roo.isIE) {
22550             var parent = range.parentElement();
22551             while (true) {
22552                 var testRange = range.duplicate();
22553                 testRange.moveToElementText(parent);
22554                 if (testRange.inRange(range)) {
22555                     break;
22556                 }
22557                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22558                     break;
22559                 }
22560                 parent = parent.parentElement;
22561             }
22562             return parent;
22563         }
22564         
22565         // is ancestor a text element.
22566         var ac =  range.commonAncestorContainer;
22567         if (ac.nodeType == 3) {
22568             ac = ac.parentNode;
22569         }
22570         
22571         var ar = ac.childNodes;
22572          
22573         var nodes = [];
22574         var other_nodes = [];
22575         var has_other_nodes = false;
22576         for (var i=0;i<ar.length;i++) {
22577             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22578                 continue;
22579             }
22580             // fullly contained node.
22581             
22582             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22583                 nodes.push(ar[i]);
22584                 continue;
22585             }
22586             
22587             // probably selected..
22588             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22589                 other_nodes.push(ar[i]);
22590                 continue;
22591             }
22592             // outer..
22593             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22594                 continue;
22595             }
22596             
22597             
22598             has_other_nodes = true;
22599         }
22600         if (!nodes.length && other_nodes.length) {
22601             nodes= other_nodes;
22602         }
22603         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22604             return false;
22605         }
22606         
22607         return nodes[0];
22608     },
22609     createRange: function(sel)
22610     {
22611         // this has strange effects when using with 
22612         // top toolbar - not sure if it's a great idea.
22613         //this.editor.contentWindow.focus();
22614         if (typeof sel != "undefined") {
22615             try {
22616                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22617             } catch(e) {
22618                 return this.doc.createRange();
22619             }
22620         } else {
22621             return this.doc.createRange();
22622         }
22623     },
22624     getParentElement: function()
22625     {
22626         
22627         this.assignDocWin();
22628         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22629         
22630         var range = this.createRange(sel);
22631          
22632         try {
22633             var p = range.commonAncestorContainer;
22634             while (p.nodeType == 3) { // text node
22635                 p = p.parentNode;
22636             }
22637             return p;
22638         } catch (e) {
22639             return null;
22640         }
22641     
22642     },
22643     /***
22644      *
22645      * Range intersection.. the hard stuff...
22646      *  '-1' = before
22647      *  '0' = hits..
22648      *  '1' = after.
22649      *         [ -- selected range --- ]
22650      *   [fail]                        [fail]
22651      *
22652      *    basically..
22653      *      if end is before start or  hits it. fail.
22654      *      if start is after end or hits it fail.
22655      *
22656      *   if either hits (but other is outside. - then it's not 
22657      *   
22658      *    
22659      **/
22660     
22661     
22662     // @see http://www.thismuchiknow.co.uk/?p=64.
22663     rangeIntersectsNode : function(range, node)
22664     {
22665         var nodeRange = node.ownerDocument.createRange();
22666         try {
22667             nodeRange.selectNode(node);
22668         } catch (e) {
22669             nodeRange.selectNodeContents(node);
22670         }
22671     
22672         var rangeStartRange = range.cloneRange();
22673         rangeStartRange.collapse(true);
22674     
22675         var rangeEndRange = range.cloneRange();
22676         rangeEndRange.collapse(false);
22677     
22678         var nodeStartRange = nodeRange.cloneRange();
22679         nodeStartRange.collapse(true);
22680     
22681         var nodeEndRange = nodeRange.cloneRange();
22682         nodeEndRange.collapse(false);
22683     
22684         return rangeStartRange.compareBoundaryPoints(
22685                  Range.START_TO_START, nodeEndRange) == -1 &&
22686                rangeEndRange.compareBoundaryPoints(
22687                  Range.START_TO_START, nodeStartRange) == 1;
22688         
22689          
22690     },
22691     rangeCompareNode : function(range, node)
22692     {
22693         var nodeRange = node.ownerDocument.createRange();
22694         try {
22695             nodeRange.selectNode(node);
22696         } catch (e) {
22697             nodeRange.selectNodeContents(node);
22698         }
22699         
22700         
22701         range.collapse(true);
22702     
22703         nodeRange.collapse(true);
22704      
22705         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22706         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22707          
22708         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22709         
22710         var nodeIsBefore   =  ss == 1;
22711         var nodeIsAfter    = ee == -1;
22712         
22713         if (nodeIsBefore && nodeIsAfter) {
22714             return 0; // outer
22715         }
22716         if (!nodeIsBefore && nodeIsAfter) {
22717             return 1; //right trailed.
22718         }
22719         
22720         if (nodeIsBefore && !nodeIsAfter) {
22721             return 2;  // left trailed.
22722         }
22723         // fully contined.
22724         return 3;
22725     },
22726
22727     // private? - in a new class?
22728     cleanUpPaste :  function()
22729     {
22730         // cleans up the whole document..
22731         Roo.log('cleanuppaste');
22732         
22733         this.cleanUpChildren(this.doc.body);
22734         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22735         if (clean != this.doc.body.innerHTML) {
22736             this.doc.body.innerHTML = clean;
22737         }
22738         
22739     },
22740     
22741     cleanWordChars : function(input) {// change the chars to hex code
22742         var he = Roo.HtmlEditorCore;
22743         
22744         var output = input;
22745         Roo.each(he.swapCodes, function(sw) { 
22746             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22747             
22748             output = output.replace(swapper, sw[1]);
22749         });
22750         
22751         return output;
22752     },
22753     
22754     
22755     cleanUpChildren : function (n)
22756     {
22757         if (!n.childNodes.length) {
22758             return;
22759         }
22760         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22761            this.cleanUpChild(n.childNodes[i]);
22762         }
22763     },
22764     
22765     
22766         
22767     
22768     cleanUpChild : function (node)
22769     {
22770         var ed = this;
22771         //console.log(node);
22772         if (node.nodeName == "#text") {
22773             // clean up silly Windows -- stuff?
22774             return; 
22775         }
22776         if (node.nodeName == "#comment") {
22777             node.parentNode.removeChild(node);
22778             // clean up silly Windows -- stuff?
22779             return; 
22780         }
22781         var lcname = node.tagName.toLowerCase();
22782         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22783         // whitelist of tags..
22784         
22785         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22786             // remove node.
22787             node.parentNode.removeChild(node);
22788             return;
22789             
22790         }
22791         
22792         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22793         
22794         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22795         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22796         
22797         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22798         //    remove_keep_children = true;
22799         //}
22800         
22801         if (remove_keep_children) {
22802             this.cleanUpChildren(node);
22803             // inserts everything just before this node...
22804             while (node.childNodes.length) {
22805                 var cn = node.childNodes[0];
22806                 node.removeChild(cn);
22807                 node.parentNode.insertBefore(cn, node);
22808             }
22809             node.parentNode.removeChild(node);
22810             return;
22811         }
22812         
22813         if (!node.attributes || !node.attributes.length) {
22814             this.cleanUpChildren(node);
22815             return;
22816         }
22817         
22818         function cleanAttr(n,v)
22819         {
22820             
22821             if (v.match(/^\./) || v.match(/^\//)) {
22822                 return;
22823             }
22824             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22825                 return;
22826             }
22827             if (v.match(/^#/)) {
22828                 return;
22829             }
22830 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22831             node.removeAttribute(n);
22832             
22833         }
22834         
22835         var cwhite = this.cwhite;
22836         var cblack = this.cblack;
22837             
22838         function cleanStyle(n,v)
22839         {
22840             if (v.match(/expression/)) { //XSS?? should we even bother..
22841                 node.removeAttribute(n);
22842                 return;
22843             }
22844             
22845             var parts = v.split(/;/);
22846             var clean = [];
22847             
22848             Roo.each(parts, function(p) {
22849                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22850                 if (!p.length) {
22851                     return true;
22852                 }
22853                 var l = p.split(':').shift().replace(/\s+/g,'');
22854                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22855                 
22856                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22857 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22858                     //node.removeAttribute(n);
22859                     return true;
22860                 }
22861                 //Roo.log()
22862                 // only allow 'c whitelisted system attributes'
22863                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22864 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22865                     //node.removeAttribute(n);
22866                     return true;
22867                 }
22868                 
22869                 
22870                  
22871                 
22872                 clean.push(p);
22873                 return true;
22874             });
22875             if (clean.length) { 
22876                 node.setAttribute(n, clean.join(';'));
22877             } else {
22878                 node.removeAttribute(n);
22879             }
22880             
22881         }
22882         
22883         
22884         for (var i = node.attributes.length-1; i > -1 ; i--) {
22885             var a = node.attributes[i];
22886             //console.log(a);
22887             
22888             if (a.name.toLowerCase().substr(0,2)=='on')  {
22889                 node.removeAttribute(a.name);
22890                 continue;
22891             }
22892             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22893                 node.removeAttribute(a.name);
22894                 continue;
22895             }
22896             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22897                 cleanAttr(a.name,a.value); // fixme..
22898                 continue;
22899             }
22900             if (a.name == 'style') {
22901                 cleanStyle(a.name,a.value);
22902                 continue;
22903             }
22904             /// clean up MS crap..
22905             // tecnically this should be a list of valid class'es..
22906             
22907             
22908             if (a.name == 'class') {
22909                 if (a.value.match(/^Mso/)) {
22910                     node.className = '';
22911                 }
22912                 
22913                 if (a.value.match(/^body$/)) {
22914                     node.className = '';
22915                 }
22916                 continue;
22917             }
22918             
22919             // style cleanup!?
22920             // class cleanup?
22921             
22922         }
22923         
22924         
22925         this.cleanUpChildren(node);
22926         
22927         
22928     },
22929     
22930     /**
22931      * Clean up MS wordisms...
22932      */
22933     cleanWord : function(node)
22934     {
22935         
22936         
22937         if (!node) {
22938             this.cleanWord(this.doc.body);
22939             return;
22940         }
22941         if (node.nodeName == "#text") {
22942             // clean up silly Windows -- stuff?
22943             return; 
22944         }
22945         if (node.nodeName == "#comment") {
22946             node.parentNode.removeChild(node);
22947             // clean up silly Windows -- stuff?
22948             return; 
22949         }
22950         
22951         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22952             node.parentNode.removeChild(node);
22953             return;
22954         }
22955         
22956         // remove - but keep children..
22957         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22958             while (node.childNodes.length) {
22959                 var cn = node.childNodes[0];
22960                 node.removeChild(cn);
22961                 node.parentNode.insertBefore(cn, node);
22962             }
22963             node.parentNode.removeChild(node);
22964             this.iterateChildren(node, this.cleanWord);
22965             return;
22966         }
22967         // clean styles
22968         if (node.className.length) {
22969             
22970             var cn = node.className.split(/\W+/);
22971             var cna = [];
22972             Roo.each(cn, function(cls) {
22973                 if (cls.match(/Mso[a-zA-Z]+/)) {
22974                     return;
22975                 }
22976                 cna.push(cls);
22977             });
22978             node.className = cna.length ? cna.join(' ') : '';
22979             if (!cna.length) {
22980                 node.removeAttribute("class");
22981             }
22982         }
22983         
22984         if (node.hasAttribute("lang")) {
22985             node.removeAttribute("lang");
22986         }
22987         
22988         if (node.hasAttribute("style")) {
22989             
22990             var styles = node.getAttribute("style").split(";");
22991             var nstyle = [];
22992             Roo.each(styles, function(s) {
22993                 if (!s.match(/:/)) {
22994                     return;
22995                 }
22996                 var kv = s.split(":");
22997                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22998                     return;
22999                 }
23000                 // what ever is left... we allow.
23001                 nstyle.push(s);
23002             });
23003             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23004             if (!nstyle.length) {
23005                 node.removeAttribute('style');
23006             }
23007         }
23008         this.iterateChildren(node, this.cleanWord);
23009         
23010         
23011         
23012     },
23013     /**
23014      * iterateChildren of a Node, calling fn each time, using this as the scole..
23015      * @param {DomNode} node node to iterate children of.
23016      * @param {Function} fn method of this class to call on each item.
23017      */
23018     iterateChildren : function(node, fn)
23019     {
23020         if (!node.childNodes.length) {
23021                 return;
23022         }
23023         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23024            fn.call(this, node.childNodes[i])
23025         }
23026     },
23027     
23028     
23029     /**
23030      * cleanTableWidths.
23031      *
23032      * Quite often pasting from word etc.. results in tables with column and widths.
23033      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23034      *
23035      */
23036     cleanTableWidths : function(node)
23037     {
23038          
23039          
23040         if (!node) {
23041             this.cleanTableWidths(this.doc.body);
23042             return;
23043         }
23044         
23045         // ignore list...
23046         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23047             return; 
23048         }
23049         Roo.log(node.tagName);
23050         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23051             this.iterateChildren(node, this.cleanTableWidths);
23052             return;
23053         }
23054         if (node.hasAttribute('width')) {
23055             node.removeAttribute('width');
23056         }
23057         
23058          
23059         if (node.hasAttribute("style")) {
23060             // pretty basic...
23061             
23062             var styles = node.getAttribute("style").split(";");
23063             var nstyle = [];
23064             Roo.each(styles, function(s) {
23065                 if (!s.match(/:/)) {
23066                     return;
23067                 }
23068                 var kv = s.split(":");
23069                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23070                     return;
23071                 }
23072                 // what ever is left... we allow.
23073                 nstyle.push(s);
23074             });
23075             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23076             if (!nstyle.length) {
23077                 node.removeAttribute('style');
23078             }
23079         }
23080         
23081         this.iterateChildren(node, this.cleanTableWidths);
23082         
23083         
23084     },
23085     
23086     
23087     
23088     
23089     domToHTML : function(currentElement, depth, nopadtext) {
23090         
23091         depth = depth || 0;
23092         nopadtext = nopadtext || false;
23093     
23094         if (!currentElement) {
23095             return this.domToHTML(this.doc.body);
23096         }
23097         
23098         //Roo.log(currentElement);
23099         var j;
23100         var allText = false;
23101         var nodeName = currentElement.nodeName;
23102         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23103         
23104         if  (nodeName == '#text') {
23105             
23106             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23107         }
23108         
23109         
23110         var ret = '';
23111         if (nodeName != 'BODY') {
23112              
23113             var i = 0;
23114             // Prints the node tagName, such as <A>, <IMG>, etc
23115             if (tagName) {
23116                 var attr = [];
23117                 for(i = 0; i < currentElement.attributes.length;i++) {
23118                     // quoting?
23119                     var aname = currentElement.attributes.item(i).name;
23120                     if (!currentElement.attributes.item(i).value.length) {
23121                         continue;
23122                     }
23123                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23124                 }
23125                 
23126                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23127             } 
23128             else {
23129                 
23130                 // eack
23131             }
23132         } else {
23133             tagName = false;
23134         }
23135         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23136             return ret;
23137         }
23138         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23139             nopadtext = true;
23140         }
23141         
23142         
23143         // Traverse the tree
23144         i = 0;
23145         var currentElementChild = currentElement.childNodes.item(i);
23146         var allText = true;
23147         var innerHTML  = '';
23148         lastnode = '';
23149         while (currentElementChild) {
23150             // Formatting code (indent the tree so it looks nice on the screen)
23151             var nopad = nopadtext;
23152             if (lastnode == 'SPAN') {
23153                 nopad  = true;
23154             }
23155             // text
23156             if  (currentElementChild.nodeName == '#text') {
23157                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23158                 toadd = nopadtext ? toadd : toadd.trim();
23159                 if (!nopad && toadd.length > 80) {
23160                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23161                 }
23162                 innerHTML  += toadd;
23163                 
23164                 i++;
23165                 currentElementChild = currentElement.childNodes.item(i);
23166                 lastNode = '';
23167                 continue;
23168             }
23169             allText = false;
23170             
23171             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23172                 
23173             // Recursively traverse the tree structure of the child node
23174             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23175             lastnode = currentElementChild.nodeName;
23176             i++;
23177             currentElementChild=currentElement.childNodes.item(i);
23178         }
23179         
23180         ret += innerHTML;
23181         
23182         if (!allText) {
23183                 // The remaining code is mostly for formatting the tree
23184             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23185         }
23186         
23187         
23188         if (tagName) {
23189             ret+= "</"+tagName+">";
23190         }
23191         return ret;
23192         
23193     },
23194         
23195     applyBlacklists : function()
23196     {
23197         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23198         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23199         
23200         this.white = [];
23201         this.black = [];
23202         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23203             if (b.indexOf(tag) > -1) {
23204                 return;
23205             }
23206             this.white.push(tag);
23207             
23208         }, this);
23209         
23210         Roo.each(w, function(tag) {
23211             if (b.indexOf(tag) > -1) {
23212                 return;
23213             }
23214             if (this.white.indexOf(tag) > -1) {
23215                 return;
23216             }
23217             this.white.push(tag);
23218             
23219         }, this);
23220         
23221         
23222         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23223             if (w.indexOf(tag) > -1) {
23224                 return;
23225             }
23226             this.black.push(tag);
23227             
23228         }, this);
23229         
23230         Roo.each(b, function(tag) {
23231             if (w.indexOf(tag) > -1) {
23232                 return;
23233             }
23234             if (this.black.indexOf(tag) > -1) {
23235                 return;
23236             }
23237             this.black.push(tag);
23238             
23239         }, this);
23240         
23241         
23242         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23243         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23244         
23245         this.cwhite = [];
23246         this.cblack = [];
23247         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23248             if (b.indexOf(tag) > -1) {
23249                 return;
23250             }
23251             this.cwhite.push(tag);
23252             
23253         }, this);
23254         
23255         Roo.each(w, function(tag) {
23256             if (b.indexOf(tag) > -1) {
23257                 return;
23258             }
23259             if (this.cwhite.indexOf(tag) > -1) {
23260                 return;
23261             }
23262             this.cwhite.push(tag);
23263             
23264         }, this);
23265         
23266         
23267         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23268             if (w.indexOf(tag) > -1) {
23269                 return;
23270             }
23271             this.cblack.push(tag);
23272             
23273         }, this);
23274         
23275         Roo.each(b, function(tag) {
23276             if (w.indexOf(tag) > -1) {
23277                 return;
23278             }
23279             if (this.cblack.indexOf(tag) > -1) {
23280                 return;
23281             }
23282             this.cblack.push(tag);
23283             
23284         }, this);
23285     },
23286     
23287     setStylesheets : function(stylesheets)
23288     {
23289         if(typeof(stylesheets) == 'string'){
23290             Roo.get(this.iframe.contentDocument.head).createChild({
23291                 tag : 'link',
23292                 rel : 'stylesheet',
23293                 type : 'text/css',
23294                 href : stylesheets
23295             });
23296             
23297             return;
23298         }
23299         var _this = this;
23300      
23301         Roo.each(stylesheets, function(s) {
23302             if(!s.length){
23303                 return;
23304             }
23305             
23306             Roo.get(_this.iframe.contentDocument.head).createChild({
23307                 tag : 'link',
23308                 rel : 'stylesheet',
23309                 type : 'text/css',
23310                 href : s
23311             });
23312         });
23313
23314         
23315     },
23316     
23317     removeStylesheets : function()
23318     {
23319         var _this = this;
23320         
23321         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23322             s.remove();
23323         });
23324     },
23325     
23326     setStyle : function(style)
23327     {
23328         Roo.get(this.iframe.contentDocument.head).createChild({
23329             tag : 'style',
23330             type : 'text/css',
23331             html : style
23332         });
23333
23334         return;
23335     }
23336     
23337     // hide stuff that is not compatible
23338     /**
23339      * @event blur
23340      * @hide
23341      */
23342     /**
23343      * @event change
23344      * @hide
23345      */
23346     /**
23347      * @event focus
23348      * @hide
23349      */
23350     /**
23351      * @event specialkey
23352      * @hide
23353      */
23354     /**
23355      * @cfg {String} fieldClass @hide
23356      */
23357     /**
23358      * @cfg {String} focusClass @hide
23359      */
23360     /**
23361      * @cfg {String} autoCreate @hide
23362      */
23363     /**
23364      * @cfg {String} inputType @hide
23365      */
23366     /**
23367      * @cfg {String} invalidClass @hide
23368      */
23369     /**
23370      * @cfg {String} invalidText @hide
23371      */
23372     /**
23373      * @cfg {String} msgFx @hide
23374      */
23375     /**
23376      * @cfg {String} validateOnBlur @hide
23377      */
23378 });
23379
23380 Roo.HtmlEditorCore.white = [
23381         'area', 'br', 'img', 'input', 'hr', 'wbr',
23382         
23383        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23384        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23385        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23386        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23387        'table',   'ul',         'xmp', 
23388        
23389        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23390       'thead',   'tr', 
23391      
23392       'dir', 'menu', 'ol', 'ul', 'dl',
23393        
23394       'embed',  'object'
23395 ];
23396
23397
23398 Roo.HtmlEditorCore.black = [
23399     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23400         'applet', // 
23401         'base',   'basefont', 'bgsound', 'blink',  'body', 
23402         'frame',  'frameset', 'head',    'html',   'ilayer', 
23403         'iframe', 'layer',  'link',     'meta',    'object',   
23404         'script', 'style' ,'title',  'xml' // clean later..
23405 ];
23406 Roo.HtmlEditorCore.clean = [
23407     'script', 'style', 'title', 'xml'
23408 ];
23409 Roo.HtmlEditorCore.remove = [
23410     'font'
23411 ];
23412 // attributes..
23413
23414 Roo.HtmlEditorCore.ablack = [
23415     'on'
23416 ];
23417     
23418 Roo.HtmlEditorCore.aclean = [ 
23419     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23420 ];
23421
23422 // protocols..
23423 Roo.HtmlEditorCore.pwhite= [
23424         'http',  'https',  'mailto'
23425 ];
23426
23427 // white listed style attributes.
23428 Roo.HtmlEditorCore.cwhite= [
23429       //  'text-align', /// default is to allow most things..
23430       
23431          
23432 //        'font-size'//??
23433 ];
23434
23435 // black listed style attributes.
23436 Roo.HtmlEditorCore.cblack= [
23437       //  'font-size' -- this can be set by the project 
23438 ];
23439
23440
23441 Roo.HtmlEditorCore.swapCodes   =[ 
23442     [    8211, "--" ], 
23443     [    8212, "--" ], 
23444     [    8216,  "'" ],  
23445     [    8217, "'" ],  
23446     [    8220, '"' ],  
23447     [    8221, '"' ],  
23448     [    8226, "*" ],  
23449     [    8230, "..." ]
23450 ]; 
23451
23452     /*
23453  * - LGPL
23454  *
23455  * HtmlEditor
23456  * 
23457  */
23458
23459 /**
23460  * @class Roo.bootstrap.HtmlEditor
23461  * @extends Roo.bootstrap.TextArea
23462  * Bootstrap HtmlEditor class
23463
23464  * @constructor
23465  * Create a new HtmlEditor
23466  * @param {Object} config The config object
23467  */
23468
23469 Roo.bootstrap.HtmlEditor = function(config){
23470     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23471     if (!this.toolbars) {
23472         this.toolbars = [];
23473     }
23474     
23475     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23476     this.addEvents({
23477             /**
23478              * @event initialize
23479              * Fires when the editor is fully initialized (including the iframe)
23480              * @param {HtmlEditor} this
23481              */
23482             initialize: true,
23483             /**
23484              * @event activate
23485              * Fires when the editor is first receives the focus. Any insertion must wait
23486              * until after this event.
23487              * @param {HtmlEditor} this
23488              */
23489             activate: true,
23490              /**
23491              * @event beforesync
23492              * Fires before the textarea is updated with content from the editor iframe. Return false
23493              * to cancel the sync.
23494              * @param {HtmlEditor} this
23495              * @param {String} html
23496              */
23497             beforesync: true,
23498              /**
23499              * @event beforepush
23500              * Fires before the iframe editor is updated with content from the textarea. Return false
23501              * to cancel the push.
23502              * @param {HtmlEditor} this
23503              * @param {String} html
23504              */
23505             beforepush: true,
23506              /**
23507              * @event sync
23508              * Fires when the textarea is updated with content from the editor iframe.
23509              * @param {HtmlEditor} this
23510              * @param {String} html
23511              */
23512             sync: true,
23513              /**
23514              * @event push
23515              * Fires when the iframe editor is updated with content from the textarea.
23516              * @param {HtmlEditor} this
23517              * @param {String} html
23518              */
23519             push: true,
23520              /**
23521              * @event editmodechange
23522              * Fires when the editor switches edit modes
23523              * @param {HtmlEditor} this
23524              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23525              */
23526             editmodechange: true,
23527             /**
23528              * @event editorevent
23529              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23530              * @param {HtmlEditor} this
23531              */
23532             editorevent: true,
23533             /**
23534              * @event firstfocus
23535              * Fires when on first focus - needed by toolbars..
23536              * @param {HtmlEditor} this
23537              */
23538             firstfocus: true,
23539             /**
23540              * @event autosave
23541              * Auto save the htmlEditor value as a file into Events
23542              * @param {HtmlEditor} this
23543              */
23544             autosave: true,
23545             /**
23546              * @event savedpreview
23547              * preview the saved version of htmlEditor
23548              * @param {HtmlEditor} this
23549              */
23550             savedpreview: true
23551         });
23552 };
23553
23554
23555 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23556     
23557     
23558       /**
23559      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23560      */
23561     toolbars : false,
23562     
23563      /**
23564     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23565     */
23566     btns : [],
23567    
23568      /**
23569      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23570      *                        Roo.resizable.
23571      */
23572     resizable : false,
23573      /**
23574      * @cfg {Number} height (in pixels)
23575      */   
23576     height: 300,
23577    /**
23578      * @cfg {Number} width (in pixels)
23579      */   
23580     width: false,
23581     
23582     /**
23583      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23584      * 
23585      */
23586     stylesheets: false,
23587     
23588     // id of frame..
23589     frameId: false,
23590     
23591     // private properties
23592     validationEvent : false,
23593     deferHeight: true,
23594     initialized : false,
23595     activated : false,
23596     
23597     onFocus : Roo.emptyFn,
23598     iframePad:3,
23599     hideMode:'offsets',
23600     
23601     tbContainer : false,
23602     
23603     bodyCls : '',
23604     
23605     toolbarContainer :function() {
23606         return this.wrap.select('.x-html-editor-tb',true).first();
23607     },
23608
23609     /**
23610      * Protected method that will not generally be called directly. It
23611      * is called when the editor creates its toolbar. Override this method if you need to
23612      * add custom toolbar buttons.
23613      * @param {HtmlEditor} editor
23614      */
23615     createToolbar : function(){
23616         Roo.log('renewing');
23617         Roo.log("create toolbars");
23618         
23619         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23620         this.toolbars[0].render(this.toolbarContainer());
23621         
23622         return;
23623         
23624 //        if (!editor.toolbars || !editor.toolbars.length) {
23625 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23626 //        }
23627 //        
23628 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23629 //            editor.toolbars[i] = Roo.factory(
23630 //                    typeof(editor.toolbars[i]) == 'string' ?
23631 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23632 //                Roo.bootstrap.HtmlEditor);
23633 //            editor.toolbars[i].init(editor);
23634 //        }
23635     },
23636
23637      
23638     // private
23639     onRender : function(ct, position)
23640     {
23641        // Roo.log("Call onRender: " + this.xtype);
23642         var _t = this;
23643         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23644       
23645         this.wrap = this.inputEl().wrap({
23646             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23647         });
23648         
23649         this.editorcore.onRender(ct, position);
23650          
23651         if (this.resizable) {
23652             this.resizeEl = new Roo.Resizable(this.wrap, {
23653                 pinned : true,
23654                 wrap: true,
23655                 dynamic : true,
23656                 minHeight : this.height,
23657                 height: this.height,
23658                 handles : this.resizable,
23659                 width: this.width,
23660                 listeners : {
23661                     resize : function(r, w, h) {
23662                         _t.onResize(w,h); // -something
23663                     }
23664                 }
23665             });
23666             
23667         }
23668         this.createToolbar(this);
23669        
23670         
23671         if(!this.width && this.resizable){
23672             this.setSize(this.wrap.getSize());
23673         }
23674         if (this.resizeEl) {
23675             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23676             // should trigger onReize..
23677         }
23678         
23679     },
23680
23681     // private
23682     onResize : function(w, h)
23683     {
23684         Roo.log('resize: ' +w + ',' + h );
23685         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23686         var ew = false;
23687         var eh = false;
23688         
23689         if(this.inputEl() ){
23690             if(typeof w == 'number'){
23691                 var aw = w - this.wrap.getFrameWidth('lr');
23692                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23693                 ew = aw;
23694             }
23695             if(typeof h == 'number'){
23696                  var tbh = -11;  // fixme it needs to tool bar size!
23697                 for (var i =0; i < this.toolbars.length;i++) {
23698                     // fixme - ask toolbars for heights?
23699                     tbh += this.toolbars[i].el.getHeight();
23700                     //if (this.toolbars[i].footer) {
23701                     //    tbh += this.toolbars[i].footer.el.getHeight();
23702                     //}
23703                 }
23704               
23705                 
23706                 
23707                 
23708                 
23709                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23710                 ah -= 5; // knock a few pixes off for look..
23711                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23712                 var eh = ah;
23713             }
23714         }
23715         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23716         this.editorcore.onResize(ew,eh);
23717         
23718     },
23719
23720     /**
23721      * Toggles the editor between standard and source edit mode.
23722      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23723      */
23724     toggleSourceEdit : function(sourceEditMode)
23725     {
23726         this.editorcore.toggleSourceEdit(sourceEditMode);
23727         
23728         if(this.editorcore.sourceEditMode){
23729             Roo.log('editor - showing textarea');
23730             
23731 //            Roo.log('in');
23732 //            Roo.log(this.syncValue());
23733             this.syncValue();
23734             this.inputEl().removeClass(['hide', 'x-hidden']);
23735             this.inputEl().dom.removeAttribute('tabIndex');
23736             this.inputEl().focus();
23737         }else{
23738             Roo.log('editor - hiding textarea');
23739 //            Roo.log('out')
23740 //            Roo.log(this.pushValue()); 
23741             this.pushValue();
23742             
23743             this.inputEl().addClass(['hide', 'x-hidden']);
23744             this.inputEl().dom.setAttribute('tabIndex', -1);
23745             //this.deferFocus();
23746         }
23747          
23748         if(this.resizable){
23749             this.setSize(this.wrap.getSize());
23750         }
23751         
23752         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23753     },
23754  
23755     // private (for BoxComponent)
23756     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23757
23758     // private (for BoxComponent)
23759     getResizeEl : function(){
23760         return this.wrap;
23761     },
23762
23763     // private (for BoxComponent)
23764     getPositionEl : function(){
23765         return this.wrap;
23766     },
23767
23768     // private
23769     initEvents : function(){
23770         this.originalValue = this.getValue();
23771     },
23772
23773 //    /**
23774 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23775 //     * @method
23776 //     */
23777 //    markInvalid : Roo.emptyFn,
23778 //    /**
23779 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23780 //     * @method
23781 //     */
23782 //    clearInvalid : Roo.emptyFn,
23783
23784     setValue : function(v){
23785         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23786         this.editorcore.pushValue();
23787     },
23788
23789      
23790     // private
23791     deferFocus : function(){
23792         this.focus.defer(10, this);
23793     },
23794
23795     // doc'ed in Field
23796     focus : function(){
23797         this.editorcore.focus();
23798         
23799     },
23800       
23801
23802     // private
23803     onDestroy : function(){
23804         
23805         
23806         
23807         if(this.rendered){
23808             
23809             for (var i =0; i < this.toolbars.length;i++) {
23810                 // fixme - ask toolbars for heights?
23811                 this.toolbars[i].onDestroy();
23812             }
23813             
23814             this.wrap.dom.innerHTML = '';
23815             this.wrap.remove();
23816         }
23817     },
23818
23819     // private
23820     onFirstFocus : function(){
23821         //Roo.log("onFirstFocus");
23822         this.editorcore.onFirstFocus();
23823          for (var i =0; i < this.toolbars.length;i++) {
23824             this.toolbars[i].onFirstFocus();
23825         }
23826         
23827     },
23828     
23829     // private
23830     syncValue : function()
23831     {   
23832         this.editorcore.syncValue();
23833     },
23834     
23835     pushValue : function()
23836     {   
23837         this.editorcore.pushValue();
23838     }
23839      
23840     
23841     // hide stuff that is not compatible
23842     /**
23843      * @event blur
23844      * @hide
23845      */
23846     /**
23847      * @event change
23848      * @hide
23849      */
23850     /**
23851      * @event focus
23852      * @hide
23853      */
23854     /**
23855      * @event specialkey
23856      * @hide
23857      */
23858     /**
23859      * @cfg {String} fieldClass @hide
23860      */
23861     /**
23862      * @cfg {String} focusClass @hide
23863      */
23864     /**
23865      * @cfg {String} autoCreate @hide
23866      */
23867     /**
23868      * @cfg {String} inputType @hide
23869      */
23870     /**
23871      * @cfg {String} invalidClass @hide
23872      */
23873     /**
23874      * @cfg {String} invalidText @hide
23875      */
23876     /**
23877      * @cfg {String} msgFx @hide
23878      */
23879     /**
23880      * @cfg {String} validateOnBlur @hide
23881      */
23882 });
23883  
23884     
23885    
23886    
23887    
23888       
23889 Roo.namespace('Roo.bootstrap.htmleditor');
23890 /**
23891  * @class Roo.bootstrap.HtmlEditorToolbar1
23892  * Basic Toolbar
23893  * 
23894  * Usage:
23895  *
23896  new Roo.bootstrap.HtmlEditor({
23897     ....
23898     toolbars : [
23899         new Roo.bootstrap.HtmlEditorToolbar1({
23900             disable : { fonts: 1 , format: 1, ..., ... , ...],
23901             btns : [ .... ]
23902         })
23903     }
23904      
23905  * 
23906  * @cfg {Object} disable List of elements to disable..
23907  * @cfg {Array} btns List of additional buttons.
23908  * 
23909  * 
23910  * NEEDS Extra CSS? 
23911  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23912  */
23913  
23914 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23915 {
23916     
23917     Roo.apply(this, config);
23918     
23919     // default disabled, based on 'good practice'..
23920     this.disable = this.disable || {};
23921     Roo.applyIf(this.disable, {
23922         fontSize : true,
23923         colors : true,
23924         specialElements : true
23925     });
23926     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23927     
23928     this.editor = config.editor;
23929     this.editorcore = config.editor.editorcore;
23930     
23931     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23932     
23933     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23934     // dont call parent... till later.
23935 }
23936 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23937      
23938     bar : true,
23939     
23940     editor : false,
23941     editorcore : false,
23942     
23943     
23944     formats : [
23945         "p" ,  
23946         "h1","h2","h3","h4","h5","h6", 
23947         "pre", "code", 
23948         "abbr", "acronym", "address", "cite", "samp", "var",
23949         'div','span'
23950     ],
23951     
23952     onRender : function(ct, position)
23953     {
23954        // Roo.log("Call onRender: " + this.xtype);
23955         
23956        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23957        Roo.log(this.el);
23958        this.el.dom.style.marginBottom = '0';
23959        var _this = this;
23960        var editorcore = this.editorcore;
23961        var editor= this.editor;
23962        
23963        var children = [];
23964        var btn = function(id,cmd , toggle, handler, html){
23965        
23966             var  event = toggle ? 'toggle' : 'click';
23967        
23968             var a = {
23969                 size : 'sm',
23970                 xtype: 'Button',
23971                 xns: Roo.bootstrap,
23972                 glyphicon : id,
23973                 cmd : id || cmd,
23974                 enableToggle:toggle !== false,
23975                 html : html || '',
23976                 pressed : toggle ? false : null,
23977                 listeners : {}
23978             };
23979             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23980                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23981             };
23982             children.push(a);
23983             return a;
23984        }
23985        
23986     //    var cb_box = function...
23987         
23988         var style = {
23989                 xtype: 'Button',
23990                 size : 'sm',
23991                 xns: Roo.bootstrap,
23992                 glyphicon : 'font',
23993                 //html : 'submit'
23994                 menu : {
23995                     xtype: 'Menu',
23996                     xns: Roo.bootstrap,
23997                     items:  []
23998                 }
23999         };
24000         Roo.each(this.formats, function(f) {
24001             style.menu.items.push({
24002                 xtype :'MenuItem',
24003                 xns: Roo.bootstrap,
24004                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24005                 tagname : f,
24006                 listeners : {
24007                     click : function()
24008                     {
24009                         editorcore.insertTag(this.tagname);
24010                         editor.focus();
24011                     }
24012                 }
24013                 
24014             });
24015         });
24016         children.push(style);   
24017         
24018         btn('bold',false,true);
24019         btn('italic',false,true);
24020         btn('align-left', 'justifyleft',true);
24021         btn('align-center', 'justifycenter',true);
24022         btn('align-right' , 'justifyright',true);
24023         btn('link', false, false, function(btn) {
24024             //Roo.log("create link?");
24025             var url = prompt(this.createLinkText, this.defaultLinkValue);
24026             if(url && url != 'http:/'+'/'){
24027                 this.editorcore.relayCmd('createlink', url);
24028             }
24029         }),
24030         btn('list','insertunorderedlist',true);
24031         btn('pencil', false,true, function(btn){
24032                 Roo.log(this);
24033                 this.toggleSourceEdit(btn.pressed);
24034         });
24035         
24036         if (this.editor.btns.length > 0) {
24037             for (var i = 0; i<this.editor.btns.length; i++) {
24038                 children.push(this.editor.btns[i]);
24039             }
24040         }
24041         
24042         /*
24043         var cog = {
24044                 xtype: 'Button',
24045                 size : 'sm',
24046                 xns: Roo.bootstrap,
24047                 glyphicon : 'cog',
24048                 //html : 'submit'
24049                 menu : {
24050                     xtype: 'Menu',
24051                     xns: Roo.bootstrap,
24052                     items:  []
24053                 }
24054         };
24055         
24056         cog.menu.items.push({
24057             xtype :'MenuItem',
24058             xns: Roo.bootstrap,
24059             html : Clean styles,
24060             tagname : f,
24061             listeners : {
24062                 click : function()
24063                 {
24064                     editorcore.insertTag(this.tagname);
24065                     editor.focus();
24066                 }
24067             }
24068             
24069         });
24070        */
24071         
24072          
24073        this.xtype = 'NavSimplebar';
24074         
24075         for(var i=0;i< children.length;i++) {
24076             
24077             this.buttons.add(this.addxtypeChild(children[i]));
24078             
24079         }
24080         
24081         editor.on('editorevent', this.updateToolbar, this);
24082     },
24083     onBtnClick : function(id)
24084     {
24085        this.editorcore.relayCmd(id);
24086        this.editorcore.focus();
24087     },
24088     
24089     /**
24090      * Protected method that will not generally be called directly. It triggers
24091      * a toolbar update by reading the markup state of the current selection in the editor.
24092      */
24093     updateToolbar: function(){
24094
24095         if(!this.editorcore.activated){
24096             this.editor.onFirstFocus(); // is this neeed?
24097             return;
24098         }
24099
24100         var btns = this.buttons; 
24101         var doc = this.editorcore.doc;
24102         btns.get('bold').setActive(doc.queryCommandState('bold'));
24103         btns.get('italic').setActive(doc.queryCommandState('italic'));
24104         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24105         
24106         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24107         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24108         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24109         
24110         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24111         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24112          /*
24113         
24114         var ans = this.editorcore.getAllAncestors();
24115         if (this.formatCombo) {
24116             
24117             
24118             var store = this.formatCombo.store;
24119             this.formatCombo.setValue("");
24120             for (var i =0; i < ans.length;i++) {
24121                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24122                     // select it..
24123                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24124                     break;
24125                 }
24126             }
24127         }
24128         
24129         
24130         
24131         // hides menus... - so this cant be on a menu...
24132         Roo.bootstrap.MenuMgr.hideAll();
24133         */
24134         Roo.bootstrap.MenuMgr.hideAll();
24135         //this.editorsyncValue();
24136     },
24137     onFirstFocus: function() {
24138         this.buttons.each(function(item){
24139            item.enable();
24140         });
24141     },
24142     toggleSourceEdit : function(sourceEditMode){
24143         
24144           
24145         if(sourceEditMode){
24146             Roo.log("disabling buttons");
24147            this.buttons.each( function(item){
24148                 if(item.cmd != 'pencil'){
24149                     item.disable();
24150                 }
24151             });
24152           
24153         }else{
24154             Roo.log("enabling buttons");
24155             if(this.editorcore.initialized){
24156                 this.buttons.each( function(item){
24157                     item.enable();
24158                 });
24159             }
24160             
24161         }
24162         Roo.log("calling toggole on editor");
24163         // tell the editor that it's been pressed..
24164         this.editor.toggleSourceEdit(sourceEditMode);
24165        
24166     }
24167 });
24168
24169
24170
24171
24172
24173 /**
24174  * @class Roo.bootstrap.Table.AbstractSelectionModel
24175  * @extends Roo.util.Observable
24176  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24177  * implemented by descendant classes.  This class should not be directly instantiated.
24178  * @constructor
24179  */
24180 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24181     this.locked = false;
24182     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24183 };
24184
24185
24186 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24187     /** @ignore Called by the grid automatically. Do not call directly. */
24188     init : function(grid){
24189         this.grid = grid;
24190         this.initEvents();
24191     },
24192
24193     /**
24194      * Locks the selections.
24195      */
24196     lock : function(){
24197         this.locked = true;
24198     },
24199
24200     /**
24201      * Unlocks the selections.
24202      */
24203     unlock : function(){
24204         this.locked = false;
24205     },
24206
24207     /**
24208      * Returns true if the selections are locked.
24209      * @return {Boolean}
24210      */
24211     isLocked : function(){
24212         return this.locked;
24213     }
24214 });
24215 /**
24216  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24217  * @class Roo.bootstrap.Table.RowSelectionModel
24218  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24219  * It supports multiple selections and keyboard selection/navigation. 
24220  * @constructor
24221  * @param {Object} config
24222  */
24223
24224 Roo.bootstrap.Table.RowSelectionModel = function(config){
24225     Roo.apply(this, config);
24226     this.selections = new Roo.util.MixedCollection(false, function(o){
24227         return o.id;
24228     });
24229
24230     this.last = false;
24231     this.lastActive = false;
24232
24233     this.addEvents({
24234         /**
24235              * @event selectionchange
24236              * Fires when the selection changes
24237              * @param {SelectionModel} this
24238              */
24239             "selectionchange" : true,
24240         /**
24241              * @event afterselectionchange
24242              * Fires after the selection changes (eg. by key press or clicking)
24243              * @param {SelectionModel} this
24244              */
24245             "afterselectionchange" : true,
24246         /**
24247              * @event beforerowselect
24248              * Fires when a row is selected being selected, return false to cancel.
24249              * @param {SelectionModel} this
24250              * @param {Number} rowIndex The selected index
24251              * @param {Boolean} keepExisting False if other selections will be cleared
24252              */
24253             "beforerowselect" : true,
24254         /**
24255              * @event rowselect
24256              * Fires when a row is selected.
24257              * @param {SelectionModel} this
24258              * @param {Number} rowIndex The selected index
24259              * @param {Roo.data.Record} r The record
24260              */
24261             "rowselect" : true,
24262         /**
24263              * @event rowdeselect
24264              * Fires when a row is deselected.
24265              * @param {SelectionModel} this
24266              * @param {Number} rowIndex The selected index
24267              */
24268         "rowdeselect" : true
24269     });
24270     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24271     this.locked = false;
24272  };
24273
24274 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24275     /**
24276      * @cfg {Boolean} singleSelect
24277      * True to allow selection of only one row at a time (defaults to false)
24278      */
24279     singleSelect : false,
24280
24281     // private
24282     initEvents : function()
24283     {
24284
24285         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24286         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24287         //}else{ // allow click to work like normal
24288          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24289         //}
24290         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24291         this.grid.on("rowclick", this.handleMouseDown, this);
24292         
24293         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24294             "up" : function(e){
24295                 if(!e.shiftKey){
24296                     this.selectPrevious(e.shiftKey);
24297                 }else if(this.last !== false && this.lastActive !== false){
24298                     var last = this.last;
24299                     this.selectRange(this.last,  this.lastActive-1);
24300                     this.grid.getView().focusRow(this.lastActive);
24301                     if(last !== false){
24302                         this.last = last;
24303                     }
24304                 }else{
24305                     this.selectFirstRow();
24306                 }
24307                 this.fireEvent("afterselectionchange", this);
24308             },
24309             "down" : function(e){
24310                 if(!e.shiftKey){
24311                     this.selectNext(e.shiftKey);
24312                 }else if(this.last !== false && this.lastActive !== false){
24313                     var last = this.last;
24314                     this.selectRange(this.last,  this.lastActive+1);
24315                     this.grid.getView().focusRow(this.lastActive);
24316                     if(last !== false){
24317                         this.last = last;
24318                     }
24319                 }else{
24320                     this.selectFirstRow();
24321                 }
24322                 this.fireEvent("afterselectionchange", this);
24323             },
24324             scope: this
24325         });
24326         this.grid.store.on('load', function(){
24327             this.selections.clear();
24328         },this);
24329         /*
24330         var view = this.grid.view;
24331         view.on("refresh", this.onRefresh, this);
24332         view.on("rowupdated", this.onRowUpdated, this);
24333         view.on("rowremoved", this.onRemove, this);
24334         */
24335     },
24336
24337     // private
24338     onRefresh : function()
24339     {
24340         var ds = this.grid.store, i, v = this.grid.view;
24341         var s = this.selections;
24342         s.each(function(r){
24343             if((i = ds.indexOfId(r.id)) != -1){
24344                 v.onRowSelect(i);
24345             }else{
24346                 s.remove(r);
24347             }
24348         });
24349     },
24350
24351     // private
24352     onRemove : function(v, index, r){
24353         this.selections.remove(r);
24354     },
24355
24356     // private
24357     onRowUpdated : function(v, index, r){
24358         if(this.isSelected(r)){
24359             v.onRowSelect(index);
24360         }
24361     },
24362
24363     /**
24364      * Select records.
24365      * @param {Array} records The records to select
24366      * @param {Boolean} keepExisting (optional) True to keep existing selections
24367      */
24368     selectRecords : function(records, keepExisting)
24369     {
24370         if(!keepExisting){
24371             this.clearSelections();
24372         }
24373             var ds = this.grid.store;
24374         for(var i = 0, len = records.length; i < len; i++){
24375             this.selectRow(ds.indexOf(records[i]), true);
24376         }
24377     },
24378
24379     /**
24380      * Gets the number of selected rows.
24381      * @return {Number}
24382      */
24383     getCount : function(){
24384         return this.selections.length;
24385     },
24386
24387     /**
24388      * Selects the first row in the grid.
24389      */
24390     selectFirstRow : function(){
24391         this.selectRow(0);
24392     },
24393
24394     /**
24395      * Select the last row.
24396      * @param {Boolean} keepExisting (optional) True to keep existing selections
24397      */
24398     selectLastRow : function(keepExisting){
24399         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24400         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24401     },
24402
24403     /**
24404      * Selects the row immediately following the last selected row.
24405      * @param {Boolean} keepExisting (optional) True to keep existing selections
24406      */
24407     selectNext : function(keepExisting)
24408     {
24409             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24410             this.selectRow(this.last+1, keepExisting);
24411             this.grid.getView().focusRow(this.last);
24412         }
24413     },
24414
24415     /**
24416      * Selects the row that precedes the last selected row.
24417      * @param {Boolean} keepExisting (optional) True to keep existing selections
24418      */
24419     selectPrevious : function(keepExisting){
24420         if(this.last){
24421             this.selectRow(this.last-1, keepExisting);
24422             this.grid.getView().focusRow(this.last);
24423         }
24424     },
24425
24426     /**
24427      * Returns the selected records
24428      * @return {Array} Array of selected records
24429      */
24430     getSelections : function(){
24431         return [].concat(this.selections.items);
24432     },
24433
24434     /**
24435      * Returns the first selected record.
24436      * @return {Record}
24437      */
24438     getSelected : function(){
24439         return this.selections.itemAt(0);
24440     },
24441
24442
24443     /**
24444      * Clears all selections.
24445      */
24446     clearSelections : function(fast)
24447     {
24448         if(this.locked) {
24449             return;
24450         }
24451         if(fast !== true){
24452                 var ds = this.grid.store;
24453             var s = this.selections;
24454             s.each(function(r){
24455                 this.deselectRow(ds.indexOfId(r.id));
24456             }, this);
24457             s.clear();
24458         }else{
24459             this.selections.clear();
24460         }
24461         this.last = false;
24462     },
24463
24464
24465     /**
24466      * Selects all rows.
24467      */
24468     selectAll : function(){
24469         if(this.locked) {
24470             return;
24471         }
24472         this.selections.clear();
24473         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24474             this.selectRow(i, true);
24475         }
24476     },
24477
24478     /**
24479      * Returns True if there is a selection.
24480      * @return {Boolean}
24481      */
24482     hasSelection : function(){
24483         return this.selections.length > 0;
24484     },
24485
24486     /**
24487      * Returns True if the specified row is selected.
24488      * @param {Number/Record} record The record or index of the record to check
24489      * @return {Boolean}
24490      */
24491     isSelected : function(index){
24492             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24493         return (r && this.selections.key(r.id) ? true : false);
24494     },
24495
24496     /**
24497      * Returns True if the specified record id is selected.
24498      * @param {String} id The id of record to check
24499      * @return {Boolean}
24500      */
24501     isIdSelected : function(id){
24502         return (this.selections.key(id) ? true : false);
24503     },
24504
24505
24506     // private
24507     handleMouseDBClick : function(e, t){
24508         
24509     },
24510     // private
24511     handleMouseDown : function(e, t)
24512     {
24513             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24514         if(this.isLocked() || rowIndex < 0 ){
24515             return;
24516         };
24517         if(e.shiftKey && this.last !== false){
24518             var last = this.last;
24519             this.selectRange(last, rowIndex, e.ctrlKey);
24520             this.last = last; // reset the last
24521             t.focus();
24522     
24523         }else{
24524             var isSelected = this.isSelected(rowIndex);
24525             //Roo.log("select row:" + rowIndex);
24526             if(isSelected){
24527                 this.deselectRow(rowIndex);
24528             } else {
24529                         this.selectRow(rowIndex, true);
24530             }
24531     
24532             /*
24533                 if(e.button !== 0 && isSelected){
24534                 alert('rowIndex 2: ' + rowIndex);
24535                     view.focusRow(rowIndex);
24536                 }else if(e.ctrlKey && isSelected){
24537                     this.deselectRow(rowIndex);
24538                 }else if(!isSelected){
24539                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24540                     view.focusRow(rowIndex);
24541                 }
24542             */
24543         }
24544         this.fireEvent("afterselectionchange", this);
24545     },
24546     // private
24547     handleDragableRowClick :  function(grid, rowIndex, e) 
24548     {
24549         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24550             this.selectRow(rowIndex, false);
24551             grid.view.focusRow(rowIndex);
24552              this.fireEvent("afterselectionchange", this);
24553         }
24554     },
24555     
24556     /**
24557      * Selects multiple rows.
24558      * @param {Array} rows Array of the indexes of the row to select
24559      * @param {Boolean} keepExisting (optional) True to keep existing selections
24560      */
24561     selectRows : function(rows, keepExisting){
24562         if(!keepExisting){
24563             this.clearSelections();
24564         }
24565         for(var i = 0, len = rows.length; i < len; i++){
24566             this.selectRow(rows[i], true);
24567         }
24568     },
24569
24570     /**
24571      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24572      * @param {Number} startRow The index of the first row in the range
24573      * @param {Number} endRow The index of the last row in the range
24574      * @param {Boolean} keepExisting (optional) True to retain existing selections
24575      */
24576     selectRange : function(startRow, endRow, keepExisting){
24577         if(this.locked) {
24578             return;
24579         }
24580         if(!keepExisting){
24581             this.clearSelections();
24582         }
24583         if(startRow <= endRow){
24584             for(var i = startRow; i <= endRow; i++){
24585                 this.selectRow(i, true);
24586             }
24587         }else{
24588             for(var i = startRow; i >= endRow; i--){
24589                 this.selectRow(i, true);
24590             }
24591         }
24592     },
24593
24594     /**
24595      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24596      * @param {Number} startRow The index of the first row in the range
24597      * @param {Number} endRow The index of the last row in the range
24598      */
24599     deselectRange : function(startRow, endRow, preventViewNotify){
24600         if(this.locked) {
24601             return;
24602         }
24603         for(var i = startRow; i <= endRow; i++){
24604             this.deselectRow(i, preventViewNotify);
24605         }
24606     },
24607
24608     /**
24609      * Selects a row.
24610      * @param {Number} row The index of the row to select
24611      * @param {Boolean} keepExisting (optional) True to keep existing selections
24612      */
24613     selectRow : function(index, keepExisting, preventViewNotify)
24614     {
24615             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24616             return;
24617         }
24618         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24619             if(!keepExisting || this.singleSelect){
24620                 this.clearSelections();
24621             }
24622             
24623             var r = this.grid.store.getAt(index);
24624             //console.log('selectRow - record id :' + r.id);
24625             
24626             this.selections.add(r);
24627             this.last = this.lastActive = index;
24628             if(!preventViewNotify){
24629                 var proxy = new Roo.Element(
24630                                 this.grid.getRowDom(index)
24631                 );
24632                 proxy.addClass('bg-info info');
24633             }
24634             this.fireEvent("rowselect", this, index, r);
24635             this.fireEvent("selectionchange", this);
24636         }
24637     },
24638
24639     /**
24640      * Deselects a row.
24641      * @param {Number} row The index of the row to deselect
24642      */
24643     deselectRow : function(index, preventViewNotify)
24644     {
24645         if(this.locked) {
24646             return;
24647         }
24648         if(this.last == index){
24649             this.last = false;
24650         }
24651         if(this.lastActive == index){
24652             this.lastActive = false;
24653         }
24654         
24655         var r = this.grid.store.getAt(index);
24656         if (!r) {
24657             return;
24658         }
24659         
24660         this.selections.remove(r);
24661         //.console.log('deselectRow - record id :' + r.id);
24662         if(!preventViewNotify){
24663         
24664             var proxy = new Roo.Element(
24665                 this.grid.getRowDom(index)
24666             );
24667             proxy.removeClass('bg-info info');
24668         }
24669         this.fireEvent("rowdeselect", this, index);
24670         this.fireEvent("selectionchange", this);
24671     },
24672
24673     // private
24674     restoreLast : function(){
24675         if(this._last){
24676             this.last = this._last;
24677         }
24678     },
24679
24680     // private
24681     acceptsNav : function(row, col, cm){
24682         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24683     },
24684
24685     // private
24686     onEditorKey : function(field, e){
24687         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24688         if(k == e.TAB){
24689             e.stopEvent();
24690             ed.completeEdit();
24691             if(e.shiftKey){
24692                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24693             }else{
24694                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24695             }
24696         }else if(k == e.ENTER && !e.ctrlKey){
24697             e.stopEvent();
24698             ed.completeEdit();
24699             if(e.shiftKey){
24700                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24701             }else{
24702                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24703             }
24704         }else if(k == e.ESC){
24705             ed.cancelEdit();
24706         }
24707         if(newCell){
24708             g.startEditing(newCell[0], newCell[1]);
24709         }
24710     }
24711 });
24712 /*
24713  * Based on:
24714  * Ext JS Library 1.1.1
24715  * Copyright(c) 2006-2007, Ext JS, LLC.
24716  *
24717  * Originally Released Under LGPL - original licence link has changed is not relivant.
24718  *
24719  * Fork - LGPL
24720  * <script type="text/javascript">
24721  */
24722  
24723 /**
24724  * @class Roo.bootstrap.PagingToolbar
24725  * @extends Roo.bootstrap.NavSimplebar
24726  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24727  * @constructor
24728  * Create a new PagingToolbar
24729  * @param {Object} config The config object
24730  * @param {Roo.data.Store} store
24731  */
24732 Roo.bootstrap.PagingToolbar = function(config)
24733 {
24734     // old args format still supported... - xtype is prefered..
24735         // created from xtype...
24736     
24737     this.ds = config.dataSource;
24738     
24739     if (config.store && !this.ds) {
24740         this.store= Roo.factory(config.store, Roo.data);
24741         this.ds = this.store;
24742         this.ds.xmodule = this.xmodule || false;
24743     }
24744     
24745     this.toolbarItems = [];
24746     if (config.items) {
24747         this.toolbarItems = config.items;
24748     }
24749     
24750     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24751     
24752     this.cursor = 0;
24753     
24754     if (this.ds) { 
24755         this.bind(this.ds);
24756     }
24757     
24758     if (Roo.bootstrap.version == 4) {
24759         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24760     } else {
24761         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24762     }
24763     
24764 };
24765
24766 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24767     /**
24768      * @cfg {Roo.data.Store} dataSource
24769      * The underlying data store providing the paged data
24770      */
24771     /**
24772      * @cfg {String/HTMLElement/Element} container
24773      * container The id or element that will contain the toolbar
24774      */
24775     /**
24776      * @cfg {Boolean} displayInfo
24777      * True to display the displayMsg (defaults to false)
24778      */
24779     /**
24780      * @cfg {Number} pageSize
24781      * The number of records to display per page (defaults to 20)
24782      */
24783     pageSize: 20,
24784     /**
24785      * @cfg {String} displayMsg
24786      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24787      */
24788     displayMsg : 'Displaying {0} - {1} of {2}',
24789     /**
24790      * @cfg {String} emptyMsg
24791      * The message to display when no records are found (defaults to "No data to display")
24792      */
24793     emptyMsg : 'No data to display',
24794     /**
24795      * Customizable piece of the default paging text (defaults to "Page")
24796      * @type String
24797      */
24798     beforePageText : "Page",
24799     /**
24800      * Customizable piece of the default paging text (defaults to "of %0")
24801      * @type String
24802      */
24803     afterPageText : "of {0}",
24804     /**
24805      * Customizable piece of the default paging text (defaults to "First Page")
24806      * @type String
24807      */
24808     firstText : "First Page",
24809     /**
24810      * Customizable piece of the default paging text (defaults to "Previous Page")
24811      * @type String
24812      */
24813     prevText : "Previous Page",
24814     /**
24815      * Customizable piece of the default paging text (defaults to "Next Page")
24816      * @type String
24817      */
24818     nextText : "Next Page",
24819     /**
24820      * Customizable piece of the default paging text (defaults to "Last Page")
24821      * @type String
24822      */
24823     lastText : "Last Page",
24824     /**
24825      * Customizable piece of the default paging text (defaults to "Refresh")
24826      * @type String
24827      */
24828     refreshText : "Refresh",
24829
24830     buttons : false,
24831     // private
24832     onRender : function(ct, position) 
24833     {
24834         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24835         this.navgroup.parentId = this.id;
24836         this.navgroup.onRender(this.el, null);
24837         // add the buttons to the navgroup
24838         
24839         if(this.displayInfo){
24840             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24841             this.displayEl = this.el.select('.x-paging-info', true).first();
24842 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24843 //            this.displayEl = navel.el.select('span',true).first();
24844         }
24845         
24846         var _this = this;
24847         
24848         if(this.buttons){
24849             Roo.each(_this.buttons, function(e){ // this might need to use render????
24850                Roo.factory(e).render(_this.el);
24851             });
24852         }
24853             
24854         Roo.each(_this.toolbarItems, function(e) {
24855             _this.navgroup.addItem(e);
24856         });
24857         
24858         
24859         this.first = this.navgroup.addItem({
24860             tooltip: this.firstText,
24861             cls: "prev btn-outline-secondary",
24862             html : ' <i class="fa fa-step-backward"></i>',
24863             disabled: true,
24864             preventDefault: true,
24865             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24866         });
24867         
24868         this.prev =  this.navgroup.addItem({
24869             tooltip: this.prevText,
24870             cls: "prev btn-outline-secondary",
24871             html : ' <i class="fa fa-backward"></i>',
24872             disabled: true,
24873             preventDefault: true,
24874             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24875         });
24876     //this.addSeparator();
24877         
24878         
24879         var field = this.navgroup.addItem( {
24880             tagtype : 'span',
24881             cls : 'x-paging-position  btn-outline-secondary',
24882              disabled: true,
24883             html : this.beforePageText  +
24884                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24885                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24886          } ); //?? escaped?
24887         
24888         this.field = field.el.select('input', true).first();
24889         this.field.on("keydown", this.onPagingKeydown, this);
24890         this.field.on("focus", function(){this.dom.select();});
24891     
24892     
24893         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24894         //this.field.setHeight(18);
24895         //this.addSeparator();
24896         this.next = this.navgroup.addItem({
24897             tooltip: this.nextText,
24898             cls: "next btn-outline-secondary",
24899             html : ' <i class="fa fa-forward"></i>',
24900             disabled: true,
24901             preventDefault: true,
24902             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24903         });
24904         this.last = this.navgroup.addItem({
24905             tooltip: this.lastText,
24906             html : ' <i class="fa fa-step-forward"></i>',
24907             cls: "next btn-outline-secondary",
24908             disabled: true,
24909             preventDefault: true,
24910             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24911         });
24912     //this.addSeparator();
24913         this.loading = this.navgroup.addItem({
24914             tooltip: this.refreshText,
24915             cls: "btn-outline-secondary",
24916             html : ' <i class="fa fa-refresh"></i>',
24917             preventDefault: true,
24918             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24919         });
24920         
24921     },
24922
24923     // private
24924     updateInfo : function(){
24925         if(this.displayEl){
24926             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24927             var msg = count == 0 ?
24928                 this.emptyMsg :
24929                 String.format(
24930                     this.displayMsg,
24931                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24932                 );
24933             this.displayEl.update(msg);
24934         }
24935     },
24936
24937     // private
24938     onLoad : function(ds, r, o)
24939     {
24940         this.cursor = o.params.start ? o.params.start : 0;
24941         
24942         var d = this.getPageData(),
24943             ap = d.activePage,
24944             ps = d.pages;
24945         
24946         
24947         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24948         this.field.dom.value = ap;
24949         this.first.setDisabled(ap == 1);
24950         this.prev.setDisabled(ap == 1);
24951         this.next.setDisabled(ap == ps);
24952         this.last.setDisabled(ap == ps);
24953         this.loading.enable();
24954         this.updateInfo();
24955     },
24956
24957     // private
24958     getPageData : function(){
24959         var total = this.ds.getTotalCount();
24960         return {
24961             total : total,
24962             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24963             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24964         };
24965     },
24966
24967     // private
24968     onLoadError : function(){
24969         this.loading.enable();
24970     },
24971
24972     // private
24973     onPagingKeydown : function(e){
24974         var k = e.getKey();
24975         var d = this.getPageData();
24976         if(k == e.RETURN){
24977             var v = this.field.dom.value, pageNum;
24978             if(!v || isNaN(pageNum = parseInt(v, 10))){
24979                 this.field.dom.value = d.activePage;
24980                 return;
24981             }
24982             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24983             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24984             e.stopEvent();
24985         }
24986         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))
24987         {
24988           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24989           this.field.dom.value = pageNum;
24990           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24991           e.stopEvent();
24992         }
24993         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24994         {
24995           var v = this.field.dom.value, pageNum; 
24996           var increment = (e.shiftKey) ? 10 : 1;
24997           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24998                 increment *= -1;
24999           }
25000           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25001             this.field.dom.value = d.activePage;
25002             return;
25003           }
25004           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25005           {
25006             this.field.dom.value = parseInt(v, 10) + increment;
25007             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25008             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25009           }
25010           e.stopEvent();
25011         }
25012     },
25013
25014     // private
25015     beforeLoad : function(){
25016         if(this.loading){
25017             this.loading.disable();
25018         }
25019     },
25020
25021     // private
25022     onClick : function(which){
25023         
25024         var ds = this.ds;
25025         if (!ds) {
25026             return;
25027         }
25028         
25029         switch(which){
25030             case "first":
25031                 ds.load({params:{start: 0, limit: this.pageSize}});
25032             break;
25033             case "prev":
25034                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25035             break;
25036             case "next":
25037                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25038             break;
25039             case "last":
25040                 var total = ds.getTotalCount();
25041                 var extra = total % this.pageSize;
25042                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25043                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25044             break;
25045             case "refresh":
25046                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25047             break;
25048         }
25049     },
25050
25051     /**
25052      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25053      * @param {Roo.data.Store} store The data store to unbind
25054      */
25055     unbind : function(ds){
25056         ds.un("beforeload", this.beforeLoad, this);
25057         ds.un("load", this.onLoad, this);
25058         ds.un("loadexception", this.onLoadError, this);
25059         ds.un("remove", this.updateInfo, this);
25060         ds.un("add", this.updateInfo, this);
25061         this.ds = undefined;
25062     },
25063
25064     /**
25065      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25066      * @param {Roo.data.Store} store The data store to bind
25067      */
25068     bind : function(ds){
25069         ds.on("beforeload", this.beforeLoad, this);
25070         ds.on("load", this.onLoad, this);
25071         ds.on("loadexception", this.onLoadError, this);
25072         ds.on("remove", this.updateInfo, this);
25073         ds.on("add", this.updateInfo, this);
25074         this.ds = ds;
25075     }
25076 });/*
25077  * - LGPL
25078  *
25079  * element
25080  * 
25081  */
25082
25083 /**
25084  * @class Roo.bootstrap.MessageBar
25085  * @extends Roo.bootstrap.Component
25086  * Bootstrap MessageBar class
25087  * @cfg {String} html contents of the MessageBar
25088  * @cfg {String} weight (info | success | warning | danger) default info
25089  * @cfg {String} beforeClass insert the bar before the given class
25090  * @cfg {Boolean} closable (true | false) default false
25091  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25092  * 
25093  * @constructor
25094  * Create a new Element
25095  * @param {Object} config The config object
25096  */
25097
25098 Roo.bootstrap.MessageBar = function(config){
25099     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25100 };
25101
25102 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25103     
25104     html: '',
25105     weight: 'info',
25106     closable: false,
25107     fixed: false,
25108     beforeClass: 'bootstrap-sticky-wrap',
25109     
25110     getAutoCreate : function(){
25111         
25112         var cfg = {
25113             tag: 'div',
25114             cls: 'alert alert-dismissable alert-' + this.weight,
25115             cn: [
25116                 {
25117                     tag: 'span',
25118                     cls: 'message',
25119                     html: this.html || ''
25120                 }
25121             ]
25122         };
25123         
25124         if(this.fixed){
25125             cfg.cls += ' alert-messages-fixed';
25126         }
25127         
25128         if(this.closable){
25129             cfg.cn.push({
25130                 tag: 'button',
25131                 cls: 'close',
25132                 html: 'x'
25133             });
25134         }
25135         
25136         return cfg;
25137     },
25138     
25139     onRender : function(ct, position)
25140     {
25141         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25142         
25143         if(!this.el){
25144             var cfg = Roo.apply({},  this.getAutoCreate());
25145             cfg.id = Roo.id();
25146             
25147             if (this.cls) {
25148                 cfg.cls += ' ' + this.cls;
25149             }
25150             if (this.style) {
25151                 cfg.style = this.style;
25152             }
25153             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25154             
25155             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25156         }
25157         
25158         this.el.select('>button.close').on('click', this.hide, this);
25159         
25160     },
25161     
25162     show : function()
25163     {
25164         if (!this.rendered) {
25165             this.render();
25166         }
25167         
25168         this.el.show();
25169         
25170         this.fireEvent('show', this);
25171         
25172     },
25173     
25174     hide : function()
25175     {
25176         if (!this.rendered) {
25177             this.render();
25178         }
25179         
25180         this.el.hide();
25181         
25182         this.fireEvent('hide', this);
25183     },
25184     
25185     update : function()
25186     {
25187 //        var e = this.el.dom.firstChild;
25188 //        
25189 //        if(this.closable){
25190 //            e = e.nextSibling;
25191 //        }
25192 //        
25193 //        e.data = this.html || '';
25194
25195         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25196     }
25197    
25198 });
25199
25200  
25201
25202      /*
25203  * - LGPL
25204  *
25205  * Graph
25206  * 
25207  */
25208
25209
25210 /**
25211  * @class Roo.bootstrap.Graph
25212  * @extends Roo.bootstrap.Component
25213  * Bootstrap Graph class
25214 > Prameters
25215  -sm {number} sm 4
25216  -md {number} md 5
25217  @cfg {String} graphtype  bar | vbar | pie
25218  @cfg {number} g_x coodinator | centre x (pie)
25219  @cfg {number} g_y coodinator | centre y (pie)
25220  @cfg {number} g_r radius (pie)
25221  @cfg {number} g_height height of the chart (respected by all elements in the set)
25222  @cfg {number} g_width width of the chart (respected by all elements in the set)
25223  @cfg {Object} title The title of the chart
25224     
25225  -{Array}  values
25226  -opts (object) options for the chart 
25227      o {
25228      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25229      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25230      o vgutter (number)
25231      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.
25232      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25233      o to
25234      o stretch (boolean)
25235      o }
25236  -opts (object) options for the pie
25237      o{
25238      o cut
25239      o startAngle (number)
25240      o endAngle (number)
25241      } 
25242  *
25243  * @constructor
25244  * Create a new Input
25245  * @param {Object} config The config object
25246  */
25247
25248 Roo.bootstrap.Graph = function(config){
25249     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25250     
25251     this.addEvents({
25252         // img events
25253         /**
25254          * @event click
25255          * The img click event for the img.
25256          * @param {Roo.EventObject} e
25257          */
25258         "click" : true
25259     });
25260 };
25261
25262 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25263     
25264     sm: 4,
25265     md: 5,
25266     graphtype: 'bar',
25267     g_height: 250,
25268     g_width: 400,
25269     g_x: 50,
25270     g_y: 50,
25271     g_r: 30,
25272     opts:{
25273         //g_colors: this.colors,
25274         g_type: 'soft',
25275         g_gutter: '20%'
25276
25277     },
25278     title : false,
25279
25280     getAutoCreate : function(){
25281         
25282         var cfg = {
25283             tag: 'div',
25284             html : null
25285         };
25286         
25287         
25288         return  cfg;
25289     },
25290
25291     onRender : function(ct,position){
25292         
25293         
25294         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25295         
25296         if (typeof(Raphael) == 'undefined') {
25297             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25298             return;
25299         }
25300         
25301         this.raphael = Raphael(this.el.dom);
25302         
25303                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25304                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25305                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25306                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25307                 /*
25308                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25309                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25310                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25311                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25312                 
25313                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25314                 r.barchart(330, 10, 300, 220, data1);
25315                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25316                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25317                 */
25318                 
25319                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25320                 // r.barchart(30, 30, 560, 250,  xdata, {
25321                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25322                 //     axis : "0 0 1 1",
25323                 //     axisxlabels :  xdata
25324                 //     //yvalues : cols,
25325                    
25326                 // });
25327 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25328 //        
25329 //        this.load(null,xdata,{
25330 //                axis : "0 0 1 1",
25331 //                axisxlabels :  xdata
25332 //                });
25333
25334     },
25335
25336     load : function(graphtype,xdata,opts)
25337     {
25338         this.raphael.clear();
25339         if(!graphtype) {
25340             graphtype = this.graphtype;
25341         }
25342         if(!opts){
25343             opts = this.opts;
25344         }
25345         var r = this.raphael,
25346             fin = function () {
25347                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25348             },
25349             fout = function () {
25350                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25351             },
25352             pfin = function() {
25353                 this.sector.stop();
25354                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25355
25356                 if (this.label) {
25357                     this.label[0].stop();
25358                     this.label[0].attr({ r: 7.5 });
25359                     this.label[1].attr({ "font-weight": 800 });
25360                 }
25361             },
25362             pfout = function() {
25363                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25364
25365                 if (this.label) {
25366                     this.label[0].animate({ r: 5 }, 500, "bounce");
25367                     this.label[1].attr({ "font-weight": 400 });
25368                 }
25369             };
25370
25371         switch(graphtype){
25372             case 'bar':
25373                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25374                 break;
25375             case 'hbar':
25376                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25377                 break;
25378             case 'pie':
25379 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25380 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25381 //            
25382                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25383                 
25384                 break;
25385
25386         }
25387         
25388         if(this.title){
25389             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25390         }
25391         
25392     },
25393     
25394     setTitle: function(o)
25395     {
25396         this.title = o;
25397     },
25398     
25399     initEvents: function() {
25400         
25401         if(!this.href){
25402             this.el.on('click', this.onClick, this);
25403         }
25404     },
25405     
25406     onClick : function(e)
25407     {
25408         Roo.log('img onclick');
25409         this.fireEvent('click', this, e);
25410     }
25411    
25412 });
25413
25414  
25415 /*
25416  * - LGPL
25417  *
25418  * numberBox
25419  * 
25420  */
25421 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25422
25423 /**
25424  * @class Roo.bootstrap.dash.NumberBox
25425  * @extends Roo.bootstrap.Component
25426  * Bootstrap NumberBox class
25427  * @cfg {String} headline Box headline
25428  * @cfg {String} content Box content
25429  * @cfg {String} icon Box icon
25430  * @cfg {String} footer Footer text
25431  * @cfg {String} fhref Footer href
25432  * 
25433  * @constructor
25434  * Create a new NumberBox
25435  * @param {Object} config The config object
25436  */
25437
25438
25439 Roo.bootstrap.dash.NumberBox = function(config){
25440     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25441     
25442 };
25443
25444 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25445     
25446     headline : '',
25447     content : '',
25448     icon : '',
25449     footer : '',
25450     fhref : '',
25451     ficon : '',
25452     
25453     getAutoCreate : function(){
25454         
25455         var cfg = {
25456             tag : 'div',
25457             cls : 'small-box ',
25458             cn : [
25459                 {
25460                     tag : 'div',
25461                     cls : 'inner',
25462                     cn :[
25463                         {
25464                             tag : 'h3',
25465                             cls : 'roo-headline',
25466                             html : this.headline
25467                         },
25468                         {
25469                             tag : 'p',
25470                             cls : 'roo-content',
25471                             html : this.content
25472                         }
25473                     ]
25474                 }
25475             ]
25476         };
25477         
25478         if(this.icon){
25479             cfg.cn.push({
25480                 tag : 'div',
25481                 cls : 'icon',
25482                 cn :[
25483                     {
25484                         tag : 'i',
25485                         cls : 'ion ' + this.icon
25486                     }
25487                 ]
25488             });
25489         }
25490         
25491         if(this.footer){
25492             var footer = {
25493                 tag : 'a',
25494                 cls : 'small-box-footer',
25495                 href : this.fhref || '#',
25496                 html : this.footer
25497             };
25498             
25499             cfg.cn.push(footer);
25500             
25501         }
25502         
25503         return  cfg;
25504     },
25505
25506     onRender : function(ct,position){
25507         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25508
25509
25510        
25511                 
25512     },
25513
25514     setHeadline: function (value)
25515     {
25516         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25517     },
25518     
25519     setFooter: function (value, href)
25520     {
25521         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25522         
25523         if(href){
25524             this.el.select('a.small-box-footer',true).first().attr('href', href);
25525         }
25526         
25527     },
25528
25529     setContent: function (value)
25530     {
25531         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25532     },
25533
25534     initEvents: function() 
25535     {   
25536         
25537     }
25538     
25539 });
25540
25541  
25542 /*
25543  * - LGPL
25544  *
25545  * TabBox
25546  * 
25547  */
25548 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25549
25550 /**
25551  * @class Roo.bootstrap.dash.TabBox
25552  * @extends Roo.bootstrap.Component
25553  * Bootstrap TabBox class
25554  * @cfg {String} title Title of the TabBox
25555  * @cfg {String} icon Icon of the TabBox
25556  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25557  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25558  * 
25559  * @constructor
25560  * Create a new TabBox
25561  * @param {Object} config The config object
25562  */
25563
25564
25565 Roo.bootstrap.dash.TabBox = function(config){
25566     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25567     this.addEvents({
25568         // raw events
25569         /**
25570          * @event addpane
25571          * When a pane is added
25572          * @param {Roo.bootstrap.dash.TabPane} pane
25573          */
25574         "addpane" : true,
25575         /**
25576          * @event activatepane
25577          * When a pane is activated
25578          * @param {Roo.bootstrap.dash.TabPane} pane
25579          */
25580         "activatepane" : true
25581         
25582          
25583     });
25584     
25585     this.panes = [];
25586 };
25587
25588 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25589
25590     title : '',
25591     icon : false,
25592     showtabs : true,
25593     tabScrollable : false,
25594     
25595     getChildContainer : function()
25596     {
25597         return this.el.select('.tab-content', true).first();
25598     },
25599     
25600     getAutoCreate : function(){
25601         
25602         var header = {
25603             tag: 'li',
25604             cls: 'pull-left header',
25605             html: this.title,
25606             cn : []
25607         };
25608         
25609         if(this.icon){
25610             header.cn.push({
25611                 tag: 'i',
25612                 cls: 'fa ' + this.icon
25613             });
25614         }
25615         
25616         var h = {
25617             tag: 'ul',
25618             cls: 'nav nav-tabs pull-right',
25619             cn: [
25620                 header
25621             ]
25622         };
25623         
25624         if(this.tabScrollable){
25625             h = {
25626                 tag: 'div',
25627                 cls: 'tab-header',
25628                 cn: [
25629                     {
25630                         tag: 'ul',
25631                         cls: 'nav nav-tabs pull-right',
25632                         cn: [
25633                             header
25634                         ]
25635                     }
25636                 ]
25637             };
25638         }
25639         
25640         var cfg = {
25641             tag: 'div',
25642             cls: 'nav-tabs-custom',
25643             cn: [
25644                 h,
25645                 {
25646                     tag: 'div',
25647                     cls: 'tab-content no-padding',
25648                     cn: []
25649                 }
25650             ]
25651         };
25652
25653         return  cfg;
25654     },
25655     initEvents : function()
25656     {
25657         //Roo.log('add add pane handler');
25658         this.on('addpane', this.onAddPane, this);
25659     },
25660      /**
25661      * Updates the box title
25662      * @param {String} html to set the title to.
25663      */
25664     setTitle : function(value)
25665     {
25666         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25667     },
25668     onAddPane : function(pane)
25669     {
25670         this.panes.push(pane);
25671         //Roo.log('addpane');
25672         //Roo.log(pane);
25673         // tabs are rendere left to right..
25674         if(!this.showtabs){
25675             return;
25676         }
25677         
25678         var ctr = this.el.select('.nav-tabs', true).first();
25679          
25680          
25681         var existing = ctr.select('.nav-tab',true);
25682         var qty = existing.getCount();;
25683         
25684         
25685         var tab = ctr.createChild({
25686             tag : 'li',
25687             cls : 'nav-tab' + (qty ? '' : ' active'),
25688             cn : [
25689                 {
25690                     tag : 'a',
25691                     href:'#',
25692                     html : pane.title
25693                 }
25694             ]
25695         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25696         pane.tab = tab;
25697         
25698         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25699         if (!qty) {
25700             pane.el.addClass('active');
25701         }
25702         
25703                 
25704     },
25705     onTabClick : function(ev,un,ob,pane)
25706     {
25707         //Roo.log('tab - prev default');
25708         ev.preventDefault();
25709         
25710         
25711         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25712         pane.tab.addClass('active');
25713         //Roo.log(pane.title);
25714         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25715         // technically we should have a deactivate event.. but maybe add later.
25716         // and it should not de-activate the selected tab...
25717         this.fireEvent('activatepane', pane);
25718         pane.el.addClass('active');
25719         pane.fireEvent('activate');
25720         
25721         
25722     },
25723     
25724     getActivePane : function()
25725     {
25726         var r = false;
25727         Roo.each(this.panes, function(p) {
25728             if(p.el.hasClass('active')){
25729                 r = p;
25730                 return false;
25731             }
25732             
25733             return;
25734         });
25735         
25736         return r;
25737     }
25738     
25739     
25740 });
25741
25742  
25743 /*
25744  * - LGPL
25745  *
25746  * Tab pane
25747  * 
25748  */
25749 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25750 /**
25751  * @class Roo.bootstrap.TabPane
25752  * @extends Roo.bootstrap.Component
25753  * Bootstrap TabPane class
25754  * @cfg {Boolean} active (false | true) Default false
25755  * @cfg {String} title title of panel
25756
25757  * 
25758  * @constructor
25759  * Create a new TabPane
25760  * @param {Object} config The config object
25761  */
25762
25763 Roo.bootstrap.dash.TabPane = function(config){
25764     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25765     
25766     this.addEvents({
25767         // raw events
25768         /**
25769          * @event activate
25770          * When a pane is activated
25771          * @param {Roo.bootstrap.dash.TabPane} pane
25772          */
25773         "activate" : true
25774          
25775     });
25776 };
25777
25778 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25779     
25780     active : false,
25781     title : '',
25782     
25783     // the tabBox that this is attached to.
25784     tab : false,
25785      
25786     getAutoCreate : function() 
25787     {
25788         var cfg = {
25789             tag: 'div',
25790             cls: 'tab-pane'
25791         };
25792         
25793         if(this.active){
25794             cfg.cls += ' active';
25795         }
25796         
25797         return cfg;
25798     },
25799     initEvents  : function()
25800     {
25801         //Roo.log('trigger add pane handler');
25802         this.parent().fireEvent('addpane', this)
25803     },
25804     
25805      /**
25806      * Updates the tab title 
25807      * @param {String} html to set the title to.
25808      */
25809     setTitle: function(str)
25810     {
25811         if (!this.tab) {
25812             return;
25813         }
25814         this.title = str;
25815         this.tab.select('a', true).first().dom.innerHTML = str;
25816         
25817     }
25818     
25819     
25820     
25821 });
25822
25823  
25824
25825
25826  /*
25827  * - LGPL
25828  *
25829  * menu
25830  * 
25831  */
25832 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25833
25834 /**
25835  * @class Roo.bootstrap.menu.Menu
25836  * @extends Roo.bootstrap.Component
25837  * Bootstrap Menu class - container for Menu
25838  * @cfg {String} html Text of the menu
25839  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25840  * @cfg {String} icon Font awesome icon
25841  * @cfg {String} pos Menu align to (top | bottom) default bottom
25842  * 
25843  * 
25844  * @constructor
25845  * Create a new Menu
25846  * @param {Object} config The config object
25847  */
25848
25849
25850 Roo.bootstrap.menu.Menu = function(config){
25851     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25852     
25853     this.addEvents({
25854         /**
25855          * @event beforeshow
25856          * Fires before this menu is displayed
25857          * @param {Roo.bootstrap.menu.Menu} this
25858          */
25859         beforeshow : true,
25860         /**
25861          * @event beforehide
25862          * Fires before this menu is hidden
25863          * @param {Roo.bootstrap.menu.Menu} this
25864          */
25865         beforehide : true,
25866         /**
25867          * @event show
25868          * Fires after this menu is displayed
25869          * @param {Roo.bootstrap.menu.Menu} this
25870          */
25871         show : true,
25872         /**
25873          * @event hide
25874          * Fires after this menu is hidden
25875          * @param {Roo.bootstrap.menu.Menu} this
25876          */
25877         hide : true,
25878         /**
25879          * @event click
25880          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25881          * @param {Roo.bootstrap.menu.Menu} this
25882          * @param {Roo.EventObject} e
25883          */
25884         click : true
25885     });
25886     
25887 };
25888
25889 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25890     
25891     submenu : false,
25892     html : '',
25893     weight : 'default',
25894     icon : false,
25895     pos : 'bottom',
25896     
25897     
25898     getChildContainer : function() {
25899         if(this.isSubMenu){
25900             return this.el;
25901         }
25902         
25903         return this.el.select('ul.dropdown-menu', true).first();  
25904     },
25905     
25906     getAutoCreate : function()
25907     {
25908         var text = [
25909             {
25910                 tag : 'span',
25911                 cls : 'roo-menu-text',
25912                 html : this.html
25913             }
25914         ];
25915         
25916         if(this.icon){
25917             text.unshift({
25918                 tag : 'i',
25919                 cls : 'fa ' + this.icon
25920             })
25921         }
25922         
25923         
25924         var cfg = {
25925             tag : 'div',
25926             cls : 'btn-group',
25927             cn : [
25928                 {
25929                     tag : 'button',
25930                     cls : 'dropdown-button btn btn-' + this.weight,
25931                     cn : text
25932                 },
25933                 {
25934                     tag : 'button',
25935                     cls : 'dropdown-toggle btn btn-' + this.weight,
25936                     cn : [
25937                         {
25938                             tag : 'span',
25939                             cls : 'caret'
25940                         }
25941                     ]
25942                 },
25943                 {
25944                     tag : 'ul',
25945                     cls : 'dropdown-menu'
25946                 }
25947             ]
25948             
25949         };
25950         
25951         if(this.pos == 'top'){
25952             cfg.cls += ' dropup';
25953         }
25954         
25955         if(this.isSubMenu){
25956             cfg = {
25957                 tag : 'ul',
25958                 cls : 'dropdown-menu'
25959             }
25960         }
25961         
25962         return cfg;
25963     },
25964     
25965     onRender : function(ct, position)
25966     {
25967         this.isSubMenu = ct.hasClass('dropdown-submenu');
25968         
25969         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25970     },
25971     
25972     initEvents : function() 
25973     {
25974         if(this.isSubMenu){
25975             return;
25976         }
25977         
25978         this.hidden = true;
25979         
25980         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25981         this.triggerEl.on('click', this.onTriggerPress, this);
25982         
25983         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25984         this.buttonEl.on('click', this.onClick, this);
25985         
25986     },
25987     
25988     list : function()
25989     {
25990         if(this.isSubMenu){
25991             return this.el;
25992         }
25993         
25994         return this.el.select('ul.dropdown-menu', true).first();
25995     },
25996     
25997     onClick : function(e)
25998     {
25999         this.fireEvent("click", this, e);
26000     },
26001     
26002     onTriggerPress  : function(e)
26003     {   
26004         if (this.isVisible()) {
26005             this.hide();
26006         } else {
26007             this.show();
26008         }
26009     },
26010     
26011     isVisible : function(){
26012         return !this.hidden;
26013     },
26014     
26015     show : function()
26016     {
26017         this.fireEvent("beforeshow", this);
26018         
26019         this.hidden = false;
26020         this.el.addClass('open');
26021         
26022         Roo.get(document).on("mouseup", this.onMouseUp, this);
26023         
26024         this.fireEvent("show", this);
26025         
26026         
26027     },
26028     
26029     hide : function()
26030     {
26031         this.fireEvent("beforehide", this);
26032         
26033         this.hidden = true;
26034         this.el.removeClass('open');
26035         
26036         Roo.get(document).un("mouseup", this.onMouseUp);
26037         
26038         this.fireEvent("hide", this);
26039     },
26040     
26041     onMouseUp : function()
26042     {
26043         this.hide();
26044     }
26045     
26046 });
26047
26048  
26049  /*
26050  * - LGPL
26051  *
26052  * menu item
26053  * 
26054  */
26055 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26056
26057 /**
26058  * @class Roo.bootstrap.menu.Item
26059  * @extends Roo.bootstrap.Component
26060  * Bootstrap MenuItem class
26061  * @cfg {Boolean} submenu (true | false) default false
26062  * @cfg {String} html text of the item
26063  * @cfg {String} href the link
26064  * @cfg {Boolean} disable (true | false) default false
26065  * @cfg {Boolean} preventDefault (true | false) default true
26066  * @cfg {String} icon Font awesome icon
26067  * @cfg {String} pos Submenu align to (left | right) default right 
26068  * 
26069  * 
26070  * @constructor
26071  * Create a new Item
26072  * @param {Object} config The config object
26073  */
26074
26075
26076 Roo.bootstrap.menu.Item = function(config){
26077     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26078     this.addEvents({
26079         /**
26080          * @event mouseover
26081          * Fires when the mouse is hovering over this menu
26082          * @param {Roo.bootstrap.menu.Item} this
26083          * @param {Roo.EventObject} e
26084          */
26085         mouseover : true,
26086         /**
26087          * @event mouseout
26088          * Fires when the mouse exits this menu
26089          * @param {Roo.bootstrap.menu.Item} this
26090          * @param {Roo.EventObject} e
26091          */
26092         mouseout : true,
26093         // raw events
26094         /**
26095          * @event click
26096          * The raw click event for the entire grid.
26097          * @param {Roo.EventObject} e
26098          */
26099         click : true
26100     });
26101 };
26102
26103 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26104     
26105     submenu : false,
26106     href : '',
26107     html : '',
26108     preventDefault: true,
26109     disable : false,
26110     icon : false,
26111     pos : 'right',
26112     
26113     getAutoCreate : function()
26114     {
26115         var text = [
26116             {
26117                 tag : 'span',
26118                 cls : 'roo-menu-item-text',
26119                 html : this.html
26120             }
26121         ];
26122         
26123         if(this.icon){
26124             text.unshift({
26125                 tag : 'i',
26126                 cls : 'fa ' + this.icon
26127             })
26128         }
26129         
26130         var cfg = {
26131             tag : 'li',
26132             cn : [
26133                 {
26134                     tag : 'a',
26135                     href : this.href || '#',
26136                     cn : text
26137                 }
26138             ]
26139         };
26140         
26141         if(this.disable){
26142             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26143         }
26144         
26145         if(this.submenu){
26146             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26147             
26148             if(this.pos == 'left'){
26149                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26150             }
26151         }
26152         
26153         return cfg;
26154     },
26155     
26156     initEvents : function() 
26157     {
26158         this.el.on('mouseover', this.onMouseOver, this);
26159         this.el.on('mouseout', this.onMouseOut, this);
26160         
26161         this.el.select('a', true).first().on('click', this.onClick, this);
26162         
26163     },
26164     
26165     onClick : function(e)
26166     {
26167         if(this.preventDefault){
26168             e.preventDefault();
26169         }
26170         
26171         this.fireEvent("click", this, e);
26172     },
26173     
26174     onMouseOver : function(e)
26175     {
26176         if(this.submenu && this.pos == 'left'){
26177             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26178         }
26179         
26180         this.fireEvent("mouseover", this, e);
26181     },
26182     
26183     onMouseOut : function(e)
26184     {
26185         this.fireEvent("mouseout", this, e);
26186     }
26187 });
26188
26189  
26190
26191  /*
26192  * - LGPL
26193  *
26194  * menu separator
26195  * 
26196  */
26197 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26198
26199 /**
26200  * @class Roo.bootstrap.menu.Separator
26201  * @extends Roo.bootstrap.Component
26202  * Bootstrap Separator class
26203  * 
26204  * @constructor
26205  * Create a new Separator
26206  * @param {Object} config The config object
26207  */
26208
26209
26210 Roo.bootstrap.menu.Separator = function(config){
26211     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26212 };
26213
26214 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26215     
26216     getAutoCreate : function(){
26217         var cfg = {
26218             tag : 'li',
26219             cls: 'divider'
26220         };
26221         
26222         return cfg;
26223     }
26224    
26225 });
26226
26227  
26228
26229  /*
26230  * - LGPL
26231  *
26232  * Tooltip
26233  * 
26234  */
26235
26236 /**
26237  * @class Roo.bootstrap.Tooltip
26238  * Bootstrap Tooltip class
26239  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26240  * to determine which dom element triggers the tooltip.
26241  * 
26242  * It needs to add support for additional attributes like tooltip-position
26243  * 
26244  * @constructor
26245  * Create a new Toolti
26246  * @param {Object} config The config object
26247  */
26248
26249 Roo.bootstrap.Tooltip = function(config){
26250     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26251     
26252     this.alignment = Roo.bootstrap.Tooltip.alignment;
26253     
26254     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26255         this.alignment = config.alignment;
26256     }
26257     
26258 };
26259
26260 Roo.apply(Roo.bootstrap.Tooltip, {
26261     /**
26262      * @function init initialize tooltip monitoring.
26263      * @static
26264      */
26265     currentEl : false,
26266     currentTip : false,
26267     currentRegion : false,
26268     
26269     //  init : delay?
26270     
26271     init : function()
26272     {
26273         Roo.get(document).on('mouseover', this.enter ,this);
26274         Roo.get(document).on('mouseout', this.leave, this);
26275          
26276         
26277         this.currentTip = new Roo.bootstrap.Tooltip();
26278     },
26279     
26280     enter : function(ev)
26281     {
26282         var dom = ev.getTarget();
26283         
26284         //Roo.log(['enter',dom]);
26285         var el = Roo.fly(dom);
26286         if (this.currentEl) {
26287             //Roo.log(dom);
26288             //Roo.log(this.currentEl);
26289             //Roo.log(this.currentEl.contains(dom));
26290             if (this.currentEl == el) {
26291                 return;
26292             }
26293             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26294                 return;
26295             }
26296
26297         }
26298         
26299         if (this.currentTip.el) {
26300             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26301         }    
26302         //Roo.log(ev);
26303         
26304         if(!el || el.dom == document){
26305             return;
26306         }
26307         
26308         var bindEl = el;
26309         
26310         // you can not look for children, as if el is the body.. then everythign is the child..
26311         if (!el.attr('tooltip')) { //
26312             if (!el.select("[tooltip]").elements.length) {
26313                 return;
26314             }
26315             // is the mouse over this child...?
26316             bindEl = el.select("[tooltip]").first();
26317             var xy = ev.getXY();
26318             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26319                 //Roo.log("not in region.");
26320                 return;
26321             }
26322             //Roo.log("child element over..");
26323             
26324         }
26325         this.currentEl = bindEl;
26326         this.currentTip.bind(bindEl);
26327         this.currentRegion = Roo.lib.Region.getRegion(dom);
26328         this.currentTip.enter();
26329         
26330     },
26331     leave : function(ev)
26332     {
26333         var dom = ev.getTarget();
26334         //Roo.log(['leave',dom]);
26335         if (!this.currentEl) {
26336             return;
26337         }
26338         
26339         
26340         if (dom != this.currentEl.dom) {
26341             return;
26342         }
26343         var xy = ev.getXY();
26344         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26345             return;
26346         }
26347         // only activate leave if mouse cursor is outside... bounding box..
26348         
26349         
26350         
26351         
26352         if (this.currentTip) {
26353             this.currentTip.leave();
26354         }
26355         //Roo.log('clear currentEl');
26356         this.currentEl = false;
26357         
26358         
26359     },
26360     alignment : {
26361         'left' : ['r-l', [-2,0], 'right'],
26362         'right' : ['l-r', [2,0], 'left'],
26363         'bottom' : ['t-b', [0,2], 'top'],
26364         'top' : [ 'b-t', [0,-2], 'bottom']
26365     }
26366     
26367 });
26368
26369
26370 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26371     
26372     
26373     bindEl : false,
26374     
26375     delay : null, // can be { show : 300 , hide: 500}
26376     
26377     timeout : null,
26378     
26379     hoverState : null, //???
26380     
26381     placement : 'bottom', 
26382     
26383     alignment : false,
26384     
26385     getAutoCreate : function(){
26386     
26387         var cfg = {
26388            cls : 'tooltip',
26389            role : 'tooltip',
26390            cn : [
26391                 {
26392                     cls : 'tooltip-arrow'
26393                 },
26394                 {
26395                     cls : 'tooltip-inner'
26396                 }
26397            ]
26398         };
26399         
26400         return cfg;
26401     },
26402     bind : function(el)
26403     {
26404         this.bindEl = el;
26405     },
26406       
26407     
26408     enter : function () {
26409        
26410         if (this.timeout != null) {
26411             clearTimeout(this.timeout);
26412         }
26413         
26414         this.hoverState = 'in';
26415          //Roo.log("enter - show");
26416         if (!this.delay || !this.delay.show) {
26417             this.show();
26418             return;
26419         }
26420         var _t = this;
26421         this.timeout = setTimeout(function () {
26422             if (_t.hoverState == 'in') {
26423                 _t.show();
26424             }
26425         }, this.delay.show);
26426     },
26427     leave : function()
26428     {
26429         clearTimeout(this.timeout);
26430     
26431         this.hoverState = 'out';
26432          if (!this.delay || !this.delay.hide) {
26433             this.hide();
26434             return;
26435         }
26436        
26437         var _t = this;
26438         this.timeout = setTimeout(function () {
26439             //Roo.log("leave - timeout");
26440             
26441             if (_t.hoverState == 'out') {
26442                 _t.hide();
26443                 Roo.bootstrap.Tooltip.currentEl = false;
26444             }
26445         }, delay);
26446     },
26447     
26448     show : function (msg)
26449     {
26450         if (!this.el) {
26451             this.render(document.body);
26452         }
26453         // set content.
26454         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26455         
26456         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26457         
26458         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26459         
26460         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26461         
26462         var placement = typeof this.placement == 'function' ?
26463             this.placement.call(this, this.el, on_el) :
26464             this.placement;
26465             
26466         var autoToken = /\s?auto?\s?/i;
26467         var autoPlace = autoToken.test(placement);
26468         if (autoPlace) {
26469             placement = placement.replace(autoToken, '') || 'top';
26470         }
26471         
26472         //this.el.detach()
26473         //this.el.setXY([0,0]);
26474         this.el.show();
26475         //this.el.dom.style.display='block';
26476         
26477         //this.el.appendTo(on_el);
26478         
26479         var p = this.getPosition();
26480         var box = this.el.getBox();
26481         
26482         if (autoPlace) {
26483             // fixme..
26484         }
26485         
26486         var align = this.alignment[placement];
26487         
26488         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26489         
26490         if(placement == 'top' || placement == 'bottom'){
26491             if(xy[0] < 0){
26492                 placement = 'right';
26493             }
26494             
26495             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26496                 placement = 'left';
26497             }
26498             
26499             var scroll = Roo.select('body', true).first().getScroll();
26500             
26501             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26502                 placement = 'top';
26503             }
26504             
26505             align = this.alignment[placement];
26506         }
26507         
26508         this.el.alignTo(this.bindEl, align[0],align[1]);
26509         //var arrow = this.el.select('.arrow',true).first();
26510         //arrow.set(align[2], 
26511         
26512         this.el.addClass(placement);
26513         
26514         this.el.addClass('in fade');
26515         
26516         this.hoverState = null;
26517         
26518         if (this.el.hasClass('fade')) {
26519             // fade it?
26520         }
26521         
26522     },
26523     hide : function()
26524     {
26525          
26526         if (!this.el) {
26527             return;
26528         }
26529         //this.el.setXY([0,0]);
26530         this.el.removeClass('in');
26531         //this.el.hide();
26532         
26533     }
26534     
26535 });
26536  
26537
26538  /*
26539  * - LGPL
26540  *
26541  * Location Picker
26542  * 
26543  */
26544
26545 /**
26546  * @class Roo.bootstrap.LocationPicker
26547  * @extends Roo.bootstrap.Component
26548  * Bootstrap LocationPicker class
26549  * @cfg {Number} latitude Position when init default 0
26550  * @cfg {Number} longitude Position when init default 0
26551  * @cfg {Number} zoom default 15
26552  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26553  * @cfg {Boolean} mapTypeControl default false
26554  * @cfg {Boolean} disableDoubleClickZoom default false
26555  * @cfg {Boolean} scrollwheel default true
26556  * @cfg {Boolean} streetViewControl default false
26557  * @cfg {Number} radius default 0
26558  * @cfg {String} locationName
26559  * @cfg {Boolean} draggable default true
26560  * @cfg {Boolean} enableAutocomplete default false
26561  * @cfg {Boolean} enableReverseGeocode default true
26562  * @cfg {String} markerTitle
26563  * 
26564  * @constructor
26565  * Create a new LocationPicker
26566  * @param {Object} config The config object
26567  */
26568
26569
26570 Roo.bootstrap.LocationPicker = function(config){
26571     
26572     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26573     
26574     this.addEvents({
26575         /**
26576          * @event initial
26577          * Fires when the picker initialized.
26578          * @param {Roo.bootstrap.LocationPicker} this
26579          * @param {Google Location} location
26580          */
26581         initial : true,
26582         /**
26583          * @event positionchanged
26584          * Fires when the picker position changed.
26585          * @param {Roo.bootstrap.LocationPicker} this
26586          * @param {Google Location} location
26587          */
26588         positionchanged : true,
26589         /**
26590          * @event resize
26591          * Fires when the map resize.
26592          * @param {Roo.bootstrap.LocationPicker} this
26593          */
26594         resize : true,
26595         /**
26596          * @event show
26597          * Fires when the map show.
26598          * @param {Roo.bootstrap.LocationPicker} this
26599          */
26600         show : true,
26601         /**
26602          * @event hide
26603          * Fires when the map hide.
26604          * @param {Roo.bootstrap.LocationPicker} this
26605          */
26606         hide : true,
26607         /**
26608          * @event mapClick
26609          * Fires when click the map.
26610          * @param {Roo.bootstrap.LocationPicker} this
26611          * @param {Map event} e
26612          */
26613         mapClick : true,
26614         /**
26615          * @event mapRightClick
26616          * Fires when right click the map.
26617          * @param {Roo.bootstrap.LocationPicker} this
26618          * @param {Map event} e
26619          */
26620         mapRightClick : true,
26621         /**
26622          * @event markerClick
26623          * Fires when click the marker.
26624          * @param {Roo.bootstrap.LocationPicker} this
26625          * @param {Map event} e
26626          */
26627         markerClick : true,
26628         /**
26629          * @event markerRightClick
26630          * Fires when right click the marker.
26631          * @param {Roo.bootstrap.LocationPicker} this
26632          * @param {Map event} e
26633          */
26634         markerRightClick : true,
26635         /**
26636          * @event OverlayViewDraw
26637          * Fires when OverlayView Draw
26638          * @param {Roo.bootstrap.LocationPicker} this
26639          */
26640         OverlayViewDraw : true,
26641         /**
26642          * @event OverlayViewOnAdd
26643          * Fires when OverlayView Draw
26644          * @param {Roo.bootstrap.LocationPicker} this
26645          */
26646         OverlayViewOnAdd : true,
26647         /**
26648          * @event OverlayViewOnRemove
26649          * Fires when OverlayView Draw
26650          * @param {Roo.bootstrap.LocationPicker} this
26651          */
26652         OverlayViewOnRemove : true,
26653         /**
26654          * @event OverlayViewShow
26655          * Fires when OverlayView Draw
26656          * @param {Roo.bootstrap.LocationPicker} this
26657          * @param {Pixel} cpx
26658          */
26659         OverlayViewShow : true,
26660         /**
26661          * @event OverlayViewHide
26662          * Fires when OverlayView Draw
26663          * @param {Roo.bootstrap.LocationPicker} this
26664          */
26665         OverlayViewHide : true,
26666         /**
26667          * @event loadexception
26668          * Fires when load google lib failed.
26669          * @param {Roo.bootstrap.LocationPicker} this
26670          */
26671         loadexception : true
26672     });
26673         
26674 };
26675
26676 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26677     
26678     gMapContext: false,
26679     
26680     latitude: 0,
26681     longitude: 0,
26682     zoom: 15,
26683     mapTypeId: false,
26684     mapTypeControl: false,
26685     disableDoubleClickZoom: false,
26686     scrollwheel: true,
26687     streetViewControl: false,
26688     radius: 0,
26689     locationName: '',
26690     draggable: true,
26691     enableAutocomplete: false,
26692     enableReverseGeocode: true,
26693     markerTitle: '',
26694     
26695     getAutoCreate: function()
26696     {
26697
26698         var cfg = {
26699             tag: 'div',
26700             cls: 'roo-location-picker'
26701         };
26702         
26703         return cfg
26704     },
26705     
26706     initEvents: function(ct, position)
26707     {       
26708         if(!this.el.getWidth() || this.isApplied()){
26709             return;
26710         }
26711         
26712         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26713         
26714         this.initial();
26715     },
26716     
26717     initial: function()
26718     {
26719         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26720             this.fireEvent('loadexception', this);
26721             return;
26722         }
26723         
26724         if(!this.mapTypeId){
26725             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26726         }
26727         
26728         this.gMapContext = this.GMapContext();
26729         
26730         this.initOverlayView();
26731         
26732         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26733         
26734         var _this = this;
26735                 
26736         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26737             _this.setPosition(_this.gMapContext.marker.position);
26738         });
26739         
26740         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26741             _this.fireEvent('mapClick', this, event);
26742             
26743         });
26744
26745         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26746             _this.fireEvent('mapRightClick', this, event);
26747             
26748         });
26749         
26750         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26751             _this.fireEvent('markerClick', this, event);
26752             
26753         });
26754
26755         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26756             _this.fireEvent('markerRightClick', this, event);
26757             
26758         });
26759         
26760         this.setPosition(this.gMapContext.location);
26761         
26762         this.fireEvent('initial', this, this.gMapContext.location);
26763     },
26764     
26765     initOverlayView: function()
26766     {
26767         var _this = this;
26768         
26769         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26770             
26771             draw: function()
26772             {
26773                 _this.fireEvent('OverlayViewDraw', _this);
26774             },
26775             
26776             onAdd: function()
26777             {
26778                 _this.fireEvent('OverlayViewOnAdd', _this);
26779             },
26780             
26781             onRemove: function()
26782             {
26783                 _this.fireEvent('OverlayViewOnRemove', _this);
26784             },
26785             
26786             show: function(cpx)
26787             {
26788                 _this.fireEvent('OverlayViewShow', _this, cpx);
26789             },
26790             
26791             hide: function()
26792             {
26793                 _this.fireEvent('OverlayViewHide', _this);
26794             }
26795             
26796         });
26797     },
26798     
26799     fromLatLngToContainerPixel: function(event)
26800     {
26801         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26802     },
26803     
26804     isApplied: function() 
26805     {
26806         return this.getGmapContext() == false ? false : true;
26807     },
26808     
26809     getGmapContext: function() 
26810     {
26811         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26812     },
26813     
26814     GMapContext: function() 
26815     {
26816         var position = new google.maps.LatLng(this.latitude, this.longitude);
26817         
26818         var _map = new google.maps.Map(this.el.dom, {
26819             center: position,
26820             zoom: this.zoom,
26821             mapTypeId: this.mapTypeId,
26822             mapTypeControl: this.mapTypeControl,
26823             disableDoubleClickZoom: this.disableDoubleClickZoom,
26824             scrollwheel: this.scrollwheel,
26825             streetViewControl: this.streetViewControl,
26826             locationName: this.locationName,
26827             draggable: this.draggable,
26828             enableAutocomplete: this.enableAutocomplete,
26829             enableReverseGeocode: this.enableReverseGeocode
26830         });
26831         
26832         var _marker = new google.maps.Marker({
26833             position: position,
26834             map: _map,
26835             title: this.markerTitle,
26836             draggable: this.draggable
26837         });
26838         
26839         return {
26840             map: _map,
26841             marker: _marker,
26842             circle: null,
26843             location: position,
26844             radius: this.radius,
26845             locationName: this.locationName,
26846             addressComponents: {
26847                 formatted_address: null,
26848                 addressLine1: null,
26849                 addressLine2: null,
26850                 streetName: null,
26851                 streetNumber: null,
26852                 city: null,
26853                 district: null,
26854                 state: null,
26855                 stateOrProvince: null
26856             },
26857             settings: this,
26858             domContainer: this.el.dom,
26859             geodecoder: new google.maps.Geocoder()
26860         };
26861     },
26862     
26863     drawCircle: function(center, radius, options) 
26864     {
26865         if (this.gMapContext.circle != null) {
26866             this.gMapContext.circle.setMap(null);
26867         }
26868         if (radius > 0) {
26869             radius *= 1;
26870             options = Roo.apply({}, options, {
26871                 strokeColor: "#0000FF",
26872                 strokeOpacity: .35,
26873                 strokeWeight: 2,
26874                 fillColor: "#0000FF",
26875                 fillOpacity: .2
26876             });
26877             
26878             options.map = this.gMapContext.map;
26879             options.radius = radius;
26880             options.center = center;
26881             this.gMapContext.circle = new google.maps.Circle(options);
26882             return this.gMapContext.circle;
26883         }
26884         
26885         return null;
26886     },
26887     
26888     setPosition: function(location) 
26889     {
26890         this.gMapContext.location = location;
26891         this.gMapContext.marker.setPosition(location);
26892         this.gMapContext.map.panTo(location);
26893         this.drawCircle(location, this.gMapContext.radius, {});
26894         
26895         var _this = this;
26896         
26897         if (this.gMapContext.settings.enableReverseGeocode) {
26898             this.gMapContext.geodecoder.geocode({
26899                 latLng: this.gMapContext.location
26900             }, function(results, status) {
26901                 
26902                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26903                     _this.gMapContext.locationName = results[0].formatted_address;
26904                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26905                     
26906                     _this.fireEvent('positionchanged', this, location);
26907                 }
26908             });
26909             
26910             return;
26911         }
26912         
26913         this.fireEvent('positionchanged', this, location);
26914     },
26915     
26916     resize: function()
26917     {
26918         google.maps.event.trigger(this.gMapContext.map, "resize");
26919         
26920         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26921         
26922         this.fireEvent('resize', this);
26923     },
26924     
26925     setPositionByLatLng: function(latitude, longitude)
26926     {
26927         this.setPosition(new google.maps.LatLng(latitude, longitude));
26928     },
26929     
26930     getCurrentPosition: function() 
26931     {
26932         return {
26933             latitude: this.gMapContext.location.lat(),
26934             longitude: this.gMapContext.location.lng()
26935         };
26936     },
26937     
26938     getAddressName: function() 
26939     {
26940         return this.gMapContext.locationName;
26941     },
26942     
26943     getAddressComponents: function() 
26944     {
26945         return this.gMapContext.addressComponents;
26946     },
26947     
26948     address_component_from_google_geocode: function(address_components) 
26949     {
26950         var result = {};
26951         
26952         for (var i = 0; i < address_components.length; i++) {
26953             var component = address_components[i];
26954             if (component.types.indexOf("postal_code") >= 0) {
26955                 result.postalCode = component.short_name;
26956             } else if (component.types.indexOf("street_number") >= 0) {
26957                 result.streetNumber = component.short_name;
26958             } else if (component.types.indexOf("route") >= 0) {
26959                 result.streetName = component.short_name;
26960             } else if (component.types.indexOf("neighborhood") >= 0) {
26961                 result.city = component.short_name;
26962             } else if (component.types.indexOf("locality") >= 0) {
26963                 result.city = component.short_name;
26964             } else if (component.types.indexOf("sublocality") >= 0) {
26965                 result.district = component.short_name;
26966             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26967                 result.stateOrProvince = component.short_name;
26968             } else if (component.types.indexOf("country") >= 0) {
26969                 result.country = component.short_name;
26970             }
26971         }
26972         
26973         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26974         result.addressLine2 = "";
26975         return result;
26976     },
26977     
26978     setZoomLevel: function(zoom)
26979     {
26980         this.gMapContext.map.setZoom(zoom);
26981     },
26982     
26983     show: function()
26984     {
26985         if(!this.el){
26986             return;
26987         }
26988         
26989         this.el.show();
26990         
26991         this.resize();
26992         
26993         this.fireEvent('show', this);
26994     },
26995     
26996     hide: function()
26997     {
26998         if(!this.el){
26999             return;
27000         }
27001         
27002         this.el.hide();
27003         
27004         this.fireEvent('hide', this);
27005     }
27006     
27007 });
27008
27009 Roo.apply(Roo.bootstrap.LocationPicker, {
27010     
27011     OverlayView : function(map, options)
27012     {
27013         options = options || {};
27014         
27015         this.setMap(map);
27016     }
27017     
27018     
27019 });/*
27020  * - LGPL
27021  *
27022  * Alert
27023  * 
27024  */
27025
27026 /**
27027  * @class Roo.bootstrap.Alert
27028  * @extends Roo.bootstrap.Component
27029  * Bootstrap Alert class
27030  * @cfg {String} title The title of alert
27031  * @cfg {String} html The content of alert
27032  * @cfg {String} weight (  success | info | warning | danger )
27033  * @cfg {String} faicon font-awesomeicon
27034  * 
27035  * @constructor
27036  * Create a new alert
27037  * @param {Object} config The config object
27038  */
27039
27040
27041 Roo.bootstrap.Alert = function(config){
27042     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27043     
27044 };
27045
27046 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27047     
27048     title: '',
27049     html: '',
27050     weight: false,
27051     faicon: false,
27052     
27053     getAutoCreate : function()
27054     {
27055         
27056         var cfg = {
27057             tag : 'div',
27058             cls : 'alert',
27059             cn : [
27060                 {
27061                     tag : 'i',
27062                     cls : 'roo-alert-icon'
27063                     
27064                 },
27065                 {
27066                     tag : 'b',
27067                     cls : 'roo-alert-title',
27068                     html : this.title
27069                 },
27070                 {
27071                     tag : 'span',
27072                     cls : 'roo-alert-text',
27073                     html : this.html
27074                 }
27075             ]
27076         };
27077         
27078         if(this.faicon){
27079             cfg.cn[0].cls += ' fa ' + this.faicon;
27080         }
27081         
27082         if(this.weight){
27083             cfg.cls += ' alert-' + this.weight;
27084         }
27085         
27086         return cfg;
27087     },
27088     
27089     initEvents: function() 
27090     {
27091         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27092     },
27093     
27094     setTitle : function(str)
27095     {
27096         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27097     },
27098     
27099     setText : function(str)
27100     {
27101         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27102     },
27103     
27104     setWeight : function(weight)
27105     {
27106         if(this.weight){
27107             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27108         }
27109         
27110         this.weight = weight;
27111         
27112         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27113     },
27114     
27115     setIcon : function(icon)
27116     {
27117         if(this.faicon){
27118             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27119         }
27120         
27121         this.faicon = icon;
27122         
27123         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27124     },
27125     
27126     hide: function() 
27127     {
27128         this.el.hide();   
27129     },
27130     
27131     show: function() 
27132     {  
27133         this.el.show();   
27134     }
27135     
27136 });
27137
27138  
27139 /*
27140 * Licence: LGPL
27141 */
27142
27143 /**
27144  * @class Roo.bootstrap.UploadCropbox
27145  * @extends Roo.bootstrap.Component
27146  * Bootstrap UploadCropbox class
27147  * @cfg {String} emptyText show when image has been loaded
27148  * @cfg {String} rotateNotify show when image too small to rotate
27149  * @cfg {Number} errorTimeout default 3000
27150  * @cfg {Number} minWidth default 300
27151  * @cfg {Number} minHeight default 300
27152  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27153  * @cfg {Boolean} isDocument (true|false) default false
27154  * @cfg {String} url action url
27155  * @cfg {String} paramName default 'imageUpload'
27156  * @cfg {String} method default POST
27157  * @cfg {Boolean} loadMask (true|false) default true
27158  * @cfg {Boolean} loadingText default 'Loading...'
27159  * 
27160  * @constructor
27161  * Create a new UploadCropbox
27162  * @param {Object} config The config object
27163  */
27164
27165 Roo.bootstrap.UploadCropbox = function(config){
27166     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27167     
27168     this.addEvents({
27169         /**
27170          * @event beforeselectfile
27171          * Fire before select file
27172          * @param {Roo.bootstrap.UploadCropbox} this
27173          */
27174         "beforeselectfile" : true,
27175         /**
27176          * @event initial
27177          * Fire after initEvent
27178          * @param {Roo.bootstrap.UploadCropbox} this
27179          */
27180         "initial" : true,
27181         /**
27182          * @event crop
27183          * Fire after initEvent
27184          * @param {Roo.bootstrap.UploadCropbox} this
27185          * @param {String} data
27186          */
27187         "crop" : true,
27188         /**
27189          * @event prepare
27190          * Fire when preparing the file data
27191          * @param {Roo.bootstrap.UploadCropbox} this
27192          * @param {Object} file
27193          */
27194         "prepare" : true,
27195         /**
27196          * @event exception
27197          * Fire when get exception
27198          * @param {Roo.bootstrap.UploadCropbox} this
27199          * @param {XMLHttpRequest} xhr
27200          */
27201         "exception" : true,
27202         /**
27203          * @event beforeloadcanvas
27204          * Fire before load the canvas
27205          * @param {Roo.bootstrap.UploadCropbox} this
27206          * @param {String} src
27207          */
27208         "beforeloadcanvas" : true,
27209         /**
27210          * @event trash
27211          * Fire when trash image
27212          * @param {Roo.bootstrap.UploadCropbox} this
27213          */
27214         "trash" : true,
27215         /**
27216          * @event download
27217          * Fire when download the image
27218          * @param {Roo.bootstrap.UploadCropbox} this
27219          */
27220         "download" : true,
27221         /**
27222          * @event footerbuttonclick
27223          * Fire when footerbuttonclick
27224          * @param {Roo.bootstrap.UploadCropbox} this
27225          * @param {String} type
27226          */
27227         "footerbuttonclick" : true,
27228         /**
27229          * @event resize
27230          * Fire when resize
27231          * @param {Roo.bootstrap.UploadCropbox} this
27232          */
27233         "resize" : true,
27234         /**
27235          * @event rotate
27236          * Fire when rotate the image
27237          * @param {Roo.bootstrap.UploadCropbox} this
27238          * @param {String} pos
27239          */
27240         "rotate" : true,
27241         /**
27242          * @event inspect
27243          * Fire when inspect the file
27244          * @param {Roo.bootstrap.UploadCropbox} this
27245          * @param {Object} file
27246          */
27247         "inspect" : true,
27248         /**
27249          * @event upload
27250          * Fire when xhr upload the file
27251          * @param {Roo.bootstrap.UploadCropbox} this
27252          * @param {Object} data
27253          */
27254         "upload" : true,
27255         /**
27256          * @event arrange
27257          * Fire when arrange the file data
27258          * @param {Roo.bootstrap.UploadCropbox} this
27259          * @param {Object} formData
27260          */
27261         "arrange" : true
27262     });
27263     
27264     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27265 };
27266
27267 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27268     
27269     emptyText : 'Click to upload image',
27270     rotateNotify : 'Image is too small to rotate',
27271     errorTimeout : 3000,
27272     scale : 0,
27273     baseScale : 1,
27274     rotate : 0,
27275     dragable : false,
27276     pinching : false,
27277     mouseX : 0,
27278     mouseY : 0,
27279     cropData : false,
27280     minWidth : 300,
27281     minHeight : 300,
27282     file : false,
27283     exif : {},
27284     baseRotate : 1,
27285     cropType : 'image/jpeg',
27286     buttons : false,
27287     canvasLoaded : false,
27288     isDocument : false,
27289     method : 'POST',
27290     paramName : 'imageUpload',
27291     loadMask : true,
27292     loadingText : 'Loading...',
27293     maskEl : false,
27294     
27295     getAutoCreate : function()
27296     {
27297         var cfg = {
27298             tag : 'div',
27299             cls : 'roo-upload-cropbox',
27300             cn : [
27301                 {
27302                     tag : 'input',
27303                     cls : 'roo-upload-cropbox-selector',
27304                     type : 'file'
27305                 },
27306                 {
27307                     tag : 'div',
27308                     cls : 'roo-upload-cropbox-body',
27309                     style : 'cursor:pointer',
27310                     cn : [
27311                         {
27312                             tag : 'div',
27313                             cls : 'roo-upload-cropbox-preview'
27314                         },
27315                         {
27316                             tag : 'div',
27317                             cls : 'roo-upload-cropbox-thumb'
27318                         },
27319                         {
27320                             tag : 'div',
27321                             cls : 'roo-upload-cropbox-empty-notify',
27322                             html : this.emptyText
27323                         },
27324                         {
27325                             tag : 'div',
27326                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27327                             html : this.rotateNotify
27328                         }
27329                     ]
27330                 },
27331                 {
27332                     tag : 'div',
27333                     cls : 'roo-upload-cropbox-footer',
27334                     cn : {
27335                         tag : 'div',
27336                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27337                         cn : []
27338                     }
27339                 }
27340             ]
27341         };
27342         
27343         return cfg;
27344     },
27345     
27346     onRender : function(ct, position)
27347     {
27348         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27349         
27350         if (this.buttons.length) {
27351             
27352             Roo.each(this.buttons, function(bb) {
27353                 
27354                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27355                 
27356                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27357                 
27358             }, this);
27359         }
27360         
27361         if(this.loadMask){
27362             this.maskEl = this.el;
27363         }
27364     },
27365     
27366     initEvents : function()
27367     {
27368         this.urlAPI = (window.createObjectURL && window) || 
27369                                 (window.URL && URL.revokeObjectURL && URL) || 
27370                                 (window.webkitURL && webkitURL);
27371                         
27372         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27373         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27374         
27375         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27376         this.selectorEl.hide();
27377         
27378         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27379         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27380         
27381         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27382         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27383         this.thumbEl.hide();
27384         
27385         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27386         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27387         
27388         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27389         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27390         this.errorEl.hide();
27391         
27392         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27393         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27394         this.footerEl.hide();
27395         
27396         this.setThumbBoxSize();
27397         
27398         this.bind();
27399         
27400         this.resize();
27401         
27402         this.fireEvent('initial', this);
27403     },
27404
27405     bind : function()
27406     {
27407         var _this = this;
27408         
27409         window.addEventListener("resize", function() { _this.resize(); } );
27410         
27411         this.bodyEl.on('click', this.beforeSelectFile, this);
27412         
27413         if(Roo.isTouch){
27414             this.bodyEl.on('touchstart', this.onTouchStart, this);
27415             this.bodyEl.on('touchmove', this.onTouchMove, this);
27416             this.bodyEl.on('touchend', this.onTouchEnd, this);
27417         }
27418         
27419         if(!Roo.isTouch){
27420             this.bodyEl.on('mousedown', this.onMouseDown, this);
27421             this.bodyEl.on('mousemove', this.onMouseMove, this);
27422             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27423             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27424             Roo.get(document).on('mouseup', this.onMouseUp, this);
27425         }
27426         
27427         this.selectorEl.on('change', this.onFileSelected, this);
27428     },
27429     
27430     reset : function()
27431     {    
27432         this.scale = 0;
27433         this.baseScale = 1;
27434         this.rotate = 0;
27435         this.baseRotate = 1;
27436         this.dragable = false;
27437         this.pinching = false;
27438         this.mouseX = 0;
27439         this.mouseY = 0;
27440         this.cropData = false;
27441         this.notifyEl.dom.innerHTML = this.emptyText;
27442         
27443         this.selectorEl.dom.value = '';
27444         
27445     },
27446     
27447     resize : function()
27448     {
27449         if(this.fireEvent('resize', this) != false){
27450             this.setThumbBoxPosition();
27451             this.setCanvasPosition();
27452         }
27453     },
27454     
27455     onFooterButtonClick : function(e, el, o, type)
27456     {
27457         switch (type) {
27458             case 'rotate-left' :
27459                 this.onRotateLeft(e);
27460                 break;
27461             case 'rotate-right' :
27462                 this.onRotateRight(e);
27463                 break;
27464             case 'picture' :
27465                 this.beforeSelectFile(e);
27466                 break;
27467             case 'trash' :
27468                 this.trash(e);
27469                 break;
27470             case 'crop' :
27471                 this.crop(e);
27472                 break;
27473             case 'download' :
27474                 this.download(e);
27475                 break;
27476             default :
27477                 break;
27478         }
27479         
27480         this.fireEvent('footerbuttonclick', this, type);
27481     },
27482     
27483     beforeSelectFile : function(e)
27484     {
27485         e.preventDefault();
27486         
27487         if(this.fireEvent('beforeselectfile', this) != false){
27488             this.selectorEl.dom.click();
27489         }
27490     },
27491     
27492     onFileSelected : function(e)
27493     {
27494         e.preventDefault();
27495         
27496         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27497             return;
27498         }
27499         
27500         var file = this.selectorEl.dom.files[0];
27501         
27502         if(this.fireEvent('inspect', this, file) != false){
27503             this.prepare(file);
27504         }
27505         
27506     },
27507     
27508     trash : function(e)
27509     {
27510         this.fireEvent('trash', this);
27511     },
27512     
27513     download : function(e)
27514     {
27515         this.fireEvent('download', this);
27516     },
27517     
27518     loadCanvas : function(src)
27519     {   
27520         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27521             
27522             this.reset();
27523             
27524             this.imageEl = document.createElement('img');
27525             
27526             var _this = this;
27527             
27528             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27529             
27530             this.imageEl.src = src;
27531         }
27532     },
27533     
27534     onLoadCanvas : function()
27535     {   
27536         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27537         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27538         
27539         this.bodyEl.un('click', this.beforeSelectFile, this);
27540         
27541         this.notifyEl.hide();
27542         this.thumbEl.show();
27543         this.footerEl.show();
27544         
27545         this.baseRotateLevel();
27546         
27547         if(this.isDocument){
27548             this.setThumbBoxSize();
27549         }
27550         
27551         this.setThumbBoxPosition();
27552         
27553         this.baseScaleLevel();
27554         
27555         this.draw();
27556         
27557         this.resize();
27558         
27559         this.canvasLoaded = true;
27560         
27561         if(this.loadMask){
27562             this.maskEl.unmask();
27563         }
27564         
27565     },
27566     
27567     setCanvasPosition : function()
27568     {   
27569         if(!this.canvasEl){
27570             return;
27571         }
27572         
27573         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27574         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27575         
27576         this.previewEl.setLeft(pw);
27577         this.previewEl.setTop(ph);
27578         
27579     },
27580     
27581     onMouseDown : function(e)
27582     {   
27583         e.stopEvent();
27584         
27585         this.dragable = true;
27586         this.pinching = false;
27587         
27588         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27589             this.dragable = false;
27590             return;
27591         }
27592         
27593         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27594         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27595         
27596     },
27597     
27598     onMouseMove : function(e)
27599     {   
27600         e.stopEvent();
27601         
27602         if(!this.canvasLoaded){
27603             return;
27604         }
27605         
27606         if (!this.dragable){
27607             return;
27608         }
27609         
27610         var minX = Math.ceil(this.thumbEl.getLeft(true));
27611         var minY = Math.ceil(this.thumbEl.getTop(true));
27612         
27613         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27614         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27615         
27616         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27617         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27618         
27619         x = x - this.mouseX;
27620         y = y - this.mouseY;
27621         
27622         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27623         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27624         
27625         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27626         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27627         
27628         this.previewEl.setLeft(bgX);
27629         this.previewEl.setTop(bgY);
27630         
27631         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27632         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27633     },
27634     
27635     onMouseUp : function(e)
27636     {   
27637         e.stopEvent();
27638         
27639         this.dragable = false;
27640     },
27641     
27642     onMouseWheel : function(e)
27643     {   
27644         e.stopEvent();
27645         
27646         this.startScale = this.scale;
27647         
27648         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27649         
27650         if(!this.zoomable()){
27651             this.scale = this.startScale;
27652             return;
27653         }
27654         
27655         this.draw();
27656         
27657         return;
27658     },
27659     
27660     zoomable : function()
27661     {
27662         var minScale = this.thumbEl.getWidth() / this.minWidth;
27663         
27664         if(this.minWidth < this.minHeight){
27665             minScale = this.thumbEl.getHeight() / this.minHeight;
27666         }
27667         
27668         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27669         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27670         
27671         if(
27672                 this.isDocument &&
27673                 (this.rotate == 0 || this.rotate == 180) && 
27674                 (
27675                     width > this.imageEl.OriginWidth || 
27676                     height > this.imageEl.OriginHeight ||
27677                     (width < this.minWidth && height < this.minHeight)
27678                 )
27679         ){
27680             return false;
27681         }
27682         
27683         if(
27684                 this.isDocument &&
27685                 (this.rotate == 90 || this.rotate == 270) && 
27686                 (
27687                     width > this.imageEl.OriginWidth || 
27688                     height > this.imageEl.OriginHeight ||
27689                     (width < this.minHeight && height < this.minWidth)
27690                 )
27691         ){
27692             return false;
27693         }
27694         
27695         if(
27696                 !this.isDocument &&
27697                 (this.rotate == 0 || this.rotate == 180) && 
27698                 (
27699                     width < this.minWidth || 
27700                     width > this.imageEl.OriginWidth || 
27701                     height < this.minHeight || 
27702                     height > this.imageEl.OriginHeight
27703                 )
27704         ){
27705             return false;
27706         }
27707         
27708         if(
27709                 !this.isDocument &&
27710                 (this.rotate == 90 || this.rotate == 270) && 
27711                 (
27712                     width < this.minHeight || 
27713                     width > this.imageEl.OriginWidth || 
27714                     height < this.minWidth || 
27715                     height > this.imageEl.OriginHeight
27716                 )
27717         ){
27718             return false;
27719         }
27720         
27721         return true;
27722         
27723     },
27724     
27725     onRotateLeft : function(e)
27726     {   
27727         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27728             
27729             var minScale = this.thumbEl.getWidth() / this.minWidth;
27730             
27731             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27732             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27733             
27734             this.startScale = this.scale;
27735             
27736             while (this.getScaleLevel() < minScale){
27737             
27738                 this.scale = this.scale + 1;
27739                 
27740                 if(!this.zoomable()){
27741                     break;
27742                 }
27743                 
27744                 if(
27745                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27746                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27747                 ){
27748                     continue;
27749                 }
27750                 
27751                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27752
27753                 this.draw();
27754                 
27755                 return;
27756             }
27757             
27758             this.scale = this.startScale;
27759             
27760             this.onRotateFail();
27761             
27762             return false;
27763         }
27764         
27765         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27766
27767         if(this.isDocument){
27768             this.setThumbBoxSize();
27769             this.setThumbBoxPosition();
27770             this.setCanvasPosition();
27771         }
27772         
27773         this.draw();
27774         
27775         this.fireEvent('rotate', this, 'left');
27776         
27777     },
27778     
27779     onRotateRight : function(e)
27780     {
27781         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27782             
27783             var minScale = this.thumbEl.getWidth() / this.minWidth;
27784         
27785             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27786             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27787             
27788             this.startScale = this.scale;
27789             
27790             while (this.getScaleLevel() < minScale){
27791             
27792                 this.scale = this.scale + 1;
27793                 
27794                 if(!this.zoomable()){
27795                     break;
27796                 }
27797                 
27798                 if(
27799                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27800                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27801                 ){
27802                     continue;
27803                 }
27804                 
27805                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27806
27807                 this.draw();
27808                 
27809                 return;
27810             }
27811             
27812             this.scale = this.startScale;
27813             
27814             this.onRotateFail();
27815             
27816             return false;
27817         }
27818         
27819         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27820
27821         if(this.isDocument){
27822             this.setThumbBoxSize();
27823             this.setThumbBoxPosition();
27824             this.setCanvasPosition();
27825         }
27826         
27827         this.draw();
27828         
27829         this.fireEvent('rotate', this, 'right');
27830     },
27831     
27832     onRotateFail : function()
27833     {
27834         this.errorEl.show(true);
27835         
27836         var _this = this;
27837         
27838         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27839     },
27840     
27841     draw : function()
27842     {
27843         this.previewEl.dom.innerHTML = '';
27844         
27845         var canvasEl = document.createElement("canvas");
27846         
27847         var contextEl = canvasEl.getContext("2d");
27848         
27849         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27850         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27851         var center = this.imageEl.OriginWidth / 2;
27852         
27853         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27854             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27855             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27856             center = this.imageEl.OriginHeight / 2;
27857         }
27858         
27859         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27860         
27861         contextEl.translate(center, center);
27862         contextEl.rotate(this.rotate * Math.PI / 180);
27863
27864         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27865         
27866         this.canvasEl = document.createElement("canvas");
27867         
27868         this.contextEl = this.canvasEl.getContext("2d");
27869         
27870         switch (this.rotate) {
27871             case 0 :
27872                 
27873                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27874                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27875                 
27876                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27877                 
27878                 break;
27879             case 90 : 
27880                 
27881                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27882                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27883                 
27884                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27885                     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);
27886                     break;
27887                 }
27888                 
27889                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27890                 
27891                 break;
27892             case 180 :
27893                 
27894                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27895                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27896                 
27897                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27898                     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);
27899                     break;
27900                 }
27901                 
27902                 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);
27903                 
27904                 break;
27905             case 270 :
27906                 
27907                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27908                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27909         
27910                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27911                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27912                     break;
27913                 }
27914                 
27915                 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);
27916                 
27917                 break;
27918             default : 
27919                 break;
27920         }
27921         
27922         this.previewEl.appendChild(this.canvasEl);
27923         
27924         this.setCanvasPosition();
27925     },
27926     
27927     crop : function()
27928     {
27929         if(!this.canvasLoaded){
27930             return;
27931         }
27932         
27933         var imageCanvas = document.createElement("canvas");
27934         
27935         var imageContext = imageCanvas.getContext("2d");
27936         
27937         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27938         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27939         
27940         var center = imageCanvas.width / 2;
27941         
27942         imageContext.translate(center, center);
27943         
27944         imageContext.rotate(this.rotate * Math.PI / 180);
27945         
27946         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27947         
27948         var canvas = document.createElement("canvas");
27949         
27950         var context = canvas.getContext("2d");
27951                 
27952         canvas.width = this.minWidth;
27953         canvas.height = this.minHeight;
27954
27955         switch (this.rotate) {
27956             case 0 :
27957                 
27958                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27959                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27960                 
27961                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27962                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27963                 
27964                 var targetWidth = this.minWidth - 2 * x;
27965                 var targetHeight = this.minHeight - 2 * y;
27966                 
27967                 var scale = 1;
27968                 
27969                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27970                     scale = targetWidth / width;
27971                 }
27972                 
27973                 if(x > 0 && y == 0){
27974                     scale = targetHeight / height;
27975                 }
27976                 
27977                 if(x > 0 && y > 0){
27978                     scale = targetWidth / width;
27979                     
27980                     if(width < height){
27981                         scale = targetHeight / height;
27982                     }
27983                 }
27984                 
27985                 context.scale(scale, scale);
27986                 
27987                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27988                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27989
27990                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27991                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27992
27993                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27994                 
27995                 break;
27996             case 90 : 
27997                 
27998                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27999                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28000                 
28001                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28002                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28003                 
28004                 var targetWidth = this.minWidth - 2 * x;
28005                 var targetHeight = this.minHeight - 2 * y;
28006                 
28007                 var scale = 1;
28008                 
28009                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28010                     scale = targetWidth / width;
28011                 }
28012                 
28013                 if(x > 0 && y == 0){
28014                     scale = targetHeight / height;
28015                 }
28016                 
28017                 if(x > 0 && y > 0){
28018                     scale = targetWidth / width;
28019                     
28020                     if(width < height){
28021                         scale = targetHeight / height;
28022                     }
28023                 }
28024                 
28025                 context.scale(scale, scale);
28026                 
28027                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28028                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28029
28030                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28031                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28032                 
28033                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28034                 
28035                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28036                 
28037                 break;
28038             case 180 :
28039                 
28040                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28041                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28042                 
28043                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28044                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28045                 
28046                 var targetWidth = this.minWidth - 2 * x;
28047                 var targetHeight = this.minHeight - 2 * y;
28048                 
28049                 var scale = 1;
28050                 
28051                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28052                     scale = targetWidth / width;
28053                 }
28054                 
28055                 if(x > 0 && y == 0){
28056                     scale = targetHeight / height;
28057                 }
28058                 
28059                 if(x > 0 && y > 0){
28060                     scale = targetWidth / width;
28061                     
28062                     if(width < height){
28063                         scale = targetHeight / height;
28064                     }
28065                 }
28066                 
28067                 context.scale(scale, scale);
28068                 
28069                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28070                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28071
28072                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28073                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28074
28075                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28076                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28077                 
28078                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28079                 
28080                 break;
28081             case 270 :
28082                 
28083                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28084                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28085                 
28086                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28087                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28088                 
28089                 var targetWidth = this.minWidth - 2 * x;
28090                 var targetHeight = this.minHeight - 2 * y;
28091                 
28092                 var scale = 1;
28093                 
28094                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28095                     scale = targetWidth / width;
28096                 }
28097                 
28098                 if(x > 0 && y == 0){
28099                     scale = targetHeight / height;
28100                 }
28101                 
28102                 if(x > 0 && y > 0){
28103                     scale = targetWidth / width;
28104                     
28105                     if(width < height){
28106                         scale = targetHeight / height;
28107                     }
28108                 }
28109                 
28110                 context.scale(scale, scale);
28111                 
28112                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28113                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28114
28115                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28116                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28117                 
28118                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28119                 
28120                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28121                 
28122                 break;
28123             default : 
28124                 break;
28125         }
28126         
28127         this.cropData = canvas.toDataURL(this.cropType);
28128         
28129         if(this.fireEvent('crop', this, this.cropData) !== false){
28130             this.process(this.file, this.cropData);
28131         }
28132         
28133         return;
28134         
28135     },
28136     
28137     setThumbBoxSize : function()
28138     {
28139         var width, height;
28140         
28141         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28142             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28143             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28144             
28145             this.minWidth = width;
28146             this.minHeight = height;
28147             
28148             if(this.rotate == 90 || this.rotate == 270){
28149                 this.minWidth = height;
28150                 this.minHeight = width;
28151             }
28152         }
28153         
28154         height = 300;
28155         width = Math.ceil(this.minWidth * height / this.minHeight);
28156         
28157         if(this.minWidth > this.minHeight){
28158             width = 300;
28159             height = Math.ceil(this.minHeight * width / this.minWidth);
28160         }
28161         
28162         this.thumbEl.setStyle({
28163             width : width + 'px',
28164             height : height + 'px'
28165         });
28166
28167         return;
28168             
28169     },
28170     
28171     setThumbBoxPosition : function()
28172     {
28173         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28174         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28175         
28176         this.thumbEl.setLeft(x);
28177         this.thumbEl.setTop(y);
28178         
28179     },
28180     
28181     baseRotateLevel : function()
28182     {
28183         this.baseRotate = 1;
28184         
28185         if(
28186                 typeof(this.exif) != 'undefined' &&
28187                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28188                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28189         ){
28190             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28191         }
28192         
28193         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28194         
28195     },
28196     
28197     baseScaleLevel : function()
28198     {
28199         var width, height;
28200         
28201         if(this.isDocument){
28202             
28203             if(this.baseRotate == 6 || this.baseRotate == 8){
28204             
28205                 height = this.thumbEl.getHeight();
28206                 this.baseScale = height / this.imageEl.OriginWidth;
28207
28208                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28209                     width = this.thumbEl.getWidth();
28210                     this.baseScale = width / this.imageEl.OriginHeight;
28211                 }
28212
28213                 return;
28214             }
28215
28216             height = this.thumbEl.getHeight();
28217             this.baseScale = height / this.imageEl.OriginHeight;
28218
28219             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28220                 width = this.thumbEl.getWidth();
28221                 this.baseScale = width / this.imageEl.OriginWidth;
28222             }
28223
28224             return;
28225         }
28226         
28227         if(this.baseRotate == 6 || this.baseRotate == 8){
28228             
28229             width = this.thumbEl.getHeight();
28230             this.baseScale = width / this.imageEl.OriginHeight;
28231             
28232             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28233                 height = this.thumbEl.getWidth();
28234                 this.baseScale = height / this.imageEl.OriginHeight;
28235             }
28236             
28237             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28238                 height = this.thumbEl.getWidth();
28239                 this.baseScale = height / this.imageEl.OriginHeight;
28240                 
28241                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28242                     width = this.thumbEl.getHeight();
28243                     this.baseScale = width / this.imageEl.OriginWidth;
28244                 }
28245             }
28246             
28247             return;
28248         }
28249         
28250         width = this.thumbEl.getWidth();
28251         this.baseScale = width / this.imageEl.OriginWidth;
28252         
28253         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28254             height = this.thumbEl.getHeight();
28255             this.baseScale = height / this.imageEl.OriginHeight;
28256         }
28257         
28258         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28259             
28260             height = this.thumbEl.getHeight();
28261             this.baseScale = height / this.imageEl.OriginHeight;
28262             
28263             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28264                 width = this.thumbEl.getWidth();
28265                 this.baseScale = width / this.imageEl.OriginWidth;
28266             }
28267             
28268         }
28269         
28270         return;
28271     },
28272     
28273     getScaleLevel : function()
28274     {
28275         return this.baseScale * Math.pow(1.1, this.scale);
28276     },
28277     
28278     onTouchStart : function(e)
28279     {
28280         if(!this.canvasLoaded){
28281             this.beforeSelectFile(e);
28282             return;
28283         }
28284         
28285         var touches = e.browserEvent.touches;
28286         
28287         if(!touches){
28288             return;
28289         }
28290         
28291         if(touches.length == 1){
28292             this.onMouseDown(e);
28293             return;
28294         }
28295         
28296         if(touches.length != 2){
28297             return;
28298         }
28299         
28300         var coords = [];
28301         
28302         for(var i = 0, finger; finger = touches[i]; i++){
28303             coords.push(finger.pageX, finger.pageY);
28304         }
28305         
28306         var x = Math.pow(coords[0] - coords[2], 2);
28307         var y = Math.pow(coords[1] - coords[3], 2);
28308         
28309         this.startDistance = Math.sqrt(x + y);
28310         
28311         this.startScale = this.scale;
28312         
28313         this.pinching = true;
28314         this.dragable = false;
28315         
28316     },
28317     
28318     onTouchMove : function(e)
28319     {
28320         if(!this.pinching && !this.dragable){
28321             return;
28322         }
28323         
28324         var touches = e.browserEvent.touches;
28325         
28326         if(!touches){
28327             return;
28328         }
28329         
28330         if(this.dragable){
28331             this.onMouseMove(e);
28332             return;
28333         }
28334         
28335         var coords = [];
28336         
28337         for(var i = 0, finger; finger = touches[i]; i++){
28338             coords.push(finger.pageX, finger.pageY);
28339         }
28340         
28341         var x = Math.pow(coords[0] - coords[2], 2);
28342         var y = Math.pow(coords[1] - coords[3], 2);
28343         
28344         this.endDistance = Math.sqrt(x + y);
28345         
28346         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28347         
28348         if(!this.zoomable()){
28349             this.scale = this.startScale;
28350             return;
28351         }
28352         
28353         this.draw();
28354         
28355     },
28356     
28357     onTouchEnd : function(e)
28358     {
28359         this.pinching = false;
28360         this.dragable = false;
28361         
28362     },
28363     
28364     process : function(file, crop)
28365     {
28366         if(this.loadMask){
28367             this.maskEl.mask(this.loadingText);
28368         }
28369         
28370         this.xhr = new XMLHttpRequest();
28371         
28372         file.xhr = this.xhr;
28373
28374         this.xhr.open(this.method, this.url, true);
28375         
28376         var headers = {
28377             "Accept": "application/json",
28378             "Cache-Control": "no-cache",
28379             "X-Requested-With": "XMLHttpRequest"
28380         };
28381         
28382         for (var headerName in headers) {
28383             var headerValue = headers[headerName];
28384             if (headerValue) {
28385                 this.xhr.setRequestHeader(headerName, headerValue);
28386             }
28387         }
28388         
28389         var _this = this;
28390         
28391         this.xhr.onload = function()
28392         {
28393             _this.xhrOnLoad(_this.xhr);
28394         }
28395         
28396         this.xhr.onerror = function()
28397         {
28398             _this.xhrOnError(_this.xhr);
28399         }
28400         
28401         var formData = new FormData();
28402
28403         formData.append('returnHTML', 'NO');
28404         
28405         if(crop){
28406             formData.append('crop', crop);
28407         }
28408         
28409         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28410             formData.append(this.paramName, file, file.name);
28411         }
28412         
28413         if(typeof(file.filename) != 'undefined'){
28414             formData.append('filename', file.filename);
28415         }
28416         
28417         if(typeof(file.mimetype) != 'undefined'){
28418             formData.append('mimetype', file.mimetype);
28419         }
28420         
28421         if(this.fireEvent('arrange', this, formData) != false){
28422             this.xhr.send(formData);
28423         };
28424     },
28425     
28426     xhrOnLoad : function(xhr)
28427     {
28428         if(this.loadMask){
28429             this.maskEl.unmask();
28430         }
28431         
28432         if (xhr.readyState !== 4) {
28433             this.fireEvent('exception', this, xhr);
28434             return;
28435         }
28436
28437         var response = Roo.decode(xhr.responseText);
28438         
28439         if(!response.success){
28440             this.fireEvent('exception', this, xhr);
28441             return;
28442         }
28443         
28444         var response = Roo.decode(xhr.responseText);
28445         
28446         this.fireEvent('upload', this, response);
28447         
28448     },
28449     
28450     xhrOnError : function()
28451     {
28452         if(this.loadMask){
28453             this.maskEl.unmask();
28454         }
28455         
28456         Roo.log('xhr on error');
28457         
28458         var response = Roo.decode(xhr.responseText);
28459           
28460         Roo.log(response);
28461         
28462     },
28463     
28464     prepare : function(file)
28465     {   
28466         if(this.loadMask){
28467             this.maskEl.mask(this.loadingText);
28468         }
28469         
28470         this.file = false;
28471         this.exif = {};
28472         
28473         if(typeof(file) === 'string'){
28474             this.loadCanvas(file);
28475             return;
28476         }
28477         
28478         if(!file || !this.urlAPI){
28479             return;
28480         }
28481         
28482         this.file = file;
28483         this.cropType = file.type;
28484         
28485         var _this = this;
28486         
28487         if(this.fireEvent('prepare', this, this.file) != false){
28488             
28489             var reader = new FileReader();
28490             
28491             reader.onload = function (e) {
28492                 if (e.target.error) {
28493                     Roo.log(e.target.error);
28494                     return;
28495                 }
28496                 
28497                 var buffer = e.target.result,
28498                     dataView = new DataView(buffer),
28499                     offset = 2,
28500                     maxOffset = dataView.byteLength - 4,
28501                     markerBytes,
28502                     markerLength;
28503                 
28504                 if (dataView.getUint16(0) === 0xffd8) {
28505                     while (offset < maxOffset) {
28506                         markerBytes = dataView.getUint16(offset);
28507                         
28508                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28509                             markerLength = dataView.getUint16(offset + 2) + 2;
28510                             if (offset + markerLength > dataView.byteLength) {
28511                                 Roo.log('Invalid meta data: Invalid segment size.');
28512                                 break;
28513                             }
28514                             
28515                             if(markerBytes == 0xffe1){
28516                                 _this.parseExifData(
28517                                     dataView,
28518                                     offset,
28519                                     markerLength
28520                                 );
28521                             }
28522                             
28523                             offset += markerLength;
28524                             
28525                             continue;
28526                         }
28527                         
28528                         break;
28529                     }
28530                     
28531                 }
28532                 
28533                 var url = _this.urlAPI.createObjectURL(_this.file);
28534                 
28535                 _this.loadCanvas(url);
28536                 
28537                 return;
28538             }
28539             
28540             reader.readAsArrayBuffer(this.file);
28541             
28542         }
28543         
28544     },
28545     
28546     parseExifData : function(dataView, offset, length)
28547     {
28548         var tiffOffset = offset + 10,
28549             littleEndian,
28550             dirOffset;
28551     
28552         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28553             // No Exif data, might be XMP data instead
28554             return;
28555         }
28556         
28557         // Check for the ASCII code for "Exif" (0x45786966):
28558         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28559             // No Exif data, might be XMP data instead
28560             return;
28561         }
28562         if (tiffOffset + 8 > dataView.byteLength) {
28563             Roo.log('Invalid Exif data: Invalid segment size.');
28564             return;
28565         }
28566         // Check for the two null bytes:
28567         if (dataView.getUint16(offset + 8) !== 0x0000) {
28568             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28569             return;
28570         }
28571         // Check the byte alignment:
28572         switch (dataView.getUint16(tiffOffset)) {
28573         case 0x4949:
28574             littleEndian = true;
28575             break;
28576         case 0x4D4D:
28577             littleEndian = false;
28578             break;
28579         default:
28580             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28581             return;
28582         }
28583         // Check for the TIFF tag marker (0x002A):
28584         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28585             Roo.log('Invalid Exif data: Missing TIFF marker.');
28586             return;
28587         }
28588         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28589         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28590         
28591         this.parseExifTags(
28592             dataView,
28593             tiffOffset,
28594             tiffOffset + dirOffset,
28595             littleEndian
28596         );
28597     },
28598     
28599     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28600     {
28601         var tagsNumber,
28602             dirEndOffset,
28603             i;
28604         if (dirOffset + 6 > dataView.byteLength) {
28605             Roo.log('Invalid Exif data: Invalid directory offset.');
28606             return;
28607         }
28608         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28609         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28610         if (dirEndOffset + 4 > dataView.byteLength) {
28611             Roo.log('Invalid Exif data: Invalid directory size.');
28612             return;
28613         }
28614         for (i = 0; i < tagsNumber; i += 1) {
28615             this.parseExifTag(
28616                 dataView,
28617                 tiffOffset,
28618                 dirOffset + 2 + 12 * i, // tag offset
28619                 littleEndian
28620             );
28621         }
28622         // Return the offset to the next directory:
28623         return dataView.getUint32(dirEndOffset, littleEndian);
28624     },
28625     
28626     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28627     {
28628         var tag = dataView.getUint16(offset, littleEndian);
28629         
28630         this.exif[tag] = this.getExifValue(
28631             dataView,
28632             tiffOffset,
28633             offset,
28634             dataView.getUint16(offset + 2, littleEndian), // tag type
28635             dataView.getUint32(offset + 4, littleEndian), // tag length
28636             littleEndian
28637         );
28638     },
28639     
28640     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28641     {
28642         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28643             tagSize,
28644             dataOffset,
28645             values,
28646             i,
28647             str,
28648             c;
28649     
28650         if (!tagType) {
28651             Roo.log('Invalid Exif data: Invalid tag type.');
28652             return;
28653         }
28654         
28655         tagSize = tagType.size * length;
28656         // Determine if the value is contained in the dataOffset bytes,
28657         // or if the value at the dataOffset is a pointer to the actual data:
28658         dataOffset = tagSize > 4 ?
28659                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28660         if (dataOffset + tagSize > dataView.byteLength) {
28661             Roo.log('Invalid Exif data: Invalid data offset.');
28662             return;
28663         }
28664         if (length === 1) {
28665             return tagType.getValue(dataView, dataOffset, littleEndian);
28666         }
28667         values = [];
28668         for (i = 0; i < length; i += 1) {
28669             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28670         }
28671         
28672         if (tagType.ascii) {
28673             str = '';
28674             // Concatenate the chars:
28675             for (i = 0; i < values.length; i += 1) {
28676                 c = values[i];
28677                 // Ignore the terminating NULL byte(s):
28678                 if (c === '\u0000') {
28679                     break;
28680                 }
28681                 str += c;
28682             }
28683             return str;
28684         }
28685         return values;
28686     }
28687     
28688 });
28689
28690 Roo.apply(Roo.bootstrap.UploadCropbox, {
28691     tags : {
28692         'Orientation': 0x0112
28693     },
28694     
28695     Orientation: {
28696             1: 0, //'top-left',
28697 //            2: 'top-right',
28698             3: 180, //'bottom-right',
28699 //            4: 'bottom-left',
28700 //            5: 'left-top',
28701             6: 90, //'right-top',
28702 //            7: 'right-bottom',
28703             8: 270 //'left-bottom'
28704     },
28705     
28706     exifTagTypes : {
28707         // byte, 8-bit unsigned int:
28708         1: {
28709             getValue: function (dataView, dataOffset) {
28710                 return dataView.getUint8(dataOffset);
28711             },
28712             size: 1
28713         },
28714         // ascii, 8-bit byte:
28715         2: {
28716             getValue: function (dataView, dataOffset) {
28717                 return String.fromCharCode(dataView.getUint8(dataOffset));
28718             },
28719             size: 1,
28720             ascii: true
28721         },
28722         // short, 16 bit int:
28723         3: {
28724             getValue: function (dataView, dataOffset, littleEndian) {
28725                 return dataView.getUint16(dataOffset, littleEndian);
28726             },
28727             size: 2
28728         },
28729         // long, 32 bit int:
28730         4: {
28731             getValue: function (dataView, dataOffset, littleEndian) {
28732                 return dataView.getUint32(dataOffset, littleEndian);
28733             },
28734             size: 4
28735         },
28736         // rational = two long values, first is numerator, second is denominator:
28737         5: {
28738             getValue: function (dataView, dataOffset, littleEndian) {
28739                 return dataView.getUint32(dataOffset, littleEndian) /
28740                     dataView.getUint32(dataOffset + 4, littleEndian);
28741             },
28742             size: 8
28743         },
28744         // slong, 32 bit signed int:
28745         9: {
28746             getValue: function (dataView, dataOffset, littleEndian) {
28747                 return dataView.getInt32(dataOffset, littleEndian);
28748             },
28749             size: 4
28750         },
28751         // srational, two slongs, first is numerator, second is denominator:
28752         10: {
28753             getValue: function (dataView, dataOffset, littleEndian) {
28754                 return dataView.getInt32(dataOffset, littleEndian) /
28755                     dataView.getInt32(dataOffset + 4, littleEndian);
28756             },
28757             size: 8
28758         }
28759     },
28760     
28761     footer : {
28762         STANDARD : [
28763             {
28764                 tag : 'div',
28765                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28766                 action : 'rotate-left',
28767                 cn : [
28768                     {
28769                         tag : 'button',
28770                         cls : 'btn btn-default',
28771                         html : '<i class="fa fa-undo"></i>'
28772                     }
28773                 ]
28774             },
28775             {
28776                 tag : 'div',
28777                 cls : 'btn-group roo-upload-cropbox-picture',
28778                 action : 'picture',
28779                 cn : [
28780                     {
28781                         tag : 'button',
28782                         cls : 'btn btn-default',
28783                         html : '<i class="fa fa-picture-o"></i>'
28784                     }
28785                 ]
28786             },
28787             {
28788                 tag : 'div',
28789                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28790                 action : 'rotate-right',
28791                 cn : [
28792                     {
28793                         tag : 'button',
28794                         cls : 'btn btn-default',
28795                         html : '<i class="fa fa-repeat"></i>'
28796                     }
28797                 ]
28798             }
28799         ],
28800         DOCUMENT : [
28801             {
28802                 tag : 'div',
28803                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28804                 action : 'rotate-left',
28805                 cn : [
28806                     {
28807                         tag : 'button',
28808                         cls : 'btn btn-default',
28809                         html : '<i class="fa fa-undo"></i>'
28810                     }
28811                 ]
28812             },
28813             {
28814                 tag : 'div',
28815                 cls : 'btn-group roo-upload-cropbox-download',
28816                 action : 'download',
28817                 cn : [
28818                     {
28819                         tag : 'button',
28820                         cls : 'btn btn-default',
28821                         html : '<i class="fa fa-download"></i>'
28822                     }
28823                 ]
28824             },
28825             {
28826                 tag : 'div',
28827                 cls : 'btn-group roo-upload-cropbox-crop',
28828                 action : 'crop',
28829                 cn : [
28830                     {
28831                         tag : 'button',
28832                         cls : 'btn btn-default',
28833                         html : '<i class="fa fa-crop"></i>'
28834                     }
28835                 ]
28836             },
28837             {
28838                 tag : 'div',
28839                 cls : 'btn-group roo-upload-cropbox-trash',
28840                 action : 'trash',
28841                 cn : [
28842                     {
28843                         tag : 'button',
28844                         cls : 'btn btn-default',
28845                         html : '<i class="fa fa-trash"></i>'
28846                     }
28847                 ]
28848             },
28849             {
28850                 tag : 'div',
28851                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28852                 action : 'rotate-right',
28853                 cn : [
28854                     {
28855                         tag : 'button',
28856                         cls : 'btn btn-default',
28857                         html : '<i class="fa fa-repeat"></i>'
28858                     }
28859                 ]
28860             }
28861         ],
28862         ROTATOR : [
28863             {
28864                 tag : 'div',
28865                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28866                 action : 'rotate-left',
28867                 cn : [
28868                     {
28869                         tag : 'button',
28870                         cls : 'btn btn-default',
28871                         html : '<i class="fa fa-undo"></i>'
28872                     }
28873                 ]
28874             },
28875             {
28876                 tag : 'div',
28877                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28878                 action : 'rotate-right',
28879                 cn : [
28880                     {
28881                         tag : 'button',
28882                         cls : 'btn btn-default',
28883                         html : '<i class="fa fa-repeat"></i>'
28884                     }
28885                 ]
28886             }
28887         ]
28888     }
28889 });
28890
28891 /*
28892 * Licence: LGPL
28893 */
28894
28895 /**
28896  * @class Roo.bootstrap.DocumentManager
28897  * @extends Roo.bootstrap.Component
28898  * Bootstrap DocumentManager class
28899  * @cfg {String} paramName default 'imageUpload'
28900  * @cfg {String} toolTipName default 'filename'
28901  * @cfg {String} method default POST
28902  * @cfg {String} url action url
28903  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28904  * @cfg {Boolean} multiple multiple upload default true
28905  * @cfg {Number} thumbSize default 300
28906  * @cfg {String} fieldLabel
28907  * @cfg {Number} labelWidth default 4
28908  * @cfg {String} labelAlign (left|top) default left
28909  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28910 * @cfg {Number} labellg set the width of label (1-12)
28911  * @cfg {Number} labelmd set the width of label (1-12)
28912  * @cfg {Number} labelsm set the width of label (1-12)
28913  * @cfg {Number} labelxs set the width of label (1-12)
28914  * 
28915  * @constructor
28916  * Create a new DocumentManager
28917  * @param {Object} config The config object
28918  */
28919
28920 Roo.bootstrap.DocumentManager = function(config){
28921     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28922     
28923     this.files = [];
28924     this.delegates = [];
28925     
28926     this.addEvents({
28927         /**
28928          * @event initial
28929          * Fire when initial the DocumentManager
28930          * @param {Roo.bootstrap.DocumentManager} this
28931          */
28932         "initial" : true,
28933         /**
28934          * @event inspect
28935          * inspect selected file
28936          * @param {Roo.bootstrap.DocumentManager} this
28937          * @param {File} file
28938          */
28939         "inspect" : true,
28940         /**
28941          * @event exception
28942          * Fire when xhr load exception
28943          * @param {Roo.bootstrap.DocumentManager} this
28944          * @param {XMLHttpRequest} xhr
28945          */
28946         "exception" : true,
28947         /**
28948          * @event afterupload
28949          * Fire when xhr load exception
28950          * @param {Roo.bootstrap.DocumentManager} this
28951          * @param {XMLHttpRequest} xhr
28952          */
28953         "afterupload" : true,
28954         /**
28955          * @event prepare
28956          * prepare the form data
28957          * @param {Roo.bootstrap.DocumentManager} this
28958          * @param {Object} formData
28959          */
28960         "prepare" : true,
28961         /**
28962          * @event remove
28963          * Fire when remove the file
28964          * @param {Roo.bootstrap.DocumentManager} this
28965          * @param {Object} file
28966          */
28967         "remove" : true,
28968         /**
28969          * @event refresh
28970          * Fire after refresh the file
28971          * @param {Roo.bootstrap.DocumentManager} this
28972          */
28973         "refresh" : true,
28974         /**
28975          * @event click
28976          * Fire after click the image
28977          * @param {Roo.bootstrap.DocumentManager} this
28978          * @param {Object} file
28979          */
28980         "click" : true,
28981         /**
28982          * @event edit
28983          * Fire when upload a image and editable set to true
28984          * @param {Roo.bootstrap.DocumentManager} this
28985          * @param {Object} file
28986          */
28987         "edit" : true,
28988         /**
28989          * @event beforeselectfile
28990          * Fire before select file
28991          * @param {Roo.bootstrap.DocumentManager} this
28992          */
28993         "beforeselectfile" : true,
28994         /**
28995          * @event process
28996          * Fire before process file
28997          * @param {Roo.bootstrap.DocumentManager} this
28998          * @param {Object} file
28999          */
29000         "process" : true,
29001         /**
29002          * @event previewrendered
29003          * Fire when preview rendered
29004          * @param {Roo.bootstrap.DocumentManager} this
29005          * @param {Object} file
29006          */
29007         "previewrendered" : true,
29008         /**
29009          */
29010         "previewResize" : true
29011         
29012     });
29013 };
29014
29015 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29016     
29017     boxes : 0,
29018     inputName : '',
29019     thumbSize : 300,
29020     multiple : true,
29021     files : false,
29022     method : 'POST',
29023     url : '',
29024     paramName : 'imageUpload',
29025     toolTipName : 'filename',
29026     fieldLabel : '',
29027     labelWidth : 4,
29028     labelAlign : 'left',
29029     editable : true,
29030     delegates : false,
29031     xhr : false, 
29032     
29033     labellg : 0,
29034     labelmd : 0,
29035     labelsm : 0,
29036     labelxs : 0,
29037     
29038     getAutoCreate : function()
29039     {   
29040         var managerWidget = {
29041             tag : 'div',
29042             cls : 'roo-document-manager',
29043             cn : [
29044                 {
29045                     tag : 'input',
29046                     cls : 'roo-document-manager-selector',
29047                     type : 'file'
29048                 },
29049                 {
29050                     tag : 'div',
29051                     cls : 'roo-document-manager-uploader',
29052                     cn : [
29053                         {
29054                             tag : 'div',
29055                             cls : 'roo-document-manager-upload-btn',
29056                             html : '<i class="fa fa-plus"></i>'
29057                         }
29058                     ]
29059                     
29060                 }
29061             ]
29062         };
29063         
29064         var content = [
29065             {
29066                 tag : 'div',
29067                 cls : 'column col-md-12',
29068                 cn : managerWidget
29069             }
29070         ];
29071         
29072         if(this.fieldLabel.length){
29073             
29074             content = [
29075                 {
29076                     tag : 'div',
29077                     cls : 'column col-md-12',
29078                     html : this.fieldLabel
29079                 },
29080                 {
29081                     tag : 'div',
29082                     cls : 'column col-md-12',
29083                     cn : managerWidget
29084                 }
29085             ];
29086
29087             if(this.labelAlign == 'left'){
29088                 content = [
29089                     {
29090                         tag : 'div',
29091                         cls : 'column',
29092                         html : this.fieldLabel
29093                     },
29094                     {
29095                         tag : 'div',
29096                         cls : 'column',
29097                         cn : managerWidget
29098                     }
29099                 ];
29100                 
29101                 if(this.labelWidth > 12){
29102                     content[0].style = "width: " + this.labelWidth + 'px';
29103                 }
29104
29105                 if(this.labelWidth < 13 && this.labelmd == 0){
29106                     this.labelmd = this.labelWidth;
29107                 }
29108
29109                 if(this.labellg > 0){
29110                     content[0].cls += ' col-lg-' + this.labellg;
29111                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29112                 }
29113
29114                 if(this.labelmd > 0){
29115                     content[0].cls += ' col-md-' + this.labelmd;
29116                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29117                 }
29118
29119                 if(this.labelsm > 0){
29120                     content[0].cls += ' col-sm-' + this.labelsm;
29121                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29122                 }
29123
29124                 if(this.labelxs > 0){
29125                     content[0].cls += ' col-xs-' + this.labelxs;
29126                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29127                 }
29128                 
29129             }
29130         }
29131         
29132         var cfg = {
29133             tag : 'div',
29134             cls : 'row clearfix',
29135             cn : content
29136         };
29137         
29138         return cfg;
29139         
29140     },
29141     
29142     initEvents : function()
29143     {
29144         this.managerEl = this.el.select('.roo-document-manager', true).first();
29145         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29146         
29147         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29148         this.selectorEl.hide();
29149         
29150         if(this.multiple){
29151             this.selectorEl.attr('multiple', 'multiple');
29152         }
29153         
29154         this.selectorEl.on('change', this.onFileSelected, this);
29155         
29156         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29157         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29158         
29159         this.uploader.on('click', this.onUploaderClick, this);
29160         
29161         this.renderProgressDialog();
29162         
29163         var _this = this;
29164         
29165         window.addEventListener("resize", function() { _this.refresh(); } );
29166         
29167         this.fireEvent('initial', this);
29168     },
29169     
29170     renderProgressDialog : function()
29171     {
29172         var _this = this;
29173         
29174         this.progressDialog = new Roo.bootstrap.Modal({
29175             cls : 'roo-document-manager-progress-dialog',
29176             allow_close : false,
29177             title : '',
29178             buttons : [
29179                 {
29180                     name  :'cancel',
29181                     weight : 'danger',
29182                     html : 'Cancel'
29183                 }
29184             ], 
29185             listeners : { 
29186                 btnclick : function() {
29187                     _this.uploadCancel();
29188                     this.hide();
29189                 }
29190             }
29191         });
29192          
29193         this.progressDialog.render(Roo.get(document.body));
29194          
29195         this.progress = new Roo.bootstrap.Progress({
29196             cls : 'roo-document-manager-progress',
29197             active : true,
29198             striped : true
29199         });
29200         
29201         this.progress.render(this.progressDialog.getChildContainer());
29202         
29203         this.progressBar = new Roo.bootstrap.ProgressBar({
29204             cls : 'roo-document-manager-progress-bar',
29205             aria_valuenow : 0,
29206             aria_valuemin : 0,
29207             aria_valuemax : 12,
29208             panel : 'success'
29209         });
29210         
29211         this.progressBar.render(this.progress.getChildContainer());
29212     },
29213     
29214     onUploaderClick : function(e)
29215     {
29216         e.preventDefault();
29217      
29218         if(this.fireEvent('beforeselectfile', this) != false){
29219             this.selectorEl.dom.click();
29220         }
29221         
29222     },
29223     
29224     onFileSelected : function(e)
29225     {
29226         e.preventDefault();
29227         
29228         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29229             return;
29230         }
29231         
29232         Roo.each(this.selectorEl.dom.files, function(file){
29233             if(this.fireEvent('inspect', this, file) != false){
29234                 this.files.push(file);
29235             }
29236         }, this);
29237         
29238         this.queue();
29239         
29240     },
29241     
29242     queue : function()
29243     {
29244         this.selectorEl.dom.value = '';
29245         
29246         if(!this.files || !this.files.length){
29247             return;
29248         }
29249         
29250         if(this.boxes > 0 && this.files.length > this.boxes){
29251             this.files = this.files.slice(0, this.boxes);
29252         }
29253         
29254         this.uploader.show();
29255         
29256         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29257             this.uploader.hide();
29258         }
29259         
29260         var _this = this;
29261         
29262         var files = [];
29263         
29264         var docs = [];
29265         
29266         Roo.each(this.files, function(file){
29267             
29268             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29269                 var f = this.renderPreview(file);
29270                 files.push(f);
29271                 return;
29272             }
29273             
29274             if(file.type.indexOf('image') != -1){
29275                 this.delegates.push(
29276                     (function(){
29277                         _this.process(file);
29278                     }).createDelegate(this)
29279                 );
29280         
29281                 return;
29282             }
29283             
29284             docs.push(
29285                 (function(){
29286                     _this.process(file);
29287                 }).createDelegate(this)
29288             );
29289             
29290         }, this);
29291         
29292         this.files = files;
29293         
29294         this.delegates = this.delegates.concat(docs);
29295         
29296         if(!this.delegates.length){
29297             this.refresh();
29298             return;
29299         }
29300         
29301         this.progressBar.aria_valuemax = this.delegates.length;
29302         
29303         this.arrange();
29304         
29305         return;
29306     },
29307     
29308     arrange : function()
29309     {
29310         if(!this.delegates.length){
29311             this.progressDialog.hide();
29312             this.refresh();
29313             return;
29314         }
29315         
29316         var delegate = this.delegates.shift();
29317         
29318         this.progressDialog.show();
29319         
29320         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29321         
29322         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29323         
29324         delegate();
29325     },
29326     
29327     refresh : function()
29328     {
29329         this.uploader.show();
29330         
29331         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29332             this.uploader.hide();
29333         }
29334         
29335         Roo.isTouch ? this.closable(false) : this.closable(true);
29336         
29337         this.fireEvent('refresh', this);
29338     },
29339     
29340     onRemove : function(e, el, o)
29341     {
29342         e.preventDefault();
29343         
29344         this.fireEvent('remove', this, o);
29345         
29346     },
29347     
29348     remove : function(o)
29349     {
29350         var files = [];
29351         
29352         Roo.each(this.files, function(file){
29353             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29354                 files.push(file);
29355                 return;
29356             }
29357
29358             o.target.remove();
29359
29360         }, this);
29361         
29362         this.files = files;
29363         
29364         this.refresh();
29365     },
29366     
29367     clear : function()
29368     {
29369         Roo.each(this.files, function(file){
29370             if(!file.target){
29371                 return;
29372             }
29373             
29374             file.target.remove();
29375
29376         }, this);
29377         
29378         this.files = [];
29379         
29380         this.refresh();
29381     },
29382     
29383     onClick : function(e, el, o)
29384     {
29385         e.preventDefault();
29386         
29387         this.fireEvent('click', this, o);
29388         
29389     },
29390     
29391     closable : function(closable)
29392     {
29393         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29394             
29395             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29396             
29397             if(closable){
29398                 el.show();
29399                 return;
29400             }
29401             
29402             el.hide();
29403             
29404         }, this);
29405     },
29406     
29407     xhrOnLoad : function(xhr)
29408     {
29409         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29410             el.remove();
29411         }, this);
29412         
29413         if (xhr.readyState !== 4) {
29414             this.arrange();
29415             this.fireEvent('exception', this, xhr);
29416             return;
29417         }
29418
29419         var response = Roo.decode(xhr.responseText);
29420         
29421         if(!response.success){
29422             this.arrange();
29423             this.fireEvent('exception', this, xhr);
29424             return;
29425         }
29426         
29427         var file = this.renderPreview(response.data);
29428         
29429         this.files.push(file);
29430         
29431         this.arrange();
29432         
29433         this.fireEvent('afterupload', this, xhr);
29434         
29435     },
29436     
29437     xhrOnError : function(xhr)
29438     {
29439         Roo.log('xhr on error');
29440         
29441         var response = Roo.decode(xhr.responseText);
29442           
29443         Roo.log(response);
29444         
29445         this.arrange();
29446     },
29447     
29448     process : function(file)
29449     {
29450         if(this.fireEvent('process', this, file) !== false){
29451             if(this.editable && file.type.indexOf('image') != -1){
29452                 this.fireEvent('edit', this, file);
29453                 return;
29454             }
29455
29456             this.uploadStart(file, false);
29457
29458             return;
29459         }
29460         
29461     },
29462     
29463     uploadStart : function(file, crop)
29464     {
29465         this.xhr = new XMLHttpRequest();
29466         
29467         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29468             this.arrange();
29469             return;
29470         }
29471         
29472         file.xhr = this.xhr;
29473             
29474         this.managerEl.createChild({
29475             tag : 'div',
29476             cls : 'roo-document-manager-loading',
29477             cn : [
29478                 {
29479                     tag : 'div',
29480                     tooltip : file.name,
29481                     cls : 'roo-document-manager-thumb',
29482                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29483                 }
29484             ]
29485
29486         });
29487
29488         this.xhr.open(this.method, this.url, true);
29489         
29490         var headers = {
29491             "Accept": "application/json",
29492             "Cache-Control": "no-cache",
29493             "X-Requested-With": "XMLHttpRequest"
29494         };
29495         
29496         for (var headerName in headers) {
29497             var headerValue = headers[headerName];
29498             if (headerValue) {
29499                 this.xhr.setRequestHeader(headerName, headerValue);
29500             }
29501         }
29502         
29503         var _this = this;
29504         
29505         this.xhr.onload = function()
29506         {
29507             _this.xhrOnLoad(_this.xhr);
29508         }
29509         
29510         this.xhr.onerror = function()
29511         {
29512             _this.xhrOnError(_this.xhr);
29513         }
29514         
29515         var formData = new FormData();
29516
29517         formData.append('returnHTML', 'NO');
29518         
29519         if(crop){
29520             formData.append('crop', crop);
29521         }
29522         
29523         formData.append(this.paramName, file, file.name);
29524         
29525         var options = {
29526             file : file, 
29527             manually : false
29528         };
29529         
29530         if(this.fireEvent('prepare', this, formData, options) != false){
29531             
29532             if(options.manually){
29533                 return;
29534             }
29535             
29536             this.xhr.send(formData);
29537             return;
29538         };
29539         
29540         this.uploadCancel();
29541     },
29542     
29543     uploadCancel : function()
29544     {
29545         if (this.xhr) {
29546             this.xhr.abort();
29547         }
29548         
29549         this.delegates = [];
29550         
29551         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29552             el.remove();
29553         }, this);
29554         
29555         this.arrange();
29556     },
29557     
29558     renderPreview : function(file)
29559     {
29560         if(typeof(file.target) != 'undefined' && file.target){
29561             return file;
29562         }
29563         
29564         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29565         
29566         var previewEl = this.managerEl.createChild({
29567             tag : 'div',
29568             cls : 'roo-document-manager-preview',
29569             cn : [
29570                 {
29571                     tag : 'div',
29572                     tooltip : file[this.toolTipName],
29573                     cls : 'roo-document-manager-thumb',
29574                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29575                 },
29576                 {
29577                     tag : 'button',
29578                     cls : 'close',
29579                     html : '<i class="fa fa-times-circle"></i>'
29580                 }
29581             ]
29582         });
29583
29584         var close = previewEl.select('button.close', true).first();
29585
29586         close.on('click', this.onRemove, this, file);
29587
29588         file.target = previewEl;
29589
29590         var image = previewEl.select('img', true).first();
29591         
29592         var _this = this;
29593         
29594         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29595         
29596         image.on('click', this.onClick, this, file);
29597         
29598         this.fireEvent('previewrendered', this, file);
29599         
29600         return file;
29601         
29602     },
29603     
29604     onPreviewLoad : function(file, image)
29605     {
29606         if(typeof(file.target) == 'undefined' || !file.target){
29607             return;
29608         }
29609         
29610         var width = image.dom.naturalWidth || image.dom.width;
29611         var height = image.dom.naturalHeight || image.dom.height;
29612         
29613         if(!this.previewResize) {
29614             return;
29615         }
29616         
29617         if(width > height){
29618             file.target.addClass('wide');
29619             return;
29620         }
29621         
29622         file.target.addClass('tall');
29623         return;
29624         
29625     },
29626     
29627     uploadFromSource : function(file, crop)
29628     {
29629         this.xhr = new XMLHttpRequest();
29630         
29631         this.managerEl.createChild({
29632             tag : 'div',
29633             cls : 'roo-document-manager-loading',
29634             cn : [
29635                 {
29636                     tag : 'div',
29637                     tooltip : file.name,
29638                     cls : 'roo-document-manager-thumb',
29639                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29640                 }
29641             ]
29642
29643         });
29644
29645         this.xhr.open(this.method, this.url, true);
29646         
29647         var headers = {
29648             "Accept": "application/json",
29649             "Cache-Control": "no-cache",
29650             "X-Requested-With": "XMLHttpRequest"
29651         };
29652         
29653         for (var headerName in headers) {
29654             var headerValue = headers[headerName];
29655             if (headerValue) {
29656                 this.xhr.setRequestHeader(headerName, headerValue);
29657             }
29658         }
29659         
29660         var _this = this;
29661         
29662         this.xhr.onload = function()
29663         {
29664             _this.xhrOnLoad(_this.xhr);
29665         }
29666         
29667         this.xhr.onerror = function()
29668         {
29669             _this.xhrOnError(_this.xhr);
29670         }
29671         
29672         var formData = new FormData();
29673
29674         formData.append('returnHTML', 'NO');
29675         
29676         formData.append('crop', crop);
29677         
29678         if(typeof(file.filename) != 'undefined'){
29679             formData.append('filename', file.filename);
29680         }
29681         
29682         if(typeof(file.mimetype) != 'undefined'){
29683             formData.append('mimetype', file.mimetype);
29684         }
29685         
29686         Roo.log(formData);
29687         
29688         if(this.fireEvent('prepare', this, formData) != false){
29689             this.xhr.send(formData);
29690         };
29691     }
29692 });
29693
29694 /*
29695 * Licence: LGPL
29696 */
29697
29698 /**
29699  * @class Roo.bootstrap.DocumentViewer
29700  * @extends Roo.bootstrap.Component
29701  * Bootstrap DocumentViewer class
29702  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29703  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29704  * 
29705  * @constructor
29706  * Create a new DocumentViewer
29707  * @param {Object} config The config object
29708  */
29709
29710 Roo.bootstrap.DocumentViewer = function(config){
29711     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29712     
29713     this.addEvents({
29714         /**
29715          * @event initial
29716          * Fire after initEvent
29717          * @param {Roo.bootstrap.DocumentViewer} this
29718          */
29719         "initial" : true,
29720         /**
29721          * @event click
29722          * Fire after click
29723          * @param {Roo.bootstrap.DocumentViewer} this
29724          */
29725         "click" : true,
29726         /**
29727          * @event download
29728          * Fire after download button
29729          * @param {Roo.bootstrap.DocumentViewer} this
29730          */
29731         "download" : true,
29732         /**
29733          * @event trash
29734          * Fire after trash button
29735          * @param {Roo.bootstrap.DocumentViewer} this
29736          */
29737         "trash" : true
29738         
29739     });
29740 };
29741
29742 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29743     
29744     showDownload : true,
29745     
29746     showTrash : true,
29747     
29748     getAutoCreate : function()
29749     {
29750         var cfg = {
29751             tag : 'div',
29752             cls : 'roo-document-viewer',
29753             cn : [
29754                 {
29755                     tag : 'div',
29756                     cls : 'roo-document-viewer-body',
29757                     cn : [
29758                         {
29759                             tag : 'div',
29760                             cls : 'roo-document-viewer-thumb',
29761                             cn : [
29762                                 {
29763                                     tag : 'img',
29764                                     cls : 'roo-document-viewer-image'
29765                                 }
29766                             ]
29767                         }
29768                     ]
29769                 },
29770                 {
29771                     tag : 'div',
29772                     cls : 'roo-document-viewer-footer',
29773                     cn : {
29774                         tag : 'div',
29775                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29776                         cn : [
29777                             {
29778                                 tag : 'div',
29779                                 cls : 'btn-group roo-document-viewer-download',
29780                                 cn : [
29781                                     {
29782                                         tag : 'button',
29783                                         cls : 'btn btn-default',
29784                                         html : '<i class="fa fa-download"></i>'
29785                                     }
29786                                 ]
29787                             },
29788                             {
29789                                 tag : 'div',
29790                                 cls : 'btn-group roo-document-viewer-trash',
29791                                 cn : [
29792                                     {
29793                                         tag : 'button',
29794                                         cls : 'btn btn-default',
29795                                         html : '<i class="fa fa-trash"></i>'
29796                                     }
29797                                 ]
29798                             }
29799                         ]
29800                     }
29801                 }
29802             ]
29803         };
29804         
29805         return cfg;
29806     },
29807     
29808     initEvents : function()
29809     {
29810         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29811         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29812         
29813         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29814         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29815         
29816         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29817         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29818         
29819         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29820         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29821         
29822         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29823         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29824         
29825         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29826         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29827         
29828         this.bodyEl.on('click', this.onClick, this);
29829         this.downloadBtn.on('click', this.onDownload, this);
29830         this.trashBtn.on('click', this.onTrash, this);
29831         
29832         this.downloadBtn.hide();
29833         this.trashBtn.hide();
29834         
29835         if(this.showDownload){
29836             this.downloadBtn.show();
29837         }
29838         
29839         if(this.showTrash){
29840             this.trashBtn.show();
29841         }
29842         
29843         if(!this.showDownload && !this.showTrash) {
29844             this.footerEl.hide();
29845         }
29846         
29847     },
29848     
29849     initial : function()
29850     {
29851         this.fireEvent('initial', this);
29852         
29853     },
29854     
29855     onClick : function(e)
29856     {
29857         e.preventDefault();
29858         
29859         this.fireEvent('click', this);
29860     },
29861     
29862     onDownload : function(e)
29863     {
29864         e.preventDefault();
29865         
29866         this.fireEvent('download', this);
29867     },
29868     
29869     onTrash : function(e)
29870     {
29871         e.preventDefault();
29872         
29873         this.fireEvent('trash', this);
29874     }
29875     
29876 });
29877 /*
29878  * - LGPL
29879  *
29880  * nav progress bar
29881  * 
29882  */
29883
29884 /**
29885  * @class Roo.bootstrap.NavProgressBar
29886  * @extends Roo.bootstrap.Component
29887  * Bootstrap NavProgressBar class
29888  * 
29889  * @constructor
29890  * Create a new nav progress bar
29891  * @param {Object} config The config object
29892  */
29893
29894 Roo.bootstrap.NavProgressBar = function(config){
29895     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29896
29897     this.bullets = this.bullets || [];
29898    
29899 //    Roo.bootstrap.NavProgressBar.register(this);
29900      this.addEvents({
29901         /**
29902              * @event changed
29903              * Fires when the active item changes
29904              * @param {Roo.bootstrap.NavProgressBar} this
29905              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29906              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29907          */
29908         'changed': true
29909      });
29910     
29911 };
29912
29913 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29914     
29915     bullets : [],
29916     barItems : [],
29917     
29918     getAutoCreate : function()
29919     {
29920         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29921         
29922         cfg = {
29923             tag : 'div',
29924             cls : 'roo-navigation-bar-group',
29925             cn : [
29926                 {
29927                     tag : 'div',
29928                     cls : 'roo-navigation-top-bar'
29929                 },
29930                 {
29931                     tag : 'div',
29932                     cls : 'roo-navigation-bullets-bar',
29933                     cn : [
29934                         {
29935                             tag : 'ul',
29936                             cls : 'roo-navigation-bar'
29937                         }
29938                     ]
29939                 },
29940                 
29941                 {
29942                     tag : 'div',
29943                     cls : 'roo-navigation-bottom-bar'
29944                 }
29945             ]
29946             
29947         };
29948         
29949         return cfg;
29950         
29951     },
29952     
29953     initEvents: function() 
29954     {
29955         
29956     },
29957     
29958     onRender : function(ct, position) 
29959     {
29960         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29961         
29962         if(this.bullets.length){
29963             Roo.each(this.bullets, function(b){
29964                this.addItem(b);
29965             }, this);
29966         }
29967         
29968         this.format();
29969         
29970     },
29971     
29972     addItem : function(cfg)
29973     {
29974         var item = new Roo.bootstrap.NavProgressItem(cfg);
29975         
29976         item.parentId = this.id;
29977         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29978         
29979         if(cfg.html){
29980             var top = new Roo.bootstrap.Element({
29981                 tag : 'div',
29982                 cls : 'roo-navigation-bar-text'
29983             });
29984             
29985             var bottom = new Roo.bootstrap.Element({
29986                 tag : 'div',
29987                 cls : 'roo-navigation-bar-text'
29988             });
29989             
29990             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29991             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29992             
29993             var topText = new Roo.bootstrap.Element({
29994                 tag : 'span',
29995                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29996             });
29997             
29998             var bottomText = new Roo.bootstrap.Element({
29999                 tag : 'span',
30000                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30001             });
30002             
30003             topText.onRender(top.el, null);
30004             bottomText.onRender(bottom.el, null);
30005             
30006             item.topEl = top;
30007             item.bottomEl = bottom;
30008         }
30009         
30010         this.barItems.push(item);
30011         
30012         return item;
30013     },
30014     
30015     getActive : function()
30016     {
30017         var active = false;
30018         
30019         Roo.each(this.barItems, function(v){
30020             
30021             if (!v.isActive()) {
30022                 return;
30023             }
30024             
30025             active = v;
30026             return false;
30027             
30028         });
30029         
30030         return active;
30031     },
30032     
30033     setActiveItem : function(item)
30034     {
30035         var prev = false;
30036         
30037         Roo.each(this.barItems, function(v){
30038             if (v.rid == item.rid) {
30039                 return ;
30040             }
30041             
30042             if (v.isActive()) {
30043                 v.setActive(false);
30044                 prev = v;
30045             }
30046         });
30047
30048         item.setActive(true);
30049         
30050         this.fireEvent('changed', this, item, prev);
30051     },
30052     
30053     getBarItem: function(rid)
30054     {
30055         var ret = false;
30056         
30057         Roo.each(this.barItems, function(e) {
30058             if (e.rid != rid) {
30059                 return;
30060             }
30061             
30062             ret =  e;
30063             return false;
30064         });
30065         
30066         return ret;
30067     },
30068     
30069     indexOfItem : function(item)
30070     {
30071         var index = false;
30072         
30073         Roo.each(this.barItems, function(v, i){
30074             
30075             if (v.rid != item.rid) {
30076                 return;
30077             }
30078             
30079             index = i;
30080             return false
30081         });
30082         
30083         return index;
30084     },
30085     
30086     setActiveNext : function()
30087     {
30088         var i = this.indexOfItem(this.getActive());
30089         
30090         if (i > this.barItems.length) {
30091             return;
30092         }
30093         
30094         this.setActiveItem(this.barItems[i+1]);
30095     },
30096     
30097     setActivePrev : function()
30098     {
30099         var i = this.indexOfItem(this.getActive());
30100         
30101         if (i  < 1) {
30102             return;
30103         }
30104         
30105         this.setActiveItem(this.barItems[i-1]);
30106     },
30107     
30108     format : function()
30109     {
30110         if(!this.barItems.length){
30111             return;
30112         }
30113      
30114         var width = 100 / this.barItems.length;
30115         
30116         Roo.each(this.barItems, function(i){
30117             i.el.setStyle('width', width + '%');
30118             i.topEl.el.setStyle('width', width + '%');
30119             i.bottomEl.el.setStyle('width', width + '%');
30120         }, this);
30121         
30122     }
30123     
30124 });
30125 /*
30126  * - LGPL
30127  *
30128  * Nav Progress Item
30129  * 
30130  */
30131
30132 /**
30133  * @class Roo.bootstrap.NavProgressItem
30134  * @extends Roo.bootstrap.Component
30135  * Bootstrap NavProgressItem class
30136  * @cfg {String} rid the reference id
30137  * @cfg {Boolean} active (true|false) Is item active default false
30138  * @cfg {Boolean} disabled (true|false) Is item active default false
30139  * @cfg {String} html
30140  * @cfg {String} position (top|bottom) text position default bottom
30141  * @cfg {String} icon show icon instead of number
30142  * 
30143  * @constructor
30144  * Create a new NavProgressItem
30145  * @param {Object} config The config object
30146  */
30147 Roo.bootstrap.NavProgressItem = function(config){
30148     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30149     this.addEvents({
30150         // raw events
30151         /**
30152          * @event click
30153          * The raw click event for the entire grid.
30154          * @param {Roo.bootstrap.NavProgressItem} this
30155          * @param {Roo.EventObject} e
30156          */
30157         "click" : true
30158     });
30159    
30160 };
30161
30162 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30163     
30164     rid : '',
30165     active : false,
30166     disabled : false,
30167     html : '',
30168     position : 'bottom',
30169     icon : false,
30170     
30171     getAutoCreate : function()
30172     {
30173         var iconCls = 'roo-navigation-bar-item-icon';
30174         
30175         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30176         
30177         var cfg = {
30178             tag: 'li',
30179             cls: 'roo-navigation-bar-item',
30180             cn : [
30181                 {
30182                     tag : 'i',
30183                     cls : iconCls
30184                 }
30185             ]
30186         };
30187         
30188         if(this.active){
30189             cfg.cls += ' active';
30190         }
30191         if(this.disabled){
30192             cfg.cls += ' disabled';
30193         }
30194         
30195         return cfg;
30196     },
30197     
30198     disable : function()
30199     {
30200         this.setDisabled(true);
30201     },
30202     
30203     enable : function()
30204     {
30205         this.setDisabled(false);
30206     },
30207     
30208     initEvents: function() 
30209     {
30210         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30211         
30212         this.iconEl.on('click', this.onClick, this);
30213     },
30214     
30215     onClick : function(e)
30216     {
30217         e.preventDefault();
30218         
30219         if(this.disabled){
30220             return;
30221         }
30222         
30223         if(this.fireEvent('click', this, e) === false){
30224             return;
30225         };
30226         
30227         this.parent().setActiveItem(this);
30228     },
30229     
30230     isActive: function () 
30231     {
30232         return this.active;
30233     },
30234     
30235     setActive : function(state)
30236     {
30237         if(this.active == state){
30238             return;
30239         }
30240         
30241         this.active = state;
30242         
30243         if (state) {
30244             this.el.addClass('active');
30245             return;
30246         }
30247         
30248         this.el.removeClass('active');
30249         
30250         return;
30251     },
30252     
30253     setDisabled : function(state)
30254     {
30255         if(this.disabled == state){
30256             return;
30257         }
30258         
30259         this.disabled = state;
30260         
30261         if (state) {
30262             this.el.addClass('disabled');
30263             return;
30264         }
30265         
30266         this.el.removeClass('disabled');
30267     },
30268     
30269     tooltipEl : function()
30270     {
30271         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30272     }
30273 });
30274  
30275
30276  /*
30277  * - LGPL
30278  *
30279  * FieldLabel
30280  * 
30281  */
30282
30283 /**
30284  * @class Roo.bootstrap.FieldLabel
30285  * @extends Roo.bootstrap.Component
30286  * Bootstrap FieldLabel class
30287  * @cfg {String} html contents of the element
30288  * @cfg {String} tag tag of the element default label
30289  * @cfg {String} cls class of the element
30290  * @cfg {String} target label target 
30291  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30292  * @cfg {String} invalidClass default "text-warning"
30293  * @cfg {String} validClass default "text-success"
30294  * @cfg {String} iconTooltip default "This field is required"
30295  * @cfg {String} indicatorpos (left|right) default left
30296  * 
30297  * @constructor
30298  * Create a new FieldLabel
30299  * @param {Object} config The config object
30300  */
30301
30302 Roo.bootstrap.FieldLabel = function(config){
30303     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30304     
30305     this.addEvents({
30306             /**
30307              * @event invalid
30308              * Fires after the field has been marked as invalid.
30309              * @param {Roo.form.FieldLabel} this
30310              * @param {String} msg The validation message
30311              */
30312             invalid : true,
30313             /**
30314              * @event valid
30315              * Fires after the field has been validated with no errors.
30316              * @param {Roo.form.FieldLabel} this
30317              */
30318             valid : true
30319         });
30320 };
30321
30322 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30323     
30324     tag: 'label',
30325     cls: '',
30326     html: '',
30327     target: '',
30328     allowBlank : true,
30329     invalidClass : 'has-warning',
30330     validClass : 'has-success',
30331     iconTooltip : 'This field is required',
30332     indicatorpos : 'left',
30333     
30334     getAutoCreate : function(){
30335         
30336         var cls = "";
30337         if (!this.allowBlank) {
30338             cls  = "visible";
30339         }
30340         
30341         var cfg = {
30342             tag : this.tag,
30343             cls : 'roo-bootstrap-field-label ' + this.cls,
30344             for : this.target,
30345             cn : [
30346                 {
30347                     tag : 'i',
30348                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30349                     tooltip : this.iconTooltip
30350                 },
30351                 {
30352                     tag : 'span',
30353                     html : this.html
30354                 }
30355             ] 
30356         };
30357         
30358         if(this.indicatorpos == 'right'){
30359             var cfg = {
30360                 tag : this.tag,
30361                 cls : 'roo-bootstrap-field-label ' + this.cls,
30362                 for : this.target,
30363                 cn : [
30364                     {
30365                         tag : 'span',
30366                         html : this.html
30367                     },
30368                     {
30369                         tag : 'i',
30370                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30371                         tooltip : this.iconTooltip
30372                     }
30373                 ] 
30374             };
30375         }
30376         
30377         return cfg;
30378     },
30379     
30380     initEvents: function() 
30381     {
30382         Roo.bootstrap.Element.superclass.initEvents.call(this);
30383         
30384         this.indicator = this.indicatorEl();
30385         
30386         if(this.indicator){
30387             this.indicator.removeClass('visible');
30388             this.indicator.addClass('invisible');
30389         }
30390         
30391         Roo.bootstrap.FieldLabel.register(this);
30392     },
30393     
30394     indicatorEl : function()
30395     {
30396         var indicator = this.el.select('i.roo-required-indicator',true).first();
30397         
30398         if(!indicator){
30399             return false;
30400         }
30401         
30402         return indicator;
30403         
30404     },
30405     
30406     /**
30407      * Mark this field as valid
30408      */
30409     markValid : function()
30410     {
30411         if(this.indicator){
30412             this.indicator.removeClass('visible');
30413             this.indicator.addClass('invisible');
30414         }
30415         
30416         this.el.removeClass(this.invalidClass);
30417         
30418         this.el.addClass(this.validClass);
30419         
30420         this.fireEvent('valid', this);
30421     },
30422     
30423     /**
30424      * Mark this field as invalid
30425      * @param {String} msg The validation message
30426      */
30427     markInvalid : function(msg)
30428     {
30429         if(this.indicator){
30430             this.indicator.removeClass('invisible');
30431             this.indicator.addClass('visible');
30432         }
30433         
30434         this.el.removeClass(this.validClass);
30435         
30436         this.el.addClass(this.invalidClass);
30437         
30438         this.fireEvent('invalid', this, msg);
30439     }
30440     
30441    
30442 });
30443
30444 Roo.apply(Roo.bootstrap.FieldLabel, {
30445     
30446     groups: {},
30447     
30448      /**
30449     * register a FieldLabel Group
30450     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30451     */
30452     register : function(label)
30453     {
30454         if(this.groups.hasOwnProperty(label.target)){
30455             return;
30456         }
30457      
30458         this.groups[label.target] = label;
30459         
30460     },
30461     /**
30462     * fetch a FieldLabel Group based on the target
30463     * @param {string} target
30464     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30465     */
30466     get: function(target) {
30467         if (typeof(this.groups[target]) == 'undefined') {
30468             return false;
30469         }
30470         
30471         return this.groups[target] ;
30472     }
30473 });
30474
30475  
30476
30477  /*
30478  * - LGPL
30479  *
30480  * page DateSplitField.
30481  * 
30482  */
30483
30484
30485 /**
30486  * @class Roo.bootstrap.DateSplitField
30487  * @extends Roo.bootstrap.Component
30488  * Bootstrap DateSplitField class
30489  * @cfg {string} fieldLabel - the label associated
30490  * @cfg {Number} labelWidth set the width of label (0-12)
30491  * @cfg {String} labelAlign (top|left)
30492  * @cfg {Boolean} dayAllowBlank (true|false) default false
30493  * @cfg {Boolean} monthAllowBlank (true|false) default false
30494  * @cfg {Boolean} yearAllowBlank (true|false) default false
30495  * @cfg {string} dayPlaceholder 
30496  * @cfg {string} monthPlaceholder
30497  * @cfg {string} yearPlaceholder
30498  * @cfg {string} dayFormat default 'd'
30499  * @cfg {string} monthFormat default 'm'
30500  * @cfg {string} yearFormat default 'Y'
30501  * @cfg {Number} labellg set the width of label (1-12)
30502  * @cfg {Number} labelmd set the width of label (1-12)
30503  * @cfg {Number} labelsm set the width of label (1-12)
30504  * @cfg {Number} labelxs set the width of label (1-12)
30505
30506  *     
30507  * @constructor
30508  * Create a new DateSplitField
30509  * @param {Object} config The config object
30510  */
30511
30512 Roo.bootstrap.DateSplitField = function(config){
30513     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30514     
30515     this.addEvents({
30516         // raw events
30517          /**
30518          * @event years
30519          * getting the data of years
30520          * @param {Roo.bootstrap.DateSplitField} this
30521          * @param {Object} years
30522          */
30523         "years" : true,
30524         /**
30525          * @event days
30526          * getting the data of days
30527          * @param {Roo.bootstrap.DateSplitField} this
30528          * @param {Object} days
30529          */
30530         "days" : true,
30531         /**
30532          * @event invalid
30533          * Fires after the field has been marked as invalid.
30534          * @param {Roo.form.Field} this
30535          * @param {String} msg The validation message
30536          */
30537         invalid : true,
30538        /**
30539          * @event valid
30540          * Fires after the field has been validated with no errors.
30541          * @param {Roo.form.Field} this
30542          */
30543         valid : true
30544     });
30545 };
30546
30547 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30548     
30549     fieldLabel : '',
30550     labelAlign : 'top',
30551     labelWidth : 3,
30552     dayAllowBlank : false,
30553     monthAllowBlank : false,
30554     yearAllowBlank : false,
30555     dayPlaceholder : '',
30556     monthPlaceholder : '',
30557     yearPlaceholder : '',
30558     dayFormat : 'd',
30559     monthFormat : 'm',
30560     yearFormat : 'Y',
30561     isFormField : true,
30562     labellg : 0,
30563     labelmd : 0,
30564     labelsm : 0,
30565     labelxs : 0,
30566     
30567     getAutoCreate : function()
30568     {
30569         var cfg = {
30570             tag : 'div',
30571             cls : 'row roo-date-split-field-group',
30572             cn : [
30573                 {
30574                     tag : 'input',
30575                     type : 'hidden',
30576                     cls : 'form-hidden-field roo-date-split-field-group-value',
30577                     name : this.name
30578                 }
30579             ]
30580         };
30581         
30582         var labelCls = 'col-md-12';
30583         var contentCls = 'col-md-4';
30584         
30585         if(this.fieldLabel){
30586             
30587             var label = {
30588                 tag : 'div',
30589                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30590                 cn : [
30591                     {
30592                         tag : 'label',
30593                         html : this.fieldLabel
30594                     }
30595                 ]
30596             };
30597             
30598             if(this.labelAlign == 'left'){
30599             
30600                 if(this.labelWidth > 12){
30601                     label.style = "width: " + this.labelWidth + 'px';
30602                 }
30603
30604                 if(this.labelWidth < 13 && this.labelmd == 0){
30605                     this.labelmd = this.labelWidth;
30606                 }
30607
30608                 if(this.labellg > 0){
30609                     labelCls = ' col-lg-' + this.labellg;
30610                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30611                 }
30612
30613                 if(this.labelmd > 0){
30614                     labelCls = ' col-md-' + this.labelmd;
30615                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30616                 }
30617
30618                 if(this.labelsm > 0){
30619                     labelCls = ' col-sm-' + this.labelsm;
30620                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30621                 }
30622
30623                 if(this.labelxs > 0){
30624                     labelCls = ' col-xs-' + this.labelxs;
30625                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30626                 }
30627             }
30628             
30629             label.cls += ' ' + labelCls;
30630             
30631             cfg.cn.push(label);
30632         }
30633         
30634         Roo.each(['day', 'month', 'year'], function(t){
30635             cfg.cn.push({
30636                 tag : 'div',
30637                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30638             });
30639         }, this);
30640         
30641         return cfg;
30642     },
30643     
30644     inputEl: function ()
30645     {
30646         return this.el.select('.roo-date-split-field-group-value', true).first();
30647     },
30648     
30649     onRender : function(ct, position) 
30650     {
30651         var _this = this;
30652         
30653         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30654         
30655         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30656         
30657         this.dayField = new Roo.bootstrap.ComboBox({
30658             allowBlank : this.dayAllowBlank,
30659             alwaysQuery : true,
30660             displayField : 'value',
30661             editable : false,
30662             fieldLabel : '',
30663             forceSelection : true,
30664             mode : 'local',
30665             placeholder : this.dayPlaceholder,
30666             selectOnFocus : true,
30667             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30668             triggerAction : 'all',
30669             typeAhead : true,
30670             valueField : 'value',
30671             store : new Roo.data.SimpleStore({
30672                 data : (function() {    
30673                     var days = [];
30674                     _this.fireEvent('days', _this, days);
30675                     return days;
30676                 })(),
30677                 fields : [ 'value' ]
30678             }),
30679             listeners : {
30680                 select : function (_self, record, index)
30681                 {
30682                     _this.setValue(_this.getValue());
30683                 }
30684             }
30685         });
30686
30687         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30688         
30689         this.monthField = new Roo.bootstrap.MonthField({
30690             after : '<i class=\"fa fa-calendar\"></i>',
30691             allowBlank : this.monthAllowBlank,
30692             placeholder : this.monthPlaceholder,
30693             readOnly : true,
30694             listeners : {
30695                 render : function (_self)
30696                 {
30697                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30698                         e.preventDefault();
30699                         _self.focus();
30700                     });
30701                 },
30702                 select : function (_self, oldvalue, newvalue)
30703                 {
30704                     _this.setValue(_this.getValue());
30705                 }
30706             }
30707         });
30708         
30709         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30710         
30711         this.yearField = new Roo.bootstrap.ComboBox({
30712             allowBlank : this.yearAllowBlank,
30713             alwaysQuery : true,
30714             displayField : 'value',
30715             editable : false,
30716             fieldLabel : '',
30717             forceSelection : true,
30718             mode : 'local',
30719             placeholder : this.yearPlaceholder,
30720             selectOnFocus : true,
30721             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30722             triggerAction : 'all',
30723             typeAhead : true,
30724             valueField : 'value',
30725             store : new Roo.data.SimpleStore({
30726                 data : (function() {
30727                     var years = [];
30728                     _this.fireEvent('years', _this, years);
30729                     return years;
30730                 })(),
30731                 fields : [ 'value' ]
30732             }),
30733             listeners : {
30734                 select : function (_self, record, index)
30735                 {
30736                     _this.setValue(_this.getValue());
30737                 }
30738             }
30739         });
30740
30741         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30742     },
30743     
30744     setValue : function(v, format)
30745     {
30746         this.inputEl.dom.value = v;
30747         
30748         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30749         
30750         var d = Date.parseDate(v, f);
30751         
30752         if(!d){
30753             this.validate();
30754             return;
30755         }
30756         
30757         this.setDay(d.format(this.dayFormat));
30758         this.setMonth(d.format(this.monthFormat));
30759         this.setYear(d.format(this.yearFormat));
30760         
30761         this.validate();
30762         
30763         return;
30764     },
30765     
30766     setDay : function(v)
30767     {
30768         this.dayField.setValue(v);
30769         this.inputEl.dom.value = this.getValue();
30770         this.validate();
30771         return;
30772     },
30773     
30774     setMonth : function(v)
30775     {
30776         this.monthField.setValue(v, true);
30777         this.inputEl.dom.value = this.getValue();
30778         this.validate();
30779         return;
30780     },
30781     
30782     setYear : function(v)
30783     {
30784         this.yearField.setValue(v);
30785         this.inputEl.dom.value = this.getValue();
30786         this.validate();
30787         return;
30788     },
30789     
30790     getDay : function()
30791     {
30792         return this.dayField.getValue();
30793     },
30794     
30795     getMonth : function()
30796     {
30797         return this.monthField.getValue();
30798     },
30799     
30800     getYear : function()
30801     {
30802         return this.yearField.getValue();
30803     },
30804     
30805     getValue : function()
30806     {
30807         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30808         
30809         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30810         
30811         return date;
30812     },
30813     
30814     reset : function()
30815     {
30816         this.setDay('');
30817         this.setMonth('');
30818         this.setYear('');
30819         this.inputEl.dom.value = '';
30820         this.validate();
30821         return;
30822     },
30823     
30824     validate : function()
30825     {
30826         var d = this.dayField.validate();
30827         var m = this.monthField.validate();
30828         var y = this.yearField.validate();
30829         
30830         var valid = true;
30831         
30832         if(
30833                 (!this.dayAllowBlank && !d) ||
30834                 (!this.monthAllowBlank && !m) ||
30835                 (!this.yearAllowBlank && !y)
30836         ){
30837             valid = false;
30838         }
30839         
30840         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30841             return valid;
30842         }
30843         
30844         if(valid){
30845             this.markValid();
30846             return valid;
30847         }
30848         
30849         this.markInvalid();
30850         
30851         return valid;
30852     },
30853     
30854     markValid : function()
30855     {
30856         
30857         var label = this.el.select('label', true).first();
30858         var icon = this.el.select('i.fa-star', true).first();
30859
30860         if(label && icon){
30861             icon.remove();
30862         }
30863         
30864         this.fireEvent('valid', this);
30865     },
30866     
30867      /**
30868      * Mark this field as invalid
30869      * @param {String} msg The validation message
30870      */
30871     markInvalid : function(msg)
30872     {
30873         
30874         var label = this.el.select('label', true).first();
30875         var icon = this.el.select('i.fa-star', true).first();
30876
30877         if(label && !icon){
30878             this.el.select('.roo-date-split-field-label', true).createChild({
30879                 tag : 'i',
30880                 cls : 'text-danger fa fa-lg fa-star',
30881                 tooltip : 'This field is required',
30882                 style : 'margin-right:5px;'
30883             }, label, true);
30884         }
30885         
30886         this.fireEvent('invalid', this, msg);
30887     },
30888     
30889     clearInvalid : function()
30890     {
30891         var label = this.el.select('label', true).first();
30892         var icon = this.el.select('i.fa-star', true).first();
30893
30894         if(label && icon){
30895             icon.remove();
30896         }
30897         
30898         this.fireEvent('valid', this);
30899     },
30900     
30901     getName: function()
30902     {
30903         return this.name;
30904     }
30905     
30906 });
30907
30908  /**
30909  *
30910  * This is based on 
30911  * http://masonry.desandro.com
30912  *
30913  * The idea is to render all the bricks based on vertical width...
30914  *
30915  * The original code extends 'outlayer' - we might need to use that....
30916  * 
30917  */
30918
30919
30920 /**
30921  * @class Roo.bootstrap.LayoutMasonry
30922  * @extends Roo.bootstrap.Component
30923  * Bootstrap Layout Masonry class
30924  * 
30925  * @constructor
30926  * Create a new Element
30927  * @param {Object} config The config object
30928  */
30929
30930 Roo.bootstrap.LayoutMasonry = function(config){
30931     
30932     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30933     
30934     this.bricks = [];
30935     
30936     Roo.bootstrap.LayoutMasonry.register(this);
30937     
30938     this.addEvents({
30939         // raw events
30940         /**
30941          * @event layout
30942          * Fire after layout the items
30943          * @param {Roo.bootstrap.LayoutMasonry} this
30944          * @param {Roo.EventObject} e
30945          */
30946         "layout" : true
30947     });
30948     
30949 };
30950
30951 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30952     
30953     /**
30954      * @cfg {Boolean} isLayoutInstant = no animation?
30955      */   
30956     isLayoutInstant : false, // needed?
30957    
30958     /**
30959      * @cfg {Number} boxWidth  width of the columns
30960      */   
30961     boxWidth : 450,
30962     
30963       /**
30964      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30965      */   
30966     boxHeight : 0,
30967     
30968     /**
30969      * @cfg {Number} padWidth padding below box..
30970      */   
30971     padWidth : 10, 
30972     
30973     /**
30974      * @cfg {Number} gutter gutter width..
30975      */   
30976     gutter : 10,
30977     
30978      /**
30979      * @cfg {Number} maxCols maximum number of columns
30980      */   
30981     
30982     maxCols: 0,
30983     
30984     /**
30985      * @cfg {Boolean} isAutoInitial defalut true
30986      */   
30987     isAutoInitial : true, 
30988     
30989     containerWidth: 0,
30990     
30991     /**
30992      * @cfg {Boolean} isHorizontal defalut false
30993      */   
30994     isHorizontal : false, 
30995
30996     currentSize : null,
30997     
30998     tag: 'div',
30999     
31000     cls: '',
31001     
31002     bricks: null, //CompositeElement
31003     
31004     cols : 1,
31005     
31006     _isLayoutInited : false,
31007     
31008 //    isAlternative : false, // only use for vertical layout...
31009     
31010     /**
31011      * @cfg {Number} alternativePadWidth padding below box..
31012      */   
31013     alternativePadWidth : 50,
31014     
31015     selectedBrick : [],
31016     
31017     getAutoCreate : function(){
31018         
31019         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31020         
31021         var cfg = {
31022             tag: this.tag,
31023             cls: 'blog-masonary-wrapper ' + this.cls,
31024             cn : {
31025                 cls : 'mas-boxes masonary'
31026             }
31027         };
31028         
31029         return cfg;
31030     },
31031     
31032     getChildContainer: function( )
31033     {
31034         if (this.boxesEl) {
31035             return this.boxesEl;
31036         }
31037         
31038         this.boxesEl = this.el.select('.mas-boxes').first();
31039         
31040         return this.boxesEl;
31041     },
31042     
31043     
31044     initEvents : function()
31045     {
31046         var _this = this;
31047         
31048         if(this.isAutoInitial){
31049             Roo.log('hook children rendered');
31050             this.on('childrenrendered', function() {
31051                 Roo.log('children rendered');
31052                 _this.initial();
31053             } ,this);
31054         }
31055     },
31056     
31057     initial : function()
31058     {
31059         this.selectedBrick = [];
31060         
31061         this.currentSize = this.el.getBox(true);
31062         
31063         Roo.EventManager.onWindowResize(this.resize, this); 
31064
31065         if(!this.isAutoInitial){
31066             this.layout();
31067             return;
31068         }
31069         
31070         this.layout();
31071         
31072         return;
31073         //this.layout.defer(500,this);
31074         
31075     },
31076     
31077     resize : function()
31078     {
31079         var cs = this.el.getBox(true);
31080         
31081         if (
31082                 this.currentSize.width == cs.width && 
31083                 this.currentSize.x == cs.x && 
31084                 this.currentSize.height == cs.height && 
31085                 this.currentSize.y == cs.y 
31086         ) {
31087             Roo.log("no change in with or X or Y");
31088             return;
31089         }
31090         
31091         this.currentSize = cs;
31092         
31093         this.layout();
31094         
31095     },
31096     
31097     layout : function()
31098     {   
31099         this._resetLayout();
31100         
31101         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31102         
31103         this.layoutItems( isInstant );
31104       
31105         this._isLayoutInited = true;
31106         
31107         this.fireEvent('layout', this);
31108         
31109     },
31110     
31111     _resetLayout : function()
31112     {
31113         if(this.isHorizontal){
31114             this.horizontalMeasureColumns();
31115             return;
31116         }
31117         
31118         this.verticalMeasureColumns();
31119         
31120     },
31121     
31122     verticalMeasureColumns : function()
31123     {
31124         this.getContainerWidth();
31125         
31126 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31127 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31128 //            return;
31129 //        }
31130         
31131         var boxWidth = this.boxWidth + this.padWidth;
31132         
31133         if(this.containerWidth < this.boxWidth){
31134             boxWidth = this.containerWidth
31135         }
31136         
31137         var containerWidth = this.containerWidth;
31138         
31139         var cols = Math.floor(containerWidth / boxWidth);
31140         
31141         this.cols = Math.max( cols, 1 );
31142         
31143         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31144         
31145         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31146         
31147         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31148         
31149         this.colWidth = boxWidth + avail - this.padWidth;
31150         
31151         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31152         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31153     },
31154     
31155     horizontalMeasureColumns : function()
31156     {
31157         this.getContainerWidth();
31158         
31159         var boxWidth = this.boxWidth;
31160         
31161         if(this.containerWidth < boxWidth){
31162             boxWidth = this.containerWidth;
31163         }
31164         
31165         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31166         
31167         this.el.setHeight(boxWidth);
31168         
31169     },
31170     
31171     getContainerWidth : function()
31172     {
31173         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31174     },
31175     
31176     layoutItems : function( isInstant )
31177     {
31178         Roo.log(this.bricks);
31179         
31180         var items = Roo.apply([], this.bricks);
31181         
31182         if(this.isHorizontal){
31183             this._horizontalLayoutItems( items , isInstant );
31184             return;
31185         }
31186         
31187 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31188 //            this._verticalAlternativeLayoutItems( items , isInstant );
31189 //            return;
31190 //        }
31191         
31192         this._verticalLayoutItems( items , isInstant );
31193         
31194     },
31195     
31196     _verticalLayoutItems : function ( items , isInstant)
31197     {
31198         if ( !items || !items.length ) {
31199             return;
31200         }
31201         
31202         var standard = [
31203             ['xs', 'xs', 'xs', 'tall'],
31204             ['xs', 'xs', 'tall'],
31205             ['xs', 'xs', 'sm'],
31206             ['xs', 'xs', 'xs'],
31207             ['xs', 'tall'],
31208             ['xs', 'sm'],
31209             ['xs', 'xs'],
31210             ['xs'],
31211             
31212             ['sm', 'xs', 'xs'],
31213             ['sm', 'xs'],
31214             ['sm'],
31215             
31216             ['tall', 'xs', 'xs', 'xs'],
31217             ['tall', 'xs', 'xs'],
31218             ['tall', 'xs'],
31219             ['tall']
31220             
31221         ];
31222         
31223         var queue = [];
31224         
31225         var boxes = [];
31226         
31227         var box = [];
31228         
31229         Roo.each(items, function(item, k){
31230             
31231             switch (item.size) {
31232                 // these layouts take up a full box,
31233                 case 'md' :
31234                 case 'md-left' :
31235                 case 'md-right' :
31236                 case 'wide' :
31237                     
31238                     if(box.length){
31239                         boxes.push(box);
31240                         box = [];
31241                     }
31242                     
31243                     boxes.push([item]);
31244                     
31245                     break;
31246                     
31247                 case 'xs' :
31248                 case 'sm' :
31249                 case 'tall' :
31250                     
31251                     box.push(item);
31252                     
31253                     break;
31254                 default :
31255                     break;
31256                     
31257             }
31258             
31259         }, this);
31260         
31261         if(box.length){
31262             boxes.push(box);
31263             box = [];
31264         }
31265         
31266         var filterPattern = function(box, length)
31267         {
31268             if(!box.length){
31269                 return;
31270             }
31271             
31272             var match = false;
31273             
31274             var pattern = box.slice(0, length);
31275             
31276             var format = [];
31277             
31278             Roo.each(pattern, function(i){
31279                 format.push(i.size);
31280             }, this);
31281             
31282             Roo.each(standard, function(s){
31283                 
31284                 if(String(s) != String(format)){
31285                     return;
31286                 }
31287                 
31288                 match = true;
31289                 return false;
31290                 
31291             }, this);
31292             
31293             if(!match && length == 1){
31294                 return;
31295             }
31296             
31297             if(!match){
31298                 filterPattern(box, length - 1);
31299                 return;
31300             }
31301                 
31302             queue.push(pattern);
31303
31304             box = box.slice(length, box.length);
31305
31306             filterPattern(box, 4);
31307
31308             return;
31309             
31310         }
31311         
31312         Roo.each(boxes, function(box, k){
31313             
31314             if(!box.length){
31315                 return;
31316             }
31317             
31318             if(box.length == 1){
31319                 queue.push(box);
31320                 return;
31321             }
31322             
31323             filterPattern(box, 4);
31324             
31325         }, this);
31326         
31327         this._processVerticalLayoutQueue( queue, isInstant );
31328         
31329     },
31330     
31331 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31332 //    {
31333 //        if ( !items || !items.length ) {
31334 //            return;
31335 //        }
31336 //
31337 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31338 //        
31339 //    },
31340     
31341     _horizontalLayoutItems : function ( items , isInstant)
31342     {
31343         if ( !items || !items.length || items.length < 3) {
31344             return;
31345         }
31346         
31347         items.reverse();
31348         
31349         var eItems = items.slice(0, 3);
31350         
31351         items = items.slice(3, items.length);
31352         
31353         var standard = [
31354             ['xs', 'xs', 'xs', 'wide'],
31355             ['xs', 'xs', 'wide'],
31356             ['xs', 'xs', 'sm'],
31357             ['xs', 'xs', 'xs'],
31358             ['xs', 'wide'],
31359             ['xs', 'sm'],
31360             ['xs', 'xs'],
31361             ['xs'],
31362             
31363             ['sm', 'xs', 'xs'],
31364             ['sm', 'xs'],
31365             ['sm'],
31366             
31367             ['wide', 'xs', 'xs', 'xs'],
31368             ['wide', 'xs', 'xs'],
31369             ['wide', 'xs'],
31370             ['wide'],
31371             
31372             ['wide-thin']
31373         ];
31374         
31375         var queue = [];
31376         
31377         var boxes = [];
31378         
31379         var box = [];
31380         
31381         Roo.each(items, function(item, k){
31382             
31383             switch (item.size) {
31384                 case 'md' :
31385                 case 'md-left' :
31386                 case 'md-right' :
31387                 case 'tall' :
31388                     
31389                     if(box.length){
31390                         boxes.push(box);
31391                         box = [];
31392                     }
31393                     
31394                     boxes.push([item]);
31395                     
31396                     break;
31397                     
31398                 case 'xs' :
31399                 case 'sm' :
31400                 case 'wide' :
31401                 case 'wide-thin' :
31402                     
31403                     box.push(item);
31404                     
31405                     break;
31406                 default :
31407                     break;
31408                     
31409             }
31410             
31411         }, this);
31412         
31413         if(box.length){
31414             boxes.push(box);
31415             box = [];
31416         }
31417         
31418         var filterPattern = function(box, length)
31419         {
31420             if(!box.length){
31421                 return;
31422             }
31423             
31424             var match = false;
31425             
31426             var pattern = box.slice(0, length);
31427             
31428             var format = [];
31429             
31430             Roo.each(pattern, function(i){
31431                 format.push(i.size);
31432             }, this);
31433             
31434             Roo.each(standard, function(s){
31435                 
31436                 if(String(s) != String(format)){
31437                     return;
31438                 }
31439                 
31440                 match = true;
31441                 return false;
31442                 
31443             }, this);
31444             
31445             if(!match && length == 1){
31446                 return;
31447             }
31448             
31449             if(!match){
31450                 filterPattern(box, length - 1);
31451                 return;
31452             }
31453                 
31454             queue.push(pattern);
31455
31456             box = box.slice(length, box.length);
31457
31458             filterPattern(box, 4);
31459
31460             return;
31461             
31462         }
31463         
31464         Roo.each(boxes, function(box, k){
31465             
31466             if(!box.length){
31467                 return;
31468             }
31469             
31470             if(box.length == 1){
31471                 queue.push(box);
31472                 return;
31473             }
31474             
31475             filterPattern(box, 4);
31476             
31477         }, this);
31478         
31479         
31480         var prune = [];
31481         
31482         var pos = this.el.getBox(true);
31483         
31484         var minX = pos.x;
31485         
31486         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31487         
31488         var hit_end = false;
31489         
31490         Roo.each(queue, function(box){
31491             
31492             if(hit_end){
31493                 
31494                 Roo.each(box, function(b){
31495                 
31496                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31497                     b.el.hide();
31498
31499                 }, this);
31500
31501                 return;
31502             }
31503             
31504             var mx = 0;
31505             
31506             Roo.each(box, function(b){
31507                 
31508                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31509                 b.el.show();
31510
31511                 mx = Math.max(mx, b.x);
31512                 
31513             }, this);
31514             
31515             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31516             
31517             if(maxX < minX){
31518                 
31519                 Roo.each(box, function(b){
31520                 
31521                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31522                     b.el.hide();
31523                     
31524                 }, this);
31525                 
31526                 hit_end = true;
31527                 
31528                 return;
31529             }
31530             
31531             prune.push(box);
31532             
31533         }, this);
31534         
31535         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31536     },
31537     
31538     /** Sets position of item in DOM
31539     * @param {Element} item
31540     * @param {Number} x - horizontal position
31541     * @param {Number} y - vertical position
31542     * @param {Boolean} isInstant - disables transitions
31543     */
31544     _processVerticalLayoutQueue : function( queue, isInstant )
31545     {
31546         var pos = this.el.getBox(true);
31547         var x = pos.x;
31548         var y = pos.y;
31549         var maxY = [];
31550         
31551         for (var i = 0; i < this.cols; i++){
31552             maxY[i] = pos.y;
31553         }
31554         
31555         Roo.each(queue, function(box, k){
31556             
31557             var col = k % this.cols;
31558             
31559             Roo.each(box, function(b,kk){
31560                 
31561                 b.el.position('absolute');
31562                 
31563                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31564                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31565                 
31566                 if(b.size == 'md-left' || b.size == 'md-right'){
31567                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31568                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31569                 }
31570                 
31571                 b.el.setWidth(width);
31572                 b.el.setHeight(height);
31573                 // iframe?
31574                 b.el.select('iframe',true).setSize(width,height);
31575                 
31576             }, this);
31577             
31578             for (var i = 0; i < this.cols; i++){
31579                 
31580                 if(maxY[i] < maxY[col]){
31581                     col = i;
31582                     continue;
31583                 }
31584                 
31585                 col = Math.min(col, i);
31586                 
31587             }
31588             
31589             x = pos.x + col * (this.colWidth + this.padWidth);
31590             
31591             y = maxY[col];
31592             
31593             var positions = [];
31594             
31595             switch (box.length){
31596                 case 1 :
31597                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31598                     break;
31599                 case 2 :
31600                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31601                     break;
31602                 case 3 :
31603                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31604                     break;
31605                 case 4 :
31606                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31607                     break;
31608                 default :
31609                     break;
31610             }
31611             
31612             Roo.each(box, function(b,kk){
31613                 
31614                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31615                 
31616                 var sz = b.el.getSize();
31617                 
31618                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31619                 
31620             }, this);
31621             
31622         }, this);
31623         
31624         var mY = 0;
31625         
31626         for (var i = 0; i < this.cols; i++){
31627             mY = Math.max(mY, maxY[i]);
31628         }
31629         
31630         this.el.setHeight(mY - pos.y);
31631         
31632     },
31633     
31634 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31635 //    {
31636 //        var pos = this.el.getBox(true);
31637 //        var x = pos.x;
31638 //        var y = pos.y;
31639 //        var maxX = pos.right;
31640 //        
31641 //        var maxHeight = 0;
31642 //        
31643 //        Roo.each(items, function(item, k){
31644 //            
31645 //            var c = k % 2;
31646 //            
31647 //            item.el.position('absolute');
31648 //                
31649 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31650 //
31651 //            item.el.setWidth(width);
31652 //
31653 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31654 //
31655 //            item.el.setHeight(height);
31656 //            
31657 //            if(c == 0){
31658 //                item.el.setXY([x, y], isInstant ? false : true);
31659 //            } else {
31660 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31661 //            }
31662 //            
31663 //            y = y + height + this.alternativePadWidth;
31664 //            
31665 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31666 //            
31667 //        }, this);
31668 //        
31669 //        this.el.setHeight(maxHeight);
31670 //        
31671 //    },
31672     
31673     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31674     {
31675         var pos = this.el.getBox(true);
31676         
31677         var minX = pos.x;
31678         var minY = pos.y;
31679         
31680         var maxX = pos.right;
31681         
31682         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31683         
31684         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31685         
31686         Roo.each(queue, function(box, k){
31687             
31688             Roo.each(box, function(b, kk){
31689                 
31690                 b.el.position('absolute');
31691                 
31692                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31693                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31694                 
31695                 if(b.size == 'md-left' || b.size == 'md-right'){
31696                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31697                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31698                 }
31699                 
31700                 b.el.setWidth(width);
31701                 b.el.setHeight(height);
31702                 
31703             }, this);
31704             
31705             if(!box.length){
31706                 return;
31707             }
31708             
31709             var positions = [];
31710             
31711             switch (box.length){
31712                 case 1 :
31713                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31714                     break;
31715                 case 2 :
31716                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31717                     break;
31718                 case 3 :
31719                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31720                     break;
31721                 case 4 :
31722                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31723                     break;
31724                 default :
31725                     break;
31726             }
31727             
31728             Roo.each(box, function(b,kk){
31729                 
31730                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31731                 
31732                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31733                 
31734             }, this);
31735             
31736         }, this);
31737         
31738     },
31739     
31740     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31741     {
31742         Roo.each(eItems, function(b,k){
31743             
31744             b.size = (k == 0) ? 'sm' : 'xs';
31745             b.x = (k == 0) ? 2 : 1;
31746             b.y = (k == 0) ? 2 : 1;
31747             
31748             b.el.position('absolute');
31749             
31750             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31751                 
31752             b.el.setWidth(width);
31753             
31754             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31755             
31756             b.el.setHeight(height);
31757             
31758         }, this);
31759
31760         var positions = [];
31761         
31762         positions.push({
31763             x : maxX - this.unitWidth * 2 - this.gutter,
31764             y : minY
31765         });
31766         
31767         positions.push({
31768             x : maxX - this.unitWidth,
31769             y : minY + (this.unitWidth + this.gutter) * 2
31770         });
31771         
31772         positions.push({
31773             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31774             y : minY
31775         });
31776         
31777         Roo.each(eItems, function(b,k){
31778             
31779             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31780
31781         }, this);
31782         
31783     },
31784     
31785     getVerticalOneBoxColPositions : function(x, y, box)
31786     {
31787         var pos = [];
31788         
31789         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31790         
31791         if(box[0].size == 'md-left'){
31792             rand = 0;
31793         }
31794         
31795         if(box[0].size == 'md-right'){
31796             rand = 1;
31797         }
31798         
31799         pos.push({
31800             x : x + (this.unitWidth + this.gutter) * rand,
31801             y : y
31802         });
31803         
31804         return pos;
31805     },
31806     
31807     getVerticalTwoBoxColPositions : function(x, y, box)
31808     {
31809         var pos = [];
31810         
31811         if(box[0].size == 'xs'){
31812             
31813             pos.push({
31814                 x : x,
31815                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31816             });
31817
31818             pos.push({
31819                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31820                 y : y
31821             });
31822             
31823             return pos;
31824             
31825         }
31826         
31827         pos.push({
31828             x : x,
31829             y : y
31830         });
31831
31832         pos.push({
31833             x : x + (this.unitWidth + this.gutter) * 2,
31834             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31835         });
31836         
31837         return pos;
31838         
31839     },
31840     
31841     getVerticalThreeBoxColPositions : function(x, y, box)
31842     {
31843         var pos = [];
31844         
31845         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31846             
31847             pos.push({
31848                 x : x,
31849                 y : y
31850             });
31851
31852             pos.push({
31853                 x : x + (this.unitWidth + this.gutter) * 1,
31854                 y : y
31855             });
31856             
31857             pos.push({
31858                 x : x + (this.unitWidth + this.gutter) * 2,
31859                 y : y
31860             });
31861             
31862             return pos;
31863             
31864         }
31865         
31866         if(box[0].size == 'xs' && box[1].size == 'xs'){
31867             
31868             pos.push({
31869                 x : x,
31870                 y : y
31871             });
31872
31873             pos.push({
31874                 x : x,
31875                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31876             });
31877             
31878             pos.push({
31879                 x : x + (this.unitWidth + this.gutter) * 1,
31880                 y : y
31881             });
31882             
31883             return pos;
31884             
31885         }
31886         
31887         pos.push({
31888             x : x,
31889             y : y
31890         });
31891
31892         pos.push({
31893             x : x + (this.unitWidth + this.gutter) * 2,
31894             y : y
31895         });
31896
31897         pos.push({
31898             x : x + (this.unitWidth + this.gutter) * 2,
31899             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31900         });
31901             
31902         return pos;
31903         
31904     },
31905     
31906     getVerticalFourBoxColPositions : function(x, y, box)
31907     {
31908         var pos = [];
31909         
31910         if(box[0].size == 'xs'){
31911             
31912             pos.push({
31913                 x : x,
31914                 y : y
31915             });
31916
31917             pos.push({
31918                 x : x,
31919                 y : y + (this.unitHeight + this.gutter) * 1
31920             });
31921             
31922             pos.push({
31923                 x : x,
31924                 y : y + (this.unitHeight + this.gutter) * 2
31925             });
31926             
31927             pos.push({
31928                 x : x + (this.unitWidth + this.gutter) * 1,
31929                 y : y
31930             });
31931             
31932             return pos;
31933             
31934         }
31935         
31936         pos.push({
31937             x : x,
31938             y : y
31939         });
31940
31941         pos.push({
31942             x : x + (this.unitWidth + this.gutter) * 2,
31943             y : y
31944         });
31945
31946         pos.push({
31947             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31948             y : y + (this.unitHeight + this.gutter) * 1
31949         });
31950
31951         pos.push({
31952             x : x + (this.unitWidth + this.gutter) * 2,
31953             y : y + (this.unitWidth + this.gutter) * 2
31954         });
31955
31956         return pos;
31957         
31958     },
31959     
31960     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31961     {
31962         var pos = [];
31963         
31964         if(box[0].size == 'md-left'){
31965             pos.push({
31966                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31967                 y : minY
31968             });
31969             
31970             return pos;
31971         }
31972         
31973         if(box[0].size == 'md-right'){
31974             pos.push({
31975                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31976                 y : minY + (this.unitWidth + this.gutter) * 1
31977             });
31978             
31979             return pos;
31980         }
31981         
31982         var rand = Math.floor(Math.random() * (4 - box[0].y));
31983         
31984         pos.push({
31985             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31986             y : minY + (this.unitWidth + this.gutter) * rand
31987         });
31988         
31989         return pos;
31990         
31991     },
31992     
31993     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31994     {
31995         var pos = [];
31996         
31997         if(box[0].size == 'xs'){
31998             
31999             pos.push({
32000                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32001                 y : minY
32002             });
32003
32004             pos.push({
32005                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32006                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32007             });
32008             
32009             return pos;
32010             
32011         }
32012         
32013         pos.push({
32014             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32015             y : minY
32016         });
32017
32018         pos.push({
32019             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32020             y : minY + (this.unitWidth + this.gutter) * 2
32021         });
32022         
32023         return pos;
32024         
32025     },
32026     
32027     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32028     {
32029         var pos = [];
32030         
32031         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32032             
32033             pos.push({
32034                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32035                 y : minY
32036             });
32037
32038             pos.push({
32039                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32040                 y : minY + (this.unitWidth + this.gutter) * 1
32041             });
32042             
32043             pos.push({
32044                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32045                 y : minY + (this.unitWidth + this.gutter) * 2
32046             });
32047             
32048             return pos;
32049             
32050         }
32051         
32052         if(box[0].size == 'xs' && box[1].size == 'xs'){
32053             
32054             pos.push({
32055                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32056                 y : minY
32057             });
32058
32059             pos.push({
32060                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32061                 y : minY
32062             });
32063             
32064             pos.push({
32065                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32066                 y : minY + (this.unitWidth + this.gutter) * 1
32067             });
32068             
32069             return pos;
32070             
32071         }
32072         
32073         pos.push({
32074             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32075             y : minY
32076         });
32077
32078         pos.push({
32079             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32080             y : minY + (this.unitWidth + this.gutter) * 2
32081         });
32082
32083         pos.push({
32084             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32085             y : minY + (this.unitWidth + this.gutter) * 2
32086         });
32087             
32088         return pos;
32089         
32090     },
32091     
32092     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32093     {
32094         var pos = [];
32095         
32096         if(box[0].size == 'xs'){
32097             
32098             pos.push({
32099                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32100                 y : minY
32101             });
32102
32103             pos.push({
32104                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32105                 y : minY
32106             });
32107             
32108             pos.push({
32109                 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),
32110                 y : minY
32111             });
32112             
32113             pos.push({
32114                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32115                 y : minY + (this.unitWidth + this.gutter) * 1
32116             });
32117             
32118             return pos;
32119             
32120         }
32121         
32122         pos.push({
32123             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32124             y : minY
32125         });
32126         
32127         pos.push({
32128             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32129             y : minY + (this.unitWidth + this.gutter) * 2
32130         });
32131         
32132         pos.push({
32133             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32134             y : minY + (this.unitWidth + this.gutter) * 2
32135         });
32136         
32137         pos.push({
32138             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),
32139             y : minY + (this.unitWidth + this.gutter) * 2
32140         });
32141
32142         return pos;
32143         
32144     },
32145     
32146     /**
32147     * remove a Masonry Brick
32148     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32149     */
32150     removeBrick : function(brick_id)
32151     {
32152         if (!brick_id) {
32153             return;
32154         }
32155         
32156         for (var i = 0; i<this.bricks.length; i++) {
32157             if (this.bricks[i].id == brick_id) {
32158                 this.bricks.splice(i,1);
32159                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32160                 this.initial();
32161             }
32162         }
32163     },
32164     
32165     /**
32166     * adds a Masonry Brick
32167     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32168     */
32169     addBrick : function(cfg)
32170     {
32171         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32172         //this.register(cn);
32173         cn.parentId = this.id;
32174         cn.render(this.el);
32175         return cn;
32176     },
32177     
32178     /**
32179     * register a Masonry Brick
32180     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32181     */
32182     
32183     register : function(brick)
32184     {
32185         this.bricks.push(brick);
32186         brick.masonryId = this.id;
32187     },
32188     
32189     /**
32190     * clear all the Masonry Brick
32191     */
32192     clearAll : function()
32193     {
32194         this.bricks = [];
32195         //this.getChildContainer().dom.innerHTML = "";
32196         this.el.dom.innerHTML = '';
32197     },
32198     
32199     getSelected : function()
32200     {
32201         if (!this.selectedBrick) {
32202             return false;
32203         }
32204         
32205         return this.selectedBrick;
32206     }
32207 });
32208
32209 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32210     
32211     groups: {},
32212      /**
32213     * register a Masonry Layout
32214     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32215     */
32216     
32217     register : function(layout)
32218     {
32219         this.groups[layout.id] = layout;
32220     },
32221     /**
32222     * fetch a  Masonry Layout based on the masonry layout ID
32223     * @param {string} the masonry layout to add
32224     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32225     */
32226     
32227     get: function(layout_id) {
32228         if (typeof(this.groups[layout_id]) == 'undefined') {
32229             return false;
32230         }
32231         return this.groups[layout_id] ;
32232     }
32233     
32234     
32235     
32236 });
32237
32238  
32239
32240  /**
32241  *
32242  * This is based on 
32243  * http://masonry.desandro.com
32244  *
32245  * The idea is to render all the bricks based on vertical width...
32246  *
32247  * The original code extends 'outlayer' - we might need to use that....
32248  * 
32249  */
32250
32251
32252 /**
32253  * @class Roo.bootstrap.LayoutMasonryAuto
32254  * @extends Roo.bootstrap.Component
32255  * Bootstrap Layout Masonry class
32256  * 
32257  * @constructor
32258  * Create a new Element
32259  * @param {Object} config The config object
32260  */
32261
32262 Roo.bootstrap.LayoutMasonryAuto = function(config){
32263     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32264 };
32265
32266 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32267     
32268       /**
32269      * @cfg {Boolean} isFitWidth  - resize the width..
32270      */   
32271     isFitWidth : false,  // options..
32272     /**
32273      * @cfg {Boolean} isOriginLeft = left align?
32274      */   
32275     isOriginLeft : true,
32276     /**
32277      * @cfg {Boolean} isOriginTop = top align?
32278      */   
32279     isOriginTop : false,
32280     /**
32281      * @cfg {Boolean} isLayoutInstant = no animation?
32282      */   
32283     isLayoutInstant : false, // needed?
32284     /**
32285      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32286      */   
32287     isResizingContainer : true,
32288     /**
32289      * @cfg {Number} columnWidth  width of the columns 
32290      */   
32291     
32292     columnWidth : 0,
32293     
32294     /**
32295      * @cfg {Number} maxCols maximum number of columns
32296      */   
32297     
32298     maxCols: 0,
32299     /**
32300      * @cfg {Number} padHeight padding below box..
32301      */   
32302     
32303     padHeight : 10, 
32304     
32305     /**
32306      * @cfg {Boolean} isAutoInitial defalut true
32307      */   
32308     
32309     isAutoInitial : true, 
32310     
32311     // private?
32312     gutter : 0,
32313     
32314     containerWidth: 0,
32315     initialColumnWidth : 0,
32316     currentSize : null,
32317     
32318     colYs : null, // array.
32319     maxY : 0,
32320     padWidth: 10,
32321     
32322     
32323     tag: 'div',
32324     cls: '',
32325     bricks: null, //CompositeElement
32326     cols : 0, // array?
32327     // element : null, // wrapped now this.el
32328     _isLayoutInited : null, 
32329     
32330     
32331     getAutoCreate : function(){
32332         
32333         var cfg = {
32334             tag: this.tag,
32335             cls: 'blog-masonary-wrapper ' + this.cls,
32336             cn : {
32337                 cls : 'mas-boxes masonary'
32338             }
32339         };
32340         
32341         return cfg;
32342     },
32343     
32344     getChildContainer: function( )
32345     {
32346         if (this.boxesEl) {
32347             return this.boxesEl;
32348         }
32349         
32350         this.boxesEl = this.el.select('.mas-boxes').first();
32351         
32352         return this.boxesEl;
32353     },
32354     
32355     
32356     initEvents : function()
32357     {
32358         var _this = this;
32359         
32360         if(this.isAutoInitial){
32361             Roo.log('hook children rendered');
32362             this.on('childrenrendered', function() {
32363                 Roo.log('children rendered');
32364                 _this.initial();
32365             } ,this);
32366         }
32367         
32368     },
32369     
32370     initial : function()
32371     {
32372         this.reloadItems();
32373
32374         this.currentSize = this.el.getBox(true);
32375
32376         /// was window resize... - let's see if this works..
32377         Roo.EventManager.onWindowResize(this.resize, this); 
32378
32379         if(!this.isAutoInitial){
32380             this.layout();
32381             return;
32382         }
32383         
32384         this.layout.defer(500,this);
32385     },
32386     
32387     reloadItems: function()
32388     {
32389         this.bricks = this.el.select('.masonry-brick', true);
32390         
32391         this.bricks.each(function(b) {
32392             //Roo.log(b.getSize());
32393             if (!b.attr('originalwidth')) {
32394                 b.attr('originalwidth',  b.getSize().width);
32395             }
32396             
32397         });
32398         
32399         Roo.log(this.bricks.elements.length);
32400     },
32401     
32402     resize : function()
32403     {
32404         Roo.log('resize');
32405         var cs = this.el.getBox(true);
32406         
32407         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32408             Roo.log("no change in with or X");
32409             return;
32410         }
32411         this.currentSize = cs;
32412         this.layout();
32413     },
32414     
32415     layout : function()
32416     {
32417          Roo.log('layout');
32418         this._resetLayout();
32419         //this._manageStamps();
32420       
32421         // don't animate first layout
32422         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32423         this.layoutItems( isInstant );
32424       
32425         // flag for initalized
32426         this._isLayoutInited = true;
32427     },
32428     
32429     layoutItems : function( isInstant )
32430     {
32431         //var items = this._getItemsForLayout( this.items );
32432         // original code supports filtering layout items.. we just ignore it..
32433         
32434         this._layoutItems( this.bricks , isInstant );
32435       
32436         this._postLayout();
32437     },
32438     _layoutItems : function ( items , isInstant)
32439     {
32440        //this.fireEvent( 'layout', this, items );
32441     
32442
32443         if ( !items || !items.elements.length ) {
32444           // no items, emit event with empty array
32445             return;
32446         }
32447
32448         var queue = [];
32449         items.each(function(item) {
32450             Roo.log("layout item");
32451             Roo.log(item);
32452             // get x/y object from method
32453             var position = this._getItemLayoutPosition( item );
32454             // enqueue
32455             position.item = item;
32456             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32457             queue.push( position );
32458         }, this);
32459       
32460         this._processLayoutQueue( queue );
32461     },
32462     /** Sets position of item in DOM
32463     * @param {Element} item
32464     * @param {Number} x - horizontal position
32465     * @param {Number} y - vertical position
32466     * @param {Boolean} isInstant - disables transitions
32467     */
32468     _processLayoutQueue : function( queue )
32469     {
32470         for ( var i=0, len = queue.length; i < len; i++ ) {
32471             var obj = queue[i];
32472             obj.item.position('absolute');
32473             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32474         }
32475     },
32476       
32477     
32478     /**
32479     * Any logic you want to do after each layout,
32480     * i.e. size the container
32481     */
32482     _postLayout : function()
32483     {
32484         this.resizeContainer();
32485     },
32486     
32487     resizeContainer : function()
32488     {
32489         if ( !this.isResizingContainer ) {
32490             return;
32491         }
32492         var size = this._getContainerSize();
32493         if ( size ) {
32494             this.el.setSize(size.width,size.height);
32495             this.boxesEl.setSize(size.width,size.height);
32496         }
32497     },
32498     
32499     
32500     
32501     _resetLayout : function()
32502     {
32503         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32504         this.colWidth = this.el.getWidth();
32505         //this.gutter = this.el.getWidth(); 
32506         
32507         this.measureColumns();
32508
32509         // reset column Y
32510         var i = this.cols;
32511         this.colYs = [];
32512         while (i--) {
32513             this.colYs.push( 0 );
32514         }
32515     
32516         this.maxY = 0;
32517     },
32518
32519     measureColumns : function()
32520     {
32521         this.getContainerWidth();
32522       // if columnWidth is 0, default to outerWidth of first item
32523         if ( !this.columnWidth ) {
32524             var firstItem = this.bricks.first();
32525             Roo.log(firstItem);
32526             this.columnWidth  = this.containerWidth;
32527             if (firstItem && firstItem.attr('originalwidth') ) {
32528                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32529             }
32530             // columnWidth fall back to item of first element
32531             Roo.log("set column width?");
32532                         this.initialColumnWidth = this.columnWidth  ;
32533
32534             // if first elem has no width, default to size of container
32535             
32536         }
32537         
32538         
32539         if (this.initialColumnWidth) {
32540             this.columnWidth = this.initialColumnWidth;
32541         }
32542         
32543         
32544             
32545         // column width is fixed at the top - however if container width get's smaller we should
32546         // reduce it...
32547         
32548         // this bit calcs how man columns..
32549             
32550         var columnWidth = this.columnWidth += this.gutter;
32551       
32552         // calculate columns
32553         var containerWidth = this.containerWidth + this.gutter;
32554         
32555         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32556         // fix rounding errors, typically with gutters
32557         var excess = columnWidth - containerWidth % columnWidth;
32558         
32559         
32560         // if overshoot is less than a pixel, round up, otherwise floor it
32561         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32562         cols = Math[ mathMethod ]( cols );
32563         this.cols = Math.max( cols, 1 );
32564         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32565         
32566          // padding positioning..
32567         var totalColWidth = this.cols * this.columnWidth;
32568         var padavail = this.containerWidth - totalColWidth;
32569         // so for 2 columns - we need 3 'pads'
32570         
32571         var padNeeded = (1+this.cols) * this.padWidth;
32572         
32573         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32574         
32575         this.columnWidth += padExtra
32576         //this.padWidth = Math.floor(padavail /  ( this.cols));
32577         
32578         // adjust colum width so that padding is fixed??
32579         
32580         // we have 3 columns ... total = width * 3
32581         // we have X left over... that should be used by 
32582         
32583         //if (this.expandC) {
32584             
32585         //}
32586         
32587         
32588         
32589     },
32590     
32591     getContainerWidth : function()
32592     {
32593        /* // container is parent if fit width
32594         var container = this.isFitWidth ? this.element.parentNode : this.element;
32595         // check that this.size and size are there
32596         // IE8 triggers resize on body size change, so they might not be
32597         
32598         var size = getSize( container );  //FIXME
32599         this.containerWidth = size && size.innerWidth; //FIXME
32600         */
32601          
32602         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32603         
32604     },
32605     
32606     _getItemLayoutPosition : function( item )  // what is item?
32607     {
32608         // we resize the item to our columnWidth..
32609       
32610         item.setWidth(this.columnWidth);
32611         item.autoBoxAdjust  = false;
32612         
32613         var sz = item.getSize();
32614  
32615         // how many columns does this brick span
32616         var remainder = this.containerWidth % this.columnWidth;
32617         
32618         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32619         // round if off by 1 pixel, otherwise use ceil
32620         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32621         colSpan = Math.min( colSpan, this.cols );
32622         
32623         // normally this should be '1' as we dont' currently allow multi width columns..
32624         
32625         var colGroup = this._getColGroup( colSpan );
32626         // get the minimum Y value from the columns
32627         var minimumY = Math.min.apply( Math, colGroup );
32628         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32629         
32630         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32631          
32632         // position the brick
32633         var position = {
32634             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32635             y: this.currentSize.y + minimumY + this.padHeight
32636         };
32637         
32638         Roo.log(position);
32639         // apply setHeight to necessary columns
32640         var setHeight = minimumY + sz.height + this.padHeight;
32641         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32642         
32643         var setSpan = this.cols + 1 - colGroup.length;
32644         for ( var i = 0; i < setSpan; i++ ) {
32645           this.colYs[ shortColIndex + i ] = setHeight ;
32646         }
32647       
32648         return position;
32649     },
32650     
32651     /**
32652      * @param {Number} colSpan - number of columns the element spans
32653      * @returns {Array} colGroup
32654      */
32655     _getColGroup : function( colSpan )
32656     {
32657         if ( colSpan < 2 ) {
32658           // if brick spans only one column, use all the column Ys
32659           return this.colYs;
32660         }
32661       
32662         var colGroup = [];
32663         // how many different places could this brick fit horizontally
32664         var groupCount = this.cols + 1 - colSpan;
32665         // for each group potential horizontal position
32666         for ( var i = 0; i < groupCount; i++ ) {
32667           // make an array of colY values for that one group
32668           var groupColYs = this.colYs.slice( i, i + colSpan );
32669           // and get the max value of the array
32670           colGroup[i] = Math.max.apply( Math, groupColYs );
32671         }
32672         return colGroup;
32673     },
32674     /*
32675     _manageStamp : function( stamp )
32676     {
32677         var stampSize =  stamp.getSize();
32678         var offset = stamp.getBox();
32679         // get the columns that this stamp affects
32680         var firstX = this.isOriginLeft ? offset.x : offset.right;
32681         var lastX = firstX + stampSize.width;
32682         var firstCol = Math.floor( firstX / this.columnWidth );
32683         firstCol = Math.max( 0, firstCol );
32684         
32685         var lastCol = Math.floor( lastX / this.columnWidth );
32686         // lastCol should not go over if multiple of columnWidth #425
32687         lastCol -= lastX % this.columnWidth ? 0 : 1;
32688         lastCol = Math.min( this.cols - 1, lastCol );
32689         
32690         // set colYs to bottom of the stamp
32691         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32692             stampSize.height;
32693             
32694         for ( var i = firstCol; i <= lastCol; i++ ) {
32695           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32696         }
32697     },
32698     */
32699     
32700     _getContainerSize : function()
32701     {
32702         this.maxY = Math.max.apply( Math, this.colYs );
32703         var size = {
32704             height: this.maxY
32705         };
32706       
32707         if ( this.isFitWidth ) {
32708             size.width = this._getContainerFitWidth();
32709         }
32710       
32711         return size;
32712     },
32713     
32714     _getContainerFitWidth : function()
32715     {
32716         var unusedCols = 0;
32717         // count unused columns
32718         var i = this.cols;
32719         while ( --i ) {
32720           if ( this.colYs[i] !== 0 ) {
32721             break;
32722           }
32723           unusedCols++;
32724         }
32725         // fit container to columns that have been used
32726         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32727     },
32728     
32729     needsResizeLayout : function()
32730     {
32731         var previousWidth = this.containerWidth;
32732         this.getContainerWidth();
32733         return previousWidth !== this.containerWidth;
32734     }
32735  
32736 });
32737
32738  
32739
32740  /*
32741  * - LGPL
32742  *
32743  * element
32744  * 
32745  */
32746
32747 /**
32748  * @class Roo.bootstrap.MasonryBrick
32749  * @extends Roo.bootstrap.Component
32750  * Bootstrap MasonryBrick class
32751  * 
32752  * @constructor
32753  * Create a new MasonryBrick
32754  * @param {Object} config The config object
32755  */
32756
32757 Roo.bootstrap.MasonryBrick = function(config){
32758     
32759     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32760     
32761     Roo.bootstrap.MasonryBrick.register(this);
32762     
32763     this.addEvents({
32764         // raw events
32765         /**
32766          * @event click
32767          * When a MasonryBrick is clcik
32768          * @param {Roo.bootstrap.MasonryBrick} this
32769          * @param {Roo.EventObject} e
32770          */
32771         "click" : true
32772     });
32773 };
32774
32775 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32776     
32777     /**
32778      * @cfg {String} title
32779      */   
32780     title : '',
32781     /**
32782      * @cfg {String} html
32783      */   
32784     html : '',
32785     /**
32786      * @cfg {String} bgimage
32787      */   
32788     bgimage : '',
32789     /**
32790      * @cfg {String} videourl
32791      */   
32792     videourl : '',
32793     /**
32794      * @cfg {String} cls
32795      */   
32796     cls : '',
32797     /**
32798      * @cfg {String} href
32799      */   
32800     href : '',
32801     /**
32802      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32803      */   
32804     size : 'xs',
32805     
32806     /**
32807      * @cfg {String} placetitle (center|bottom)
32808      */   
32809     placetitle : '',
32810     
32811     /**
32812      * @cfg {Boolean} isFitContainer defalut true
32813      */   
32814     isFitContainer : true, 
32815     
32816     /**
32817      * @cfg {Boolean} preventDefault defalut false
32818      */   
32819     preventDefault : false, 
32820     
32821     /**
32822      * @cfg {Boolean} inverse defalut false
32823      */   
32824     maskInverse : false, 
32825     
32826     getAutoCreate : function()
32827     {
32828         if(!this.isFitContainer){
32829             return this.getSplitAutoCreate();
32830         }
32831         
32832         var cls = 'masonry-brick masonry-brick-full';
32833         
32834         if(this.href.length){
32835             cls += ' masonry-brick-link';
32836         }
32837         
32838         if(this.bgimage.length){
32839             cls += ' masonry-brick-image';
32840         }
32841         
32842         if(this.maskInverse){
32843             cls += ' mask-inverse';
32844         }
32845         
32846         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32847             cls += ' enable-mask';
32848         }
32849         
32850         if(this.size){
32851             cls += ' masonry-' + this.size + '-brick';
32852         }
32853         
32854         if(this.placetitle.length){
32855             
32856             switch (this.placetitle) {
32857                 case 'center' :
32858                     cls += ' masonry-center-title';
32859                     break;
32860                 case 'bottom' :
32861                     cls += ' masonry-bottom-title';
32862                     break;
32863                 default:
32864                     break;
32865             }
32866             
32867         } else {
32868             if(!this.html.length && !this.bgimage.length){
32869                 cls += ' masonry-center-title';
32870             }
32871
32872             if(!this.html.length && this.bgimage.length){
32873                 cls += ' masonry-bottom-title';
32874             }
32875         }
32876         
32877         if(this.cls){
32878             cls += ' ' + this.cls;
32879         }
32880         
32881         var cfg = {
32882             tag: (this.href.length) ? 'a' : 'div',
32883             cls: cls,
32884             cn: [
32885                 {
32886                     tag: 'div',
32887                     cls: 'masonry-brick-mask'
32888                 },
32889                 {
32890                     tag: 'div',
32891                     cls: 'masonry-brick-paragraph',
32892                     cn: []
32893                 }
32894             ]
32895         };
32896         
32897         if(this.href.length){
32898             cfg.href = this.href;
32899         }
32900         
32901         var cn = cfg.cn[1].cn;
32902         
32903         if(this.title.length){
32904             cn.push({
32905                 tag: 'h4',
32906                 cls: 'masonry-brick-title',
32907                 html: this.title
32908             });
32909         }
32910         
32911         if(this.html.length){
32912             cn.push({
32913                 tag: 'p',
32914                 cls: 'masonry-brick-text',
32915                 html: this.html
32916             });
32917         }
32918         
32919         if (!this.title.length && !this.html.length) {
32920             cfg.cn[1].cls += ' hide';
32921         }
32922         
32923         if(this.bgimage.length){
32924             cfg.cn.push({
32925                 tag: 'img',
32926                 cls: 'masonry-brick-image-view',
32927                 src: this.bgimage
32928             });
32929         }
32930         
32931         if(this.videourl.length){
32932             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32933             // youtube support only?
32934             cfg.cn.push({
32935                 tag: 'iframe',
32936                 cls: 'masonry-brick-image-view',
32937                 src: vurl,
32938                 frameborder : 0,
32939                 allowfullscreen : true
32940             });
32941         }
32942         
32943         return cfg;
32944         
32945     },
32946     
32947     getSplitAutoCreate : function()
32948     {
32949         var cls = 'masonry-brick masonry-brick-split';
32950         
32951         if(this.href.length){
32952             cls += ' masonry-brick-link';
32953         }
32954         
32955         if(this.bgimage.length){
32956             cls += ' masonry-brick-image';
32957         }
32958         
32959         if(this.size){
32960             cls += ' masonry-' + this.size + '-brick';
32961         }
32962         
32963         switch (this.placetitle) {
32964             case 'center' :
32965                 cls += ' masonry-center-title';
32966                 break;
32967             case 'bottom' :
32968                 cls += ' masonry-bottom-title';
32969                 break;
32970             default:
32971                 if(!this.bgimage.length){
32972                     cls += ' masonry-center-title';
32973                 }
32974
32975                 if(this.bgimage.length){
32976                     cls += ' masonry-bottom-title';
32977                 }
32978                 break;
32979         }
32980         
32981         if(this.cls){
32982             cls += ' ' + this.cls;
32983         }
32984         
32985         var cfg = {
32986             tag: (this.href.length) ? 'a' : 'div',
32987             cls: cls,
32988             cn: [
32989                 {
32990                     tag: 'div',
32991                     cls: 'masonry-brick-split-head',
32992                     cn: [
32993                         {
32994                             tag: 'div',
32995                             cls: 'masonry-brick-paragraph',
32996                             cn: []
32997                         }
32998                     ]
32999                 },
33000                 {
33001                     tag: 'div',
33002                     cls: 'masonry-brick-split-body',
33003                     cn: []
33004                 }
33005             ]
33006         };
33007         
33008         if(this.href.length){
33009             cfg.href = this.href;
33010         }
33011         
33012         if(this.title.length){
33013             cfg.cn[0].cn[0].cn.push({
33014                 tag: 'h4',
33015                 cls: 'masonry-brick-title',
33016                 html: this.title
33017             });
33018         }
33019         
33020         if(this.html.length){
33021             cfg.cn[1].cn.push({
33022                 tag: 'p',
33023                 cls: 'masonry-brick-text',
33024                 html: this.html
33025             });
33026         }
33027
33028         if(this.bgimage.length){
33029             cfg.cn[0].cn.push({
33030                 tag: 'img',
33031                 cls: 'masonry-brick-image-view',
33032                 src: this.bgimage
33033             });
33034         }
33035         
33036         if(this.videourl.length){
33037             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33038             // youtube support only?
33039             cfg.cn[0].cn.cn.push({
33040                 tag: 'iframe',
33041                 cls: 'masonry-brick-image-view',
33042                 src: vurl,
33043                 frameborder : 0,
33044                 allowfullscreen : true
33045             });
33046         }
33047         
33048         return cfg;
33049     },
33050     
33051     initEvents: function() 
33052     {
33053         switch (this.size) {
33054             case 'xs' :
33055                 this.x = 1;
33056                 this.y = 1;
33057                 break;
33058             case 'sm' :
33059                 this.x = 2;
33060                 this.y = 2;
33061                 break;
33062             case 'md' :
33063             case 'md-left' :
33064             case 'md-right' :
33065                 this.x = 3;
33066                 this.y = 3;
33067                 break;
33068             case 'tall' :
33069                 this.x = 2;
33070                 this.y = 3;
33071                 break;
33072             case 'wide' :
33073                 this.x = 3;
33074                 this.y = 2;
33075                 break;
33076             case 'wide-thin' :
33077                 this.x = 3;
33078                 this.y = 1;
33079                 break;
33080                         
33081             default :
33082                 break;
33083         }
33084         
33085         if(Roo.isTouch){
33086             this.el.on('touchstart', this.onTouchStart, this);
33087             this.el.on('touchmove', this.onTouchMove, this);
33088             this.el.on('touchend', this.onTouchEnd, this);
33089             this.el.on('contextmenu', this.onContextMenu, this);
33090         } else {
33091             this.el.on('mouseenter'  ,this.enter, this);
33092             this.el.on('mouseleave', this.leave, this);
33093             this.el.on('click', this.onClick, this);
33094         }
33095         
33096         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33097             this.parent().bricks.push(this);   
33098         }
33099         
33100     },
33101     
33102     onClick: function(e, el)
33103     {
33104         var time = this.endTimer - this.startTimer;
33105         // Roo.log(e.preventDefault());
33106         if(Roo.isTouch){
33107             if(time > 1000){
33108                 e.preventDefault();
33109                 return;
33110             }
33111         }
33112         
33113         if(!this.preventDefault){
33114             return;
33115         }
33116         
33117         e.preventDefault();
33118         
33119         if (this.activeClass != '') {
33120             this.selectBrick();
33121         }
33122         
33123         this.fireEvent('click', this, e);
33124     },
33125     
33126     enter: function(e, el)
33127     {
33128         e.preventDefault();
33129         
33130         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33131             return;
33132         }
33133         
33134         if(this.bgimage.length && this.html.length){
33135             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33136         }
33137     },
33138     
33139     leave: function(e, el)
33140     {
33141         e.preventDefault();
33142         
33143         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33144             return;
33145         }
33146         
33147         if(this.bgimage.length && this.html.length){
33148             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33149         }
33150     },
33151     
33152     onTouchStart: function(e, el)
33153     {
33154 //        e.preventDefault();
33155         
33156         this.touchmoved = false;
33157         
33158         if(!this.isFitContainer){
33159             return;
33160         }
33161         
33162         if(!this.bgimage.length || !this.html.length){
33163             return;
33164         }
33165         
33166         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33167         
33168         this.timer = new Date().getTime();
33169         
33170     },
33171     
33172     onTouchMove: function(e, el)
33173     {
33174         this.touchmoved = true;
33175     },
33176     
33177     onContextMenu : function(e,el)
33178     {
33179         e.preventDefault();
33180         e.stopPropagation();
33181         return false;
33182     },
33183     
33184     onTouchEnd: function(e, el)
33185     {
33186 //        e.preventDefault();
33187         
33188         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33189         
33190             this.leave(e,el);
33191             
33192             return;
33193         }
33194         
33195         if(!this.bgimage.length || !this.html.length){
33196             
33197             if(this.href.length){
33198                 window.location.href = this.href;
33199             }
33200             
33201             return;
33202         }
33203         
33204         if(!this.isFitContainer){
33205             return;
33206         }
33207         
33208         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33209         
33210         window.location.href = this.href;
33211     },
33212     
33213     //selection on single brick only
33214     selectBrick : function() {
33215         
33216         if (!this.parentId) {
33217             return;
33218         }
33219         
33220         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33221         var index = m.selectedBrick.indexOf(this.id);
33222         
33223         if ( index > -1) {
33224             m.selectedBrick.splice(index,1);
33225             this.el.removeClass(this.activeClass);
33226             return;
33227         }
33228         
33229         for(var i = 0; i < m.selectedBrick.length; i++) {
33230             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33231             b.el.removeClass(b.activeClass);
33232         }
33233         
33234         m.selectedBrick = [];
33235         
33236         m.selectedBrick.push(this.id);
33237         this.el.addClass(this.activeClass);
33238         return;
33239     },
33240     
33241     isSelected : function(){
33242         return this.el.hasClass(this.activeClass);
33243         
33244     }
33245 });
33246
33247 Roo.apply(Roo.bootstrap.MasonryBrick, {
33248     
33249     //groups: {},
33250     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33251      /**
33252     * register a Masonry Brick
33253     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33254     */
33255     
33256     register : function(brick)
33257     {
33258         //this.groups[brick.id] = brick;
33259         this.groups.add(brick.id, brick);
33260     },
33261     /**
33262     * fetch a  masonry brick based on the masonry brick ID
33263     * @param {string} the masonry brick to add
33264     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33265     */
33266     
33267     get: function(brick_id) 
33268     {
33269         // if (typeof(this.groups[brick_id]) == 'undefined') {
33270         //     return false;
33271         // }
33272         // return this.groups[brick_id] ;
33273         
33274         if(this.groups.key(brick_id)) {
33275             return this.groups.key(brick_id);
33276         }
33277         
33278         return false;
33279     }
33280     
33281     
33282     
33283 });
33284
33285  /*
33286  * - LGPL
33287  *
33288  * element
33289  * 
33290  */
33291
33292 /**
33293  * @class Roo.bootstrap.Brick
33294  * @extends Roo.bootstrap.Component
33295  * Bootstrap Brick class
33296  * 
33297  * @constructor
33298  * Create a new Brick
33299  * @param {Object} config The config object
33300  */
33301
33302 Roo.bootstrap.Brick = function(config){
33303     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33304     
33305     this.addEvents({
33306         // raw events
33307         /**
33308          * @event click
33309          * When a Brick is click
33310          * @param {Roo.bootstrap.Brick} this
33311          * @param {Roo.EventObject} e
33312          */
33313         "click" : true
33314     });
33315 };
33316
33317 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33318     
33319     /**
33320      * @cfg {String} title
33321      */   
33322     title : '',
33323     /**
33324      * @cfg {String} html
33325      */   
33326     html : '',
33327     /**
33328      * @cfg {String} bgimage
33329      */   
33330     bgimage : '',
33331     /**
33332      * @cfg {String} cls
33333      */   
33334     cls : '',
33335     /**
33336      * @cfg {String} href
33337      */   
33338     href : '',
33339     /**
33340      * @cfg {String} video
33341      */   
33342     video : '',
33343     /**
33344      * @cfg {Boolean} square
33345      */   
33346     square : true,
33347     
33348     getAutoCreate : function()
33349     {
33350         var cls = 'roo-brick';
33351         
33352         if(this.href.length){
33353             cls += ' roo-brick-link';
33354         }
33355         
33356         if(this.bgimage.length){
33357             cls += ' roo-brick-image';
33358         }
33359         
33360         if(!this.html.length && !this.bgimage.length){
33361             cls += ' roo-brick-center-title';
33362         }
33363         
33364         if(!this.html.length && this.bgimage.length){
33365             cls += ' roo-brick-bottom-title';
33366         }
33367         
33368         if(this.cls){
33369             cls += ' ' + this.cls;
33370         }
33371         
33372         var cfg = {
33373             tag: (this.href.length) ? 'a' : 'div',
33374             cls: cls,
33375             cn: [
33376                 {
33377                     tag: 'div',
33378                     cls: 'roo-brick-paragraph',
33379                     cn: []
33380                 }
33381             ]
33382         };
33383         
33384         if(this.href.length){
33385             cfg.href = this.href;
33386         }
33387         
33388         var cn = cfg.cn[0].cn;
33389         
33390         if(this.title.length){
33391             cn.push({
33392                 tag: 'h4',
33393                 cls: 'roo-brick-title',
33394                 html: this.title
33395             });
33396         }
33397         
33398         if(this.html.length){
33399             cn.push({
33400                 tag: 'p',
33401                 cls: 'roo-brick-text',
33402                 html: this.html
33403             });
33404         } else {
33405             cn.cls += ' hide';
33406         }
33407         
33408         if(this.bgimage.length){
33409             cfg.cn.push({
33410                 tag: 'img',
33411                 cls: 'roo-brick-image-view',
33412                 src: this.bgimage
33413             });
33414         }
33415         
33416         return cfg;
33417     },
33418     
33419     initEvents: function() 
33420     {
33421         if(this.title.length || this.html.length){
33422             this.el.on('mouseenter'  ,this.enter, this);
33423             this.el.on('mouseleave', this.leave, this);
33424         }
33425         
33426         Roo.EventManager.onWindowResize(this.resize, this); 
33427         
33428         if(this.bgimage.length){
33429             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33430             this.imageEl.on('load', this.onImageLoad, this);
33431             return;
33432         }
33433         
33434         this.resize();
33435     },
33436     
33437     onImageLoad : function()
33438     {
33439         this.resize();
33440     },
33441     
33442     resize : function()
33443     {
33444         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33445         
33446         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33447         
33448         if(this.bgimage.length){
33449             var image = this.el.select('.roo-brick-image-view', true).first();
33450             
33451             image.setWidth(paragraph.getWidth());
33452             
33453             if(this.square){
33454                 image.setHeight(paragraph.getWidth());
33455             }
33456             
33457             this.el.setHeight(image.getHeight());
33458             paragraph.setHeight(image.getHeight());
33459             
33460         }
33461         
33462     },
33463     
33464     enter: function(e, el)
33465     {
33466         e.preventDefault();
33467         
33468         if(this.bgimage.length){
33469             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33470             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33471         }
33472     },
33473     
33474     leave: function(e, el)
33475     {
33476         e.preventDefault();
33477         
33478         if(this.bgimage.length){
33479             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33480             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33481         }
33482     }
33483     
33484 });
33485
33486  
33487
33488  /*
33489  * - LGPL
33490  *
33491  * Number field 
33492  */
33493
33494 /**
33495  * @class Roo.bootstrap.NumberField
33496  * @extends Roo.bootstrap.Input
33497  * Bootstrap NumberField class
33498  * 
33499  * 
33500  * 
33501  * 
33502  * @constructor
33503  * Create a new NumberField
33504  * @param {Object} config The config object
33505  */
33506
33507 Roo.bootstrap.NumberField = function(config){
33508     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33509 };
33510
33511 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33512     
33513     /**
33514      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33515      */
33516     allowDecimals : true,
33517     /**
33518      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33519      */
33520     decimalSeparator : ".",
33521     /**
33522      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33523      */
33524     decimalPrecision : 2,
33525     /**
33526      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33527      */
33528     allowNegative : true,
33529     
33530     /**
33531      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33532      */
33533     allowZero: true,
33534     /**
33535      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33536      */
33537     minValue : Number.NEGATIVE_INFINITY,
33538     /**
33539      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33540      */
33541     maxValue : Number.MAX_VALUE,
33542     /**
33543      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33544      */
33545     minText : "The minimum value for this field is {0}",
33546     /**
33547      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33548      */
33549     maxText : "The maximum value for this field is {0}",
33550     /**
33551      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33552      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33553      */
33554     nanText : "{0} is not a valid number",
33555     /**
33556      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33557      */
33558     thousandsDelimiter : false,
33559     /**
33560      * @cfg {String} valueAlign alignment of value
33561      */
33562     valueAlign : "left",
33563
33564     getAutoCreate : function()
33565     {
33566         var hiddenInput = {
33567             tag: 'input',
33568             type: 'hidden',
33569             id: Roo.id(),
33570             cls: 'hidden-number-input'
33571         };
33572         
33573         if (this.name) {
33574             hiddenInput.name = this.name;
33575         }
33576         
33577         this.name = '';
33578         
33579         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33580         
33581         this.name = hiddenInput.name;
33582         
33583         if(cfg.cn.length > 0) {
33584             cfg.cn.push(hiddenInput);
33585         }
33586         
33587         return cfg;
33588     },
33589
33590     // private
33591     initEvents : function()
33592     {   
33593         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33594         
33595         var allowed = "0123456789";
33596         
33597         if(this.allowDecimals){
33598             allowed += this.decimalSeparator;
33599         }
33600         
33601         if(this.allowNegative){
33602             allowed += "-";
33603         }
33604         
33605         if(this.thousandsDelimiter) {
33606             allowed += ",";
33607         }
33608         
33609         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33610         
33611         var keyPress = function(e){
33612             
33613             var k = e.getKey();
33614             
33615             var c = e.getCharCode();
33616             
33617             if(
33618                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33619                     allowed.indexOf(String.fromCharCode(c)) === -1
33620             ){
33621                 e.stopEvent();
33622                 return;
33623             }
33624             
33625             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33626                 return;
33627             }
33628             
33629             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33630                 e.stopEvent();
33631             }
33632         };
33633         
33634         this.el.on("keypress", keyPress, this);
33635     },
33636     
33637     validateValue : function(value)
33638     {
33639         
33640         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33641             return false;
33642         }
33643         
33644         var num = this.parseValue(value);
33645         
33646         if(isNaN(num)){
33647             this.markInvalid(String.format(this.nanText, value));
33648             return false;
33649         }
33650         
33651         if(num < this.minValue){
33652             this.markInvalid(String.format(this.minText, this.minValue));
33653             return false;
33654         }
33655         
33656         if(num > this.maxValue){
33657             this.markInvalid(String.format(this.maxText, this.maxValue));
33658             return false;
33659         }
33660         
33661         return true;
33662     },
33663
33664     getValue : function()
33665     {
33666         var v = this.hiddenEl().getValue();
33667         
33668         return this.fixPrecision(this.parseValue(v));
33669     },
33670
33671     parseValue : function(value)
33672     {
33673         if(this.thousandsDelimiter) {
33674             value += "";
33675             r = new RegExp(",", "g");
33676             value = value.replace(r, "");
33677         }
33678         
33679         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33680         return isNaN(value) ? '' : value;
33681     },
33682
33683     fixPrecision : function(value)
33684     {
33685         if(this.thousandsDelimiter) {
33686             value += "";
33687             r = new RegExp(",", "g");
33688             value = value.replace(r, "");
33689         }
33690         
33691         var nan = isNaN(value);
33692         
33693         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33694             return nan ? '' : value;
33695         }
33696         return parseFloat(value).toFixed(this.decimalPrecision);
33697     },
33698
33699     setValue : function(v)
33700     {
33701         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33702         
33703         this.value = v;
33704         
33705         if(this.rendered){
33706             
33707             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33708             
33709             this.inputEl().dom.value = (v == '') ? '' :
33710                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33711             
33712             if(!this.allowZero && v === '0') {
33713                 this.hiddenEl().dom.value = '';
33714                 this.inputEl().dom.value = '';
33715             }
33716             
33717             this.validate();
33718         }
33719     },
33720
33721     decimalPrecisionFcn : function(v)
33722     {
33723         return Math.floor(v);
33724     },
33725
33726     beforeBlur : function()
33727     {
33728         var v = this.parseValue(this.getRawValue());
33729         
33730         if(v || v === 0 || v === ''){
33731             this.setValue(v);
33732         }
33733     },
33734     
33735     hiddenEl : function()
33736     {
33737         return this.el.select('input.hidden-number-input',true).first();
33738     }
33739     
33740 });
33741
33742  
33743
33744 /*
33745 * Licence: LGPL
33746 */
33747
33748 /**
33749  * @class Roo.bootstrap.DocumentSlider
33750  * @extends Roo.bootstrap.Component
33751  * Bootstrap DocumentSlider class
33752  * 
33753  * @constructor
33754  * Create a new DocumentViewer
33755  * @param {Object} config The config object
33756  */
33757
33758 Roo.bootstrap.DocumentSlider = function(config){
33759     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33760     
33761     this.files = [];
33762     
33763     this.addEvents({
33764         /**
33765          * @event initial
33766          * Fire after initEvent
33767          * @param {Roo.bootstrap.DocumentSlider} this
33768          */
33769         "initial" : true,
33770         /**
33771          * @event update
33772          * Fire after update
33773          * @param {Roo.bootstrap.DocumentSlider} this
33774          */
33775         "update" : true,
33776         /**
33777          * @event click
33778          * Fire after click
33779          * @param {Roo.bootstrap.DocumentSlider} this
33780          */
33781         "click" : true
33782     });
33783 };
33784
33785 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33786     
33787     files : false,
33788     
33789     indicator : 0,
33790     
33791     getAutoCreate : function()
33792     {
33793         var cfg = {
33794             tag : 'div',
33795             cls : 'roo-document-slider',
33796             cn : [
33797                 {
33798                     tag : 'div',
33799                     cls : 'roo-document-slider-header',
33800                     cn : [
33801                         {
33802                             tag : 'div',
33803                             cls : 'roo-document-slider-header-title'
33804                         }
33805                     ]
33806                 },
33807                 {
33808                     tag : 'div',
33809                     cls : 'roo-document-slider-body',
33810                     cn : [
33811                         {
33812                             tag : 'div',
33813                             cls : 'roo-document-slider-prev',
33814                             cn : [
33815                                 {
33816                                     tag : 'i',
33817                                     cls : 'fa fa-chevron-left'
33818                                 }
33819                             ]
33820                         },
33821                         {
33822                             tag : 'div',
33823                             cls : 'roo-document-slider-thumb',
33824                             cn : [
33825                                 {
33826                                     tag : 'img',
33827                                     cls : 'roo-document-slider-image'
33828                                 }
33829                             ]
33830                         },
33831                         {
33832                             tag : 'div',
33833                             cls : 'roo-document-slider-next',
33834                             cn : [
33835                                 {
33836                                     tag : 'i',
33837                                     cls : 'fa fa-chevron-right'
33838                                 }
33839                             ]
33840                         }
33841                     ]
33842                 }
33843             ]
33844         };
33845         
33846         return cfg;
33847     },
33848     
33849     initEvents : function()
33850     {
33851         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33852         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33853         
33854         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33855         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33856         
33857         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33858         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33859         
33860         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33861         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33862         
33863         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33864         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33865         
33866         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33867         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33868         
33869         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33870         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33871         
33872         this.thumbEl.on('click', this.onClick, this);
33873         
33874         this.prevIndicator.on('click', this.prev, this);
33875         
33876         this.nextIndicator.on('click', this.next, this);
33877         
33878     },
33879     
33880     initial : function()
33881     {
33882         if(this.files.length){
33883             this.indicator = 1;
33884             this.update()
33885         }
33886         
33887         this.fireEvent('initial', this);
33888     },
33889     
33890     update : function()
33891     {
33892         this.imageEl.attr('src', this.files[this.indicator - 1]);
33893         
33894         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33895         
33896         this.prevIndicator.show();
33897         
33898         if(this.indicator == 1){
33899             this.prevIndicator.hide();
33900         }
33901         
33902         this.nextIndicator.show();
33903         
33904         if(this.indicator == this.files.length){
33905             this.nextIndicator.hide();
33906         }
33907         
33908         this.thumbEl.scrollTo('top');
33909         
33910         this.fireEvent('update', this);
33911     },
33912     
33913     onClick : function(e)
33914     {
33915         e.preventDefault();
33916         
33917         this.fireEvent('click', this);
33918     },
33919     
33920     prev : function(e)
33921     {
33922         e.preventDefault();
33923         
33924         this.indicator = Math.max(1, this.indicator - 1);
33925         
33926         this.update();
33927     },
33928     
33929     next : function(e)
33930     {
33931         e.preventDefault();
33932         
33933         this.indicator = Math.min(this.files.length, this.indicator + 1);
33934         
33935         this.update();
33936     }
33937 });
33938 /*
33939  * - LGPL
33940  *
33941  * RadioSet
33942  *
33943  *
33944  */
33945
33946 /**
33947  * @class Roo.bootstrap.RadioSet
33948  * @extends Roo.bootstrap.Input
33949  * Bootstrap RadioSet class
33950  * @cfg {String} indicatorpos (left|right) default left
33951  * @cfg {Boolean} inline (true|false) inline the element (default true)
33952  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33953  * @constructor
33954  * Create a new RadioSet
33955  * @param {Object} config The config object
33956  */
33957
33958 Roo.bootstrap.RadioSet = function(config){
33959     
33960     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33961     
33962     this.radioes = [];
33963     
33964     Roo.bootstrap.RadioSet.register(this);
33965     
33966     this.addEvents({
33967         /**
33968         * @event check
33969         * Fires when the element is checked or unchecked.
33970         * @param {Roo.bootstrap.RadioSet} this This radio
33971         * @param {Roo.bootstrap.Radio} item The checked item
33972         */
33973        check : true,
33974        /**
33975         * @event click
33976         * Fires when the element is click.
33977         * @param {Roo.bootstrap.RadioSet} this This radio set
33978         * @param {Roo.bootstrap.Radio} item The checked item
33979         * @param {Roo.EventObject} e The event object
33980         */
33981        click : true
33982     });
33983     
33984 };
33985
33986 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33987
33988     radioes : false,
33989     
33990     inline : true,
33991     
33992     weight : '',
33993     
33994     indicatorpos : 'left',
33995     
33996     getAutoCreate : function()
33997     {
33998         var label = {
33999             tag : 'label',
34000             cls : 'roo-radio-set-label',
34001             cn : [
34002                 {
34003                     tag : 'span',
34004                     html : this.fieldLabel
34005                 }
34006             ]
34007         };
34008         
34009         if(this.indicatorpos == 'left'){
34010             label.cn.unshift({
34011                 tag : 'i',
34012                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34013                 tooltip : 'This field is required'
34014             });
34015         } else {
34016             label.cn.push({
34017                 tag : 'i',
34018                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34019                 tooltip : 'This field is required'
34020             });
34021         }
34022         
34023         var items = {
34024             tag : 'div',
34025             cls : 'roo-radio-set-items'
34026         };
34027         
34028         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34029         
34030         if (align === 'left' && this.fieldLabel.length) {
34031             
34032             items = {
34033                 cls : "roo-radio-set-right", 
34034                 cn: [
34035                     items
34036                 ]
34037             };
34038             
34039             if(this.labelWidth > 12){
34040                 label.style = "width: " + this.labelWidth + 'px';
34041             }
34042             
34043             if(this.labelWidth < 13 && this.labelmd == 0){
34044                 this.labelmd = this.labelWidth;
34045             }
34046             
34047             if(this.labellg > 0){
34048                 label.cls += ' col-lg-' + this.labellg;
34049                 items.cls += ' col-lg-' + (12 - this.labellg);
34050             }
34051             
34052             if(this.labelmd > 0){
34053                 label.cls += ' col-md-' + this.labelmd;
34054                 items.cls += ' col-md-' + (12 - this.labelmd);
34055             }
34056             
34057             if(this.labelsm > 0){
34058                 label.cls += ' col-sm-' + this.labelsm;
34059                 items.cls += ' col-sm-' + (12 - this.labelsm);
34060             }
34061             
34062             if(this.labelxs > 0){
34063                 label.cls += ' col-xs-' + this.labelxs;
34064                 items.cls += ' col-xs-' + (12 - this.labelxs);
34065             }
34066         }
34067         
34068         var cfg = {
34069             tag : 'div',
34070             cls : 'roo-radio-set',
34071             cn : [
34072                 {
34073                     tag : 'input',
34074                     cls : 'roo-radio-set-input',
34075                     type : 'hidden',
34076                     name : this.name,
34077                     value : this.value ? this.value :  ''
34078                 },
34079                 label,
34080                 items
34081             ]
34082         };
34083         
34084         if(this.weight.length){
34085             cfg.cls += ' roo-radio-' + this.weight;
34086         }
34087         
34088         if(this.inline) {
34089             cfg.cls += ' roo-radio-set-inline';
34090         }
34091         
34092         var settings=this;
34093         ['xs','sm','md','lg'].map(function(size){
34094             if (settings[size]) {
34095                 cfg.cls += ' col-' + size + '-' + settings[size];
34096             }
34097         });
34098         
34099         return cfg;
34100         
34101     },
34102
34103     initEvents : function()
34104     {
34105         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34106         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34107         
34108         if(!this.fieldLabel.length){
34109             this.labelEl.hide();
34110         }
34111         
34112         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34113         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34114         
34115         this.indicator = this.indicatorEl();
34116         
34117         if(this.indicator){
34118             this.indicator.addClass('invisible');
34119         }
34120         
34121         this.originalValue = this.getValue();
34122         
34123     },
34124     
34125     inputEl: function ()
34126     {
34127         return this.el.select('.roo-radio-set-input', true).first();
34128     },
34129     
34130     getChildContainer : function()
34131     {
34132         return this.itemsEl;
34133     },
34134     
34135     register : function(item)
34136     {
34137         this.radioes.push(item);
34138         
34139     },
34140     
34141     validate : function()
34142     {   
34143         if(this.getVisibilityEl().hasClass('hidden')){
34144             return true;
34145         }
34146         
34147         var valid = false;
34148         
34149         Roo.each(this.radioes, function(i){
34150             if(!i.checked){
34151                 return;
34152             }
34153             
34154             valid = true;
34155             return false;
34156         });
34157         
34158         if(this.allowBlank) {
34159             return true;
34160         }
34161         
34162         if(this.disabled || valid){
34163             this.markValid();
34164             return true;
34165         }
34166         
34167         this.markInvalid();
34168         return false;
34169         
34170     },
34171     
34172     markValid : function()
34173     {
34174         if(this.labelEl.isVisible(true)){
34175             this.indicatorEl().removeClass('visible');
34176             this.indicatorEl().addClass('invisible');
34177         }
34178         
34179         this.el.removeClass([this.invalidClass, this.validClass]);
34180         this.el.addClass(this.validClass);
34181         
34182         this.fireEvent('valid', this);
34183     },
34184     
34185     markInvalid : function(msg)
34186     {
34187         if(this.allowBlank || this.disabled){
34188             return;
34189         }
34190         
34191         if(this.labelEl.isVisible(true)){
34192             this.indicatorEl().removeClass('invisible');
34193             this.indicatorEl().addClass('visible');
34194         }
34195         
34196         this.el.removeClass([this.invalidClass, this.validClass]);
34197         this.el.addClass(this.invalidClass);
34198         
34199         this.fireEvent('invalid', this, msg);
34200         
34201     },
34202     
34203     setValue : function(v, suppressEvent)
34204     {   
34205         if(this.value === v){
34206             return;
34207         }
34208         
34209         this.value = v;
34210         
34211         if(this.rendered){
34212             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34213         }
34214         
34215         Roo.each(this.radioes, function(i){
34216             i.checked = false;
34217             i.el.removeClass('checked');
34218         });
34219         
34220         Roo.each(this.radioes, function(i){
34221             
34222             if(i.value === v || i.value.toString() === v.toString()){
34223                 i.checked = true;
34224                 i.el.addClass('checked');
34225                 
34226                 if(suppressEvent !== true){
34227                     this.fireEvent('check', this, i);
34228                 }
34229                 
34230                 return false;
34231             }
34232             
34233         }, this);
34234         
34235         this.validate();
34236     },
34237     
34238     clearInvalid : function(){
34239         
34240         if(!this.el || this.preventMark){
34241             return;
34242         }
34243         
34244         this.el.removeClass([this.invalidClass]);
34245         
34246         this.fireEvent('valid', this);
34247     }
34248     
34249 });
34250
34251 Roo.apply(Roo.bootstrap.RadioSet, {
34252     
34253     groups: {},
34254     
34255     register : function(set)
34256     {
34257         this.groups[set.name] = set;
34258     },
34259     
34260     get: function(name) 
34261     {
34262         if (typeof(this.groups[name]) == 'undefined') {
34263             return false;
34264         }
34265         
34266         return this.groups[name] ;
34267     }
34268     
34269 });
34270 /*
34271  * Based on:
34272  * Ext JS Library 1.1.1
34273  * Copyright(c) 2006-2007, Ext JS, LLC.
34274  *
34275  * Originally Released Under LGPL - original licence link has changed is not relivant.
34276  *
34277  * Fork - LGPL
34278  * <script type="text/javascript">
34279  */
34280
34281
34282 /**
34283  * @class Roo.bootstrap.SplitBar
34284  * @extends Roo.util.Observable
34285  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34286  * <br><br>
34287  * Usage:
34288  * <pre><code>
34289 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34290                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34291 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34292 split.minSize = 100;
34293 split.maxSize = 600;
34294 split.animate = true;
34295 split.on('moved', splitterMoved);
34296 </code></pre>
34297  * @constructor
34298  * Create a new SplitBar
34299  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34300  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34301  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34302  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34303                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34304                         position of the SplitBar).
34305  */
34306 Roo.bootstrap.SplitBar = function(cfg){
34307     
34308     /** @private */
34309     
34310     //{
34311     //  dragElement : elm
34312     //  resizingElement: el,
34313         // optional..
34314     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34315     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34316         // existingProxy ???
34317     //}
34318     
34319     this.el = Roo.get(cfg.dragElement, true);
34320     this.el.dom.unselectable = "on";
34321     /** @private */
34322     this.resizingEl = Roo.get(cfg.resizingElement, true);
34323
34324     /**
34325      * @private
34326      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34327      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34328      * @type Number
34329      */
34330     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34331     
34332     /**
34333      * The minimum size of the resizing element. (Defaults to 0)
34334      * @type Number
34335      */
34336     this.minSize = 0;
34337     
34338     /**
34339      * The maximum size of the resizing element. (Defaults to 2000)
34340      * @type Number
34341      */
34342     this.maxSize = 2000;
34343     
34344     /**
34345      * Whether to animate the transition to the new size
34346      * @type Boolean
34347      */
34348     this.animate = false;
34349     
34350     /**
34351      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34352      * @type Boolean
34353      */
34354     this.useShim = false;
34355     
34356     /** @private */
34357     this.shim = null;
34358     
34359     if(!cfg.existingProxy){
34360         /** @private */
34361         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34362     }else{
34363         this.proxy = Roo.get(cfg.existingProxy).dom;
34364     }
34365     /** @private */
34366     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34367     
34368     /** @private */
34369     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34370     
34371     /** @private */
34372     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34373     
34374     /** @private */
34375     this.dragSpecs = {};
34376     
34377     /**
34378      * @private The adapter to use to positon and resize elements
34379      */
34380     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34381     this.adapter.init(this);
34382     
34383     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34384         /** @private */
34385         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34386         this.el.addClass("roo-splitbar-h");
34387     }else{
34388         /** @private */
34389         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34390         this.el.addClass("roo-splitbar-v");
34391     }
34392     
34393     this.addEvents({
34394         /**
34395          * @event resize
34396          * Fires when the splitter is moved (alias for {@link #event-moved})
34397          * @param {Roo.bootstrap.SplitBar} this
34398          * @param {Number} newSize the new width or height
34399          */
34400         "resize" : true,
34401         /**
34402          * @event moved
34403          * Fires when the splitter is moved
34404          * @param {Roo.bootstrap.SplitBar} this
34405          * @param {Number} newSize the new width or height
34406          */
34407         "moved" : true,
34408         /**
34409          * @event beforeresize
34410          * Fires before the splitter is dragged
34411          * @param {Roo.bootstrap.SplitBar} this
34412          */
34413         "beforeresize" : true,
34414
34415         "beforeapply" : true
34416     });
34417
34418     Roo.util.Observable.call(this);
34419 };
34420
34421 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34422     onStartProxyDrag : function(x, y){
34423         this.fireEvent("beforeresize", this);
34424         if(!this.overlay){
34425             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34426             o.unselectable();
34427             o.enableDisplayMode("block");
34428             // all splitbars share the same overlay
34429             Roo.bootstrap.SplitBar.prototype.overlay = o;
34430         }
34431         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34432         this.overlay.show();
34433         Roo.get(this.proxy).setDisplayed("block");
34434         var size = this.adapter.getElementSize(this);
34435         this.activeMinSize = this.getMinimumSize();;
34436         this.activeMaxSize = this.getMaximumSize();;
34437         var c1 = size - this.activeMinSize;
34438         var c2 = Math.max(this.activeMaxSize - size, 0);
34439         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34440             this.dd.resetConstraints();
34441             this.dd.setXConstraint(
34442                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34443                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34444             );
34445             this.dd.setYConstraint(0, 0);
34446         }else{
34447             this.dd.resetConstraints();
34448             this.dd.setXConstraint(0, 0);
34449             this.dd.setYConstraint(
34450                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34451                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34452             );
34453          }
34454         this.dragSpecs.startSize = size;
34455         this.dragSpecs.startPoint = [x, y];
34456         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34457     },
34458     
34459     /** 
34460      * @private Called after the drag operation by the DDProxy
34461      */
34462     onEndProxyDrag : function(e){
34463         Roo.get(this.proxy).setDisplayed(false);
34464         var endPoint = Roo.lib.Event.getXY(e);
34465         if(this.overlay){
34466             this.overlay.hide();
34467         }
34468         var newSize;
34469         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34470             newSize = this.dragSpecs.startSize + 
34471                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34472                     endPoint[0] - this.dragSpecs.startPoint[0] :
34473                     this.dragSpecs.startPoint[0] - endPoint[0]
34474                 );
34475         }else{
34476             newSize = this.dragSpecs.startSize + 
34477                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34478                     endPoint[1] - this.dragSpecs.startPoint[1] :
34479                     this.dragSpecs.startPoint[1] - endPoint[1]
34480                 );
34481         }
34482         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34483         if(newSize != this.dragSpecs.startSize){
34484             if(this.fireEvent('beforeapply', this, newSize) !== false){
34485                 this.adapter.setElementSize(this, newSize);
34486                 this.fireEvent("moved", this, newSize);
34487                 this.fireEvent("resize", this, newSize);
34488             }
34489         }
34490     },
34491     
34492     /**
34493      * Get the adapter this SplitBar uses
34494      * @return The adapter object
34495      */
34496     getAdapter : function(){
34497         return this.adapter;
34498     },
34499     
34500     /**
34501      * Set the adapter this SplitBar uses
34502      * @param {Object} adapter A SplitBar adapter object
34503      */
34504     setAdapter : function(adapter){
34505         this.adapter = adapter;
34506         this.adapter.init(this);
34507     },
34508     
34509     /**
34510      * Gets the minimum size for the resizing element
34511      * @return {Number} The minimum size
34512      */
34513     getMinimumSize : function(){
34514         return this.minSize;
34515     },
34516     
34517     /**
34518      * Sets the minimum size for the resizing element
34519      * @param {Number} minSize The minimum size
34520      */
34521     setMinimumSize : function(minSize){
34522         this.minSize = minSize;
34523     },
34524     
34525     /**
34526      * Gets the maximum size for the resizing element
34527      * @return {Number} The maximum size
34528      */
34529     getMaximumSize : function(){
34530         return this.maxSize;
34531     },
34532     
34533     /**
34534      * Sets the maximum size for the resizing element
34535      * @param {Number} maxSize The maximum size
34536      */
34537     setMaximumSize : function(maxSize){
34538         this.maxSize = maxSize;
34539     },
34540     
34541     /**
34542      * Sets the initialize size for the resizing element
34543      * @param {Number} size The initial size
34544      */
34545     setCurrentSize : function(size){
34546         var oldAnimate = this.animate;
34547         this.animate = false;
34548         this.adapter.setElementSize(this, size);
34549         this.animate = oldAnimate;
34550     },
34551     
34552     /**
34553      * Destroy this splitbar. 
34554      * @param {Boolean} removeEl True to remove the element
34555      */
34556     destroy : function(removeEl){
34557         if(this.shim){
34558             this.shim.remove();
34559         }
34560         this.dd.unreg();
34561         this.proxy.parentNode.removeChild(this.proxy);
34562         if(removeEl){
34563             this.el.remove();
34564         }
34565     }
34566 });
34567
34568 /**
34569  * @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.
34570  */
34571 Roo.bootstrap.SplitBar.createProxy = function(dir){
34572     var proxy = new Roo.Element(document.createElement("div"));
34573     proxy.unselectable();
34574     var cls = 'roo-splitbar-proxy';
34575     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34576     document.body.appendChild(proxy.dom);
34577     return proxy.dom;
34578 };
34579
34580 /** 
34581  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34582  * Default Adapter. It assumes the splitter and resizing element are not positioned
34583  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34584  */
34585 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34586 };
34587
34588 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34589     // do nothing for now
34590     init : function(s){
34591     
34592     },
34593     /**
34594      * Called before drag operations to get the current size of the resizing element. 
34595      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34596      */
34597      getElementSize : function(s){
34598         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34599             return s.resizingEl.getWidth();
34600         }else{
34601             return s.resizingEl.getHeight();
34602         }
34603     },
34604     
34605     /**
34606      * Called after drag operations to set the size of the resizing element.
34607      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34608      * @param {Number} newSize The new size to set
34609      * @param {Function} onComplete A function to be invoked when resizing is complete
34610      */
34611     setElementSize : function(s, newSize, onComplete){
34612         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34613             if(!s.animate){
34614                 s.resizingEl.setWidth(newSize);
34615                 if(onComplete){
34616                     onComplete(s, newSize);
34617                 }
34618             }else{
34619                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34620             }
34621         }else{
34622             
34623             if(!s.animate){
34624                 s.resizingEl.setHeight(newSize);
34625                 if(onComplete){
34626                     onComplete(s, newSize);
34627                 }
34628             }else{
34629                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34630             }
34631         }
34632     }
34633 };
34634
34635 /** 
34636  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34637  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34638  * Adapter that  moves the splitter element to align with the resized sizing element. 
34639  * Used with an absolute positioned SplitBar.
34640  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34641  * document.body, make sure you assign an id to the body element.
34642  */
34643 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34644     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34645     this.container = Roo.get(container);
34646 };
34647
34648 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34649     init : function(s){
34650         this.basic.init(s);
34651     },
34652     
34653     getElementSize : function(s){
34654         return this.basic.getElementSize(s);
34655     },
34656     
34657     setElementSize : function(s, newSize, onComplete){
34658         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34659     },
34660     
34661     moveSplitter : function(s){
34662         var yes = Roo.bootstrap.SplitBar;
34663         switch(s.placement){
34664             case yes.LEFT:
34665                 s.el.setX(s.resizingEl.getRight());
34666                 break;
34667             case yes.RIGHT:
34668                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34669                 break;
34670             case yes.TOP:
34671                 s.el.setY(s.resizingEl.getBottom());
34672                 break;
34673             case yes.BOTTOM:
34674                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34675                 break;
34676         }
34677     }
34678 };
34679
34680 /**
34681  * Orientation constant - Create a vertical SplitBar
34682  * @static
34683  * @type Number
34684  */
34685 Roo.bootstrap.SplitBar.VERTICAL = 1;
34686
34687 /**
34688  * Orientation constant - Create a horizontal SplitBar
34689  * @static
34690  * @type Number
34691  */
34692 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34693
34694 /**
34695  * Placement constant - The resizing element is to the left of the splitter element
34696  * @static
34697  * @type Number
34698  */
34699 Roo.bootstrap.SplitBar.LEFT = 1;
34700
34701 /**
34702  * Placement constant - The resizing element is to the right of the splitter element
34703  * @static
34704  * @type Number
34705  */
34706 Roo.bootstrap.SplitBar.RIGHT = 2;
34707
34708 /**
34709  * Placement constant - The resizing element is positioned above the splitter element
34710  * @static
34711  * @type Number
34712  */
34713 Roo.bootstrap.SplitBar.TOP = 3;
34714
34715 /**
34716  * Placement constant - The resizing element is positioned under splitter element
34717  * @static
34718  * @type Number
34719  */
34720 Roo.bootstrap.SplitBar.BOTTOM = 4;
34721 Roo.namespace("Roo.bootstrap.layout");/*
34722  * Based on:
34723  * Ext JS Library 1.1.1
34724  * Copyright(c) 2006-2007, Ext JS, LLC.
34725  *
34726  * Originally Released Under LGPL - original licence link has changed is not relivant.
34727  *
34728  * Fork - LGPL
34729  * <script type="text/javascript">
34730  */
34731
34732 /**
34733  * @class Roo.bootstrap.layout.Manager
34734  * @extends Roo.bootstrap.Component
34735  * Base class for layout managers.
34736  */
34737 Roo.bootstrap.layout.Manager = function(config)
34738 {
34739     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34740
34741
34742
34743
34744
34745     /** false to disable window resize monitoring @type Boolean */
34746     this.monitorWindowResize = true;
34747     this.regions = {};
34748     this.addEvents({
34749         /**
34750          * @event layout
34751          * Fires when a layout is performed.
34752          * @param {Roo.LayoutManager} this
34753          */
34754         "layout" : true,
34755         /**
34756          * @event regionresized
34757          * Fires when the user resizes a region.
34758          * @param {Roo.LayoutRegion} region The resized region
34759          * @param {Number} newSize The new size (width for east/west, height for north/south)
34760          */
34761         "regionresized" : true,
34762         /**
34763          * @event regioncollapsed
34764          * Fires when a region is collapsed.
34765          * @param {Roo.LayoutRegion} region The collapsed region
34766          */
34767         "regioncollapsed" : true,
34768         /**
34769          * @event regionexpanded
34770          * Fires when a region is expanded.
34771          * @param {Roo.LayoutRegion} region The expanded region
34772          */
34773         "regionexpanded" : true
34774     });
34775     this.updating = false;
34776
34777     if (config.el) {
34778         this.el = Roo.get(config.el);
34779         this.initEvents();
34780     }
34781
34782 };
34783
34784 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34785
34786
34787     regions : null,
34788
34789     monitorWindowResize : true,
34790
34791
34792     updating : false,
34793
34794
34795     onRender : function(ct, position)
34796     {
34797         if(!this.el){
34798             this.el = Roo.get(ct);
34799             this.initEvents();
34800         }
34801         //this.fireEvent('render',this);
34802     },
34803
34804
34805     initEvents: function()
34806     {
34807
34808
34809         // ie scrollbar fix
34810         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34811             document.body.scroll = "no";
34812         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34813             this.el.position('relative');
34814         }
34815         this.id = this.el.id;
34816         this.el.addClass("roo-layout-container");
34817         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34818         if(this.el.dom != document.body ) {
34819             this.el.on('resize', this.layout,this);
34820             this.el.on('show', this.layout,this);
34821         }
34822
34823     },
34824
34825     /**
34826      * Returns true if this layout is currently being updated
34827      * @return {Boolean}
34828      */
34829     isUpdating : function(){
34830         return this.updating;
34831     },
34832
34833     /**
34834      * Suspend the LayoutManager from doing auto-layouts while
34835      * making multiple add or remove calls
34836      */
34837     beginUpdate : function(){
34838         this.updating = true;
34839     },
34840
34841     /**
34842      * Restore auto-layouts and optionally disable the manager from performing a layout
34843      * @param {Boolean} noLayout true to disable a layout update
34844      */
34845     endUpdate : function(noLayout){
34846         this.updating = false;
34847         if(!noLayout){
34848             this.layout();
34849         }
34850     },
34851
34852     layout: function(){
34853         // abstract...
34854     },
34855
34856     onRegionResized : function(region, newSize){
34857         this.fireEvent("regionresized", region, newSize);
34858         this.layout();
34859     },
34860
34861     onRegionCollapsed : function(region){
34862         this.fireEvent("regioncollapsed", region);
34863     },
34864
34865     onRegionExpanded : function(region){
34866         this.fireEvent("regionexpanded", region);
34867     },
34868
34869     /**
34870      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34871      * performs box-model adjustments.
34872      * @return {Object} The size as an object {width: (the width), height: (the height)}
34873      */
34874     getViewSize : function()
34875     {
34876         var size;
34877         if(this.el.dom != document.body){
34878             size = this.el.getSize();
34879         }else{
34880             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34881         }
34882         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34883         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34884         return size;
34885     },
34886
34887     /**
34888      * Returns the Element this layout is bound to.
34889      * @return {Roo.Element}
34890      */
34891     getEl : function(){
34892         return this.el;
34893     },
34894
34895     /**
34896      * Returns the specified region.
34897      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34898      * @return {Roo.LayoutRegion}
34899      */
34900     getRegion : function(target){
34901         return this.regions[target.toLowerCase()];
34902     },
34903
34904     onWindowResize : function(){
34905         if(this.monitorWindowResize){
34906             this.layout();
34907         }
34908     }
34909 });
34910 /*
34911  * Based on:
34912  * Ext JS Library 1.1.1
34913  * Copyright(c) 2006-2007, Ext JS, LLC.
34914  *
34915  * Originally Released Under LGPL - original licence link has changed is not relivant.
34916  *
34917  * Fork - LGPL
34918  * <script type="text/javascript">
34919  */
34920 /**
34921  * @class Roo.bootstrap.layout.Border
34922  * @extends Roo.bootstrap.layout.Manager
34923  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34924  * please see: examples/bootstrap/nested.html<br><br>
34925  
34926 <b>The container the layout is rendered into can be either the body element or any other element.
34927 If it is not the body element, the container needs to either be an absolute positioned element,
34928 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34929 the container size if it is not the body element.</b>
34930
34931 * @constructor
34932 * Create a new Border
34933 * @param {Object} config Configuration options
34934  */
34935 Roo.bootstrap.layout.Border = function(config){
34936     config = config || {};
34937     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34938     
34939     
34940     
34941     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34942         if(config[region]){
34943             config[region].region = region;
34944             this.addRegion(config[region]);
34945         }
34946     },this);
34947     
34948 };
34949
34950 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34951
34952 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34953     /**
34954      * Creates and adds a new region if it doesn't already exist.
34955      * @param {String} target The target region key (north, south, east, west or center).
34956      * @param {Object} config The regions config object
34957      * @return {BorderLayoutRegion} The new region
34958      */
34959     addRegion : function(config)
34960     {
34961         if(!this.regions[config.region]){
34962             var r = this.factory(config);
34963             this.bindRegion(r);
34964         }
34965         return this.regions[config.region];
34966     },
34967
34968     // private (kinda)
34969     bindRegion : function(r){
34970         this.regions[r.config.region] = r;
34971         
34972         r.on("visibilitychange",    this.layout, this);
34973         r.on("paneladded",          this.layout, this);
34974         r.on("panelremoved",        this.layout, this);
34975         r.on("invalidated",         this.layout, this);
34976         r.on("resized",             this.onRegionResized, this);
34977         r.on("collapsed",           this.onRegionCollapsed, this);
34978         r.on("expanded",            this.onRegionExpanded, this);
34979     },
34980
34981     /**
34982      * Performs a layout update.
34983      */
34984     layout : function()
34985     {
34986         if(this.updating) {
34987             return;
34988         }
34989         
34990         // render all the rebions if they have not been done alreayd?
34991         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34992             if(this.regions[region] && !this.regions[region].bodyEl){
34993                 this.regions[region].onRender(this.el)
34994             }
34995         },this);
34996         
34997         var size = this.getViewSize();
34998         var w = size.width;
34999         var h = size.height;
35000         var centerW = w;
35001         var centerH = h;
35002         var centerY = 0;
35003         var centerX = 0;
35004         //var x = 0, y = 0;
35005
35006         var rs = this.regions;
35007         var north = rs["north"];
35008         var south = rs["south"]; 
35009         var west = rs["west"];
35010         var east = rs["east"];
35011         var center = rs["center"];
35012         //if(this.hideOnLayout){ // not supported anymore
35013             //c.el.setStyle("display", "none");
35014         //}
35015         if(north && north.isVisible()){
35016             var b = north.getBox();
35017             var m = north.getMargins();
35018             b.width = w - (m.left+m.right);
35019             b.x = m.left;
35020             b.y = m.top;
35021             centerY = b.height + b.y + m.bottom;
35022             centerH -= centerY;
35023             north.updateBox(this.safeBox(b));
35024         }
35025         if(south && south.isVisible()){
35026             var b = south.getBox();
35027             var m = south.getMargins();
35028             b.width = w - (m.left+m.right);
35029             b.x = m.left;
35030             var totalHeight = (b.height + m.top + m.bottom);
35031             b.y = h - totalHeight + m.top;
35032             centerH -= totalHeight;
35033             south.updateBox(this.safeBox(b));
35034         }
35035         if(west && west.isVisible()){
35036             var b = west.getBox();
35037             var m = west.getMargins();
35038             b.height = centerH - (m.top+m.bottom);
35039             b.x = m.left;
35040             b.y = centerY + m.top;
35041             var totalWidth = (b.width + m.left + m.right);
35042             centerX += totalWidth;
35043             centerW -= totalWidth;
35044             west.updateBox(this.safeBox(b));
35045         }
35046         if(east && east.isVisible()){
35047             var b = east.getBox();
35048             var m = east.getMargins();
35049             b.height = centerH - (m.top+m.bottom);
35050             var totalWidth = (b.width + m.left + m.right);
35051             b.x = w - totalWidth + m.left;
35052             b.y = centerY + m.top;
35053             centerW -= totalWidth;
35054             east.updateBox(this.safeBox(b));
35055         }
35056         if(center){
35057             var m = center.getMargins();
35058             var centerBox = {
35059                 x: centerX + m.left,
35060                 y: centerY + m.top,
35061                 width: centerW - (m.left+m.right),
35062                 height: centerH - (m.top+m.bottom)
35063             };
35064             //if(this.hideOnLayout){
35065                 //center.el.setStyle("display", "block");
35066             //}
35067             center.updateBox(this.safeBox(centerBox));
35068         }
35069         this.el.repaint();
35070         this.fireEvent("layout", this);
35071     },
35072
35073     // private
35074     safeBox : function(box){
35075         box.width = Math.max(0, box.width);
35076         box.height = Math.max(0, box.height);
35077         return box;
35078     },
35079
35080     /**
35081      * Adds a ContentPanel (or subclass) to this layout.
35082      * @param {String} target The target region key (north, south, east, west or center).
35083      * @param {Roo.ContentPanel} panel The panel to add
35084      * @return {Roo.ContentPanel} The added panel
35085      */
35086     add : function(target, panel){
35087          
35088         target = target.toLowerCase();
35089         return this.regions[target].add(panel);
35090     },
35091
35092     /**
35093      * Remove a ContentPanel (or subclass) to this layout.
35094      * @param {String} target The target region key (north, south, east, west or center).
35095      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35096      * @return {Roo.ContentPanel} The removed panel
35097      */
35098     remove : function(target, panel){
35099         target = target.toLowerCase();
35100         return this.regions[target].remove(panel);
35101     },
35102
35103     /**
35104      * Searches all regions for a panel with the specified id
35105      * @param {String} panelId
35106      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35107      */
35108     findPanel : function(panelId){
35109         var rs = this.regions;
35110         for(var target in rs){
35111             if(typeof rs[target] != "function"){
35112                 var p = rs[target].getPanel(panelId);
35113                 if(p){
35114                     return p;
35115                 }
35116             }
35117         }
35118         return null;
35119     },
35120
35121     /**
35122      * Searches all regions for a panel with the specified id and activates (shows) it.
35123      * @param {String/ContentPanel} panelId The panels id or the panel itself
35124      * @return {Roo.ContentPanel} The shown panel or null
35125      */
35126     showPanel : function(panelId) {
35127       var rs = this.regions;
35128       for(var target in rs){
35129          var r = rs[target];
35130          if(typeof r != "function"){
35131             if(r.hasPanel(panelId)){
35132                return r.showPanel(panelId);
35133             }
35134          }
35135       }
35136       return null;
35137    },
35138
35139    /**
35140      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35141      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35142      */
35143    /*
35144     restoreState : function(provider){
35145         if(!provider){
35146             provider = Roo.state.Manager;
35147         }
35148         var sm = new Roo.LayoutStateManager();
35149         sm.init(this, provider);
35150     },
35151 */
35152  
35153  
35154     /**
35155      * Adds a xtype elements to the layout.
35156      * <pre><code>
35157
35158 layout.addxtype({
35159        xtype : 'ContentPanel',
35160        region: 'west',
35161        items: [ .... ]
35162    }
35163 );
35164
35165 layout.addxtype({
35166         xtype : 'NestedLayoutPanel',
35167         region: 'west',
35168         layout: {
35169            center: { },
35170            west: { }   
35171         },
35172         items : [ ... list of content panels or nested layout panels.. ]
35173    }
35174 );
35175 </code></pre>
35176      * @param {Object} cfg Xtype definition of item to add.
35177      */
35178     addxtype : function(cfg)
35179     {
35180         // basically accepts a pannel...
35181         // can accept a layout region..!?!?
35182         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35183         
35184         
35185         // theory?  children can only be panels??
35186         
35187         //if (!cfg.xtype.match(/Panel$/)) {
35188         //    return false;
35189         //}
35190         var ret = false;
35191         
35192         if (typeof(cfg.region) == 'undefined') {
35193             Roo.log("Failed to add Panel, region was not set");
35194             Roo.log(cfg);
35195             return false;
35196         }
35197         var region = cfg.region;
35198         delete cfg.region;
35199         
35200           
35201         var xitems = [];
35202         if (cfg.items) {
35203             xitems = cfg.items;
35204             delete cfg.items;
35205         }
35206         var nb = false;
35207         
35208         switch(cfg.xtype) 
35209         {
35210             case 'Content':  // ContentPanel (el, cfg)
35211             case 'Scroll':  // ContentPanel (el, cfg)
35212             case 'View': 
35213                 cfg.autoCreate = true;
35214                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35215                 //} else {
35216                 //    var el = this.el.createChild();
35217                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35218                 //}
35219                 
35220                 this.add(region, ret);
35221                 break;
35222             
35223             /*
35224             case 'TreePanel': // our new panel!
35225                 cfg.el = this.el.createChild();
35226                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35227                 this.add(region, ret);
35228                 break;
35229             */
35230             
35231             case 'Nest': 
35232                 // create a new Layout (which is  a Border Layout...
35233                 
35234                 var clayout = cfg.layout;
35235                 clayout.el  = this.el.createChild();
35236                 clayout.items   = clayout.items  || [];
35237                 
35238                 delete cfg.layout;
35239                 
35240                 // replace this exitems with the clayout ones..
35241                 xitems = clayout.items;
35242                  
35243                 // force background off if it's in center...
35244                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35245                     cfg.background = false;
35246                 }
35247                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35248                 
35249                 
35250                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35251                 //console.log('adding nested layout panel '  + cfg.toSource());
35252                 this.add(region, ret);
35253                 nb = {}; /// find first...
35254                 break;
35255             
35256             case 'Grid':
35257                 
35258                 // needs grid and region
35259                 
35260                 //var el = this.getRegion(region).el.createChild();
35261                 /*
35262                  *var el = this.el.createChild();
35263                 // create the grid first...
35264                 cfg.grid.container = el;
35265                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35266                 */
35267                 
35268                 if (region == 'center' && this.active ) {
35269                     cfg.background = false;
35270                 }
35271                 
35272                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35273                 
35274                 this.add(region, ret);
35275                 /*
35276                 if (cfg.background) {
35277                     // render grid on panel activation (if panel background)
35278                     ret.on('activate', function(gp) {
35279                         if (!gp.grid.rendered) {
35280                     //        gp.grid.render(el);
35281                         }
35282                     });
35283                 } else {
35284                   //  cfg.grid.render(el);
35285                 }
35286                 */
35287                 break;
35288            
35289            
35290             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35291                 // it was the old xcomponent building that caused this before.
35292                 // espeically if border is the top element in the tree.
35293                 ret = this;
35294                 break; 
35295                 
35296                     
35297                 
35298                 
35299                 
35300             default:
35301                 /*
35302                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35303                     
35304                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35305                     this.add(region, ret);
35306                 } else {
35307                 */
35308                     Roo.log(cfg);
35309                     throw "Can not add '" + cfg.xtype + "' to Border";
35310                     return null;
35311              
35312                                 
35313              
35314         }
35315         this.beginUpdate();
35316         // add children..
35317         var region = '';
35318         var abn = {};
35319         Roo.each(xitems, function(i)  {
35320             region = nb && i.region ? i.region : false;
35321             
35322             var add = ret.addxtype(i);
35323            
35324             if (region) {
35325                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35326                 if (!i.background) {
35327                     abn[region] = nb[region] ;
35328                 }
35329             }
35330             
35331         });
35332         this.endUpdate();
35333
35334         // make the last non-background panel active..
35335         //if (nb) { Roo.log(abn); }
35336         if (nb) {
35337             
35338             for(var r in abn) {
35339                 region = this.getRegion(r);
35340                 if (region) {
35341                     // tried using nb[r], but it does not work..
35342                      
35343                     region.showPanel(abn[r]);
35344                    
35345                 }
35346             }
35347         }
35348         return ret;
35349         
35350     },
35351     
35352     
35353 // private
35354     factory : function(cfg)
35355     {
35356         
35357         var validRegions = Roo.bootstrap.layout.Border.regions;
35358
35359         var target = cfg.region;
35360         cfg.mgr = this;
35361         
35362         var r = Roo.bootstrap.layout;
35363         Roo.log(target);
35364         switch(target){
35365             case "north":
35366                 return new r.North(cfg);
35367             case "south":
35368                 return new r.South(cfg);
35369             case "east":
35370                 return new r.East(cfg);
35371             case "west":
35372                 return new r.West(cfg);
35373             case "center":
35374                 return new r.Center(cfg);
35375         }
35376         throw 'Layout region "'+target+'" not supported.';
35377     }
35378     
35379     
35380 });
35381  /*
35382  * Based on:
35383  * Ext JS Library 1.1.1
35384  * Copyright(c) 2006-2007, Ext JS, LLC.
35385  *
35386  * Originally Released Under LGPL - original licence link has changed is not relivant.
35387  *
35388  * Fork - LGPL
35389  * <script type="text/javascript">
35390  */
35391  
35392 /**
35393  * @class Roo.bootstrap.layout.Basic
35394  * @extends Roo.util.Observable
35395  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35396  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35397  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35398  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35399  * @cfg {string}   region  the region that it inhabits..
35400  * @cfg {bool}   skipConfig skip config?
35401  * 
35402
35403  */
35404 Roo.bootstrap.layout.Basic = function(config){
35405     
35406     this.mgr = config.mgr;
35407     
35408     this.position = config.region;
35409     
35410     var skipConfig = config.skipConfig;
35411     
35412     this.events = {
35413         /**
35414          * @scope Roo.BasicLayoutRegion
35415          */
35416         
35417         /**
35418          * @event beforeremove
35419          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35420          * @param {Roo.LayoutRegion} this
35421          * @param {Roo.ContentPanel} panel The panel
35422          * @param {Object} e The cancel event object
35423          */
35424         "beforeremove" : true,
35425         /**
35426          * @event invalidated
35427          * Fires when the layout for this region is changed.
35428          * @param {Roo.LayoutRegion} this
35429          */
35430         "invalidated" : true,
35431         /**
35432          * @event visibilitychange
35433          * Fires when this region is shown or hidden 
35434          * @param {Roo.LayoutRegion} this
35435          * @param {Boolean} visibility true or false
35436          */
35437         "visibilitychange" : true,
35438         /**
35439          * @event paneladded
35440          * Fires when a panel is added. 
35441          * @param {Roo.LayoutRegion} this
35442          * @param {Roo.ContentPanel} panel The panel
35443          */
35444         "paneladded" : true,
35445         /**
35446          * @event panelremoved
35447          * Fires when a panel is removed. 
35448          * @param {Roo.LayoutRegion} this
35449          * @param {Roo.ContentPanel} panel The panel
35450          */
35451         "panelremoved" : true,
35452         /**
35453          * @event beforecollapse
35454          * Fires when this region before collapse.
35455          * @param {Roo.LayoutRegion} this
35456          */
35457         "beforecollapse" : true,
35458         /**
35459          * @event collapsed
35460          * Fires when this region is collapsed.
35461          * @param {Roo.LayoutRegion} this
35462          */
35463         "collapsed" : true,
35464         /**
35465          * @event expanded
35466          * Fires when this region is expanded.
35467          * @param {Roo.LayoutRegion} this
35468          */
35469         "expanded" : true,
35470         /**
35471          * @event slideshow
35472          * Fires when this region is slid into view.
35473          * @param {Roo.LayoutRegion} this
35474          */
35475         "slideshow" : true,
35476         /**
35477          * @event slidehide
35478          * Fires when this region slides out of view. 
35479          * @param {Roo.LayoutRegion} this
35480          */
35481         "slidehide" : true,
35482         /**
35483          * @event panelactivated
35484          * Fires when a panel is activated. 
35485          * @param {Roo.LayoutRegion} this
35486          * @param {Roo.ContentPanel} panel The activated panel
35487          */
35488         "panelactivated" : true,
35489         /**
35490          * @event resized
35491          * Fires when the user resizes this region. 
35492          * @param {Roo.LayoutRegion} this
35493          * @param {Number} newSize The new size (width for east/west, height for north/south)
35494          */
35495         "resized" : true
35496     };
35497     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35498     this.panels = new Roo.util.MixedCollection();
35499     this.panels.getKey = this.getPanelId.createDelegate(this);
35500     this.box = null;
35501     this.activePanel = null;
35502     // ensure listeners are added...
35503     
35504     if (config.listeners || config.events) {
35505         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35506             listeners : config.listeners || {},
35507             events : config.events || {}
35508         });
35509     }
35510     
35511     if(skipConfig !== true){
35512         this.applyConfig(config);
35513     }
35514 };
35515
35516 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35517 {
35518     getPanelId : function(p){
35519         return p.getId();
35520     },
35521     
35522     applyConfig : function(config){
35523         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35524         this.config = config;
35525         
35526     },
35527     
35528     /**
35529      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35530      * the width, for horizontal (north, south) the height.
35531      * @param {Number} newSize The new width or height
35532      */
35533     resizeTo : function(newSize){
35534         var el = this.el ? this.el :
35535                  (this.activePanel ? this.activePanel.getEl() : null);
35536         if(el){
35537             switch(this.position){
35538                 case "east":
35539                 case "west":
35540                     el.setWidth(newSize);
35541                     this.fireEvent("resized", this, newSize);
35542                 break;
35543                 case "north":
35544                 case "south":
35545                     el.setHeight(newSize);
35546                     this.fireEvent("resized", this, newSize);
35547                 break;                
35548             }
35549         }
35550     },
35551     
35552     getBox : function(){
35553         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35554     },
35555     
35556     getMargins : function(){
35557         return this.margins;
35558     },
35559     
35560     updateBox : function(box){
35561         this.box = box;
35562         var el = this.activePanel.getEl();
35563         el.dom.style.left = box.x + "px";
35564         el.dom.style.top = box.y + "px";
35565         this.activePanel.setSize(box.width, box.height);
35566     },
35567     
35568     /**
35569      * Returns the container element for this region.
35570      * @return {Roo.Element}
35571      */
35572     getEl : function(){
35573         return this.activePanel;
35574     },
35575     
35576     /**
35577      * Returns true if this region is currently visible.
35578      * @return {Boolean}
35579      */
35580     isVisible : function(){
35581         return this.activePanel ? true : false;
35582     },
35583     
35584     setActivePanel : function(panel){
35585         panel = this.getPanel(panel);
35586         if(this.activePanel && this.activePanel != panel){
35587             this.activePanel.setActiveState(false);
35588             this.activePanel.getEl().setLeftTop(-10000,-10000);
35589         }
35590         this.activePanel = panel;
35591         panel.setActiveState(true);
35592         if(this.box){
35593             panel.setSize(this.box.width, this.box.height);
35594         }
35595         this.fireEvent("panelactivated", this, panel);
35596         this.fireEvent("invalidated");
35597     },
35598     
35599     /**
35600      * Show the specified panel.
35601      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35602      * @return {Roo.ContentPanel} The shown panel or null
35603      */
35604     showPanel : function(panel){
35605         panel = this.getPanel(panel);
35606         if(panel){
35607             this.setActivePanel(panel);
35608         }
35609         return panel;
35610     },
35611     
35612     /**
35613      * Get the active panel for this region.
35614      * @return {Roo.ContentPanel} The active panel or null
35615      */
35616     getActivePanel : function(){
35617         return this.activePanel;
35618     },
35619     
35620     /**
35621      * Add the passed ContentPanel(s)
35622      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35623      * @return {Roo.ContentPanel} The panel added (if only one was added)
35624      */
35625     add : function(panel){
35626         if(arguments.length > 1){
35627             for(var i = 0, len = arguments.length; i < len; i++) {
35628                 this.add(arguments[i]);
35629             }
35630             return null;
35631         }
35632         if(this.hasPanel(panel)){
35633             this.showPanel(panel);
35634             return panel;
35635         }
35636         var el = panel.getEl();
35637         if(el.dom.parentNode != this.mgr.el.dom){
35638             this.mgr.el.dom.appendChild(el.dom);
35639         }
35640         if(panel.setRegion){
35641             panel.setRegion(this);
35642         }
35643         this.panels.add(panel);
35644         el.setStyle("position", "absolute");
35645         if(!panel.background){
35646             this.setActivePanel(panel);
35647             if(this.config.initialSize && this.panels.getCount()==1){
35648                 this.resizeTo(this.config.initialSize);
35649             }
35650         }
35651         this.fireEvent("paneladded", this, panel);
35652         return panel;
35653     },
35654     
35655     /**
35656      * Returns true if the panel is in this region.
35657      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35658      * @return {Boolean}
35659      */
35660     hasPanel : function(panel){
35661         if(typeof panel == "object"){ // must be panel obj
35662             panel = panel.getId();
35663         }
35664         return this.getPanel(panel) ? true : false;
35665     },
35666     
35667     /**
35668      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35669      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35670      * @param {Boolean} preservePanel Overrides the config preservePanel option
35671      * @return {Roo.ContentPanel} The panel that was removed
35672      */
35673     remove : function(panel, preservePanel){
35674         panel = this.getPanel(panel);
35675         if(!panel){
35676             return null;
35677         }
35678         var e = {};
35679         this.fireEvent("beforeremove", this, panel, e);
35680         if(e.cancel === true){
35681             return null;
35682         }
35683         var panelId = panel.getId();
35684         this.panels.removeKey(panelId);
35685         return panel;
35686     },
35687     
35688     /**
35689      * Returns the panel specified or null if it's not in this region.
35690      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35691      * @return {Roo.ContentPanel}
35692      */
35693     getPanel : function(id){
35694         if(typeof id == "object"){ // must be panel obj
35695             return id;
35696         }
35697         return this.panels.get(id);
35698     },
35699     
35700     /**
35701      * Returns this regions position (north/south/east/west/center).
35702      * @return {String} 
35703      */
35704     getPosition: function(){
35705         return this.position;    
35706     }
35707 });/*
35708  * Based on:
35709  * Ext JS Library 1.1.1
35710  * Copyright(c) 2006-2007, Ext JS, LLC.
35711  *
35712  * Originally Released Under LGPL - original licence link has changed is not relivant.
35713  *
35714  * Fork - LGPL
35715  * <script type="text/javascript">
35716  */
35717  
35718 /**
35719  * @class Roo.bootstrap.layout.Region
35720  * @extends Roo.bootstrap.layout.Basic
35721  * This class represents a region in a layout manager.
35722  
35723  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35724  * @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})
35725  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35726  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35727  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35728  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35729  * @cfg {String}    title           The title for the region (overrides panel titles)
35730  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35731  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35732  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35733  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35734  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35735  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35736  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35737  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35738  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35739  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35740
35741  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35742  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35743  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35744  * @cfg {Number}    width           For East/West panels
35745  * @cfg {Number}    height          For North/South panels
35746  * @cfg {Boolean}   split           To show the splitter
35747  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35748  * 
35749  * @cfg {string}   cls             Extra CSS classes to add to region
35750  * 
35751  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35752  * @cfg {string}   region  the region that it inhabits..
35753  *
35754
35755  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35756  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35757
35758  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35759  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35760  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35761  */
35762 Roo.bootstrap.layout.Region = function(config)
35763 {
35764     this.applyConfig(config);
35765
35766     var mgr = config.mgr;
35767     var pos = config.region;
35768     config.skipConfig = true;
35769     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35770     
35771     if (mgr.el) {
35772         this.onRender(mgr.el);   
35773     }
35774      
35775     this.visible = true;
35776     this.collapsed = false;
35777     this.unrendered_panels = [];
35778 };
35779
35780 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35781
35782     position: '', // set by wrapper (eg. north/south etc..)
35783     unrendered_panels : null,  // unrendered panels.
35784     createBody : function(){
35785         /** This region's body element 
35786         * @type Roo.Element */
35787         this.bodyEl = this.el.createChild({
35788                 tag: "div",
35789                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35790         });
35791     },
35792
35793     onRender: function(ctr, pos)
35794     {
35795         var dh = Roo.DomHelper;
35796         /** This region's container element 
35797         * @type Roo.Element */
35798         this.el = dh.append(ctr.dom, {
35799                 tag: "div",
35800                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35801             }, true);
35802         /** This region's title element 
35803         * @type Roo.Element */
35804     
35805         this.titleEl = dh.append(this.el.dom,
35806             {
35807                     tag: "div",
35808                     unselectable: "on",
35809                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35810                     children:[
35811                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35812                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35813                     ]}, true);
35814         
35815         this.titleEl.enableDisplayMode();
35816         /** This region's title text element 
35817         * @type HTMLElement */
35818         this.titleTextEl = this.titleEl.dom.firstChild;
35819         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35820         /*
35821         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35822         this.closeBtn.enableDisplayMode();
35823         this.closeBtn.on("click", this.closeClicked, this);
35824         this.closeBtn.hide();
35825     */
35826         this.createBody(this.config);
35827         if(this.config.hideWhenEmpty){
35828             this.hide();
35829             this.on("paneladded", this.validateVisibility, this);
35830             this.on("panelremoved", this.validateVisibility, this);
35831         }
35832         if(this.autoScroll){
35833             this.bodyEl.setStyle("overflow", "auto");
35834         }else{
35835             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35836         }
35837         //if(c.titlebar !== false){
35838             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35839                 this.titleEl.hide();
35840             }else{
35841                 this.titleEl.show();
35842                 if(this.config.title){
35843                     this.titleTextEl.innerHTML = this.config.title;
35844                 }
35845             }
35846         //}
35847         if(this.config.collapsed){
35848             this.collapse(true);
35849         }
35850         if(this.config.hidden){
35851             this.hide();
35852         }
35853         
35854         if (this.unrendered_panels && this.unrendered_panels.length) {
35855             for (var i =0;i< this.unrendered_panels.length; i++) {
35856                 this.add(this.unrendered_panels[i]);
35857             }
35858             this.unrendered_panels = null;
35859             
35860         }
35861         
35862     },
35863     
35864     applyConfig : function(c)
35865     {
35866         /*
35867          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35868             var dh = Roo.DomHelper;
35869             if(c.titlebar !== false){
35870                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35871                 this.collapseBtn.on("click", this.collapse, this);
35872                 this.collapseBtn.enableDisplayMode();
35873                 /*
35874                 if(c.showPin === true || this.showPin){
35875                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35876                     this.stickBtn.enableDisplayMode();
35877                     this.stickBtn.on("click", this.expand, this);
35878                     this.stickBtn.hide();
35879                 }
35880                 
35881             }
35882             */
35883             /** This region's collapsed element
35884             * @type Roo.Element */
35885             /*
35886              *
35887             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35888                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35889             ]}, true);
35890             
35891             if(c.floatable !== false){
35892                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35893                this.collapsedEl.on("click", this.collapseClick, this);
35894             }
35895
35896             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35897                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35898                    id: "message", unselectable: "on", style:{"float":"left"}});
35899                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35900              }
35901             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35902             this.expandBtn.on("click", this.expand, this);
35903             
35904         }
35905         
35906         if(this.collapseBtn){
35907             this.collapseBtn.setVisible(c.collapsible == true);
35908         }
35909         
35910         this.cmargins = c.cmargins || this.cmargins ||
35911                          (this.position == "west" || this.position == "east" ?
35912                              {top: 0, left: 2, right:2, bottom: 0} :
35913                              {top: 2, left: 0, right:0, bottom: 2});
35914         */
35915         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35916         
35917         
35918         this.bottomTabs = c.tabPosition != "top";
35919         
35920         this.autoScroll = c.autoScroll || false;
35921         
35922         
35923        
35924         
35925         this.duration = c.duration || .30;
35926         this.slideDuration = c.slideDuration || .45;
35927         this.config = c;
35928        
35929     },
35930     /**
35931      * Returns true if this region is currently visible.
35932      * @return {Boolean}
35933      */
35934     isVisible : function(){
35935         return this.visible;
35936     },
35937
35938     /**
35939      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35940      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35941      */
35942     //setCollapsedTitle : function(title){
35943     //    title = title || "&#160;";
35944      //   if(this.collapsedTitleTextEl){
35945       //      this.collapsedTitleTextEl.innerHTML = title;
35946        // }
35947     //},
35948
35949     getBox : function(){
35950         var b;
35951       //  if(!this.collapsed){
35952             b = this.el.getBox(false, true);
35953        // }else{
35954           //  b = this.collapsedEl.getBox(false, true);
35955         //}
35956         return b;
35957     },
35958
35959     getMargins : function(){
35960         return this.margins;
35961         //return this.collapsed ? this.cmargins : this.margins;
35962     },
35963 /*
35964     highlight : function(){
35965         this.el.addClass("x-layout-panel-dragover");
35966     },
35967
35968     unhighlight : function(){
35969         this.el.removeClass("x-layout-panel-dragover");
35970     },
35971 */
35972     updateBox : function(box)
35973     {
35974         if (!this.bodyEl) {
35975             return; // not rendered yet..
35976         }
35977         
35978         this.box = box;
35979         if(!this.collapsed){
35980             this.el.dom.style.left = box.x + "px";
35981             this.el.dom.style.top = box.y + "px";
35982             this.updateBody(box.width, box.height);
35983         }else{
35984             this.collapsedEl.dom.style.left = box.x + "px";
35985             this.collapsedEl.dom.style.top = box.y + "px";
35986             this.collapsedEl.setSize(box.width, box.height);
35987         }
35988         if(this.tabs){
35989             this.tabs.autoSizeTabs();
35990         }
35991     },
35992
35993     updateBody : function(w, h)
35994     {
35995         if(w !== null){
35996             this.el.setWidth(w);
35997             w -= this.el.getBorderWidth("rl");
35998             if(this.config.adjustments){
35999                 w += this.config.adjustments[0];
36000             }
36001         }
36002         if(h !== null && h > 0){
36003             this.el.setHeight(h);
36004             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36005             h -= this.el.getBorderWidth("tb");
36006             if(this.config.adjustments){
36007                 h += this.config.adjustments[1];
36008             }
36009             this.bodyEl.setHeight(h);
36010             if(this.tabs){
36011                 h = this.tabs.syncHeight(h);
36012             }
36013         }
36014         if(this.panelSize){
36015             w = w !== null ? w : this.panelSize.width;
36016             h = h !== null ? h : this.panelSize.height;
36017         }
36018         if(this.activePanel){
36019             var el = this.activePanel.getEl();
36020             w = w !== null ? w : el.getWidth();
36021             h = h !== null ? h : el.getHeight();
36022             this.panelSize = {width: w, height: h};
36023             this.activePanel.setSize(w, h);
36024         }
36025         if(Roo.isIE && this.tabs){
36026             this.tabs.el.repaint();
36027         }
36028     },
36029
36030     /**
36031      * Returns the container element for this region.
36032      * @return {Roo.Element}
36033      */
36034     getEl : function(){
36035         return this.el;
36036     },
36037
36038     /**
36039      * Hides this region.
36040      */
36041     hide : function(){
36042         //if(!this.collapsed){
36043             this.el.dom.style.left = "-2000px";
36044             this.el.hide();
36045         //}else{
36046          //   this.collapsedEl.dom.style.left = "-2000px";
36047          //   this.collapsedEl.hide();
36048        // }
36049         this.visible = false;
36050         this.fireEvent("visibilitychange", this, false);
36051     },
36052
36053     /**
36054      * Shows this region if it was previously hidden.
36055      */
36056     show : function(){
36057         //if(!this.collapsed){
36058             this.el.show();
36059         //}else{
36060         //    this.collapsedEl.show();
36061        // }
36062         this.visible = true;
36063         this.fireEvent("visibilitychange", this, true);
36064     },
36065 /*
36066     closeClicked : function(){
36067         if(this.activePanel){
36068             this.remove(this.activePanel);
36069         }
36070     },
36071
36072     collapseClick : function(e){
36073         if(this.isSlid){
36074            e.stopPropagation();
36075            this.slideIn();
36076         }else{
36077            e.stopPropagation();
36078            this.slideOut();
36079         }
36080     },
36081 */
36082     /**
36083      * Collapses this region.
36084      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36085      */
36086     /*
36087     collapse : function(skipAnim, skipCheck = false){
36088         if(this.collapsed) {
36089             return;
36090         }
36091         
36092         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36093             
36094             this.collapsed = true;
36095             if(this.split){
36096                 this.split.el.hide();
36097             }
36098             if(this.config.animate && skipAnim !== true){
36099                 this.fireEvent("invalidated", this);
36100                 this.animateCollapse();
36101             }else{
36102                 this.el.setLocation(-20000,-20000);
36103                 this.el.hide();
36104                 this.collapsedEl.show();
36105                 this.fireEvent("collapsed", this);
36106                 this.fireEvent("invalidated", this);
36107             }
36108         }
36109         
36110     },
36111 */
36112     animateCollapse : function(){
36113         // overridden
36114     },
36115
36116     /**
36117      * Expands this region if it was previously collapsed.
36118      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36119      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36120      */
36121     /*
36122     expand : function(e, skipAnim){
36123         if(e) {
36124             e.stopPropagation();
36125         }
36126         if(!this.collapsed || this.el.hasActiveFx()) {
36127             return;
36128         }
36129         if(this.isSlid){
36130             this.afterSlideIn();
36131             skipAnim = true;
36132         }
36133         this.collapsed = false;
36134         if(this.config.animate && skipAnim !== true){
36135             this.animateExpand();
36136         }else{
36137             this.el.show();
36138             if(this.split){
36139                 this.split.el.show();
36140             }
36141             this.collapsedEl.setLocation(-2000,-2000);
36142             this.collapsedEl.hide();
36143             this.fireEvent("invalidated", this);
36144             this.fireEvent("expanded", this);
36145         }
36146     },
36147 */
36148     animateExpand : function(){
36149         // overridden
36150     },
36151
36152     initTabs : function()
36153     {
36154         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36155         
36156         var ts = new Roo.bootstrap.panel.Tabs({
36157                 el: this.bodyEl.dom,
36158                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36159                 disableTooltips: this.config.disableTabTips,
36160                 toolbar : this.config.toolbar
36161             });
36162         
36163         if(this.config.hideTabs){
36164             ts.stripWrap.setDisplayed(false);
36165         }
36166         this.tabs = ts;
36167         ts.resizeTabs = this.config.resizeTabs === true;
36168         ts.minTabWidth = this.config.minTabWidth || 40;
36169         ts.maxTabWidth = this.config.maxTabWidth || 250;
36170         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36171         ts.monitorResize = false;
36172         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36173         ts.bodyEl.addClass('roo-layout-tabs-body');
36174         this.panels.each(this.initPanelAsTab, this);
36175     },
36176
36177     initPanelAsTab : function(panel){
36178         var ti = this.tabs.addTab(
36179             panel.getEl().id,
36180             panel.getTitle(),
36181             null,
36182             this.config.closeOnTab && panel.isClosable(),
36183             panel.tpl
36184         );
36185         if(panel.tabTip !== undefined){
36186             ti.setTooltip(panel.tabTip);
36187         }
36188         ti.on("activate", function(){
36189               this.setActivePanel(panel);
36190         }, this);
36191         
36192         if(this.config.closeOnTab){
36193             ti.on("beforeclose", function(t, e){
36194                 e.cancel = true;
36195                 this.remove(panel);
36196             }, this);
36197         }
36198         
36199         panel.tabItem = ti;
36200         
36201         return ti;
36202     },
36203
36204     updatePanelTitle : function(panel, title)
36205     {
36206         if(this.activePanel == panel){
36207             this.updateTitle(title);
36208         }
36209         if(this.tabs){
36210             var ti = this.tabs.getTab(panel.getEl().id);
36211             ti.setText(title);
36212             if(panel.tabTip !== undefined){
36213                 ti.setTooltip(panel.tabTip);
36214             }
36215         }
36216     },
36217
36218     updateTitle : function(title){
36219         if(this.titleTextEl && !this.config.title){
36220             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36221         }
36222     },
36223
36224     setActivePanel : function(panel)
36225     {
36226         panel = this.getPanel(panel);
36227         if(this.activePanel && this.activePanel != panel){
36228             if(this.activePanel.setActiveState(false) === false){
36229                 return;
36230             }
36231         }
36232         this.activePanel = panel;
36233         panel.setActiveState(true);
36234         if(this.panelSize){
36235             panel.setSize(this.panelSize.width, this.panelSize.height);
36236         }
36237         if(this.closeBtn){
36238             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36239         }
36240         this.updateTitle(panel.getTitle());
36241         if(this.tabs){
36242             this.fireEvent("invalidated", this);
36243         }
36244         this.fireEvent("panelactivated", this, panel);
36245     },
36246
36247     /**
36248      * Shows the specified panel.
36249      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36250      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36251      */
36252     showPanel : function(panel)
36253     {
36254         panel = this.getPanel(panel);
36255         if(panel){
36256             if(this.tabs){
36257                 var tab = this.tabs.getTab(panel.getEl().id);
36258                 if(tab.isHidden()){
36259                     this.tabs.unhideTab(tab.id);
36260                 }
36261                 tab.activate();
36262             }else{
36263                 this.setActivePanel(panel);
36264             }
36265         }
36266         return panel;
36267     },
36268
36269     /**
36270      * Get the active panel for this region.
36271      * @return {Roo.ContentPanel} The active panel or null
36272      */
36273     getActivePanel : function(){
36274         return this.activePanel;
36275     },
36276
36277     validateVisibility : function(){
36278         if(this.panels.getCount() < 1){
36279             this.updateTitle("&#160;");
36280             this.closeBtn.hide();
36281             this.hide();
36282         }else{
36283             if(!this.isVisible()){
36284                 this.show();
36285             }
36286         }
36287     },
36288
36289     /**
36290      * Adds the passed ContentPanel(s) to this region.
36291      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36292      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36293      */
36294     add : function(panel)
36295     {
36296         if(arguments.length > 1){
36297             for(var i = 0, len = arguments.length; i < len; i++) {
36298                 this.add(arguments[i]);
36299             }
36300             return null;
36301         }
36302         
36303         // if we have not been rendered yet, then we can not really do much of this..
36304         if (!this.bodyEl) {
36305             this.unrendered_panels.push(panel);
36306             return panel;
36307         }
36308         
36309         
36310         
36311         
36312         if(this.hasPanel(panel)){
36313             this.showPanel(panel);
36314             return panel;
36315         }
36316         panel.setRegion(this);
36317         this.panels.add(panel);
36318        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36319             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36320             // and hide them... ???
36321             this.bodyEl.dom.appendChild(panel.getEl().dom);
36322             if(panel.background !== true){
36323                 this.setActivePanel(panel);
36324             }
36325             this.fireEvent("paneladded", this, panel);
36326             return panel;
36327         }
36328         */
36329         if(!this.tabs){
36330             this.initTabs();
36331         }else{
36332             this.initPanelAsTab(panel);
36333         }
36334         
36335         
36336         if(panel.background !== true){
36337             this.tabs.activate(panel.getEl().id);
36338         }
36339         this.fireEvent("paneladded", this, panel);
36340         return panel;
36341     },
36342
36343     /**
36344      * Hides the tab for the specified panel.
36345      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36346      */
36347     hidePanel : function(panel){
36348         if(this.tabs && (panel = this.getPanel(panel))){
36349             this.tabs.hideTab(panel.getEl().id);
36350         }
36351     },
36352
36353     /**
36354      * Unhides the tab for a previously hidden panel.
36355      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36356      */
36357     unhidePanel : function(panel){
36358         if(this.tabs && (panel = this.getPanel(panel))){
36359             this.tabs.unhideTab(panel.getEl().id);
36360         }
36361     },
36362
36363     clearPanels : function(){
36364         while(this.panels.getCount() > 0){
36365              this.remove(this.panels.first());
36366         }
36367     },
36368
36369     /**
36370      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36371      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36372      * @param {Boolean} preservePanel Overrides the config preservePanel option
36373      * @return {Roo.ContentPanel} The panel that was removed
36374      */
36375     remove : function(panel, preservePanel)
36376     {
36377         panel = this.getPanel(panel);
36378         if(!panel){
36379             return null;
36380         }
36381         var e = {};
36382         this.fireEvent("beforeremove", this, panel, e);
36383         if(e.cancel === true){
36384             return null;
36385         }
36386         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36387         var panelId = panel.getId();
36388         this.panels.removeKey(panelId);
36389         if(preservePanel){
36390             document.body.appendChild(panel.getEl().dom);
36391         }
36392         if(this.tabs){
36393             this.tabs.removeTab(panel.getEl().id);
36394         }else if (!preservePanel){
36395             this.bodyEl.dom.removeChild(panel.getEl().dom);
36396         }
36397         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36398             var p = this.panels.first();
36399             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36400             tempEl.appendChild(p.getEl().dom);
36401             this.bodyEl.update("");
36402             this.bodyEl.dom.appendChild(p.getEl().dom);
36403             tempEl = null;
36404             this.updateTitle(p.getTitle());
36405             this.tabs = null;
36406             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36407             this.setActivePanel(p);
36408         }
36409         panel.setRegion(null);
36410         if(this.activePanel == panel){
36411             this.activePanel = null;
36412         }
36413         if(this.config.autoDestroy !== false && preservePanel !== true){
36414             try{panel.destroy();}catch(e){}
36415         }
36416         this.fireEvent("panelremoved", this, panel);
36417         return panel;
36418     },
36419
36420     /**
36421      * Returns the TabPanel component used by this region
36422      * @return {Roo.TabPanel}
36423      */
36424     getTabs : function(){
36425         return this.tabs;
36426     },
36427
36428     createTool : function(parentEl, className){
36429         var btn = Roo.DomHelper.append(parentEl, {
36430             tag: "div",
36431             cls: "x-layout-tools-button",
36432             children: [ {
36433                 tag: "div",
36434                 cls: "roo-layout-tools-button-inner " + className,
36435                 html: "&#160;"
36436             }]
36437         }, true);
36438         btn.addClassOnOver("roo-layout-tools-button-over");
36439         return btn;
36440     }
36441 });/*
36442  * Based on:
36443  * Ext JS Library 1.1.1
36444  * Copyright(c) 2006-2007, Ext JS, LLC.
36445  *
36446  * Originally Released Under LGPL - original licence link has changed is not relivant.
36447  *
36448  * Fork - LGPL
36449  * <script type="text/javascript">
36450  */
36451  
36452
36453
36454 /**
36455  * @class Roo.SplitLayoutRegion
36456  * @extends Roo.LayoutRegion
36457  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36458  */
36459 Roo.bootstrap.layout.Split = function(config){
36460     this.cursor = config.cursor;
36461     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36462 };
36463
36464 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36465 {
36466     splitTip : "Drag to resize.",
36467     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36468     useSplitTips : false,
36469
36470     applyConfig : function(config){
36471         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36472     },
36473     
36474     onRender : function(ctr,pos) {
36475         
36476         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36477         if(!this.config.split){
36478             return;
36479         }
36480         if(!this.split){
36481             
36482             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36483                             tag: "div",
36484                             id: this.el.id + "-split",
36485                             cls: "roo-layout-split roo-layout-split-"+this.position,
36486                             html: "&#160;"
36487             });
36488             /** The SplitBar for this region 
36489             * @type Roo.SplitBar */
36490             // does not exist yet...
36491             Roo.log([this.position, this.orientation]);
36492             
36493             this.split = new Roo.bootstrap.SplitBar({
36494                 dragElement : splitEl,
36495                 resizingElement: this.el,
36496                 orientation : this.orientation
36497             });
36498             
36499             this.split.on("moved", this.onSplitMove, this);
36500             this.split.useShim = this.config.useShim === true;
36501             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36502             if(this.useSplitTips){
36503                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36504             }
36505             //if(config.collapsible){
36506             //    this.split.el.on("dblclick", this.collapse,  this);
36507             //}
36508         }
36509         if(typeof this.config.minSize != "undefined"){
36510             this.split.minSize = this.config.minSize;
36511         }
36512         if(typeof this.config.maxSize != "undefined"){
36513             this.split.maxSize = this.config.maxSize;
36514         }
36515         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36516             this.hideSplitter();
36517         }
36518         
36519     },
36520
36521     getHMaxSize : function(){
36522          var cmax = this.config.maxSize || 10000;
36523          var center = this.mgr.getRegion("center");
36524          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36525     },
36526
36527     getVMaxSize : function(){
36528          var cmax = this.config.maxSize || 10000;
36529          var center = this.mgr.getRegion("center");
36530          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36531     },
36532
36533     onSplitMove : function(split, newSize){
36534         this.fireEvent("resized", this, newSize);
36535     },
36536     
36537     /** 
36538      * Returns the {@link Roo.SplitBar} for this region.
36539      * @return {Roo.SplitBar}
36540      */
36541     getSplitBar : function(){
36542         return this.split;
36543     },
36544     
36545     hide : function(){
36546         this.hideSplitter();
36547         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36548     },
36549
36550     hideSplitter : function(){
36551         if(this.split){
36552             this.split.el.setLocation(-2000,-2000);
36553             this.split.el.hide();
36554         }
36555     },
36556
36557     show : function(){
36558         if(this.split){
36559             this.split.el.show();
36560         }
36561         Roo.bootstrap.layout.Split.superclass.show.call(this);
36562     },
36563     
36564     beforeSlide: function(){
36565         if(Roo.isGecko){// firefox overflow auto bug workaround
36566             this.bodyEl.clip();
36567             if(this.tabs) {
36568                 this.tabs.bodyEl.clip();
36569             }
36570             if(this.activePanel){
36571                 this.activePanel.getEl().clip();
36572                 
36573                 if(this.activePanel.beforeSlide){
36574                     this.activePanel.beforeSlide();
36575                 }
36576             }
36577         }
36578     },
36579     
36580     afterSlide : function(){
36581         if(Roo.isGecko){// firefox overflow auto bug workaround
36582             this.bodyEl.unclip();
36583             if(this.tabs) {
36584                 this.tabs.bodyEl.unclip();
36585             }
36586             if(this.activePanel){
36587                 this.activePanel.getEl().unclip();
36588                 if(this.activePanel.afterSlide){
36589                     this.activePanel.afterSlide();
36590                 }
36591             }
36592         }
36593     },
36594
36595     initAutoHide : function(){
36596         if(this.autoHide !== false){
36597             if(!this.autoHideHd){
36598                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36599                 this.autoHideHd = {
36600                     "mouseout": function(e){
36601                         if(!e.within(this.el, true)){
36602                             st.delay(500);
36603                         }
36604                     },
36605                     "mouseover" : function(e){
36606                         st.cancel();
36607                     },
36608                     scope : this
36609                 };
36610             }
36611             this.el.on(this.autoHideHd);
36612         }
36613     },
36614
36615     clearAutoHide : function(){
36616         if(this.autoHide !== false){
36617             this.el.un("mouseout", this.autoHideHd.mouseout);
36618             this.el.un("mouseover", this.autoHideHd.mouseover);
36619         }
36620     },
36621
36622     clearMonitor : function(){
36623         Roo.get(document).un("click", this.slideInIf, this);
36624     },
36625
36626     // these names are backwards but not changed for compat
36627     slideOut : function(){
36628         if(this.isSlid || this.el.hasActiveFx()){
36629             return;
36630         }
36631         this.isSlid = true;
36632         if(this.collapseBtn){
36633             this.collapseBtn.hide();
36634         }
36635         this.closeBtnState = this.closeBtn.getStyle('display');
36636         this.closeBtn.hide();
36637         if(this.stickBtn){
36638             this.stickBtn.show();
36639         }
36640         this.el.show();
36641         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36642         this.beforeSlide();
36643         this.el.setStyle("z-index", 10001);
36644         this.el.slideIn(this.getSlideAnchor(), {
36645             callback: function(){
36646                 this.afterSlide();
36647                 this.initAutoHide();
36648                 Roo.get(document).on("click", this.slideInIf, this);
36649                 this.fireEvent("slideshow", this);
36650             },
36651             scope: this,
36652             block: true
36653         });
36654     },
36655
36656     afterSlideIn : function(){
36657         this.clearAutoHide();
36658         this.isSlid = false;
36659         this.clearMonitor();
36660         this.el.setStyle("z-index", "");
36661         if(this.collapseBtn){
36662             this.collapseBtn.show();
36663         }
36664         this.closeBtn.setStyle('display', this.closeBtnState);
36665         if(this.stickBtn){
36666             this.stickBtn.hide();
36667         }
36668         this.fireEvent("slidehide", this);
36669     },
36670
36671     slideIn : function(cb){
36672         if(!this.isSlid || this.el.hasActiveFx()){
36673             Roo.callback(cb);
36674             return;
36675         }
36676         this.isSlid = false;
36677         this.beforeSlide();
36678         this.el.slideOut(this.getSlideAnchor(), {
36679             callback: function(){
36680                 this.el.setLeftTop(-10000, -10000);
36681                 this.afterSlide();
36682                 this.afterSlideIn();
36683                 Roo.callback(cb);
36684             },
36685             scope: this,
36686             block: true
36687         });
36688     },
36689     
36690     slideInIf : function(e){
36691         if(!e.within(this.el)){
36692             this.slideIn();
36693         }
36694     },
36695
36696     animateCollapse : function(){
36697         this.beforeSlide();
36698         this.el.setStyle("z-index", 20000);
36699         var anchor = this.getSlideAnchor();
36700         this.el.slideOut(anchor, {
36701             callback : function(){
36702                 this.el.setStyle("z-index", "");
36703                 this.collapsedEl.slideIn(anchor, {duration:.3});
36704                 this.afterSlide();
36705                 this.el.setLocation(-10000,-10000);
36706                 this.el.hide();
36707                 this.fireEvent("collapsed", this);
36708             },
36709             scope: this,
36710             block: true
36711         });
36712     },
36713
36714     animateExpand : function(){
36715         this.beforeSlide();
36716         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36717         this.el.setStyle("z-index", 20000);
36718         this.collapsedEl.hide({
36719             duration:.1
36720         });
36721         this.el.slideIn(this.getSlideAnchor(), {
36722             callback : function(){
36723                 this.el.setStyle("z-index", "");
36724                 this.afterSlide();
36725                 if(this.split){
36726                     this.split.el.show();
36727                 }
36728                 this.fireEvent("invalidated", this);
36729                 this.fireEvent("expanded", this);
36730             },
36731             scope: this,
36732             block: true
36733         });
36734     },
36735
36736     anchors : {
36737         "west" : "left",
36738         "east" : "right",
36739         "north" : "top",
36740         "south" : "bottom"
36741     },
36742
36743     sanchors : {
36744         "west" : "l",
36745         "east" : "r",
36746         "north" : "t",
36747         "south" : "b"
36748     },
36749
36750     canchors : {
36751         "west" : "tl-tr",
36752         "east" : "tr-tl",
36753         "north" : "tl-bl",
36754         "south" : "bl-tl"
36755     },
36756
36757     getAnchor : function(){
36758         return this.anchors[this.position];
36759     },
36760
36761     getCollapseAnchor : function(){
36762         return this.canchors[this.position];
36763     },
36764
36765     getSlideAnchor : function(){
36766         return this.sanchors[this.position];
36767     },
36768
36769     getAlignAdj : function(){
36770         var cm = this.cmargins;
36771         switch(this.position){
36772             case "west":
36773                 return [0, 0];
36774             break;
36775             case "east":
36776                 return [0, 0];
36777             break;
36778             case "north":
36779                 return [0, 0];
36780             break;
36781             case "south":
36782                 return [0, 0];
36783             break;
36784         }
36785     },
36786
36787     getExpandAdj : function(){
36788         var c = this.collapsedEl, cm = this.cmargins;
36789         switch(this.position){
36790             case "west":
36791                 return [-(cm.right+c.getWidth()+cm.left), 0];
36792             break;
36793             case "east":
36794                 return [cm.right+c.getWidth()+cm.left, 0];
36795             break;
36796             case "north":
36797                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36798             break;
36799             case "south":
36800                 return [0, cm.top+cm.bottom+c.getHeight()];
36801             break;
36802         }
36803     }
36804 });/*
36805  * Based on:
36806  * Ext JS Library 1.1.1
36807  * Copyright(c) 2006-2007, Ext JS, LLC.
36808  *
36809  * Originally Released Under LGPL - original licence link has changed is not relivant.
36810  *
36811  * Fork - LGPL
36812  * <script type="text/javascript">
36813  */
36814 /*
36815  * These classes are private internal classes
36816  */
36817 Roo.bootstrap.layout.Center = function(config){
36818     config.region = "center";
36819     Roo.bootstrap.layout.Region.call(this, config);
36820     this.visible = true;
36821     this.minWidth = config.minWidth || 20;
36822     this.minHeight = config.minHeight || 20;
36823 };
36824
36825 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36826     hide : function(){
36827         // center panel can't be hidden
36828     },
36829     
36830     show : function(){
36831         // center panel can't be hidden
36832     },
36833     
36834     getMinWidth: function(){
36835         return this.minWidth;
36836     },
36837     
36838     getMinHeight: function(){
36839         return this.minHeight;
36840     }
36841 });
36842
36843
36844
36845
36846  
36847
36848
36849
36850
36851
36852 Roo.bootstrap.layout.North = function(config)
36853 {
36854     config.region = 'north';
36855     config.cursor = 'n-resize';
36856     
36857     Roo.bootstrap.layout.Split.call(this, config);
36858     
36859     
36860     if(this.split){
36861         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36862         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36863         this.split.el.addClass("roo-layout-split-v");
36864     }
36865     var size = config.initialSize || config.height;
36866     if(typeof size != "undefined"){
36867         this.el.setHeight(size);
36868     }
36869 };
36870 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36871 {
36872     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36873     
36874     
36875     
36876     getBox : function(){
36877         if(this.collapsed){
36878             return this.collapsedEl.getBox();
36879         }
36880         var box = this.el.getBox();
36881         if(this.split){
36882             box.height += this.split.el.getHeight();
36883         }
36884         return box;
36885     },
36886     
36887     updateBox : function(box){
36888         if(this.split && !this.collapsed){
36889             box.height -= this.split.el.getHeight();
36890             this.split.el.setLeft(box.x);
36891             this.split.el.setTop(box.y+box.height);
36892             this.split.el.setWidth(box.width);
36893         }
36894         if(this.collapsed){
36895             this.updateBody(box.width, null);
36896         }
36897         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36898     }
36899 });
36900
36901
36902
36903
36904
36905 Roo.bootstrap.layout.South = function(config){
36906     config.region = 'south';
36907     config.cursor = 's-resize';
36908     Roo.bootstrap.layout.Split.call(this, config);
36909     if(this.split){
36910         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36911         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36912         this.split.el.addClass("roo-layout-split-v");
36913     }
36914     var size = config.initialSize || config.height;
36915     if(typeof size != "undefined"){
36916         this.el.setHeight(size);
36917     }
36918 };
36919
36920 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36921     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36922     getBox : function(){
36923         if(this.collapsed){
36924             return this.collapsedEl.getBox();
36925         }
36926         var box = this.el.getBox();
36927         if(this.split){
36928             var sh = this.split.el.getHeight();
36929             box.height += sh;
36930             box.y -= sh;
36931         }
36932         return box;
36933     },
36934     
36935     updateBox : function(box){
36936         if(this.split && !this.collapsed){
36937             var sh = this.split.el.getHeight();
36938             box.height -= sh;
36939             box.y += sh;
36940             this.split.el.setLeft(box.x);
36941             this.split.el.setTop(box.y-sh);
36942             this.split.el.setWidth(box.width);
36943         }
36944         if(this.collapsed){
36945             this.updateBody(box.width, null);
36946         }
36947         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36948     }
36949 });
36950
36951 Roo.bootstrap.layout.East = function(config){
36952     config.region = "east";
36953     config.cursor = "e-resize";
36954     Roo.bootstrap.layout.Split.call(this, config);
36955     if(this.split){
36956         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36957         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36958         this.split.el.addClass("roo-layout-split-h");
36959     }
36960     var size = config.initialSize || config.width;
36961     if(typeof size != "undefined"){
36962         this.el.setWidth(size);
36963     }
36964 };
36965 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36966     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36967     getBox : function(){
36968         if(this.collapsed){
36969             return this.collapsedEl.getBox();
36970         }
36971         var box = this.el.getBox();
36972         if(this.split){
36973             var sw = this.split.el.getWidth();
36974             box.width += sw;
36975             box.x -= sw;
36976         }
36977         return box;
36978     },
36979
36980     updateBox : function(box){
36981         if(this.split && !this.collapsed){
36982             var sw = this.split.el.getWidth();
36983             box.width -= sw;
36984             this.split.el.setLeft(box.x);
36985             this.split.el.setTop(box.y);
36986             this.split.el.setHeight(box.height);
36987             box.x += sw;
36988         }
36989         if(this.collapsed){
36990             this.updateBody(null, box.height);
36991         }
36992         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36993     }
36994 });
36995
36996 Roo.bootstrap.layout.West = function(config){
36997     config.region = "west";
36998     config.cursor = "w-resize";
36999     
37000     Roo.bootstrap.layout.Split.call(this, config);
37001     if(this.split){
37002         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37003         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37004         this.split.el.addClass("roo-layout-split-h");
37005     }
37006     
37007 };
37008 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37009     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37010     
37011     onRender: function(ctr, pos)
37012     {
37013         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37014         var size = this.config.initialSize || this.config.width;
37015         if(typeof size != "undefined"){
37016             this.el.setWidth(size);
37017         }
37018     },
37019     
37020     getBox : function(){
37021         if(this.collapsed){
37022             return this.collapsedEl.getBox();
37023         }
37024         var box = this.el.getBox();
37025         if(this.split){
37026             box.width += this.split.el.getWidth();
37027         }
37028         return box;
37029     },
37030     
37031     updateBox : function(box){
37032         if(this.split && !this.collapsed){
37033             var sw = this.split.el.getWidth();
37034             box.width -= sw;
37035             this.split.el.setLeft(box.x+box.width);
37036             this.split.el.setTop(box.y);
37037             this.split.el.setHeight(box.height);
37038         }
37039         if(this.collapsed){
37040             this.updateBody(null, box.height);
37041         }
37042         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37043     }
37044 });
37045 Roo.namespace("Roo.bootstrap.panel");/*
37046  * Based on:
37047  * Ext JS Library 1.1.1
37048  * Copyright(c) 2006-2007, Ext JS, LLC.
37049  *
37050  * Originally Released Under LGPL - original licence link has changed is not relivant.
37051  *
37052  * Fork - LGPL
37053  * <script type="text/javascript">
37054  */
37055 /**
37056  * @class Roo.ContentPanel
37057  * @extends Roo.util.Observable
37058  * A basic ContentPanel element.
37059  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37060  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37061  * @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
37062  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37063  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37064  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37065  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37066  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37067  * @cfg {String} title          The title for this panel
37068  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37069  * @cfg {String} url            Calls {@link #setUrl} with this value
37070  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37071  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37072  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37073  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37074  * @cfg {Boolean} badges render the badges
37075
37076  * @constructor
37077  * Create a new ContentPanel.
37078  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37079  * @param {String/Object} config A string to set only the title or a config object
37080  * @param {String} content (optional) Set the HTML content for this panel
37081  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37082  */
37083 Roo.bootstrap.panel.Content = function( config){
37084     
37085     this.tpl = config.tpl || false;
37086     
37087     var el = config.el;
37088     var content = config.content;
37089
37090     if(config.autoCreate){ // xtype is available if this is called from factory
37091         el = Roo.id();
37092     }
37093     this.el = Roo.get(el);
37094     if(!this.el && config && config.autoCreate){
37095         if(typeof config.autoCreate == "object"){
37096             if(!config.autoCreate.id){
37097                 config.autoCreate.id = config.id||el;
37098             }
37099             this.el = Roo.DomHelper.append(document.body,
37100                         config.autoCreate, true);
37101         }else{
37102             var elcfg =  {   tag: "div",
37103                             cls: "roo-layout-inactive-content",
37104                             id: config.id||el
37105                             };
37106             if (config.html) {
37107                 elcfg.html = config.html;
37108                 
37109             }
37110                         
37111             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37112         }
37113     } 
37114     this.closable = false;
37115     this.loaded = false;
37116     this.active = false;
37117    
37118       
37119     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37120         
37121         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37122         
37123         this.wrapEl = this.el; //this.el.wrap();
37124         var ti = [];
37125         if (config.toolbar.items) {
37126             ti = config.toolbar.items ;
37127             delete config.toolbar.items ;
37128         }
37129         
37130         var nitems = [];
37131         this.toolbar.render(this.wrapEl, 'before');
37132         for(var i =0;i < ti.length;i++) {
37133           //  Roo.log(['add child', items[i]]);
37134             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37135         }
37136         this.toolbar.items = nitems;
37137         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37138         delete config.toolbar;
37139         
37140     }
37141     /*
37142     // xtype created footer. - not sure if will work as we normally have to render first..
37143     if (this.footer && !this.footer.el && this.footer.xtype) {
37144         if (!this.wrapEl) {
37145             this.wrapEl = this.el.wrap();
37146         }
37147     
37148         this.footer.container = this.wrapEl.createChild();
37149          
37150         this.footer = Roo.factory(this.footer, Roo);
37151         
37152     }
37153     */
37154     
37155      if(typeof config == "string"){
37156         this.title = config;
37157     }else{
37158         Roo.apply(this, config);
37159     }
37160     
37161     if(this.resizeEl){
37162         this.resizeEl = Roo.get(this.resizeEl, true);
37163     }else{
37164         this.resizeEl = this.el;
37165     }
37166     // handle view.xtype
37167     
37168  
37169     
37170     
37171     this.addEvents({
37172         /**
37173          * @event activate
37174          * Fires when this panel is activated. 
37175          * @param {Roo.ContentPanel} this
37176          */
37177         "activate" : true,
37178         /**
37179          * @event deactivate
37180          * Fires when this panel is activated. 
37181          * @param {Roo.ContentPanel} this
37182          */
37183         "deactivate" : true,
37184
37185         /**
37186          * @event resize
37187          * Fires when this panel is resized if fitToFrame is true.
37188          * @param {Roo.ContentPanel} this
37189          * @param {Number} width The width after any component adjustments
37190          * @param {Number} height The height after any component adjustments
37191          */
37192         "resize" : true,
37193         
37194          /**
37195          * @event render
37196          * Fires when this tab is created
37197          * @param {Roo.ContentPanel} this
37198          */
37199         "render" : true
37200         
37201         
37202         
37203     });
37204     
37205
37206     
37207     
37208     if(this.autoScroll){
37209         this.resizeEl.setStyle("overflow", "auto");
37210     } else {
37211         // fix randome scrolling
37212         //this.el.on('scroll', function() {
37213         //    Roo.log('fix random scolling');
37214         //    this.scrollTo('top',0); 
37215         //});
37216     }
37217     content = content || this.content;
37218     if(content){
37219         this.setContent(content);
37220     }
37221     if(config && config.url){
37222         this.setUrl(this.url, this.params, this.loadOnce);
37223     }
37224     
37225     
37226     
37227     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37228     
37229     if (this.view && typeof(this.view.xtype) != 'undefined') {
37230         this.view.el = this.el.appendChild(document.createElement("div"));
37231         this.view = Roo.factory(this.view); 
37232         this.view.render  &&  this.view.render(false, '');  
37233     }
37234     
37235     
37236     this.fireEvent('render', this);
37237 };
37238
37239 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37240     
37241     tabTip : '',
37242     
37243     setRegion : function(region){
37244         this.region = region;
37245         this.setActiveClass(region && !this.background);
37246     },
37247     
37248     
37249     setActiveClass: function(state)
37250     {
37251         if(state){
37252            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37253            this.el.setStyle('position','relative');
37254         }else{
37255            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37256            this.el.setStyle('position', 'absolute');
37257         } 
37258     },
37259     
37260     /**
37261      * Returns the toolbar for this Panel if one was configured. 
37262      * @return {Roo.Toolbar} 
37263      */
37264     getToolbar : function(){
37265         return this.toolbar;
37266     },
37267     
37268     setActiveState : function(active)
37269     {
37270         this.active = active;
37271         this.setActiveClass(active);
37272         if(!active){
37273             if(this.fireEvent("deactivate", this) === false){
37274                 return false;
37275             }
37276             return true;
37277         }
37278         this.fireEvent("activate", this);
37279         return true;
37280     },
37281     /**
37282      * Updates this panel's element
37283      * @param {String} content The new content
37284      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37285     */
37286     setContent : function(content, loadScripts){
37287         this.el.update(content, loadScripts);
37288     },
37289
37290     ignoreResize : function(w, h){
37291         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37292             return true;
37293         }else{
37294             this.lastSize = {width: w, height: h};
37295             return false;
37296         }
37297     },
37298     /**
37299      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37300      * @return {Roo.UpdateManager} The UpdateManager
37301      */
37302     getUpdateManager : function(){
37303         return this.el.getUpdateManager();
37304     },
37305      /**
37306      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37307      * @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:
37308 <pre><code>
37309 panel.load({
37310     url: "your-url.php",
37311     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37312     callback: yourFunction,
37313     scope: yourObject, //(optional scope)
37314     discardUrl: false,
37315     nocache: false,
37316     text: "Loading...",
37317     timeout: 30,
37318     scripts: false
37319 });
37320 </code></pre>
37321      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37322      * 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.
37323      * @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}
37324      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37325      * @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.
37326      * @return {Roo.ContentPanel} this
37327      */
37328     load : function(){
37329         var um = this.el.getUpdateManager();
37330         um.update.apply(um, arguments);
37331         return this;
37332     },
37333
37334
37335     /**
37336      * 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.
37337      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37338      * @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)
37339      * @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)
37340      * @return {Roo.UpdateManager} The UpdateManager
37341      */
37342     setUrl : function(url, params, loadOnce){
37343         if(this.refreshDelegate){
37344             this.removeListener("activate", this.refreshDelegate);
37345         }
37346         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37347         this.on("activate", this.refreshDelegate);
37348         return this.el.getUpdateManager();
37349     },
37350     
37351     _handleRefresh : function(url, params, loadOnce){
37352         if(!loadOnce || !this.loaded){
37353             var updater = this.el.getUpdateManager();
37354             updater.update(url, params, this._setLoaded.createDelegate(this));
37355         }
37356     },
37357     
37358     _setLoaded : function(){
37359         this.loaded = true;
37360     }, 
37361     
37362     /**
37363      * Returns this panel's id
37364      * @return {String} 
37365      */
37366     getId : function(){
37367         return this.el.id;
37368     },
37369     
37370     /** 
37371      * Returns this panel's element - used by regiosn to add.
37372      * @return {Roo.Element} 
37373      */
37374     getEl : function(){
37375         return this.wrapEl || this.el;
37376     },
37377     
37378    
37379     
37380     adjustForComponents : function(width, height)
37381     {
37382         //Roo.log('adjustForComponents ');
37383         if(this.resizeEl != this.el){
37384             width -= this.el.getFrameWidth('lr');
37385             height -= this.el.getFrameWidth('tb');
37386         }
37387         if(this.toolbar){
37388             var te = this.toolbar.getEl();
37389             te.setWidth(width);
37390             height -= te.getHeight();
37391         }
37392         if(this.footer){
37393             var te = this.footer.getEl();
37394             te.setWidth(width);
37395             height -= te.getHeight();
37396         }
37397         
37398         
37399         if(this.adjustments){
37400             width += this.adjustments[0];
37401             height += this.adjustments[1];
37402         }
37403         return {"width": width, "height": height};
37404     },
37405     
37406     setSize : function(width, height){
37407         if(this.fitToFrame && !this.ignoreResize(width, height)){
37408             if(this.fitContainer && this.resizeEl != this.el){
37409                 this.el.setSize(width, height);
37410             }
37411             var size = this.adjustForComponents(width, height);
37412             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37413             this.fireEvent('resize', this, size.width, size.height);
37414         }
37415     },
37416     
37417     /**
37418      * Returns this panel's title
37419      * @return {String} 
37420      */
37421     getTitle : function(){
37422         
37423         if (typeof(this.title) != 'object') {
37424             return this.title;
37425         }
37426         
37427         var t = '';
37428         for (var k in this.title) {
37429             if (!this.title.hasOwnProperty(k)) {
37430                 continue;
37431             }
37432             
37433             if (k.indexOf('-') >= 0) {
37434                 var s = k.split('-');
37435                 for (var i = 0; i<s.length; i++) {
37436                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37437                 }
37438             } else {
37439                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37440             }
37441         }
37442         return t;
37443     },
37444     
37445     /**
37446      * Set this panel's title
37447      * @param {String} title
37448      */
37449     setTitle : function(title){
37450         this.title = title;
37451         if(this.region){
37452             this.region.updatePanelTitle(this, title);
37453         }
37454     },
37455     
37456     /**
37457      * Returns true is this panel was configured to be closable
37458      * @return {Boolean} 
37459      */
37460     isClosable : function(){
37461         return this.closable;
37462     },
37463     
37464     beforeSlide : function(){
37465         this.el.clip();
37466         this.resizeEl.clip();
37467     },
37468     
37469     afterSlide : function(){
37470         this.el.unclip();
37471         this.resizeEl.unclip();
37472     },
37473     
37474     /**
37475      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37476      *   Will fail silently if the {@link #setUrl} method has not been called.
37477      *   This does not activate the panel, just updates its content.
37478      */
37479     refresh : function(){
37480         if(this.refreshDelegate){
37481            this.loaded = false;
37482            this.refreshDelegate();
37483         }
37484     },
37485     
37486     /**
37487      * Destroys this panel
37488      */
37489     destroy : function(){
37490         this.el.removeAllListeners();
37491         var tempEl = document.createElement("span");
37492         tempEl.appendChild(this.el.dom);
37493         tempEl.innerHTML = "";
37494         this.el.remove();
37495         this.el = null;
37496     },
37497     
37498     /**
37499      * form - if the content panel contains a form - this is a reference to it.
37500      * @type {Roo.form.Form}
37501      */
37502     form : false,
37503     /**
37504      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37505      *    This contains a reference to it.
37506      * @type {Roo.View}
37507      */
37508     view : false,
37509     
37510       /**
37511      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37512      * <pre><code>
37513
37514 layout.addxtype({
37515        xtype : 'Form',
37516        items: [ .... ]
37517    }
37518 );
37519
37520 </code></pre>
37521      * @param {Object} cfg Xtype definition of item to add.
37522      */
37523     
37524     
37525     getChildContainer: function () {
37526         return this.getEl();
37527     }
37528     
37529     
37530     /*
37531         var  ret = new Roo.factory(cfg);
37532         return ret;
37533         
37534         
37535         // add form..
37536         if (cfg.xtype.match(/^Form$/)) {
37537             
37538             var el;
37539             //if (this.footer) {
37540             //    el = this.footer.container.insertSibling(false, 'before');
37541             //} else {
37542                 el = this.el.createChild();
37543             //}
37544
37545             this.form = new  Roo.form.Form(cfg);
37546             
37547             
37548             if ( this.form.allItems.length) {
37549                 this.form.render(el.dom);
37550             }
37551             return this.form;
37552         }
37553         // should only have one of theses..
37554         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37555             // views.. should not be just added - used named prop 'view''
37556             
37557             cfg.el = this.el.appendChild(document.createElement("div"));
37558             // factory?
37559             
37560             var ret = new Roo.factory(cfg);
37561              
37562              ret.render && ret.render(false, ''); // render blank..
37563             this.view = ret;
37564             return ret;
37565         }
37566         return false;
37567     }
37568     \*/
37569 });
37570  
37571 /**
37572  * @class Roo.bootstrap.panel.Grid
37573  * @extends Roo.bootstrap.panel.Content
37574  * @constructor
37575  * Create a new GridPanel.
37576  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37577  * @param {Object} config A the config object
37578   
37579  */
37580
37581
37582
37583 Roo.bootstrap.panel.Grid = function(config)
37584 {
37585     
37586       
37587     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37588         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37589
37590     config.el = this.wrapper;
37591     //this.el = this.wrapper;
37592     
37593       if (config.container) {
37594         // ctor'ed from a Border/panel.grid
37595         
37596         
37597         this.wrapper.setStyle("overflow", "hidden");
37598         this.wrapper.addClass('roo-grid-container');
37599
37600     }
37601     
37602     
37603     if(config.toolbar){
37604         var tool_el = this.wrapper.createChild();    
37605         this.toolbar = Roo.factory(config.toolbar);
37606         var ti = [];
37607         if (config.toolbar.items) {
37608             ti = config.toolbar.items ;
37609             delete config.toolbar.items ;
37610         }
37611         
37612         var nitems = [];
37613         this.toolbar.render(tool_el);
37614         for(var i =0;i < ti.length;i++) {
37615           //  Roo.log(['add child', items[i]]);
37616             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37617         }
37618         this.toolbar.items = nitems;
37619         
37620         delete config.toolbar;
37621     }
37622     
37623     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37624     config.grid.scrollBody = true;;
37625     config.grid.monitorWindowResize = false; // turn off autosizing
37626     config.grid.autoHeight = false;
37627     config.grid.autoWidth = false;
37628     
37629     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37630     
37631     if (config.background) {
37632         // render grid on panel activation (if panel background)
37633         this.on('activate', function(gp) {
37634             if (!gp.grid.rendered) {
37635                 gp.grid.render(this.wrapper);
37636                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37637             }
37638         });
37639             
37640     } else {
37641         this.grid.render(this.wrapper);
37642         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37643
37644     }
37645     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37646     // ??? needed ??? config.el = this.wrapper;
37647     
37648     
37649     
37650   
37651     // xtype created footer. - not sure if will work as we normally have to render first..
37652     if (this.footer && !this.footer.el && this.footer.xtype) {
37653         
37654         var ctr = this.grid.getView().getFooterPanel(true);
37655         this.footer.dataSource = this.grid.dataSource;
37656         this.footer = Roo.factory(this.footer, Roo);
37657         this.footer.render(ctr);
37658         
37659     }
37660     
37661     
37662     
37663     
37664      
37665 };
37666
37667 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37668     getId : function(){
37669         return this.grid.id;
37670     },
37671     
37672     /**
37673      * Returns the grid for this panel
37674      * @return {Roo.bootstrap.Table} 
37675      */
37676     getGrid : function(){
37677         return this.grid;    
37678     },
37679     
37680     setSize : function(width, height){
37681         if(!this.ignoreResize(width, height)){
37682             var grid = this.grid;
37683             var size = this.adjustForComponents(width, height);
37684             var gridel = grid.getGridEl();
37685             gridel.setSize(size.width, size.height);
37686             /*
37687             var thd = grid.getGridEl().select('thead',true).first();
37688             var tbd = grid.getGridEl().select('tbody', true).first();
37689             if (tbd) {
37690                 tbd.setSize(width, height - thd.getHeight());
37691             }
37692             */
37693             grid.autoSize();
37694         }
37695     },
37696      
37697     
37698     
37699     beforeSlide : function(){
37700         this.grid.getView().scroller.clip();
37701     },
37702     
37703     afterSlide : function(){
37704         this.grid.getView().scroller.unclip();
37705     },
37706     
37707     destroy : function(){
37708         this.grid.destroy();
37709         delete this.grid;
37710         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37711     }
37712 });
37713
37714 /**
37715  * @class Roo.bootstrap.panel.Nest
37716  * @extends Roo.bootstrap.panel.Content
37717  * @constructor
37718  * Create a new Panel, that can contain a layout.Border.
37719  * 
37720  * 
37721  * @param {Roo.BorderLayout} layout The layout for this panel
37722  * @param {String/Object} config A string to set only the title or a config object
37723  */
37724 Roo.bootstrap.panel.Nest = function(config)
37725 {
37726     // construct with only one argument..
37727     /* FIXME - implement nicer consturctors
37728     if (layout.layout) {
37729         config = layout;
37730         layout = config.layout;
37731         delete config.layout;
37732     }
37733     if (layout.xtype && !layout.getEl) {
37734         // then layout needs constructing..
37735         layout = Roo.factory(layout, Roo);
37736     }
37737     */
37738     
37739     config.el =  config.layout.getEl();
37740     
37741     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37742     
37743     config.layout.monitorWindowResize = false; // turn off autosizing
37744     this.layout = config.layout;
37745     this.layout.getEl().addClass("roo-layout-nested-layout");
37746     
37747     
37748     
37749     
37750 };
37751
37752 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37753
37754     setSize : function(width, height){
37755         if(!this.ignoreResize(width, height)){
37756             var size = this.adjustForComponents(width, height);
37757             var el = this.layout.getEl();
37758             if (size.height < 1) {
37759                 el.setWidth(size.width);   
37760             } else {
37761                 el.setSize(size.width, size.height);
37762             }
37763             var touch = el.dom.offsetWidth;
37764             this.layout.layout();
37765             // ie requires a double layout on the first pass
37766             if(Roo.isIE && !this.initialized){
37767                 this.initialized = true;
37768                 this.layout.layout();
37769             }
37770         }
37771     },
37772     
37773     // activate all subpanels if not currently active..
37774     
37775     setActiveState : function(active){
37776         this.active = active;
37777         this.setActiveClass(active);
37778         
37779         if(!active){
37780             this.fireEvent("deactivate", this);
37781             return;
37782         }
37783         
37784         this.fireEvent("activate", this);
37785         // not sure if this should happen before or after..
37786         if (!this.layout) {
37787             return; // should not happen..
37788         }
37789         var reg = false;
37790         for (var r in this.layout.regions) {
37791             reg = this.layout.getRegion(r);
37792             if (reg.getActivePanel()) {
37793                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37794                 reg.setActivePanel(reg.getActivePanel());
37795                 continue;
37796             }
37797             if (!reg.panels.length) {
37798                 continue;
37799             }
37800             reg.showPanel(reg.getPanel(0));
37801         }
37802         
37803         
37804         
37805         
37806     },
37807     
37808     /**
37809      * Returns the nested BorderLayout for this panel
37810      * @return {Roo.BorderLayout} 
37811      */
37812     getLayout : function(){
37813         return this.layout;
37814     },
37815     
37816      /**
37817      * Adds a xtype elements to the layout of the nested panel
37818      * <pre><code>
37819
37820 panel.addxtype({
37821        xtype : 'ContentPanel',
37822        region: 'west',
37823        items: [ .... ]
37824    }
37825 );
37826
37827 panel.addxtype({
37828         xtype : 'NestedLayoutPanel',
37829         region: 'west',
37830         layout: {
37831            center: { },
37832            west: { }   
37833         },
37834         items : [ ... list of content panels or nested layout panels.. ]
37835    }
37836 );
37837 </code></pre>
37838      * @param {Object} cfg Xtype definition of item to add.
37839      */
37840     addxtype : function(cfg) {
37841         return this.layout.addxtype(cfg);
37842     
37843     }
37844 });        /*
37845  * Based on:
37846  * Ext JS Library 1.1.1
37847  * Copyright(c) 2006-2007, Ext JS, LLC.
37848  *
37849  * Originally Released Under LGPL - original licence link has changed is not relivant.
37850  *
37851  * Fork - LGPL
37852  * <script type="text/javascript">
37853  */
37854 /**
37855  * @class Roo.TabPanel
37856  * @extends Roo.util.Observable
37857  * A lightweight tab container.
37858  * <br><br>
37859  * Usage:
37860  * <pre><code>
37861 // basic tabs 1, built from existing content
37862 var tabs = new Roo.TabPanel("tabs1");
37863 tabs.addTab("script", "View Script");
37864 tabs.addTab("markup", "View Markup");
37865 tabs.activate("script");
37866
37867 // more advanced tabs, built from javascript
37868 var jtabs = new Roo.TabPanel("jtabs");
37869 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37870
37871 // set up the UpdateManager
37872 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37873 var updater = tab2.getUpdateManager();
37874 updater.setDefaultUrl("ajax1.htm");
37875 tab2.on('activate', updater.refresh, updater, true);
37876
37877 // Use setUrl for Ajax loading
37878 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37879 tab3.setUrl("ajax2.htm", null, true);
37880
37881 // Disabled tab
37882 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37883 tab4.disable();
37884
37885 jtabs.activate("jtabs-1");
37886  * </code></pre>
37887  * @constructor
37888  * Create a new TabPanel.
37889  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37890  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37891  */
37892 Roo.bootstrap.panel.Tabs = function(config){
37893     /**
37894     * The container element for this TabPanel.
37895     * @type Roo.Element
37896     */
37897     this.el = Roo.get(config.el);
37898     delete config.el;
37899     if(config){
37900         if(typeof config == "boolean"){
37901             this.tabPosition = config ? "bottom" : "top";
37902         }else{
37903             Roo.apply(this, config);
37904         }
37905     }
37906     
37907     if(this.tabPosition == "bottom"){
37908         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37909         this.el.addClass("roo-tabs-bottom");
37910     }
37911     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37912     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37913     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37914     if(Roo.isIE){
37915         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37916     }
37917     if(this.tabPosition != "bottom"){
37918         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37919          * @type Roo.Element
37920          */
37921         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37922         this.el.addClass("roo-tabs-top");
37923     }
37924     this.items = [];
37925
37926     this.bodyEl.setStyle("position", "relative");
37927
37928     this.active = null;
37929     this.activateDelegate = this.activate.createDelegate(this);
37930
37931     this.addEvents({
37932         /**
37933          * @event tabchange
37934          * Fires when the active tab changes
37935          * @param {Roo.TabPanel} this
37936          * @param {Roo.TabPanelItem} activePanel The new active tab
37937          */
37938         "tabchange": true,
37939         /**
37940          * @event beforetabchange
37941          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37942          * @param {Roo.TabPanel} this
37943          * @param {Object} e Set cancel to true on this object to cancel the tab change
37944          * @param {Roo.TabPanelItem} tab The tab being changed to
37945          */
37946         "beforetabchange" : true
37947     });
37948
37949     Roo.EventManager.onWindowResize(this.onResize, this);
37950     this.cpad = this.el.getPadding("lr");
37951     this.hiddenCount = 0;
37952
37953
37954     // toolbar on the tabbar support...
37955     if (this.toolbar) {
37956         alert("no toolbar support yet");
37957         this.toolbar  = false;
37958         /*
37959         var tcfg = this.toolbar;
37960         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37961         this.toolbar = new Roo.Toolbar(tcfg);
37962         if (Roo.isSafari) {
37963             var tbl = tcfg.container.child('table', true);
37964             tbl.setAttribute('width', '100%');
37965         }
37966         */
37967         
37968     }
37969    
37970
37971
37972     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37973 };
37974
37975 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37976     /*
37977      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37978      */
37979     tabPosition : "top",
37980     /*
37981      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37982      */
37983     currentTabWidth : 0,
37984     /*
37985      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37986      */
37987     minTabWidth : 40,
37988     /*
37989      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37990      */
37991     maxTabWidth : 250,
37992     /*
37993      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37994      */
37995     preferredTabWidth : 175,
37996     /*
37997      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37998      */
37999     resizeTabs : false,
38000     /*
38001      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38002      */
38003     monitorResize : true,
38004     /*
38005      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38006      */
38007     toolbar : false,
38008
38009     /**
38010      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38011      * @param {String} id The id of the div to use <b>or create</b>
38012      * @param {String} text The text for the tab
38013      * @param {String} content (optional) Content to put in the TabPanelItem body
38014      * @param {Boolean} closable (optional) True to create a close icon on the tab
38015      * @return {Roo.TabPanelItem} The created TabPanelItem
38016      */
38017     addTab : function(id, text, content, closable, tpl)
38018     {
38019         var item = new Roo.bootstrap.panel.TabItem({
38020             panel: this,
38021             id : id,
38022             text : text,
38023             closable : closable,
38024             tpl : tpl
38025         });
38026         this.addTabItem(item);
38027         if(content){
38028             item.setContent(content);
38029         }
38030         return item;
38031     },
38032
38033     /**
38034      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38035      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38036      * @return {Roo.TabPanelItem}
38037      */
38038     getTab : function(id){
38039         return this.items[id];
38040     },
38041
38042     /**
38043      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38044      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38045      */
38046     hideTab : function(id){
38047         var t = this.items[id];
38048         if(!t.isHidden()){
38049            t.setHidden(true);
38050            this.hiddenCount++;
38051            this.autoSizeTabs();
38052         }
38053     },
38054
38055     /**
38056      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38057      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38058      */
38059     unhideTab : function(id){
38060         var t = this.items[id];
38061         if(t.isHidden()){
38062            t.setHidden(false);
38063            this.hiddenCount--;
38064            this.autoSizeTabs();
38065         }
38066     },
38067
38068     /**
38069      * Adds an existing {@link Roo.TabPanelItem}.
38070      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38071      */
38072     addTabItem : function(item){
38073         this.items[item.id] = item;
38074         this.items.push(item);
38075       //  if(this.resizeTabs){
38076     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38077   //         this.autoSizeTabs();
38078 //        }else{
38079 //            item.autoSize();
38080        // }
38081     },
38082
38083     /**
38084      * Removes a {@link Roo.TabPanelItem}.
38085      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38086      */
38087     removeTab : function(id){
38088         var items = this.items;
38089         var tab = items[id];
38090         if(!tab) { return; }
38091         var index = items.indexOf(tab);
38092         if(this.active == tab && items.length > 1){
38093             var newTab = this.getNextAvailable(index);
38094             if(newTab) {
38095                 newTab.activate();
38096             }
38097         }
38098         this.stripEl.dom.removeChild(tab.pnode.dom);
38099         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38100             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38101         }
38102         items.splice(index, 1);
38103         delete this.items[tab.id];
38104         tab.fireEvent("close", tab);
38105         tab.purgeListeners();
38106         this.autoSizeTabs();
38107     },
38108
38109     getNextAvailable : function(start){
38110         var items = this.items;
38111         var index = start;
38112         // look for a next tab that will slide over to
38113         // replace the one being removed
38114         while(index < items.length){
38115             var item = items[++index];
38116             if(item && !item.isHidden()){
38117                 return item;
38118             }
38119         }
38120         // if one isn't found select the previous tab (on the left)
38121         index = start;
38122         while(index >= 0){
38123             var item = items[--index];
38124             if(item && !item.isHidden()){
38125                 return item;
38126             }
38127         }
38128         return null;
38129     },
38130
38131     /**
38132      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38133      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38134      */
38135     disableTab : function(id){
38136         var tab = this.items[id];
38137         if(tab && this.active != tab){
38138             tab.disable();
38139         }
38140     },
38141
38142     /**
38143      * Enables a {@link Roo.TabPanelItem} that is disabled.
38144      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38145      */
38146     enableTab : function(id){
38147         var tab = this.items[id];
38148         tab.enable();
38149     },
38150
38151     /**
38152      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38153      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38154      * @return {Roo.TabPanelItem} The TabPanelItem.
38155      */
38156     activate : function(id){
38157         var tab = this.items[id];
38158         if(!tab){
38159             return null;
38160         }
38161         if(tab == this.active || tab.disabled){
38162             return tab;
38163         }
38164         var e = {};
38165         this.fireEvent("beforetabchange", this, e, tab);
38166         if(e.cancel !== true && !tab.disabled){
38167             if(this.active){
38168                 this.active.hide();
38169             }
38170             this.active = this.items[id];
38171             this.active.show();
38172             this.fireEvent("tabchange", this, this.active);
38173         }
38174         return tab;
38175     },
38176
38177     /**
38178      * Gets the active {@link Roo.TabPanelItem}.
38179      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38180      */
38181     getActiveTab : function(){
38182         return this.active;
38183     },
38184
38185     /**
38186      * Updates the tab body element to fit the height of the container element
38187      * for overflow scrolling
38188      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38189      */
38190     syncHeight : function(targetHeight){
38191         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38192         var bm = this.bodyEl.getMargins();
38193         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38194         this.bodyEl.setHeight(newHeight);
38195         return newHeight;
38196     },
38197
38198     onResize : function(){
38199         if(this.monitorResize){
38200             this.autoSizeTabs();
38201         }
38202     },
38203
38204     /**
38205      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38206      */
38207     beginUpdate : function(){
38208         this.updating = true;
38209     },
38210
38211     /**
38212      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38213      */
38214     endUpdate : function(){
38215         this.updating = false;
38216         this.autoSizeTabs();
38217     },
38218
38219     /**
38220      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38221      */
38222     autoSizeTabs : function(){
38223         var count = this.items.length;
38224         var vcount = count - this.hiddenCount;
38225         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38226             return;
38227         }
38228         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38229         var availWidth = Math.floor(w / vcount);
38230         var b = this.stripBody;
38231         if(b.getWidth() > w){
38232             var tabs = this.items;
38233             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38234             if(availWidth < this.minTabWidth){
38235                 /*if(!this.sleft){    // incomplete scrolling code
38236                     this.createScrollButtons();
38237                 }
38238                 this.showScroll();
38239                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38240             }
38241         }else{
38242             if(this.currentTabWidth < this.preferredTabWidth){
38243                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38244             }
38245         }
38246     },
38247
38248     /**
38249      * Returns the number of tabs in this TabPanel.
38250      * @return {Number}
38251      */
38252      getCount : function(){
38253          return this.items.length;
38254      },
38255
38256     /**
38257      * Resizes all the tabs to the passed width
38258      * @param {Number} The new width
38259      */
38260     setTabWidth : function(width){
38261         this.currentTabWidth = width;
38262         for(var i = 0, len = this.items.length; i < len; i++) {
38263                 if(!this.items[i].isHidden()) {
38264                 this.items[i].setWidth(width);
38265             }
38266         }
38267     },
38268
38269     /**
38270      * Destroys this TabPanel
38271      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38272      */
38273     destroy : function(removeEl){
38274         Roo.EventManager.removeResizeListener(this.onResize, this);
38275         for(var i = 0, len = this.items.length; i < len; i++){
38276             this.items[i].purgeListeners();
38277         }
38278         if(removeEl === true){
38279             this.el.update("");
38280             this.el.remove();
38281         }
38282     },
38283     
38284     createStrip : function(container)
38285     {
38286         var strip = document.createElement("nav");
38287         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38288         container.appendChild(strip);
38289         return strip;
38290     },
38291     
38292     createStripList : function(strip)
38293     {
38294         // div wrapper for retard IE
38295         // returns the "tr" element.
38296         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38297         //'<div class="x-tabs-strip-wrap">'+
38298           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38299           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38300         return strip.firstChild; //.firstChild.firstChild.firstChild;
38301     },
38302     createBody : function(container)
38303     {
38304         var body = document.createElement("div");
38305         Roo.id(body, "tab-body");
38306         //Roo.fly(body).addClass("x-tabs-body");
38307         Roo.fly(body).addClass("tab-content");
38308         container.appendChild(body);
38309         return body;
38310     },
38311     createItemBody :function(bodyEl, id){
38312         var body = Roo.getDom(id);
38313         if(!body){
38314             body = document.createElement("div");
38315             body.id = id;
38316         }
38317         //Roo.fly(body).addClass("x-tabs-item-body");
38318         Roo.fly(body).addClass("tab-pane");
38319          bodyEl.insertBefore(body, bodyEl.firstChild);
38320         return body;
38321     },
38322     /** @private */
38323     createStripElements :  function(stripEl, text, closable, tpl)
38324     {
38325         var td = document.createElement("li"); // was td..
38326         
38327         
38328         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38329         
38330         
38331         stripEl.appendChild(td);
38332         /*if(closable){
38333             td.className = "x-tabs-closable";
38334             if(!this.closeTpl){
38335                 this.closeTpl = new Roo.Template(
38336                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38337                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38338                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38339                 );
38340             }
38341             var el = this.closeTpl.overwrite(td, {"text": text});
38342             var close = el.getElementsByTagName("div")[0];
38343             var inner = el.getElementsByTagName("em")[0];
38344             return {"el": el, "close": close, "inner": inner};
38345         } else {
38346         */
38347         // not sure what this is..
38348 //            if(!this.tabTpl){
38349                 //this.tabTpl = new Roo.Template(
38350                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38351                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38352                 //);
38353 //                this.tabTpl = new Roo.Template(
38354 //                   '<a href="#">' +
38355 //                   '<span unselectable="on"' +
38356 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38357 //                            ' >{text}</span></a>'
38358 //                );
38359 //                
38360 //            }
38361
38362
38363             var template = tpl || this.tabTpl || false;
38364             
38365             if(!template){
38366                 
38367                 template = new Roo.Template(
38368                    '<a href="#">' +
38369                    '<span unselectable="on"' +
38370                             (this.disableTooltips ? '' : ' title="{text}"') +
38371                             ' >{text}</span></a>'
38372                 );
38373             }
38374             
38375             switch (typeof(template)) {
38376                 case 'object' :
38377                     break;
38378                 case 'string' :
38379                     template = new Roo.Template(template);
38380                     break;
38381                 default :
38382                     break;
38383             }
38384             
38385             var el = template.overwrite(td, {"text": text});
38386             
38387             var inner = el.getElementsByTagName("span")[0];
38388             
38389             return {"el": el, "inner": inner};
38390             
38391     }
38392         
38393     
38394 });
38395
38396 /**
38397  * @class Roo.TabPanelItem
38398  * @extends Roo.util.Observable
38399  * Represents an individual item (tab plus body) in a TabPanel.
38400  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38401  * @param {String} id The id of this TabPanelItem
38402  * @param {String} text The text for the tab of this TabPanelItem
38403  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38404  */
38405 Roo.bootstrap.panel.TabItem = function(config){
38406     /**
38407      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38408      * @type Roo.TabPanel
38409      */
38410     this.tabPanel = config.panel;
38411     /**
38412      * The id for this TabPanelItem
38413      * @type String
38414      */
38415     this.id = config.id;
38416     /** @private */
38417     this.disabled = false;
38418     /** @private */
38419     this.text = config.text;
38420     /** @private */
38421     this.loaded = false;
38422     this.closable = config.closable;
38423
38424     /**
38425      * The body element for this TabPanelItem.
38426      * @type Roo.Element
38427      */
38428     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38429     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38430     this.bodyEl.setStyle("display", "block");
38431     this.bodyEl.setStyle("zoom", "1");
38432     //this.hideAction();
38433
38434     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38435     /** @private */
38436     this.el = Roo.get(els.el);
38437     this.inner = Roo.get(els.inner, true);
38438     this.textEl = Roo.get(this.el.dom.firstChild, true);
38439     this.pnode = Roo.get(els.el.parentNode, true);
38440 //    this.el.on("mousedown", this.onTabMouseDown, this);
38441     this.el.on("click", this.onTabClick, this);
38442     /** @private */
38443     if(config.closable){
38444         var c = Roo.get(els.close, true);
38445         c.dom.title = this.closeText;
38446         c.addClassOnOver("close-over");
38447         c.on("click", this.closeClick, this);
38448      }
38449
38450     this.addEvents({
38451          /**
38452          * @event activate
38453          * Fires when this tab becomes the active tab.
38454          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38455          * @param {Roo.TabPanelItem} this
38456          */
38457         "activate": true,
38458         /**
38459          * @event beforeclose
38460          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38461          * @param {Roo.TabPanelItem} this
38462          * @param {Object} e Set cancel to true on this object to cancel the close.
38463          */
38464         "beforeclose": true,
38465         /**
38466          * @event close
38467          * Fires when this tab is closed.
38468          * @param {Roo.TabPanelItem} this
38469          */
38470          "close": true,
38471         /**
38472          * @event deactivate
38473          * Fires when this tab is no longer the active tab.
38474          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38475          * @param {Roo.TabPanelItem} this
38476          */
38477          "deactivate" : true
38478     });
38479     this.hidden = false;
38480
38481     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38482 };
38483
38484 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38485            {
38486     purgeListeners : function(){
38487        Roo.util.Observable.prototype.purgeListeners.call(this);
38488        this.el.removeAllListeners();
38489     },
38490     /**
38491      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38492      */
38493     show : function(){
38494         this.pnode.addClass("active");
38495         this.showAction();
38496         if(Roo.isOpera){
38497             this.tabPanel.stripWrap.repaint();
38498         }
38499         this.fireEvent("activate", this.tabPanel, this);
38500     },
38501
38502     /**
38503      * Returns true if this tab is the active tab.
38504      * @return {Boolean}
38505      */
38506     isActive : function(){
38507         return this.tabPanel.getActiveTab() == this;
38508     },
38509
38510     /**
38511      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38512      */
38513     hide : function(){
38514         this.pnode.removeClass("active");
38515         this.hideAction();
38516         this.fireEvent("deactivate", this.tabPanel, this);
38517     },
38518
38519     hideAction : function(){
38520         this.bodyEl.hide();
38521         this.bodyEl.setStyle("position", "absolute");
38522         this.bodyEl.setLeft("-20000px");
38523         this.bodyEl.setTop("-20000px");
38524     },
38525
38526     showAction : function(){
38527         this.bodyEl.setStyle("position", "relative");
38528         this.bodyEl.setTop("");
38529         this.bodyEl.setLeft("");
38530         this.bodyEl.show();
38531     },
38532
38533     /**
38534      * Set the tooltip for the tab.
38535      * @param {String} tooltip The tab's tooltip
38536      */
38537     setTooltip : function(text){
38538         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38539             this.textEl.dom.qtip = text;
38540             this.textEl.dom.removeAttribute('title');
38541         }else{
38542             this.textEl.dom.title = text;
38543         }
38544     },
38545
38546     onTabClick : function(e){
38547         e.preventDefault();
38548         this.tabPanel.activate(this.id);
38549     },
38550
38551     onTabMouseDown : function(e){
38552         e.preventDefault();
38553         this.tabPanel.activate(this.id);
38554     },
38555 /*
38556     getWidth : function(){
38557         return this.inner.getWidth();
38558     },
38559
38560     setWidth : function(width){
38561         var iwidth = width - this.pnode.getPadding("lr");
38562         this.inner.setWidth(iwidth);
38563         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38564         this.pnode.setWidth(width);
38565     },
38566 */
38567     /**
38568      * Show or hide the tab
38569      * @param {Boolean} hidden True to hide or false to show.
38570      */
38571     setHidden : function(hidden){
38572         this.hidden = hidden;
38573         this.pnode.setStyle("display", hidden ? "none" : "");
38574     },
38575
38576     /**
38577      * Returns true if this tab is "hidden"
38578      * @return {Boolean}
38579      */
38580     isHidden : function(){
38581         return this.hidden;
38582     },
38583
38584     /**
38585      * Returns the text for this tab
38586      * @return {String}
38587      */
38588     getText : function(){
38589         return this.text;
38590     },
38591     /*
38592     autoSize : function(){
38593         //this.el.beginMeasure();
38594         this.textEl.setWidth(1);
38595         /*
38596          *  #2804 [new] Tabs in Roojs
38597          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38598          */
38599         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38600         //this.el.endMeasure();
38601     //},
38602
38603     /**
38604      * Sets the text for the tab (Note: this also sets the tooltip text)
38605      * @param {String} text The tab's text and tooltip
38606      */
38607     setText : function(text){
38608         this.text = text;
38609         this.textEl.update(text);
38610         this.setTooltip(text);
38611         //if(!this.tabPanel.resizeTabs){
38612         //    this.autoSize();
38613         //}
38614     },
38615     /**
38616      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38617      */
38618     activate : function(){
38619         this.tabPanel.activate(this.id);
38620     },
38621
38622     /**
38623      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38624      */
38625     disable : function(){
38626         if(this.tabPanel.active != this){
38627             this.disabled = true;
38628             this.pnode.addClass("disabled");
38629         }
38630     },
38631
38632     /**
38633      * Enables this TabPanelItem if it was previously disabled.
38634      */
38635     enable : function(){
38636         this.disabled = false;
38637         this.pnode.removeClass("disabled");
38638     },
38639
38640     /**
38641      * Sets the content for this TabPanelItem.
38642      * @param {String} content The content
38643      * @param {Boolean} loadScripts true to look for and load scripts
38644      */
38645     setContent : function(content, loadScripts){
38646         this.bodyEl.update(content, loadScripts);
38647     },
38648
38649     /**
38650      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38651      * @return {Roo.UpdateManager} The UpdateManager
38652      */
38653     getUpdateManager : function(){
38654         return this.bodyEl.getUpdateManager();
38655     },
38656
38657     /**
38658      * Set a URL to be used to load the content for this TabPanelItem.
38659      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38660      * @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)
38661      * @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)
38662      * @return {Roo.UpdateManager} The UpdateManager
38663      */
38664     setUrl : function(url, params, loadOnce){
38665         if(this.refreshDelegate){
38666             this.un('activate', this.refreshDelegate);
38667         }
38668         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38669         this.on("activate", this.refreshDelegate);
38670         return this.bodyEl.getUpdateManager();
38671     },
38672
38673     /** @private */
38674     _handleRefresh : function(url, params, loadOnce){
38675         if(!loadOnce || !this.loaded){
38676             var updater = this.bodyEl.getUpdateManager();
38677             updater.update(url, params, this._setLoaded.createDelegate(this));
38678         }
38679     },
38680
38681     /**
38682      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38683      *   Will fail silently if the setUrl method has not been called.
38684      *   This does not activate the panel, just updates its content.
38685      */
38686     refresh : function(){
38687         if(this.refreshDelegate){
38688            this.loaded = false;
38689            this.refreshDelegate();
38690         }
38691     },
38692
38693     /** @private */
38694     _setLoaded : function(){
38695         this.loaded = true;
38696     },
38697
38698     /** @private */
38699     closeClick : function(e){
38700         var o = {};
38701         e.stopEvent();
38702         this.fireEvent("beforeclose", this, o);
38703         if(o.cancel !== true){
38704             this.tabPanel.removeTab(this.id);
38705         }
38706     },
38707     /**
38708      * The text displayed in the tooltip for the close icon.
38709      * @type String
38710      */
38711     closeText : "Close this tab"
38712 });
38713 /**
38714 *    This script refer to:
38715 *    Title: International Telephone Input
38716 *    Author: Jack O'Connor
38717 *    Code version:  v12.1.12
38718 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38719 **/
38720
38721 Roo.bootstrap.PhoneInputData = function() {
38722     var d = [
38723       [
38724         "Afghanistan (‫افغانستان‬‎)",
38725         "af",
38726         "93"
38727       ],
38728       [
38729         "Albania (Shqipëri)",
38730         "al",
38731         "355"
38732       ],
38733       [
38734         "Algeria (‫الجزائر‬‎)",
38735         "dz",
38736         "213"
38737       ],
38738       [
38739         "American Samoa",
38740         "as",
38741         "1684"
38742       ],
38743       [
38744         "Andorra",
38745         "ad",
38746         "376"
38747       ],
38748       [
38749         "Angola",
38750         "ao",
38751         "244"
38752       ],
38753       [
38754         "Anguilla",
38755         "ai",
38756         "1264"
38757       ],
38758       [
38759         "Antigua and Barbuda",
38760         "ag",
38761         "1268"
38762       ],
38763       [
38764         "Argentina",
38765         "ar",
38766         "54"
38767       ],
38768       [
38769         "Armenia (Հայաստան)",
38770         "am",
38771         "374"
38772       ],
38773       [
38774         "Aruba",
38775         "aw",
38776         "297"
38777       ],
38778       [
38779         "Australia",
38780         "au",
38781         "61",
38782         0
38783       ],
38784       [
38785         "Austria (Österreich)",
38786         "at",
38787         "43"
38788       ],
38789       [
38790         "Azerbaijan (Azərbaycan)",
38791         "az",
38792         "994"
38793       ],
38794       [
38795         "Bahamas",
38796         "bs",
38797         "1242"
38798       ],
38799       [
38800         "Bahrain (‫البحرين‬‎)",
38801         "bh",
38802         "973"
38803       ],
38804       [
38805         "Bangladesh (বাংলাদেশ)",
38806         "bd",
38807         "880"
38808       ],
38809       [
38810         "Barbados",
38811         "bb",
38812         "1246"
38813       ],
38814       [
38815         "Belarus (Беларусь)",
38816         "by",
38817         "375"
38818       ],
38819       [
38820         "Belgium (België)",
38821         "be",
38822         "32"
38823       ],
38824       [
38825         "Belize",
38826         "bz",
38827         "501"
38828       ],
38829       [
38830         "Benin (Bénin)",
38831         "bj",
38832         "229"
38833       ],
38834       [
38835         "Bermuda",
38836         "bm",
38837         "1441"
38838       ],
38839       [
38840         "Bhutan (འབྲུག)",
38841         "bt",
38842         "975"
38843       ],
38844       [
38845         "Bolivia",
38846         "bo",
38847         "591"
38848       ],
38849       [
38850         "Bosnia and Herzegovina (Босна и Херцеговина)",
38851         "ba",
38852         "387"
38853       ],
38854       [
38855         "Botswana",
38856         "bw",
38857         "267"
38858       ],
38859       [
38860         "Brazil (Brasil)",
38861         "br",
38862         "55"
38863       ],
38864       [
38865         "British Indian Ocean Territory",
38866         "io",
38867         "246"
38868       ],
38869       [
38870         "British Virgin Islands",
38871         "vg",
38872         "1284"
38873       ],
38874       [
38875         "Brunei",
38876         "bn",
38877         "673"
38878       ],
38879       [
38880         "Bulgaria (България)",
38881         "bg",
38882         "359"
38883       ],
38884       [
38885         "Burkina Faso",
38886         "bf",
38887         "226"
38888       ],
38889       [
38890         "Burundi (Uburundi)",
38891         "bi",
38892         "257"
38893       ],
38894       [
38895         "Cambodia (កម្ពុជា)",
38896         "kh",
38897         "855"
38898       ],
38899       [
38900         "Cameroon (Cameroun)",
38901         "cm",
38902         "237"
38903       ],
38904       [
38905         "Canada",
38906         "ca",
38907         "1",
38908         1,
38909         ["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"]
38910       ],
38911       [
38912         "Cape Verde (Kabu Verdi)",
38913         "cv",
38914         "238"
38915       ],
38916       [
38917         "Caribbean Netherlands",
38918         "bq",
38919         "599",
38920         1
38921       ],
38922       [
38923         "Cayman Islands",
38924         "ky",
38925         "1345"
38926       ],
38927       [
38928         "Central African Republic (République centrafricaine)",
38929         "cf",
38930         "236"
38931       ],
38932       [
38933         "Chad (Tchad)",
38934         "td",
38935         "235"
38936       ],
38937       [
38938         "Chile",
38939         "cl",
38940         "56"
38941       ],
38942       [
38943         "China (中国)",
38944         "cn",
38945         "86"
38946       ],
38947       [
38948         "Christmas Island",
38949         "cx",
38950         "61",
38951         2
38952       ],
38953       [
38954         "Cocos (Keeling) Islands",
38955         "cc",
38956         "61",
38957         1
38958       ],
38959       [
38960         "Colombia",
38961         "co",
38962         "57"
38963       ],
38964       [
38965         "Comoros (‫جزر القمر‬‎)",
38966         "km",
38967         "269"
38968       ],
38969       [
38970         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38971         "cd",
38972         "243"
38973       ],
38974       [
38975         "Congo (Republic) (Congo-Brazzaville)",
38976         "cg",
38977         "242"
38978       ],
38979       [
38980         "Cook Islands",
38981         "ck",
38982         "682"
38983       ],
38984       [
38985         "Costa Rica",
38986         "cr",
38987         "506"
38988       ],
38989       [
38990         "Côte d’Ivoire",
38991         "ci",
38992         "225"
38993       ],
38994       [
38995         "Croatia (Hrvatska)",
38996         "hr",
38997         "385"
38998       ],
38999       [
39000         "Cuba",
39001         "cu",
39002         "53"
39003       ],
39004       [
39005         "Curaçao",
39006         "cw",
39007         "599",
39008         0
39009       ],
39010       [
39011         "Cyprus (Κύπρος)",
39012         "cy",
39013         "357"
39014       ],
39015       [
39016         "Czech Republic (Česká republika)",
39017         "cz",
39018         "420"
39019       ],
39020       [
39021         "Denmark (Danmark)",
39022         "dk",
39023         "45"
39024       ],
39025       [
39026         "Djibouti",
39027         "dj",
39028         "253"
39029       ],
39030       [
39031         "Dominica",
39032         "dm",
39033         "1767"
39034       ],
39035       [
39036         "Dominican Republic (República Dominicana)",
39037         "do",
39038         "1",
39039         2,
39040         ["809", "829", "849"]
39041       ],
39042       [
39043         "Ecuador",
39044         "ec",
39045         "593"
39046       ],
39047       [
39048         "Egypt (‫مصر‬‎)",
39049         "eg",
39050         "20"
39051       ],
39052       [
39053         "El Salvador",
39054         "sv",
39055         "503"
39056       ],
39057       [
39058         "Equatorial Guinea (Guinea Ecuatorial)",
39059         "gq",
39060         "240"
39061       ],
39062       [
39063         "Eritrea",
39064         "er",
39065         "291"
39066       ],
39067       [
39068         "Estonia (Eesti)",
39069         "ee",
39070         "372"
39071       ],
39072       [
39073         "Ethiopia",
39074         "et",
39075         "251"
39076       ],
39077       [
39078         "Falkland Islands (Islas Malvinas)",
39079         "fk",
39080         "500"
39081       ],
39082       [
39083         "Faroe Islands (Føroyar)",
39084         "fo",
39085         "298"
39086       ],
39087       [
39088         "Fiji",
39089         "fj",
39090         "679"
39091       ],
39092       [
39093         "Finland (Suomi)",
39094         "fi",
39095         "358",
39096         0
39097       ],
39098       [
39099         "France",
39100         "fr",
39101         "33"
39102       ],
39103       [
39104         "French Guiana (Guyane française)",
39105         "gf",
39106         "594"
39107       ],
39108       [
39109         "French Polynesia (Polynésie française)",
39110         "pf",
39111         "689"
39112       ],
39113       [
39114         "Gabon",
39115         "ga",
39116         "241"
39117       ],
39118       [
39119         "Gambia",
39120         "gm",
39121         "220"
39122       ],
39123       [
39124         "Georgia (საქართველო)",
39125         "ge",
39126         "995"
39127       ],
39128       [
39129         "Germany (Deutschland)",
39130         "de",
39131         "49"
39132       ],
39133       [
39134         "Ghana (Gaana)",
39135         "gh",
39136         "233"
39137       ],
39138       [
39139         "Gibraltar",
39140         "gi",
39141         "350"
39142       ],
39143       [
39144         "Greece (Ελλάδα)",
39145         "gr",
39146         "30"
39147       ],
39148       [
39149         "Greenland (Kalaallit Nunaat)",
39150         "gl",
39151         "299"
39152       ],
39153       [
39154         "Grenada",
39155         "gd",
39156         "1473"
39157       ],
39158       [
39159         "Guadeloupe",
39160         "gp",
39161         "590",
39162         0
39163       ],
39164       [
39165         "Guam",
39166         "gu",
39167         "1671"
39168       ],
39169       [
39170         "Guatemala",
39171         "gt",
39172         "502"
39173       ],
39174       [
39175         "Guernsey",
39176         "gg",
39177         "44",
39178         1
39179       ],
39180       [
39181         "Guinea (Guinée)",
39182         "gn",
39183         "224"
39184       ],
39185       [
39186         "Guinea-Bissau (Guiné Bissau)",
39187         "gw",
39188         "245"
39189       ],
39190       [
39191         "Guyana",
39192         "gy",
39193         "592"
39194       ],
39195       [
39196         "Haiti",
39197         "ht",
39198         "509"
39199       ],
39200       [
39201         "Honduras",
39202         "hn",
39203         "504"
39204       ],
39205       [
39206         "Hong Kong (香港)",
39207         "hk",
39208         "852"
39209       ],
39210       [
39211         "Hungary (Magyarország)",
39212         "hu",
39213         "36"
39214       ],
39215       [
39216         "Iceland (Ísland)",
39217         "is",
39218         "354"
39219       ],
39220       [
39221         "India (भारत)",
39222         "in",
39223         "91"
39224       ],
39225       [
39226         "Indonesia",
39227         "id",
39228         "62"
39229       ],
39230       [
39231         "Iran (‫ایران‬‎)",
39232         "ir",
39233         "98"
39234       ],
39235       [
39236         "Iraq (‫العراق‬‎)",
39237         "iq",
39238         "964"
39239       ],
39240       [
39241         "Ireland",
39242         "ie",
39243         "353"
39244       ],
39245       [
39246         "Isle of Man",
39247         "im",
39248         "44",
39249         2
39250       ],
39251       [
39252         "Israel (‫ישראל‬‎)",
39253         "il",
39254         "972"
39255       ],
39256       [
39257         "Italy (Italia)",
39258         "it",
39259         "39",
39260         0
39261       ],
39262       [
39263         "Jamaica",
39264         "jm",
39265         "1876"
39266       ],
39267       [
39268         "Japan (日本)",
39269         "jp",
39270         "81"
39271       ],
39272       [
39273         "Jersey",
39274         "je",
39275         "44",
39276         3
39277       ],
39278       [
39279         "Jordan (‫الأردن‬‎)",
39280         "jo",
39281         "962"
39282       ],
39283       [
39284         "Kazakhstan (Казахстан)",
39285         "kz",
39286         "7",
39287         1
39288       ],
39289       [
39290         "Kenya",
39291         "ke",
39292         "254"
39293       ],
39294       [
39295         "Kiribati",
39296         "ki",
39297         "686"
39298       ],
39299       [
39300         "Kosovo",
39301         "xk",
39302         "383"
39303       ],
39304       [
39305         "Kuwait (‫الكويت‬‎)",
39306         "kw",
39307         "965"
39308       ],
39309       [
39310         "Kyrgyzstan (Кыргызстан)",
39311         "kg",
39312         "996"
39313       ],
39314       [
39315         "Laos (ລາວ)",
39316         "la",
39317         "856"
39318       ],
39319       [
39320         "Latvia (Latvija)",
39321         "lv",
39322         "371"
39323       ],
39324       [
39325         "Lebanon (‫لبنان‬‎)",
39326         "lb",
39327         "961"
39328       ],
39329       [
39330         "Lesotho",
39331         "ls",
39332         "266"
39333       ],
39334       [
39335         "Liberia",
39336         "lr",
39337         "231"
39338       ],
39339       [
39340         "Libya (‫ليبيا‬‎)",
39341         "ly",
39342         "218"
39343       ],
39344       [
39345         "Liechtenstein",
39346         "li",
39347         "423"
39348       ],
39349       [
39350         "Lithuania (Lietuva)",
39351         "lt",
39352         "370"
39353       ],
39354       [
39355         "Luxembourg",
39356         "lu",
39357         "352"
39358       ],
39359       [
39360         "Macau (澳門)",
39361         "mo",
39362         "853"
39363       ],
39364       [
39365         "Macedonia (FYROM) (Македонија)",
39366         "mk",
39367         "389"
39368       ],
39369       [
39370         "Madagascar (Madagasikara)",
39371         "mg",
39372         "261"
39373       ],
39374       [
39375         "Malawi",
39376         "mw",
39377         "265"
39378       ],
39379       [
39380         "Malaysia",
39381         "my",
39382         "60"
39383       ],
39384       [
39385         "Maldives",
39386         "mv",
39387         "960"
39388       ],
39389       [
39390         "Mali",
39391         "ml",
39392         "223"
39393       ],
39394       [
39395         "Malta",
39396         "mt",
39397         "356"
39398       ],
39399       [
39400         "Marshall Islands",
39401         "mh",
39402         "692"
39403       ],
39404       [
39405         "Martinique",
39406         "mq",
39407         "596"
39408       ],
39409       [
39410         "Mauritania (‫موريتانيا‬‎)",
39411         "mr",
39412         "222"
39413       ],
39414       [
39415         "Mauritius (Moris)",
39416         "mu",
39417         "230"
39418       ],
39419       [
39420         "Mayotte",
39421         "yt",
39422         "262",
39423         1
39424       ],
39425       [
39426         "Mexico (México)",
39427         "mx",
39428         "52"
39429       ],
39430       [
39431         "Micronesia",
39432         "fm",
39433         "691"
39434       ],
39435       [
39436         "Moldova (Republica Moldova)",
39437         "md",
39438         "373"
39439       ],
39440       [
39441         "Monaco",
39442         "mc",
39443         "377"
39444       ],
39445       [
39446         "Mongolia (Монгол)",
39447         "mn",
39448         "976"
39449       ],
39450       [
39451         "Montenegro (Crna Gora)",
39452         "me",
39453         "382"
39454       ],
39455       [
39456         "Montserrat",
39457         "ms",
39458         "1664"
39459       ],
39460       [
39461         "Morocco (‫المغرب‬‎)",
39462         "ma",
39463         "212",
39464         0
39465       ],
39466       [
39467         "Mozambique (Moçambique)",
39468         "mz",
39469         "258"
39470       ],
39471       [
39472         "Myanmar (Burma) (မြန်မာ)",
39473         "mm",
39474         "95"
39475       ],
39476       [
39477         "Namibia (Namibië)",
39478         "na",
39479         "264"
39480       ],
39481       [
39482         "Nauru",
39483         "nr",
39484         "674"
39485       ],
39486       [
39487         "Nepal (नेपाल)",
39488         "np",
39489         "977"
39490       ],
39491       [
39492         "Netherlands (Nederland)",
39493         "nl",
39494         "31"
39495       ],
39496       [
39497         "New Caledonia (Nouvelle-Calédonie)",
39498         "nc",
39499         "687"
39500       ],
39501       [
39502         "New Zealand",
39503         "nz",
39504         "64"
39505       ],
39506       [
39507         "Nicaragua",
39508         "ni",
39509         "505"
39510       ],
39511       [
39512         "Niger (Nijar)",
39513         "ne",
39514         "227"
39515       ],
39516       [
39517         "Nigeria",
39518         "ng",
39519         "234"
39520       ],
39521       [
39522         "Niue",
39523         "nu",
39524         "683"
39525       ],
39526       [
39527         "Norfolk Island",
39528         "nf",
39529         "672"
39530       ],
39531       [
39532         "North Korea (조선 민주주의 인민 공화국)",
39533         "kp",
39534         "850"
39535       ],
39536       [
39537         "Northern Mariana Islands",
39538         "mp",
39539         "1670"
39540       ],
39541       [
39542         "Norway (Norge)",
39543         "no",
39544         "47",
39545         0
39546       ],
39547       [
39548         "Oman (‫عُمان‬‎)",
39549         "om",
39550         "968"
39551       ],
39552       [
39553         "Pakistan (‫پاکستان‬‎)",
39554         "pk",
39555         "92"
39556       ],
39557       [
39558         "Palau",
39559         "pw",
39560         "680"
39561       ],
39562       [
39563         "Palestine (‫فلسطين‬‎)",
39564         "ps",
39565         "970"
39566       ],
39567       [
39568         "Panama (Panamá)",
39569         "pa",
39570         "507"
39571       ],
39572       [
39573         "Papua New Guinea",
39574         "pg",
39575         "675"
39576       ],
39577       [
39578         "Paraguay",
39579         "py",
39580         "595"
39581       ],
39582       [
39583         "Peru (Perú)",
39584         "pe",
39585         "51"
39586       ],
39587       [
39588         "Philippines",
39589         "ph",
39590         "63"
39591       ],
39592       [
39593         "Poland (Polska)",
39594         "pl",
39595         "48"
39596       ],
39597       [
39598         "Portugal",
39599         "pt",
39600         "351"
39601       ],
39602       [
39603         "Puerto Rico",
39604         "pr",
39605         "1",
39606         3,
39607         ["787", "939"]
39608       ],
39609       [
39610         "Qatar (‫قطر‬‎)",
39611         "qa",
39612         "974"
39613       ],
39614       [
39615         "Réunion (La Réunion)",
39616         "re",
39617         "262",
39618         0
39619       ],
39620       [
39621         "Romania (România)",
39622         "ro",
39623         "40"
39624       ],
39625       [
39626         "Russia (Россия)",
39627         "ru",
39628         "7",
39629         0
39630       ],
39631       [
39632         "Rwanda",
39633         "rw",
39634         "250"
39635       ],
39636       [
39637         "Saint Barthélemy",
39638         "bl",
39639         "590",
39640         1
39641       ],
39642       [
39643         "Saint Helena",
39644         "sh",
39645         "290"
39646       ],
39647       [
39648         "Saint Kitts and Nevis",
39649         "kn",
39650         "1869"
39651       ],
39652       [
39653         "Saint Lucia",
39654         "lc",
39655         "1758"
39656       ],
39657       [
39658         "Saint Martin (Saint-Martin (partie française))",
39659         "mf",
39660         "590",
39661         2
39662       ],
39663       [
39664         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39665         "pm",
39666         "508"
39667       ],
39668       [
39669         "Saint Vincent and the Grenadines",
39670         "vc",
39671         "1784"
39672       ],
39673       [
39674         "Samoa",
39675         "ws",
39676         "685"
39677       ],
39678       [
39679         "San Marino",
39680         "sm",
39681         "378"
39682       ],
39683       [
39684         "São Tomé and Príncipe (São Tomé e Príncipe)",
39685         "st",
39686         "239"
39687       ],
39688       [
39689         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39690         "sa",
39691         "966"
39692       ],
39693       [
39694         "Senegal (Sénégal)",
39695         "sn",
39696         "221"
39697       ],
39698       [
39699         "Serbia (Србија)",
39700         "rs",
39701         "381"
39702       ],
39703       [
39704         "Seychelles",
39705         "sc",
39706         "248"
39707       ],
39708       [
39709         "Sierra Leone",
39710         "sl",
39711         "232"
39712       ],
39713       [
39714         "Singapore",
39715         "sg",
39716         "65"
39717       ],
39718       [
39719         "Sint Maarten",
39720         "sx",
39721         "1721"
39722       ],
39723       [
39724         "Slovakia (Slovensko)",
39725         "sk",
39726         "421"
39727       ],
39728       [
39729         "Slovenia (Slovenija)",
39730         "si",
39731         "386"
39732       ],
39733       [
39734         "Solomon Islands",
39735         "sb",
39736         "677"
39737       ],
39738       [
39739         "Somalia (Soomaaliya)",
39740         "so",
39741         "252"
39742       ],
39743       [
39744         "South Africa",
39745         "za",
39746         "27"
39747       ],
39748       [
39749         "South Korea (대한민국)",
39750         "kr",
39751         "82"
39752       ],
39753       [
39754         "South Sudan (‫جنوب السودان‬‎)",
39755         "ss",
39756         "211"
39757       ],
39758       [
39759         "Spain (España)",
39760         "es",
39761         "34"
39762       ],
39763       [
39764         "Sri Lanka (ශ්‍රී ලංකාව)",
39765         "lk",
39766         "94"
39767       ],
39768       [
39769         "Sudan (‫السودان‬‎)",
39770         "sd",
39771         "249"
39772       ],
39773       [
39774         "Suriname",
39775         "sr",
39776         "597"
39777       ],
39778       [
39779         "Svalbard and Jan Mayen",
39780         "sj",
39781         "47",
39782         1
39783       ],
39784       [
39785         "Swaziland",
39786         "sz",
39787         "268"
39788       ],
39789       [
39790         "Sweden (Sverige)",
39791         "se",
39792         "46"
39793       ],
39794       [
39795         "Switzerland (Schweiz)",
39796         "ch",
39797         "41"
39798       ],
39799       [
39800         "Syria (‫سوريا‬‎)",
39801         "sy",
39802         "963"
39803       ],
39804       [
39805         "Taiwan (台灣)",
39806         "tw",
39807         "886"
39808       ],
39809       [
39810         "Tajikistan",
39811         "tj",
39812         "992"
39813       ],
39814       [
39815         "Tanzania",
39816         "tz",
39817         "255"
39818       ],
39819       [
39820         "Thailand (ไทย)",
39821         "th",
39822         "66"
39823       ],
39824       [
39825         "Timor-Leste",
39826         "tl",
39827         "670"
39828       ],
39829       [
39830         "Togo",
39831         "tg",
39832         "228"
39833       ],
39834       [
39835         "Tokelau",
39836         "tk",
39837         "690"
39838       ],
39839       [
39840         "Tonga",
39841         "to",
39842         "676"
39843       ],
39844       [
39845         "Trinidad and Tobago",
39846         "tt",
39847         "1868"
39848       ],
39849       [
39850         "Tunisia (‫تونس‬‎)",
39851         "tn",
39852         "216"
39853       ],
39854       [
39855         "Turkey (Türkiye)",
39856         "tr",
39857         "90"
39858       ],
39859       [
39860         "Turkmenistan",
39861         "tm",
39862         "993"
39863       ],
39864       [
39865         "Turks and Caicos Islands",
39866         "tc",
39867         "1649"
39868       ],
39869       [
39870         "Tuvalu",
39871         "tv",
39872         "688"
39873       ],
39874       [
39875         "U.S. Virgin Islands",
39876         "vi",
39877         "1340"
39878       ],
39879       [
39880         "Uganda",
39881         "ug",
39882         "256"
39883       ],
39884       [
39885         "Ukraine (Україна)",
39886         "ua",
39887         "380"
39888       ],
39889       [
39890         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39891         "ae",
39892         "971"
39893       ],
39894       [
39895         "United Kingdom",
39896         "gb",
39897         "44",
39898         0
39899       ],
39900       [
39901         "United States",
39902         "us",
39903         "1",
39904         0
39905       ],
39906       [
39907         "Uruguay",
39908         "uy",
39909         "598"
39910       ],
39911       [
39912         "Uzbekistan (Oʻzbekiston)",
39913         "uz",
39914         "998"
39915       ],
39916       [
39917         "Vanuatu",
39918         "vu",
39919         "678"
39920       ],
39921       [
39922         "Vatican City (Città del Vaticano)",
39923         "va",
39924         "39",
39925         1
39926       ],
39927       [
39928         "Venezuela",
39929         "ve",
39930         "58"
39931       ],
39932       [
39933         "Vietnam (Việt Nam)",
39934         "vn",
39935         "84"
39936       ],
39937       [
39938         "Wallis and Futuna (Wallis-et-Futuna)",
39939         "wf",
39940         "681"
39941       ],
39942       [
39943         "Western Sahara (‫الصحراء الغربية‬‎)",
39944         "eh",
39945         "212",
39946         1
39947       ],
39948       [
39949         "Yemen (‫اليمن‬‎)",
39950         "ye",
39951         "967"
39952       ],
39953       [
39954         "Zambia",
39955         "zm",
39956         "260"
39957       ],
39958       [
39959         "Zimbabwe",
39960         "zw",
39961         "263"
39962       ],
39963       [
39964         "Åland Islands",
39965         "ax",
39966         "358",
39967         1
39968       ]
39969   ];
39970   
39971   return d;
39972 }/**
39973 *    This script refer to:
39974 *    Title: International Telephone Input
39975 *    Author: Jack O'Connor
39976 *    Code version:  v12.1.12
39977 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39978 **/
39979
39980 /**
39981  * @class Roo.bootstrap.PhoneInput
39982  * @extends Roo.bootstrap.TriggerField
39983  * An input with International dial-code selection
39984  
39985  * @cfg {String} defaultDialCode default '+852'
39986  * @cfg {Array} preferedCountries default []
39987   
39988  * @constructor
39989  * Create a new PhoneInput.
39990  * @param {Object} config Configuration options
39991  */
39992
39993 Roo.bootstrap.PhoneInput = function(config) {
39994     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39995 };
39996
39997 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39998         
39999         listWidth: undefined,
40000         
40001         selectedClass: 'active',
40002         
40003         invalidClass : "has-warning",
40004         
40005         validClass: 'has-success',
40006         
40007         allowed: '0123456789',
40008         
40009         max_length: 15,
40010         
40011         /**
40012          * @cfg {String} defaultDialCode The default dial code when initializing the input
40013          */
40014         defaultDialCode: '+852',
40015         
40016         /**
40017          * @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
40018          */
40019         preferedCountries: false,
40020         
40021         getAutoCreate : function()
40022         {
40023             var data = Roo.bootstrap.PhoneInputData();
40024             var align = this.labelAlign || this.parentLabelAlign();
40025             var id = Roo.id();
40026             
40027             this.allCountries = [];
40028             this.dialCodeMapping = [];
40029             
40030             for (var i = 0; i < data.length; i++) {
40031               var c = data[i];
40032               this.allCountries[i] = {
40033                 name: c[0],
40034                 iso2: c[1],
40035                 dialCode: c[2],
40036                 priority: c[3] || 0,
40037                 areaCodes: c[4] || null
40038               };
40039               this.dialCodeMapping[c[2]] = {
40040                   name: c[0],
40041                   iso2: c[1],
40042                   priority: c[3] || 0,
40043                   areaCodes: c[4] || null
40044               };
40045             }
40046             
40047             var cfg = {
40048                 cls: 'form-group',
40049                 cn: []
40050             };
40051             
40052             var input =  {
40053                 tag: 'input',
40054                 id : id,
40055                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40056                 maxlength: this.max_length,
40057                 cls : 'form-control tel-input',
40058                 autocomplete: 'new-password'
40059             };
40060             
40061             var hiddenInput = {
40062                 tag: 'input',
40063                 type: 'hidden',
40064                 cls: 'hidden-tel-input'
40065             };
40066             
40067             if (this.name) {
40068                 hiddenInput.name = this.name;
40069             }
40070             
40071             if (this.disabled) {
40072                 input.disabled = true;
40073             }
40074             
40075             var flag_container = {
40076                 tag: 'div',
40077                 cls: 'flag-box',
40078                 cn: [
40079                     {
40080                         tag: 'div',
40081                         cls: 'flag'
40082                     },
40083                     {
40084                         tag: 'div',
40085                         cls: 'caret'
40086                     }
40087                 ]
40088             };
40089             
40090             var box = {
40091                 tag: 'div',
40092                 cls: this.hasFeedback ? 'has-feedback' : '',
40093                 cn: [
40094                     hiddenInput,
40095                     input,
40096                     {
40097                         tag: 'input',
40098                         cls: 'dial-code-holder',
40099                         disabled: true
40100                     }
40101                 ]
40102             };
40103             
40104             var container = {
40105                 cls: 'roo-select2-container input-group',
40106                 cn: [
40107                     flag_container,
40108                     box
40109                 ]
40110             };
40111             
40112             if (this.fieldLabel.length) {
40113                 var indicator = {
40114                     tag: 'i',
40115                     tooltip: 'This field is required'
40116                 };
40117                 
40118                 var label = {
40119                     tag: 'label',
40120                     'for':  id,
40121                     cls: 'control-label',
40122                     cn: []
40123                 };
40124                 
40125                 var label_text = {
40126                     tag: 'span',
40127                     html: this.fieldLabel
40128                 };
40129                 
40130                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40131                 label.cn = [
40132                     indicator,
40133                     label_text
40134                 ];
40135                 
40136                 if(this.indicatorpos == 'right') {
40137                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40138                     label.cn = [
40139                         label_text,
40140                         indicator
40141                     ];
40142                 }
40143                 
40144                 if(align == 'left') {
40145                     container = {
40146                         tag: 'div',
40147                         cn: [
40148                             container
40149                         ]
40150                     };
40151                     
40152                     if(this.labelWidth > 12){
40153                         label.style = "width: " + this.labelWidth + 'px';
40154                     }
40155                     if(this.labelWidth < 13 && this.labelmd == 0){
40156                         this.labelmd = this.labelWidth;
40157                     }
40158                     if(this.labellg > 0){
40159                         label.cls += ' col-lg-' + this.labellg;
40160                         input.cls += ' col-lg-' + (12 - this.labellg);
40161                     }
40162                     if(this.labelmd > 0){
40163                         label.cls += ' col-md-' + this.labelmd;
40164                         container.cls += ' col-md-' + (12 - this.labelmd);
40165                     }
40166                     if(this.labelsm > 0){
40167                         label.cls += ' col-sm-' + this.labelsm;
40168                         container.cls += ' col-sm-' + (12 - this.labelsm);
40169                     }
40170                     if(this.labelxs > 0){
40171                         label.cls += ' col-xs-' + this.labelxs;
40172                         container.cls += ' col-xs-' + (12 - this.labelxs);
40173                     }
40174                 }
40175             }
40176             
40177             cfg.cn = [
40178                 label,
40179                 container
40180             ];
40181             
40182             var settings = this;
40183             
40184             ['xs','sm','md','lg'].map(function(size){
40185                 if (settings[size]) {
40186                     cfg.cls += ' col-' + size + '-' + settings[size];
40187                 }
40188             });
40189             
40190             this.store = new Roo.data.Store({
40191                 proxy : new Roo.data.MemoryProxy({}),
40192                 reader : new Roo.data.JsonReader({
40193                     fields : [
40194                         {
40195                             'name' : 'name',
40196                             'type' : 'string'
40197                         },
40198                         {
40199                             'name' : 'iso2',
40200                             'type' : 'string'
40201                         },
40202                         {
40203                             'name' : 'dialCode',
40204                             'type' : 'string'
40205                         },
40206                         {
40207                             'name' : 'priority',
40208                             'type' : 'string'
40209                         },
40210                         {
40211                             'name' : 'areaCodes',
40212                             'type' : 'string'
40213                         }
40214                     ]
40215                 })
40216             });
40217             
40218             if(!this.preferedCountries) {
40219                 this.preferedCountries = [
40220                     'hk',
40221                     'gb',
40222                     'us'
40223                 ];
40224             }
40225             
40226             var p = this.preferedCountries.reverse();
40227             
40228             if(p) {
40229                 for (var i = 0; i < p.length; i++) {
40230                     for (var j = 0; j < this.allCountries.length; j++) {
40231                         if(this.allCountries[j].iso2 == p[i]) {
40232                             var t = this.allCountries[j];
40233                             this.allCountries.splice(j,1);
40234                             this.allCountries.unshift(t);
40235                         }
40236                     } 
40237                 }
40238             }
40239             
40240             this.store.proxy.data = {
40241                 success: true,
40242                 data: this.allCountries
40243             };
40244             
40245             return cfg;
40246         },
40247         
40248         initEvents : function()
40249         {
40250             this.createList();
40251             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40252             
40253             this.indicator = this.indicatorEl();
40254             this.flag = this.flagEl();
40255             this.dialCodeHolder = this.dialCodeHolderEl();
40256             
40257             this.trigger = this.el.select('div.flag-box',true).first();
40258             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40259             
40260             var _this = this;
40261             
40262             (function(){
40263                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40264                 _this.list.setWidth(lw);
40265             }).defer(100);
40266             
40267             this.list.on('mouseover', this.onViewOver, this);
40268             this.list.on('mousemove', this.onViewMove, this);
40269             this.inputEl().on("keyup", this.onKeyUp, this);
40270             this.inputEl().on("keypress", this.onKeyPress, this);
40271             
40272             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40273
40274             this.view = new Roo.View(this.list, this.tpl, {
40275                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40276             });
40277             
40278             this.view.on('click', this.onViewClick, this);
40279             this.setValue(this.defaultDialCode);
40280         },
40281         
40282         onTriggerClick : function(e)
40283         {
40284             Roo.log('trigger click');
40285             if(this.disabled){
40286                 return;
40287             }
40288             
40289             if(this.isExpanded()){
40290                 this.collapse();
40291                 this.hasFocus = false;
40292             }else {
40293                 this.store.load({});
40294                 this.hasFocus = true;
40295                 this.expand();
40296             }
40297         },
40298         
40299         isExpanded : function()
40300         {
40301             return this.list.isVisible();
40302         },
40303         
40304         collapse : function()
40305         {
40306             if(!this.isExpanded()){
40307                 return;
40308             }
40309             this.list.hide();
40310             Roo.get(document).un('mousedown', this.collapseIf, this);
40311             Roo.get(document).un('mousewheel', this.collapseIf, this);
40312             this.fireEvent('collapse', this);
40313             this.validate();
40314         },
40315         
40316         expand : function()
40317         {
40318             Roo.log('expand');
40319
40320             if(this.isExpanded() || !this.hasFocus){
40321                 return;
40322             }
40323             
40324             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40325             this.list.setWidth(lw);
40326             
40327             this.list.show();
40328             this.restrictHeight();
40329             
40330             Roo.get(document).on('mousedown', this.collapseIf, this);
40331             Roo.get(document).on('mousewheel', this.collapseIf, this);
40332             
40333             this.fireEvent('expand', this);
40334         },
40335         
40336         restrictHeight : function()
40337         {
40338             this.list.alignTo(this.inputEl(), this.listAlign);
40339             this.list.alignTo(this.inputEl(), this.listAlign);
40340         },
40341         
40342         onViewOver : function(e, t)
40343         {
40344             if(this.inKeyMode){
40345                 return;
40346             }
40347             var item = this.view.findItemFromChild(t);
40348             
40349             if(item){
40350                 var index = this.view.indexOf(item);
40351                 this.select(index, false);
40352             }
40353         },
40354
40355         // private
40356         onViewClick : function(view, doFocus, el, e)
40357         {
40358             var index = this.view.getSelectedIndexes()[0];
40359             
40360             var r = this.store.getAt(index);
40361             
40362             if(r){
40363                 this.onSelect(r, index);
40364             }
40365             if(doFocus !== false && !this.blockFocus){
40366                 this.inputEl().focus();
40367             }
40368         },
40369         
40370         onViewMove : function(e, t)
40371         {
40372             this.inKeyMode = false;
40373         },
40374         
40375         select : function(index, scrollIntoView)
40376         {
40377             this.selectedIndex = index;
40378             this.view.select(index);
40379             if(scrollIntoView !== false){
40380                 var el = this.view.getNode(index);
40381                 if(el){
40382                     this.list.scrollChildIntoView(el, false);
40383                 }
40384             }
40385         },
40386         
40387         createList : function()
40388         {
40389             this.list = Roo.get(document.body).createChild({
40390                 tag: 'ul',
40391                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40392                 style: 'display:none'
40393             });
40394             
40395             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40396         },
40397         
40398         collapseIf : function(e)
40399         {
40400             var in_combo  = e.within(this.el);
40401             var in_list =  e.within(this.list);
40402             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40403             
40404             if (in_combo || in_list || is_list) {
40405                 return;
40406             }
40407             this.collapse();
40408         },
40409         
40410         onSelect : function(record, index)
40411         {
40412             if(this.fireEvent('beforeselect', this, record, index) !== false){
40413                 
40414                 this.setFlagClass(record.data.iso2);
40415                 this.setDialCode(record.data.dialCode);
40416                 this.hasFocus = false;
40417                 this.collapse();
40418                 this.fireEvent('select', this, record, index);
40419             }
40420         },
40421         
40422         flagEl : function()
40423         {
40424             var flag = this.el.select('div.flag',true).first();
40425             if(!flag){
40426                 return false;
40427             }
40428             return flag;
40429         },
40430         
40431         dialCodeHolderEl : function()
40432         {
40433             var d = this.el.select('input.dial-code-holder',true).first();
40434             if(!d){
40435                 return false;
40436             }
40437             return d;
40438         },
40439         
40440         setDialCode : function(v)
40441         {
40442             this.dialCodeHolder.dom.value = '+'+v;
40443         },
40444         
40445         setFlagClass : function(n)
40446         {
40447             this.flag.dom.className = 'flag '+n;
40448         },
40449         
40450         getValue : function()
40451         {
40452             var v = this.inputEl().getValue();
40453             if(this.dialCodeHolder) {
40454                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40455             }
40456             return v;
40457         },
40458         
40459         setValue : function(v)
40460         {
40461             var d = this.getDialCode(v);
40462             
40463             //invalid dial code
40464             if(v.length == 0 || !d || d.length == 0) {
40465                 if(this.rendered){
40466                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40467                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40468                 }
40469                 return;
40470             }
40471             
40472             //valid dial code
40473             this.setFlagClass(this.dialCodeMapping[d].iso2);
40474             this.setDialCode(d);
40475             this.inputEl().dom.value = v.replace('+'+d,'');
40476             this.hiddenEl().dom.value = this.getValue();
40477             
40478             this.validate();
40479         },
40480         
40481         getDialCode : function(v)
40482         {
40483             v = v ||  '';
40484             
40485             if (v.length == 0) {
40486                 return this.dialCodeHolder.dom.value;
40487             }
40488             
40489             var dialCode = "";
40490             if (v.charAt(0) != "+") {
40491                 return false;
40492             }
40493             var numericChars = "";
40494             for (var i = 1; i < v.length; i++) {
40495               var c = v.charAt(i);
40496               if (!isNaN(c)) {
40497                 numericChars += c;
40498                 if (this.dialCodeMapping[numericChars]) {
40499                   dialCode = v.substr(1, i);
40500                 }
40501                 if (numericChars.length == 4) {
40502                   break;
40503                 }
40504               }
40505             }
40506             return dialCode;
40507         },
40508         
40509         reset : function()
40510         {
40511             this.setValue(this.defaultDialCode);
40512             this.validate();
40513         },
40514         
40515         hiddenEl : function()
40516         {
40517             return this.el.select('input.hidden-tel-input',true).first();
40518         },
40519         
40520         // after setting val
40521         onKeyUp : function(e){
40522             this.setValue(this.getValue());
40523         },
40524         
40525         onKeyPress : function(e){
40526             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40527                 e.stopEvent();
40528             }
40529         }
40530         
40531 });
40532 /**
40533  * @class Roo.bootstrap.MoneyField
40534  * @extends Roo.bootstrap.ComboBox
40535  * Bootstrap MoneyField class
40536  * 
40537  * @constructor
40538  * Create a new MoneyField.
40539  * @param {Object} config Configuration options
40540  */
40541
40542 Roo.bootstrap.MoneyField = function(config) {
40543     
40544     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40545     
40546 };
40547
40548 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40549     
40550     /**
40551      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40552      */
40553     allowDecimals : true,
40554     /**
40555      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40556      */
40557     decimalSeparator : ".",
40558     /**
40559      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40560      */
40561     decimalPrecision : 0,
40562     /**
40563      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40564      */
40565     allowNegative : true,
40566     /**
40567      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40568      */
40569     allowZero: true,
40570     /**
40571      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40572      */
40573     minValue : Number.NEGATIVE_INFINITY,
40574     /**
40575      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40576      */
40577     maxValue : Number.MAX_VALUE,
40578     /**
40579      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40580      */
40581     minText : "The minimum value for this field is {0}",
40582     /**
40583      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40584      */
40585     maxText : "The maximum value for this field is {0}",
40586     /**
40587      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40588      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40589      */
40590     nanText : "{0} is not a valid number",
40591     /**
40592      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40593      */
40594     castInt : true,
40595     /**
40596      * @cfg {String} defaults currency of the MoneyField
40597      * value should be in lkey
40598      */
40599     defaultCurrency : false,
40600     /**
40601      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40602      */
40603     thousandsDelimiter : false,
40604     /**
40605      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40606      */
40607     max_length: false,
40608     
40609     inputlg : 9,
40610     inputmd : 9,
40611     inputsm : 9,
40612     inputxs : 6,
40613     
40614     store : false,
40615     
40616     getAutoCreate : function()
40617     {
40618         var align = this.labelAlign || this.parentLabelAlign();
40619         
40620         var id = Roo.id();
40621
40622         var cfg = {
40623             cls: 'form-group',
40624             cn: []
40625         };
40626
40627         var input =  {
40628             tag: 'input',
40629             id : id,
40630             cls : 'form-control roo-money-amount-input',
40631             autocomplete: 'new-password'
40632         };
40633         
40634         var hiddenInput = {
40635             tag: 'input',
40636             type: 'hidden',
40637             id: Roo.id(),
40638             cls: 'hidden-number-input'
40639         };
40640         
40641         if(this.max_length) {
40642             input.maxlength = this.max_length; 
40643         }
40644         
40645         if (this.name) {
40646             hiddenInput.name = this.name;
40647         }
40648
40649         if (this.disabled) {
40650             input.disabled = true;
40651         }
40652
40653         var clg = 12 - this.inputlg;
40654         var cmd = 12 - this.inputmd;
40655         var csm = 12 - this.inputsm;
40656         var cxs = 12 - this.inputxs;
40657         
40658         var container = {
40659             tag : 'div',
40660             cls : 'row roo-money-field',
40661             cn : [
40662                 {
40663                     tag : 'div',
40664                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40665                     cn : [
40666                         {
40667                             tag : 'div',
40668                             cls: 'roo-select2-container input-group',
40669                             cn: [
40670                                 {
40671                                     tag : 'input',
40672                                     cls : 'form-control roo-money-currency-input',
40673                                     autocomplete: 'new-password',
40674                                     readOnly : 1,
40675                                     name : this.currencyName
40676                                 },
40677                                 {
40678                                     tag :'span',
40679                                     cls : 'input-group-addon',
40680                                     cn : [
40681                                         {
40682                                             tag: 'span',
40683                                             cls: 'caret'
40684                                         }
40685                                     ]
40686                                 }
40687                             ]
40688                         }
40689                     ]
40690                 },
40691                 {
40692                     tag : 'div',
40693                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40694                     cn : [
40695                         {
40696                             tag: 'div',
40697                             cls: this.hasFeedback ? 'has-feedback' : '',
40698                             cn: [
40699                                 input
40700                             ]
40701                         }
40702                     ]
40703                 }
40704             ]
40705             
40706         };
40707         
40708         if (this.fieldLabel.length) {
40709             var indicator = {
40710                 tag: 'i',
40711                 tooltip: 'This field is required'
40712             };
40713
40714             var label = {
40715                 tag: 'label',
40716                 'for':  id,
40717                 cls: 'control-label',
40718                 cn: []
40719             };
40720
40721             var label_text = {
40722                 tag: 'span',
40723                 html: this.fieldLabel
40724             };
40725
40726             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40727             label.cn = [
40728                 indicator,
40729                 label_text
40730             ];
40731
40732             if(this.indicatorpos == 'right') {
40733                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40734                 label.cn = [
40735                     label_text,
40736                     indicator
40737                 ];
40738             }
40739
40740             if(align == 'left') {
40741                 container = {
40742                     tag: 'div',
40743                     cn: [
40744                         container
40745                     ]
40746                 };
40747
40748                 if(this.labelWidth > 12){
40749                     label.style = "width: " + this.labelWidth + 'px';
40750                 }
40751                 if(this.labelWidth < 13 && this.labelmd == 0){
40752                     this.labelmd = this.labelWidth;
40753                 }
40754                 if(this.labellg > 0){
40755                     label.cls += ' col-lg-' + this.labellg;
40756                     input.cls += ' col-lg-' + (12 - this.labellg);
40757                 }
40758                 if(this.labelmd > 0){
40759                     label.cls += ' col-md-' + this.labelmd;
40760                     container.cls += ' col-md-' + (12 - this.labelmd);
40761                 }
40762                 if(this.labelsm > 0){
40763                     label.cls += ' col-sm-' + this.labelsm;
40764                     container.cls += ' col-sm-' + (12 - this.labelsm);
40765                 }
40766                 if(this.labelxs > 0){
40767                     label.cls += ' col-xs-' + this.labelxs;
40768                     container.cls += ' col-xs-' + (12 - this.labelxs);
40769                 }
40770             }
40771         }
40772
40773         cfg.cn = [
40774             label,
40775             container,
40776             hiddenInput
40777         ];
40778         
40779         var settings = this;
40780
40781         ['xs','sm','md','lg'].map(function(size){
40782             if (settings[size]) {
40783                 cfg.cls += ' col-' + size + '-' + settings[size];
40784             }
40785         });
40786         
40787         return cfg;
40788     },
40789     
40790     initEvents : function()
40791     {
40792         this.indicator = this.indicatorEl();
40793         
40794         this.initCurrencyEvent();
40795         
40796         this.initNumberEvent();
40797     },
40798     
40799     initCurrencyEvent : function()
40800     {
40801         if (!this.store) {
40802             throw "can not find store for combo";
40803         }
40804         
40805         this.store = Roo.factory(this.store, Roo.data);
40806         this.store.parent = this;
40807         
40808         this.createList();
40809         
40810         this.triggerEl = this.el.select('.input-group-addon', true).first();
40811         
40812         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40813         
40814         var _this = this;
40815         
40816         (function(){
40817             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40818             _this.list.setWidth(lw);
40819         }).defer(100);
40820         
40821         this.list.on('mouseover', this.onViewOver, this);
40822         this.list.on('mousemove', this.onViewMove, this);
40823         this.list.on('scroll', this.onViewScroll, this);
40824         
40825         if(!this.tpl){
40826             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40827         }
40828         
40829         this.view = new Roo.View(this.list, this.tpl, {
40830             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40831         });
40832         
40833         this.view.on('click', this.onViewClick, this);
40834         
40835         this.store.on('beforeload', this.onBeforeLoad, this);
40836         this.store.on('load', this.onLoad, this);
40837         this.store.on('loadexception', this.onLoadException, this);
40838         
40839         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40840             "up" : function(e){
40841                 this.inKeyMode = true;
40842                 this.selectPrev();
40843             },
40844
40845             "down" : function(e){
40846                 if(!this.isExpanded()){
40847                     this.onTriggerClick();
40848                 }else{
40849                     this.inKeyMode = true;
40850                     this.selectNext();
40851                 }
40852             },
40853
40854             "enter" : function(e){
40855                 this.collapse();
40856                 
40857                 if(this.fireEvent("specialkey", this, e)){
40858                     this.onViewClick(false);
40859                 }
40860                 
40861                 return true;
40862             },
40863
40864             "esc" : function(e){
40865                 this.collapse();
40866             },
40867
40868             "tab" : function(e){
40869                 this.collapse();
40870                 
40871                 if(this.fireEvent("specialkey", this, e)){
40872                     this.onViewClick(false);
40873                 }
40874                 
40875                 return true;
40876             },
40877
40878             scope : this,
40879
40880             doRelay : function(foo, bar, hname){
40881                 if(hname == 'down' || this.scope.isExpanded()){
40882                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40883                 }
40884                 return true;
40885             },
40886
40887             forceKeyDown: true
40888         });
40889         
40890         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40891         
40892     },
40893     
40894     initNumberEvent : function(e)
40895     {
40896         this.inputEl().on("keydown" , this.fireKey,  this);
40897         this.inputEl().on("focus", this.onFocus,  this);
40898         this.inputEl().on("blur", this.onBlur,  this);
40899         
40900         this.inputEl().relayEvent('keyup', this);
40901         
40902         if(this.indicator){
40903             this.indicator.addClass('invisible');
40904         }
40905  
40906         this.originalValue = this.getValue();
40907         
40908         if(this.validationEvent == 'keyup'){
40909             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40910             this.inputEl().on('keyup', this.filterValidation, this);
40911         }
40912         else if(this.validationEvent !== false){
40913             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40914         }
40915         
40916         if(this.selectOnFocus){
40917             this.on("focus", this.preFocus, this);
40918             
40919         }
40920         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40921             this.inputEl().on("keypress", this.filterKeys, this);
40922         } else {
40923             this.inputEl().relayEvent('keypress', this);
40924         }
40925         
40926         var allowed = "0123456789";
40927         
40928         if(this.allowDecimals){
40929             allowed += this.decimalSeparator;
40930         }
40931         
40932         if(this.allowNegative){
40933             allowed += "-";
40934         }
40935         
40936         if(this.thousandsDelimiter) {
40937             allowed += ",";
40938         }
40939         
40940         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40941         
40942         var keyPress = function(e){
40943             
40944             var k = e.getKey();
40945             
40946             var c = e.getCharCode();
40947             
40948             if(
40949                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40950                     allowed.indexOf(String.fromCharCode(c)) === -1
40951             ){
40952                 e.stopEvent();
40953                 return;
40954             }
40955             
40956             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40957                 return;
40958             }
40959             
40960             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40961                 e.stopEvent();
40962             }
40963         };
40964         
40965         this.inputEl().on("keypress", keyPress, this);
40966         
40967     },
40968     
40969     onTriggerClick : function(e)
40970     {   
40971         if(this.disabled){
40972             return;
40973         }
40974         
40975         this.page = 0;
40976         this.loadNext = false;
40977         
40978         if(this.isExpanded()){
40979             this.collapse();
40980             return;
40981         }
40982         
40983         this.hasFocus = true;
40984         
40985         if(this.triggerAction == 'all') {
40986             this.doQuery(this.allQuery, true);
40987             return;
40988         }
40989         
40990         this.doQuery(this.getRawValue());
40991     },
40992     
40993     getCurrency : function()
40994     {   
40995         var v = this.currencyEl().getValue();
40996         
40997         return v;
40998     },
40999     
41000     restrictHeight : function()
41001     {
41002         this.list.alignTo(this.currencyEl(), this.listAlign);
41003         this.list.alignTo(this.currencyEl(), this.listAlign);
41004     },
41005     
41006     onViewClick : function(view, doFocus, el, e)
41007     {
41008         var index = this.view.getSelectedIndexes()[0];
41009         
41010         var r = this.store.getAt(index);
41011         
41012         if(r){
41013             this.onSelect(r, index);
41014         }
41015     },
41016     
41017     onSelect : function(record, index){
41018         
41019         if(this.fireEvent('beforeselect', this, record, index) !== false){
41020         
41021             this.setFromCurrencyData(index > -1 ? record.data : false);
41022             
41023             this.collapse();
41024             
41025             this.fireEvent('select', this, record, index);
41026         }
41027     },
41028     
41029     setFromCurrencyData : function(o)
41030     {
41031         var currency = '';
41032         
41033         this.lastCurrency = o;
41034         
41035         if (this.currencyField) {
41036             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41037         } else {
41038             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41039         }
41040         
41041         this.lastSelectionText = currency;
41042         
41043         //setting default currency
41044         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41045             this.setCurrency(this.defaultCurrency);
41046             return;
41047         }
41048         
41049         this.setCurrency(currency);
41050     },
41051     
41052     setFromData : function(o)
41053     {
41054         var c = {};
41055         
41056         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41057         
41058         this.setFromCurrencyData(c);
41059         
41060         var value = '';
41061         
41062         if (this.name) {
41063             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41064         } else {
41065             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41066         }
41067         
41068         this.setValue(value);
41069         
41070     },
41071     
41072     setCurrency : function(v)
41073     {   
41074         this.currencyValue = v;
41075         
41076         if(this.rendered){
41077             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41078             this.validate();
41079         }
41080     },
41081     
41082     setValue : function(v)
41083     {
41084         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41085         
41086         this.value = v;
41087         
41088         if(this.rendered){
41089             
41090             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41091             
41092             this.inputEl().dom.value = (v == '') ? '' :
41093                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41094             
41095             if(!this.allowZero && v === '0') {
41096                 this.hiddenEl().dom.value = '';
41097                 this.inputEl().dom.value = '';
41098             }
41099             
41100             this.validate();
41101         }
41102     },
41103     
41104     getRawValue : function()
41105     {
41106         var v = this.inputEl().getValue();
41107         
41108         return v;
41109     },
41110     
41111     getValue : function()
41112     {
41113         return this.fixPrecision(this.parseValue(this.getRawValue()));
41114     },
41115     
41116     parseValue : function(value)
41117     {
41118         if(this.thousandsDelimiter) {
41119             value += "";
41120             r = new RegExp(",", "g");
41121             value = value.replace(r, "");
41122         }
41123         
41124         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41125         return isNaN(value) ? '' : value;
41126         
41127     },
41128     
41129     fixPrecision : function(value)
41130     {
41131         if(this.thousandsDelimiter) {
41132             value += "";
41133             r = new RegExp(",", "g");
41134             value = value.replace(r, "");
41135         }
41136         
41137         var nan = isNaN(value);
41138         
41139         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41140             return nan ? '' : value;
41141         }
41142         return parseFloat(value).toFixed(this.decimalPrecision);
41143     },
41144     
41145     decimalPrecisionFcn : function(v)
41146     {
41147         return Math.floor(v);
41148     },
41149     
41150     validateValue : function(value)
41151     {
41152         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41153             return false;
41154         }
41155         
41156         var num = this.parseValue(value);
41157         
41158         if(isNaN(num)){
41159             this.markInvalid(String.format(this.nanText, value));
41160             return false;
41161         }
41162         
41163         if(num < this.minValue){
41164             this.markInvalid(String.format(this.minText, this.minValue));
41165             return false;
41166         }
41167         
41168         if(num > this.maxValue){
41169             this.markInvalid(String.format(this.maxText, this.maxValue));
41170             return false;
41171         }
41172         
41173         return true;
41174     },
41175     
41176     validate : function()
41177     {
41178         if(this.disabled || this.allowBlank){
41179             this.markValid();
41180             return true;
41181         }
41182         
41183         var currency = this.getCurrency();
41184         
41185         if(this.validateValue(this.getRawValue()) && currency.length){
41186             this.markValid();
41187             return true;
41188         }
41189         
41190         this.markInvalid();
41191         return false;
41192     },
41193     
41194     getName: function()
41195     {
41196         return this.name;
41197     },
41198     
41199     beforeBlur : function()
41200     {
41201         if(!this.castInt){
41202             return;
41203         }
41204         
41205         var v = this.parseValue(this.getRawValue());
41206         
41207         if(v || v == 0){
41208             this.setValue(v);
41209         }
41210     },
41211     
41212     onBlur : function()
41213     {
41214         this.beforeBlur();
41215         
41216         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41217             //this.el.removeClass(this.focusClass);
41218         }
41219         
41220         this.hasFocus = false;
41221         
41222         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41223             this.validate();
41224         }
41225         
41226         var v = this.getValue();
41227         
41228         if(String(v) !== String(this.startValue)){
41229             this.fireEvent('change', this, v, this.startValue);
41230         }
41231         
41232         this.fireEvent("blur", this);
41233     },
41234     
41235     inputEl : function()
41236     {
41237         return this.el.select('.roo-money-amount-input', true).first();
41238     },
41239     
41240     currencyEl : function()
41241     {
41242         return this.el.select('.roo-money-currency-input', true).first();
41243     },
41244     
41245     hiddenEl : function()
41246     {
41247         return this.el.select('input.hidden-number-input',true).first();
41248     }
41249     
41250 });