Roo/bootstrap/Navbar.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 dropdown-item'
2517             };
2518         }
2519         var ctag = {
2520             tag: 'span',
2521             html: 'Link'
2522         };
2523         
2524         var anc = {
2525             tag : 'a',
2526             href : '#',
2527             cn : [  ]
2528         };
2529         
2530         if (this.fa !== false) {
2531             anc.cn.push({
2532                 tag : 'i',
2533                 cls : 'fa fa-' + this.fa
2534             });
2535         }
2536         
2537         anc.cn.push(ctag);
2538         
2539         
2540         var cfg= {
2541             tag: 'li',
2542             cls: 'dropdown-menu-item dropdown-item',
2543             cn: [ anc ]
2544         };
2545         if (this.parent().type == 'treeview') {
2546             cfg.cls = 'treeview-menu';
2547         }
2548         if (this.active) {
2549             cfg.cls += ' active';
2550         }
2551         
2552         
2553         
2554         anc.href = this.href || cfg.cn[0].href ;
2555         ctag.html = this.html || cfg.cn[0].html ;
2556         return cfg;
2557     },
2558     
2559     initEvents: function()
2560     {
2561         if (this.parent().type == 'treeview') {
2562             this.el.select('a').on('click', this.onClick, this);
2563         }
2564         
2565         if (this.menu) {
2566             this.menu.parentType = this.xtype;
2567             this.menu.triggerEl = this.el;
2568             this.menu = this.addxtype(Roo.apply({}, this.menu));
2569         }
2570         
2571     },
2572     onClick : function(e)
2573     {
2574         Roo.log('item on click ');
2575         
2576         if(this.preventDefault){
2577             e.preventDefault();
2578         }
2579         //this.parent().hideMenuItems();
2580         
2581         this.fireEvent('click', this, e);
2582     },
2583     getEl : function()
2584     {
2585         return this.el;
2586     } 
2587 });
2588
2589  
2590
2591  /*
2592  * - LGPL
2593  *
2594  * menu separator
2595  * 
2596  */
2597
2598
2599 /**
2600  * @class Roo.bootstrap.MenuSeparator
2601  * @extends Roo.bootstrap.Component
2602  * Bootstrap MenuSeparator class
2603  * 
2604  * @constructor
2605  * Create a new MenuItem
2606  * @param {Object} config The config object
2607  */
2608
2609
2610 Roo.bootstrap.MenuSeparator = function(config){
2611     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2612 };
2613
2614 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2615     
2616     getAutoCreate : function(){
2617         var cfg = {
2618             cls: 'divider',
2619             tag : 'li'
2620         };
2621         
2622         return cfg;
2623     }
2624    
2625 });
2626
2627  
2628
2629  
2630 /*
2631 * Licence: LGPL
2632 */
2633
2634 /**
2635  * @class Roo.bootstrap.Modal
2636  * @extends Roo.bootstrap.Component
2637  * Bootstrap Modal class
2638  * @cfg {String} title Title of dialog
2639  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2640  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2641  * @cfg {Boolean} specificTitle default false
2642  * @cfg {Array} buttons Array of buttons or standard button set..
2643  * @cfg {String} buttonPosition (left|right|center) default right
2644  * @cfg {Boolean} animate default true
2645  * @cfg {Boolean} allow_close default true
2646  * @cfg {Boolean} fitwindow default false
2647  * @cfg {String} size (sm|lg) default empty
2648  * @cfg {Number} max_width set the max width of modal
2649  *
2650  *
2651  * @constructor
2652  * Create a new Modal Dialog
2653  * @param {Object} config The config object
2654  */
2655
2656 Roo.bootstrap.Modal = function(config){
2657     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2658     this.addEvents({
2659         // raw events
2660         /**
2661          * @event btnclick
2662          * The raw btnclick event for the button
2663          * @param {Roo.EventObject} e
2664          */
2665         "btnclick" : true,
2666         /**
2667          * @event resize
2668          * Fire when dialog resize
2669          * @param {Roo.bootstrap.Modal} this
2670          * @param {Roo.EventObject} e
2671          */
2672         "resize" : true
2673     });
2674     this.buttons = this.buttons || [];
2675
2676     if (this.tmpl) {
2677         this.tmpl = Roo.factory(this.tmpl);
2678     }
2679
2680 };
2681
2682 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2683
2684     title : 'test dialog',
2685
2686     buttons : false,
2687
2688     // set on load...
2689
2690     html: false,
2691
2692     tmp: false,
2693
2694     specificTitle: false,
2695
2696     buttonPosition: 'right',
2697
2698     allow_close : true,
2699
2700     animate : true,
2701
2702     fitwindow: false,
2703     
2704      // private
2705     dialogEl: false,
2706     bodyEl:  false,
2707     footerEl:  false,
2708     titleEl:  false,
2709     closeEl:  false,
2710
2711     size: '',
2712     
2713     max_width: 0,
2714     
2715     max_height: 0,
2716     
2717     fit_content: false,
2718
2719     onRender : function(ct, position)
2720     {
2721         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2722
2723         if(!this.el){
2724             var cfg = Roo.apply({},  this.getAutoCreate());
2725             cfg.id = Roo.id();
2726             //if(!cfg.name){
2727             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2728             //}
2729             //if (!cfg.name.length) {
2730             //    delete cfg.name;
2731            // }
2732             if (this.cls) {
2733                 cfg.cls += ' ' + this.cls;
2734             }
2735             if (this.style) {
2736                 cfg.style = this.style;
2737             }
2738             this.el = Roo.get(document.body).createChild(cfg, position);
2739         }
2740         //var type = this.el.dom.type;
2741
2742
2743         if(this.tabIndex !== undefined){
2744             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2745         }
2746
2747         this.dialogEl = this.el.select('.modal-dialog',true).first();
2748         this.bodyEl = this.el.select('.modal-body',true).first();
2749         this.closeEl = this.el.select('.modal-header .close', true).first();
2750         this.headerEl = this.el.select('.modal-header',true).first();
2751         this.titleEl = this.el.select('.modal-title',true).first();
2752         this.footerEl = this.el.select('.modal-footer',true).first();
2753
2754         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2755         
2756         //this.el.addClass("x-dlg-modal");
2757
2758         if (this.buttons.length) {
2759             Roo.each(this.buttons, function(bb) {
2760                 var b = Roo.apply({}, bb);
2761                 b.xns = b.xns || Roo.bootstrap;
2762                 b.xtype = b.xtype || 'Button';
2763                 if (typeof(b.listeners) == 'undefined') {
2764                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2765                 }
2766
2767                 var btn = Roo.factory(b);
2768
2769                 btn.render(this.el.select('.modal-footer div').first());
2770
2771             },this);
2772         }
2773         // render the children.
2774         var nitems = [];
2775
2776         if(typeof(this.items) != 'undefined'){
2777             var items = this.items;
2778             delete this.items;
2779
2780             for(var i =0;i < items.length;i++) {
2781                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2782             }
2783         }
2784
2785         this.items = nitems;
2786
2787         // where are these used - they used to be body/close/footer
2788
2789
2790         this.initEvents();
2791         //this.el.addClass([this.fieldClass, this.cls]);
2792
2793     },
2794
2795     getAutoCreate : function()
2796     {
2797         var bdy = {
2798                 cls : 'modal-body',
2799                 html : this.html || ''
2800         };
2801
2802         var title = {
2803             tag: 'h4',
2804             cls : 'modal-title',
2805             html : this.title
2806         };
2807
2808         if(this.specificTitle){
2809             title = this.title;
2810
2811         };
2812
2813         var header = [];
2814         if (this.allow_close && Roo.bootstrap.version == 3) {
2815             header.push({
2816                 tag: 'button',
2817                 cls : 'close',
2818                 html : '&times'
2819             });
2820         }
2821
2822         header.push(title);
2823
2824         if (this.allow_close && Roo.bootstrap.version == 4) {
2825             header.push({
2826                 tag: 'button',
2827                 cls : 'close',
2828                 html : '&times'
2829             });
2830         }
2831         
2832         var size = '';
2833
2834         if(this.size.length){
2835             size = 'modal-' + this.size;
2836         }
2837
2838         var modal = {
2839             cls: "modal",
2840              cn : [
2841                 {
2842                     cls: "modal-dialog " + size,
2843                     cn : [
2844                         {
2845                             cls : "modal-content",
2846                             cn : [
2847                                 {
2848                                     cls : 'modal-header',
2849                                     cn : header
2850                                 },
2851                                 bdy,
2852                                 {
2853                                     cls : 'modal-footer',
2854                                     cn : [
2855                                         {
2856                                             tag: 'div',
2857                                             cls: 'btn-' + this.buttonPosition
2858                                         }
2859                                     ]
2860
2861                                 }
2862
2863
2864                             ]
2865
2866                         }
2867                     ]
2868
2869                 }
2870             ]
2871         };
2872
2873         if(this.animate){
2874             modal.cls += ' fade';
2875         }
2876
2877         return modal;
2878
2879     },
2880     getChildContainer : function() {
2881
2882          return this.bodyEl;
2883
2884     },
2885     getButtonContainer : function() {
2886          return this.el.select('.modal-footer div',true).first();
2887
2888     },
2889     initEvents : function()
2890     {
2891         if (this.allow_close) {
2892             this.closeEl.on('click', this.hide, this);
2893         }
2894         Roo.EventManager.onWindowResize(this.resize, this, true);
2895
2896
2897     },
2898
2899     resize : function()
2900     {
2901         this.maskEl.setSize(
2902             Roo.lib.Dom.getViewWidth(true),
2903             Roo.lib.Dom.getViewHeight(true)
2904         );
2905         
2906         if (this.fitwindow) {
2907             this.setSize(
2908                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2909                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2910             );
2911             return;
2912         }
2913         
2914         if(this.max_width !== 0) {
2915             
2916             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2917             
2918             if(this.height) {
2919                 this.setSize(w, this.height);
2920                 return;
2921             }
2922             
2923             if(this.max_height) {
2924                 this.setSize(w,Math.min(
2925                     this.max_height,
2926                     Roo.lib.Dom.getViewportHeight(true) - 60
2927                 ));
2928                 
2929                 return;
2930             }
2931             
2932             if(!this.fit_content) {
2933                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2934                 return;
2935             }
2936             
2937             this.setSize(w, Math.min(
2938                 60 +
2939                 this.headerEl.getHeight() + 
2940                 this.footerEl.getHeight() + 
2941                 this.getChildHeight(this.bodyEl.dom.childNodes),
2942                 Roo.lib.Dom.getViewportHeight(true) - 60)
2943             );
2944         }
2945         
2946     },
2947
2948     setSize : function(w,h)
2949     {
2950         if (!w && !h) {
2951             return;
2952         }
2953         
2954         this.resizeTo(w,h);
2955     },
2956
2957     show : function() {
2958
2959         if (!this.rendered) {
2960             this.render();
2961         }
2962
2963         //this.el.setStyle('display', 'block');
2964         this.el.removeClass('hideing');
2965         this.el.dom.style.display='block';
2966         
2967         Roo.get(document.body).addClass('modal-open');
2968  
2969         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2970             var _this = this;
2971             (function(){
2972                 this.el.addClass('show');
2973                 this.el.addClass('in');
2974             }).defer(50, this);
2975         }else{
2976             this.el.addClass('show');
2977             this.el.addClass('in');
2978         }
2979
2980         // not sure how we can show data in here..
2981         //if (this.tmpl) {
2982         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2983         //}
2984
2985         Roo.get(document.body).addClass("x-body-masked");
2986         
2987         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2988         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2989         this.maskEl.dom.style.display = 'block';
2990         this.maskEl.addClass('show');
2991         
2992         
2993         this.resize();
2994         
2995         this.fireEvent('show', this);
2996
2997         // set zindex here - otherwise it appears to be ignored...
2998         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2999
3000         (function () {
3001             this.items.forEach( function(e) {
3002                 e.layout ? e.layout() : false;
3003
3004             });
3005         }).defer(100,this);
3006
3007     },
3008     hide : function()
3009     {
3010         if(this.fireEvent("beforehide", this) !== false){
3011             
3012             this.maskEl.removeClass('show');
3013             
3014             this.maskEl.dom.style.display = '';
3015             Roo.get(document.body).removeClass("x-body-masked");
3016             this.el.removeClass('in');
3017             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3018
3019             if(this.animate){ // why
3020                 this.el.addClass('hideing');
3021                 this.el.removeClass('show');
3022                 (function(){
3023                     if (!this.el.hasClass('hideing')) {
3024                         return; // it's been shown again...
3025                     }
3026                     
3027                     this.el.dom.style.display='';
3028
3029                     Roo.get(document.body).removeClass('modal-open');
3030                     this.el.removeClass('hideing');
3031                 }).defer(150,this);
3032                 
3033             }else{
3034                 this.el.removeClass('show');
3035                 this.el.dom.style.display='';
3036                 Roo.get(document.body).removeClass('modal-open');
3037
3038             }
3039             this.fireEvent('hide', this);
3040         }
3041     },
3042     isVisible : function()
3043     {
3044         
3045         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3046         
3047     },
3048
3049     addButton : function(str, cb)
3050     {
3051
3052
3053         var b = Roo.apply({}, { html : str } );
3054         b.xns = b.xns || Roo.bootstrap;
3055         b.xtype = b.xtype || 'Button';
3056         if (typeof(b.listeners) == 'undefined') {
3057             b.listeners = { click : cb.createDelegate(this)  };
3058         }
3059
3060         var btn = Roo.factory(b);
3061
3062         btn.render(this.el.select('.modal-footer div').first());
3063
3064         return btn;
3065
3066     },
3067
3068     setDefaultButton : function(btn)
3069     {
3070         //this.el.select('.modal-footer').()
3071     },
3072     diff : false,
3073
3074     resizeTo: function(w,h)
3075     {
3076         // skip.. ?? why??
3077
3078         this.dialogEl.setWidth(w);
3079         if (this.diff === false) {
3080             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3081         }
3082
3083         this.bodyEl.setHeight(h - this.diff);
3084
3085         this.fireEvent('resize', this);
3086
3087     },
3088     setContentSize  : function(w, h)
3089     {
3090
3091     },
3092     onButtonClick: function(btn,e)
3093     {
3094         //Roo.log([a,b,c]);
3095         this.fireEvent('btnclick', btn.name, e);
3096     },
3097      /**
3098      * Set the title of the Dialog
3099      * @param {String} str new Title
3100      */
3101     setTitle: function(str) {
3102         this.titleEl.dom.innerHTML = str;
3103     },
3104     /**
3105      * Set the body of the Dialog
3106      * @param {String} str new Title
3107      */
3108     setBody: function(str) {
3109         this.bodyEl.dom.innerHTML = str;
3110     },
3111     /**
3112      * Set the body of the Dialog using the template
3113      * @param {Obj} data - apply this data to the template and replace the body contents.
3114      */
3115     applyBody: function(obj)
3116     {
3117         if (!this.tmpl) {
3118             Roo.log("Error - using apply Body without a template");
3119             //code
3120         }
3121         this.tmpl.overwrite(this.bodyEl, obj);
3122     },
3123     
3124     getChildHeight : function(child_nodes)
3125     {
3126         if(
3127             !child_nodes ||
3128             child_nodes.length == 0
3129         ) {
3130             return;
3131         }
3132         
3133         var child_height = 0;
3134         
3135         for(var i = 0; i < child_nodes.length; i++) {
3136             
3137             /*
3138             * for modal with tabs...
3139             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3140                 
3141                 var layout_childs = child_nodes[i].childNodes;
3142                 
3143                 for(var j = 0; j < layout_childs.length; j++) {
3144                     
3145                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3146                         
3147                         var layout_body_childs = layout_childs[j].childNodes;
3148                         
3149                         for(var k = 0; k < layout_body_childs.length; k++) {
3150                             
3151                             if(layout_body_childs[k].classList.contains('navbar')) {
3152                                 child_height += layout_body_childs[k].offsetHeight;
3153                                 continue;
3154                             }
3155                             
3156                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3157                                 
3158                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3159                                 
3160                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3161                                     
3162                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3163                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3164                                         continue;
3165                                     }
3166                                     
3167                                 }
3168                                 
3169                             }
3170                             
3171                         }
3172                     }
3173                 }
3174                 continue;
3175             }
3176             */
3177             
3178             child_height += child_nodes[i].offsetHeight;
3179             // Roo.log(child_nodes[i].offsetHeight);
3180         }
3181         
3182         return child_height;
3183     }
3184
3185 });
3186
3187
3188 Roo.apply(Roo.bootstrap.Modal,  {
3189     /**
3190          * Button config that displays a single OK button
3191          * @type Object
3192          */
3193         OK :  [{
3194             name : 'ok',
3195             weight : 'primary',
3196             html : 'OK'
3197         }],
3198         /**
3199          * Button config that displays Yes and No buttons
3200          * @type Object
3201          */
3202         YESNO : [
3203             {
3204                 name  : 'no',
3205                 html : 'No'
3206             },
3207             {
3208                 name  :'yes',
3209                 weight : 'primary',
3210                 html : 'Yes'
3211             }
3212         ],
3213
3214         /**
3215          * Button config that displays OK and Cancel buttons
3216          * @type Object
3217          */
3218         OKCANCEL : [
3219             {
3220                name : 'cancel',
3221                 html : 'Cancel'
3222             },
3223             {
3224                 name : 'ok',
3225                 weight : 'primary',
3226                 html : 'OK'
3227             }
3228         ],
3229         /**
3230          * Button config that displays Yes, No and Cancel buttons
3231          * @type Object
3232          */
3233         YESNOCANCEL : [
3234             {
3235                 name : 'yes',
3236                 weight : 'primary',
3237                 html : 'Yes'
3238             },
3239             {
3240                 name : 'no',
3241                 html : 'No'
3242             },
3243             {
3244                 name : 'cancel',
3245                 html : 'Cancel'
3246             }
3247         ],
3248         
3249         zIndex : 10001
3250 });
3251 /*
3252  * - LGPL
3253  *
3254  * messagebox - can be used as a replace
3255  * 
3256  */
3257 /**
3258  * @class Roo.MessageBox
3259  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3260  * Example usage:
3261  *<pre><code>
3262 // Basic alert:
3263 Roo.Msg.alert('Status', 'Changes saved successfully.');
3264
3265 // Prompt for user data:
3266 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3267     if (btn == 'ok'){
3268         // process text value...
3269     }
3270 });
3271
3272 // Show a dialog using config options:
3273 Roo.Msg.show({
3274    title:'Save Changes?',
3275    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3276    buttons: Roo.Msg.YESNOCANCEL,
3277    fn: processResult,
3278    animEl: 'elId'
3279 });
3280 </code></pre>
3281  * @singleton
3282  */
3283 Roo.bootstrap.MessageBox = function(){
3284     var dlg, opt, mask, waitTimer;
3285     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3286     var buttons, activeTextEl, bwidth;
3287
3288     
3289     // private
3290     var handleButton = function(button){
3291         dlg.hide();
3292         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3293     };
3294
3295     // private
3296     var handleHide = function(){
3297         if(opt && opt.cls){
3298             dlg.el.removeClass(opt.cls);
3299         }
3300         //if(waitTimer){
3301         //    Roo.TaskMgr.stop(waitTimer);
3302         //    waitTimer = null;
3303         //}
3304     };
3305
3306     // private
3307     var updateButtons = function(b){
3308         var width = 0;
3309         if(!b){
3310             buttons["ok"].hide();
3311             buttons["cancel"].hide();
3312             buttons["yes"].hide();
3313             buttons["no"].hide();
3314             //dlg.footer.dom.style.display = 'none';
3315             return width;
3316         }
3317         dlg.footerEl.dom.style.display = '';
3318         for(var k in buttons){
3319             if(typeof buttons[k] != "function"){
3320                 if(b[k]){
3321                     buttons[k].show();
3322                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3323                     width += buttons[k].el.getWidth()+15;
3324                 }else{
3325                     buttons[k].hide();
3326                 }
3327             }
3328         }
3329         return width;
3330     };
3331
3332     // private
3333     var handleEsc = function(d, k, e){
3334         if(opt && opt.closable !== false){
3335             dlg.hide();
3336         }
3337         if(e){
3338             e.stopEvent();
3339         }
3340     };
3341
3342     return {
3343         /**
3344          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3345          * @return {Roo.BasicDialog} The BasicDialog element
3346          */
3347         getDialog : function(){
3348            if(!dlg){
3349                 dlg = new Roo.bootstrap.Modal( {
3350                     //draggable: true,
3351                     //resizable:false,
3352                     //constraintoviewport:false,
3353                     //fixedcenter:true,
3354                     //collapsible : false,
3355                     //shim:true,
3356                     //modal: true,
3357                 //    width: 'auto',
3358                   //  height:100,
3359                     //buttonAlign:"center",
3360                     closeClick : function(){
3361                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3362                             handleButton("no");
3363                         }else{
3364                             handleButton("cancel");
3365                         }
3366                     }
3367                 });
3368                 dlg.render();
3369                 dlg.on("hide", handleHide);
3370                 mask = dlg.mask;
3371                 //dlg.addKeyListener(27, handleEsc);
3372                 buttons = {};
3373                 this.buttons = buttons;
3374                 var bt = this.buttonText;
3375                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3376                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3377                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3378                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3379                 //Roo.log(buttons);
3380                 bodyEl = dlg.bodyEl.createChild({
3381
3382                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3383                         '<textarea class="roo-mb-textarea"></textarea>' +
3384                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3385                 });
3386                 msgEl = bodyEl.dom.firstChild;
3387                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3388                 textboxEl.enableDisplayMode();
3389                 textboxEl.addKeyListener([10,13], function(){
3390                     if(dlg.isVisible() && opt && opt.buttons){
3391                         if(opt.buttons.ok){
3392                             handleButton("ok");
3393                         }else if(opt.buttons.yes){
3394                             handleButton("yes");
3395                         }
3396                     }
3397                 });
3398                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3399                 textareaEl.enableDisplayMode();
3400                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3401                 progressEl.enableDisplayMode();
3402                 
3403                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3404                 var pf = progressEl.dom.firstChild;
3405                 if (pf) {
3406                     pp = Roo.get(pf.firstChild);
3407                     pp.setHeight(pf.offsetHeight);
3408                 }
3409                 
3410             }
3411             return dlg;
3412         },
3413
3414         /**
3415          * Updates the message box body text
3416          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3417          * the XHTML-compliant non-breaking space character '&amp;#160;')
3418          * @return {Roo.MessageBox} This message box
3419          */
3420         updateText : function(text)
3421         {
3422             if(!dlg.isVisible() && !opt.width){
3423                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3424                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3425             }
3426             msgEl.innerHTML = text || '&#160;';
3427       
3428             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3429             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3430             var w = Math.max(
3431                     Math.min(opt.width || cw , this.maxWidth), 
3432                     Math.max(opt.minWidth || this.minWidth, bwidth)
3433             );
3434             if(opt.prompt){
3435                 activeTextEl.setWidth(w);
3436             }
3437             if(dlg.isVisible()){
3438                 dlg.fixedcenter = false;
3439             }
3440             // to big, make it scroll. = But as usual stupid IE does not support
3441             // !important..
3442             
3443             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3444                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3445                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3446             } else {
3447                 bodyEl.dom.style.height = '';
3448                 bodyEl.dom.style.overflowY = '';
3449             }
3450             if (cw > w) {
3451                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3452             } else {
3453                 bodyEl.dom.style.overflowX = '';
3454             }
3455             
3456             dlg.setContentSize(w, bodyEl.getHeight());
3457             if(dlg.isVisible()){
3458                 dlg.fixedcenter = true;
3459             }
3460             return this;
3461         },
3462
3463         /**
3464          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3465          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3466          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3467          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3468          * @return {Roo.MessageBox} This message box
3469          */
3470         updateProgress : function(value, text){
3471             if(text){
3472                 this.updateText(text);
3473             }
3474             
3475             if (pp) { // weird bug on my firefox - for some reason this is not defined
3476                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3477                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3478             }
3479             return this;
3480         },        
3481
3482         /**
3483          * Returns true if the message box is currently displayed
3484          * @return {Boolean} True if the message box is visible, else false
3485          */
3486         isVisible : function(){
3487             return dlg && dlg.isVisible();  
3488         },
3489
3490         /**
3491          * Hides the message box if it is displayed
3492          */
3493         hide : function(){
3494             if(this.isVisible()){
3495                 dlg.hide();
3496             }  
3497         },
3498
3499         /**
3500          * Displays a new message box, or reinitializes an existing message box, based on the config options
3501          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3502          * The following config object properties are supported:
3503          * <pre>
3504 Property    Type             Description
3505 ----------  ---------------  ------------------------------------------------------------------------------------
3506 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3507                                    closes (defaults to undefined)
3508 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3509                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3510 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3511                                    progress and wait dialogs will ignore this property and always hide the
3512                                    close button as they can only be closed programmatically.
3513 cls               String           A custom CSS class to apply to the message box element
3514 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3515                                    displayed (defaults to 75)
3516 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3517                                    function will be btn (the name of the button that was clicked, if applicable,
3518                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3519                                    Progress and wait dialogs will ignore this option since they do not respond to
3520                                    user actions and can only be closed programmatically, so any required function
3521                                    should be called by the same code after it closes the dialog.
3522 icon              String           A CSS class that provides a background image to be used as an icon for
3523                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3524 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3525 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3526 modal             Boolean          False to allow user interaction with the page while the message box is
3527                                    displayed (defaults to true)
3528 msg               String           A string that will replace the existing message box body text (defaults
3529                                    to the XHTML-compliant non-breaking space character '&#160;')
3530 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3531 progress          Boolean          True to display a progress bar (defaults to false)
3532 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3533 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3534 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3535 title             String           The title text
3536 value             String           The string value to set into the active textbox element if displayed
3537 wait              Boolean          True to display a progress bar (defaults to false)
3538 width             Number           The width of the dialog in pixels
3539 </pre>
3540          *
3541          * Example usage:
3542          * <pre><code>
3543 Roo.Msg.show({
3544    title: 'Address',
3545    msg: 'Please enter your address:',
3546    width: 300,
3547    buttons: Roo.MessageBox.OKCANCEL,
3548    multiline: true,
3549    fn: saveAddress,
3550    animEl: 'addAddressBtn'
3551 });
3552 </code></pre>
3553          * @param {Object} config Configuration options
3554          * @return {Roo.MessageBox} This message box
3555          */
3556         show : function(options)
3557         {
3558             
3559             // this causes nightmares if you show one dialog after another
3560             // especially on callbacks..
3561              
3562             if(this.isVisible()){
3563                 
3564                 this.hide();
3565                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3566                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3567                 Roo.log("New Dialog Message:" +  options.msg )
3568                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3569                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3570                 
3571             }
3572             var d = this.getDialog();
3573             opt = options;
3574             d.setTitle(opt.title || "&#160;");
3575             d.closeEl.setDisplayed(opt.closable !== false);
3576             activeTextEl = textboxEl;
3577             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3578             if(opt.prompt){
3579                 if(opt.multiline){
3580                     textboxEl.hide();
3581                     textareaEl.show();
3582                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3583                         opt.multiline : this.defaultTextHeight);
3584                     activeTextEl = textareaEl;
3585                 }else{
3586                     textboxEl.show();
3587                     textareaEl.hide();
3588                 }
3589             }else{
3590                 textboxEl.hide();
3591                 textareaEl.hide();
3592             }
3593             progressEl.setDisplayed(opt.progress === true);
3594             this.updateProgress(0);
3595             activeTextEl.dom.value = opt.value || "";
3596             if(opt.prompt){
3597                 dlg.setDefaultButton(activeTextEl);
3598             }else{
3599                 var bs = opt.buttons;
3600                 var db = null;
3601                 if(bs && bs.ok){
3602                     db = buttons["ok"];
3603                 }else if(bs && bs.yes){
3604                     db = buttons["yes"];
3605                 }
3606                 dlg.setDefaultButton(db);
3607             }
3608             bwidth = updateButtons(opt.buttons);
3609             this.updateText(opt.msg);
3610             if(opt.cls){
3611                 d.el.addClass(opt.cls);
3612             }
3613             d.proxyDrag = opt.proxyDrag === true;
3614             d.modal = opt.modal !== false;
3615             d.mask = opt.modal !== false ? mask : false;
3616             if(!d.isVisible()){
3617                 // force it to the end of the z-index stack so it gets a cursor in FF
3618                 document.body.appendChild(dlg.el.dom);
3619                 d.animateTarget = null;
3620                 d.show(options.animEl);
3621             }
3622             return this;
3623         },
3624
3625         /**
3626          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3627          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3628          * and closing the message box when the process is complete.
3629          * @param {String} title The title bar text
3630          * @param {String} msg The message box body text
3631          * @return {Roo.MessageBox} This message box
3632          */
3633         progress : function(title, msg){
3634             this.show({
3635                 title : title,
3636                 msg : msg,
3637                 buttons: false,
3638                 progress:true,
3639                 closable:false,
3640                 minWidth: this.minProgressWidth,
3641                 modal : true
3642             });
3643             return this;
3644         },
3645
3646         /**
3647          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3648          * If a callback function is passed it will be called after the user clicks the button, and the
3649          * id of the button that was clicked will be passed as the only parameter to the callback
3650          * (could also be the top-right close button).
3651          * @param {String} title The title bar text
3652          * @param {String} msg The message box body text
3653          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3654          * @param {Object} scope (optional) The scope of the callback function
3655          * @return {Roo.MessageBox} This message box
3656          */
3657         alert : function(title, msg, fn, scope)
3658         {
3659             this.show({
3660                 title : title,
3661                 msg : msg,
3662                 buttons: this.OK,
3663                 fn: fn,
3664                 closable : false,
3665                 scope : scope,
3666                 modal : true
3667             });
3668             return this;
3669         },
3670
3671         /**
3672          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3673          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3674          * You are responsible for closing the message box when the process is complete.
3675          * @param {String} msg The message box body text
3676          * @param {String} title (optional) The title bar text
3677          * @return {Roo.MessageBox} This message box
3678          */
3679         wait : function(msg, title){
3680             this.show({
3681                 title : title,
3682                 msg : msg,
3683                 buttons: false,
3684                 closable:false,
3685                 progress:true,
3686                 modal:true,
3687                 width:300,
3688                 wait:true
3689             });
3690             waitTimer = Roo.TaskMgr.start({
3691                 run: function(i){
3692                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3693                 },
3694                 interval: 1000
3695             });
3696             return this;
3697         },
3698
3699         /**
3700          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3701          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3702          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3703          * @param {String} title The title bar text
3704          * @param {String} msg The message box body text
3705          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3706          * @param {Object} scope (optional) The scope of the callback function
3707          * @return {Roo.MessageBox} This message box
3708          */
3709         confirm : function(title, msg, fn, scope){
3710             this.show({
3711                 title : title,
3712                 msg : msg,
3713                 buttons: this.YESNO,
3714                 fn: fn,
3715                 scope : scope,
3716                 modal : true
3717             });
3718             return this;
3719         },
3720
3721         /**
3722          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3723          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3724          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3725          * (could also be the top-right close button) and the text that was entered will be passed as the two
3726          * parameters to the callback.
3727          * @param {String} title The title bar text
3728          * @param {String} msg The message box body text
3729          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3730          * @param {Object} scope (optional) The scope of the callback function
3731          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3732          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3733          * @return {Roo.MessageBox} This message box
3734          */
3735         prompt : function(title, msg, fn, scope, multiline){
3736             this.show({
3737                 title : title,
3738                 msg : msg,
3739                 buttons: this.OKCANCEL,
3740                 fn: fn,
3741                 minWidth:250,
3742                 scope : scope,
3743                 prompt:true,
3744                 multiline: multiline,
3745                 modal : true
3746             });
3747             return this;
3748         },
3749
3750         /**
3751          * Button config that displays a single OK button
3752          * @type Object
3753          */
3754         OK : {ok:true},
3755         /**
3756          * Button config that displays Yes and No buttons
3757          * @type Object
3758          */
3759         YESNO : {yes:true, no:true},
3760         /**
3761          * Button config that displays OK and Cancel buttons
3762          * @type Object
3763          */
3764         OKCANCEL : {ok:true, cancel:true},
3765         /**
3766          * Button config that displays Yes, No and Cancel buttons
3767          * @type Object
3768          */
3769         YESNOCANCEL : {yes:true, no:true, cancel:true},
3770
3771         /**
3772          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3773          * @type Number
3774          */
3775         defaultTextHeight : 75,
3776         /**
3777          * The maximum width in pixels of the message box (defaults to 600)
3778          * @type Number
3779          */
3780         maxWidth : 600,
3781         /**
3782          * The minimum width in pixels of the message box (defaults to 100)
3783          * @type Number
3784          */
3785         minWidth : 100,
3786         /**
3787          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3788          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3789          * @type Number
3790          */
3791         minProgressWidth : 250,
3792         /**
3793          * An object containing the default button text strings that can be overriden for localized language support.
3794          * Supported properties are: ok, cancel, yes and no.
3795          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3796          * @type Object
3797          */
3798         buttonText : {
3799             ok : "OK",
3800             cancel : "Cancel",
3801             yes : "Yes",
3802             no : "No"
3803         }
3804     };
3805 }();
3806
3807 /**
3808  * Shorthand for {@link Roo.MessageBox}
3809  */
3810 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3811 Roo.Msg = Roo.Msg || Roo.MessageBox;
3812 /*
3813  * - LGPL
3814  *
3815  * navbar
3816  * 
3817  */
3818
3819 /**
3820  * @class Roo.bootstrap.Navbar
3821  * @extends Roo.bootstrap.Component
3822  * Bootstrap Navbar class
3823
3824  * @constructor
3825  * Create a new Navbar
3826  * @param {Object} config The config object
3827  */
3828
3829
3830 Roo.bootstrap.Navbar = function(config){
3831     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3832     this.addEvents({
3833         // raw events
3834         /**
3835          * @event beforetoggle
3836          * Fire before toggle the menu
3837          * @param {Roo.EventObject} e
3838          */
3839         "beforetoggle" : true
3840     });
3841 };
3842
3843 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3844     
3845     
3846    
3847     // private
3848     navItems : false,
3849     loadMask : false,
3850     
3851     
3852     getAutoCreate : function(){
3853         
3854         
3855         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3856         
3857     },
3858     
3859     initEvents :function ()
3860     {
3861         //Roo.log(this.el.select('.navbar-toggle',true));
3862         this.el.select('.navbar-toggle',true).on('click', function() {
3863             if(this.fireEvent('beforetoggle', this) !== false){
3864                 var ce = this.el.select('.navbar-collapse',true).first();
3865                 ce.toggleClass('in'); // old...
3866                 if (ce.hasClass('collapse')) {
3867                     // show it...
3868                     ce.removeClass('collapse');
3869                     ce.addClass('show');
3870                     var h = ce.getHeight();
3871                     Roo.log(h);
3872                     ce.removeClass('show');
3873                     // at this point we should be able to see it..
3874                     ce.addClass('collapsing');
3875                     
3876                     ce.setHeight(0); // resize it ...
3877                     ce.on('transitionend', function() {
3878                         Roo.log('done transition');
3879                         ce.removeClass('collapsing');
3880                         ce.addClass('show');
3881                         ce.removeClass('collapse');
3882
3883                         ce.dom.style.height = '';
3884                     }, this, { single: true} );
3885                     ce.setHeight(h);
3886                     
3887                 } else {
3888                     ce.setHeight(ce.getHeight());
3889                     ce.removeClass('show');
3890                     ce.addClass('collapsing');
3891                     
3892                     ce.on('transitionend', function() {
3893                         ce.dom.style.height = '';
3894                         ce.removeClass('collapsing');
3895                         ce.addClass('collapse');
3896                     }, this, { single: true} );
3897                     ce.setHeight(0);
3898                 }
3899             }
3900             
3901         }, this);
3902         
3903         var mark = {
3904             tag: "div",
3905             cls:"x-dlg-mask"
3906         };
3907         
3908         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3909         
3910         var size = this.el.getSize();
3911         this.maskEl.setSize(size.width, size.height);
3912         this.maskEl.enableDisplayMode("block");
3913         this.maskEl.hide();
3914         
3915         if(this.loadMask){
3916             this.maskEl.show();
3917         }
3918     },
3919     
3920     
3921     getChildContainer : function()
3922     {
3923         if (this.el.select('.collapse').getCount()) {
3924             return this.el.select('.collapse',true).first();
3925         }
3926         
3927         return this.el;
3928     },
3929     
3930     mask : function()
3931     {
3932         this.maskEl.show();
3933     },
3934     
3935     unmask : function()
3936     {
3937         this.maskEl.hide();
3938     } 
3939     
3940     
3941     
3942     
3943 });
3944
3945
3946
3947  
3948
3949  /*
3950  * - LGPL
3951  *
3952  * navbar
3953  * 
3954  */
3955
3956 /**
3957  * @class Roo.bootstrap.NavSimplebar
3958  * @extends Roo.bootstrap.Navbar
3959  * Bootstrap Sidebar class
3960  *
3961  * @cfg {Boolean} inverse is inverted color
3962  * 
3963  * @cfg {String} type (nav | pills | tabs)
3964  * @cfg {Boolean} arrangement stacked | justified
3965  * @cfg {String} align (left | right) alignment
3966  * 
3967  * @cfg {Boolean} main (true|false) main nav bar? default false
3968  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3969  * 
3970  * @cfg {String} tag (header|footer|nav|div) default is nav 
3971
3972  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3973  * 
3974  * 
3975  * @constructor
3976  * Create a new Sidebar
3977  * @param {Object} config The config object
3978  */
3979
3980
3981 Roo.bootstrap.NavSimplebar = function(config){
3982     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3983 };
3984
3985 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3986     
3987     inverse: false,
3988     
3989     type: false,
3990     arrangement: '',
3991     align : false,
3992     
3993     weight : 'light',
3994     
3995     main : false,
3996     
3997     
3998     tag : false,
3999     
4000     
4001     getAutoCreate : function(){
4002         
4003         
4004         var cfg = {
4005             tag : this.tag || 'div',
4006             cls : 'navbar navbar-expand-lg'
4007         };
4008         if (['light','white'].indexOf(this.weight) > -1) {
4009             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4010         }
4011         cfg.cls += ' bg-' + this.weight;
4012         
4013           
4014         
4015         cfg.cn = [
4016             {
4017                 cls: 'nav',
4018                 tag : 'ul'
4019             }
4020         ];
4021         
4022          
4023         this.type = this.type || 'nav';
4024         if (['tabs','pills'].indexOf(this.type)!==-1) {
4025             cfg.cn[0].cls += ' nav-' + this.type
4026         
4027         
4028         } else {
4029             if (this.type!=='nav') {
4030                 Roo.log('nav type must be nav/tabs/pills')
4031             }
4032             cfg.cn[0].cls += ' navbar-nav'
4033         }
4034         
4035         
4036         
4037         
4038         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4039             cfg.cn[0].cls += ' nav-' + this.arrangement;
4040         }
4041         
4042         
4043         if (this.align === 'right') {
4044             cfg.cn[0].cls += ' navbar-right';
4045         }
4046         
4047         if (this.inverse) {
4048             cfg.cls += ' navbar-inverse';
4049             
4050         }
4051         
4052         
4053         return cfg;
4054     
4055         
4056     }
4057     
4058     
4059     
4060 });
4061
4062
4063
4064  
4065
4066  
4067        /*
4068  * - LGPL
4069  *
4070  * navbar
4071  * navbar-fixed-top
4072  * navbar-expand-md  fixed-top 
4073  */
4074
4075 /**
4076  * @class Roo.bootstrap.NavHeaderbar
4077  * @extends Roo.bootstrap.NavSimplebar
4078  * Bootstrap Sidebar class
4079  *
4080  * @cfg {String} brand what is brand
4081  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4082  * @cfg {String} brand_href href of the brand
4083  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4084  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4085  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4086  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4087  * 
4088  * @constructor
4089  * Create a new Sidebar
4090  * @param {Object} config The config object
4091  */
4092
4093
4094 Roo.bootstrap.NavHeaderbar = function(config){
4095     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4096       
4097 };
4098
4099 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4100     
4101     position: '',
4102     brand: '',
4103     brand_href: false,
4104     srButton : true,
4105     autohide : false,
4106     desktopCenter : false,
4107    
4108     
4109     getAutoCreate : function(){
4110         
4111         var   cfg = {
4112             tag: this.nav || 'nav',
4113             cls: 'navbar navbar-expand-md',
4114             role: 'navigation',
4115             cn: []
4116         };
4117         
4118         var cn = cfg.cn;
4119         if (this.desktopCenter) {
4120             cn.push({cls : 'container', cn : []});
4121             cn = cn[0].cn;
4122         }
4123         
4124         if(this.srButton){
4125             var btn = {
4126                 tag: 'button',
4127                 type: 'button',
4128                 cls: 'navbar-toggle navbar-toggler',
4129                 'data-toggle': 'collapse',
4130                 cn: [
4131                     {
4132                         tag: 'span',
4133                         cls: 'sr-only',
4134                         html: 'Toggle navigation'
4135                     },
4136                     {
4137                         tag: 'span',
4138                         cls: 'icon-bar navbar-toggler-icon'
4139                     },
4140                     {
4141                         tag: 'span',
4142                         cls: 'icon-bar'
4143                     },
4144                     {
4145                         tag: 'span',
4146                         cls: 'icon-bar'
4147                     }
4148                 ]
4149             };
4150             
4151             cn.push( Roo.bootstrap.version == 4 ? btn : {
4152                 tag: 'div',
4153                 cls: 'navbar-header',
4154                 cn: [
4155                     btn
4156                 ]
4157             });
4158         }
4159         
4160         cn.push({
4161             tag: 'div',
4162             cls: 'collapse navbar-collapse',
4163             cn : []
4164         });
4165         
4166         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4167         
4168         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4169             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4170             
4171             // tag can override this..
4172             
4173             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4174         }
4175         
4176         if (this.brand !== '') {
4177             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4178             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4179                 tag: 'a',
4180                 href: this.brand_href ? this.brand_href : '#',
4181                 cls: 'navbar-brand',
4182                 cn: [
4183                 this.brand
4184                 ]
4185             });
4186         }
4187         
4188         if(this.main){
4189             cfg.cls += ' main-nav';
4190         }
4191         
4192         
4193         return cfg;
4194
4195         
4196     },
4197     getHeaderChildContainer : function()
4198     {
4199         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4200             return this.el.select('.navbar-header',true).first();
4201         }
4202         
4203         return this.getChildContainer();
4204     },
4205     
4206     
4207     initEvents : function()
4208     {
4209         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4210         
4211         if (this.autohide) {
4212             
4213             var prevScroll = 0;
4214             var ft = this.el;
4215             
4216             Roo.get(document).on('scroll',function(e) {
4217                 var ns = Roo.get(document).getScroll().top;
4218                 var os = prevScroll;
4219                 prevScroll = ns;
4220                 
4221                 if(ns > os){
4222                     ft.removeClass('slideDown');
4223                     ft.addClass('slideUp');
4224                     return;
4225                 }
4226                 ft.removeClass('slideUp');
4227                 ft.addClass('slideDown');
4228                  
4229               
4230           },this);
4231         }
4232     }    
4233     
4234 });
4235
4236
4237
4238  
4239
4240  /*
4241  * - LGPL
4242  *
4243  * navbar
4244  * 
4245  */
4246
4247 /**
4248  * @class Roo.bootstrap.NavSidebar
4249  * @extends Roo.bootstrap.Navbar
4250  * Bootstrap Sidebar class
4251  * 
4252  * @constructor
4253  * Create a new Sidebar
4254  * @param {Object} config The config object
4255  */
4256
4257
4258 Roo.bootstrap.NavSidebar = function(config){
4259     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4260 };
4261
4262 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4263     
4264     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4265     
4266     getAutoCreate : function(){
4267         
4268         
4269         return  {
4270             tag: 'div',
4271             cls: 'sidebar sidebar-nav'
4272         };
4273     
4274         
4275     }
4276     
4277     
4278     
4279 });
4280
4281
4282
4283  
4284
4285  /*
4286  * - LGPL
4287  *
4288  * nav group
4289  * 
4290  */
4291
4292 /**
4293  * @class Roo.bootstrap.NavGroup
4294  * @extends Roo.bootstrap.Component
4295  * Bootstrap NavGroup class
4296  * @cfg {String} align (left|right)
4297  * @cfg {Boolean} inverse
4298  * @cfg {String} type (nav|pills|tab) default nav
4299  * @cfg {String} navId - reference Id for navbar.
4300
4301  * 
4302  * @constructor
4303  * Create a new nav group
4304  * @param {Object} config The config object
4305  */
4306
4307 Roo.bootstrap.NavGroup = function(config){
4308     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4309     this.navItems = [];
4310    
4311     Roo.bootstrap.NavGroup.register(this);
4312      this.addEvents({
4313         /**
4314              * @event changed
4315              * Fires when the active item changes
4316              * @param {Roo.bootstrap.NavGroup} this
4317              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4318              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4319          */
4320         'changed': true
4321      });
4322     
4323 };
4324
4325 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4326     
4327     align: '',
4328     inverse: false,
4329     form: false,
4330     type: 'nav',
4331     navId : '',
4332     // private
4333     
4334     navItems : false, 
4335     
4336     getAutoCreate : function()
4337     {
4338         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4339         
4340         cfg = {
4341             tag : 'ul',
4342             cls: 'nav' 
4343         };
4344         
4345         if (['tabs','pills'].indexOf(this.type)!==-1) {
4346             cfg.cls += ' nav-' + this.type
4347         } else {
4348             if (this.type!=='nav') {
4349                 Roo.log('nav type must be nav/tabs/pills')
4350             }
4351             cfg.cls += ' navbar-nav'
4352         }
4353         
4354         if (this.parent() && this.parent().sidebar) {
4355             cfg = {
4356                 tag: 'ul',
4357                 cls: 'dashboard-menu sidebar-menu'
4358             };
4359             
4360             return cfg;
4361         }
4362         
4363         if (this.form === true) {
4364             cfg = {
4365                 tag: 'form',
4366                 cls: 'navbar-form'
4367             };
4368             
4369             if (this.align === 'right') {
4370                 cfg.cls += ' navbar-right ml-md-auto';
4371             } else {
4372                 cfg.cls += ' navbar-left';
4373             }
4374         }
4375         
4376         if (this.align === 'right') {
4377             cfg.cls += ' navbar-right ml-md-auto';
4378         } else {
4379             cfg.cls += ' mr-auto';
4380         }
4381         
4382         if (this.inverse) {
4383             cfg.cls += ' navbar-inverse';
4384             
4385         }
4386         
4387         
4388         return cfg;
4389     },
4390     /**
4391     * sets the active Navigation item
4392     * @param {Roo.bootstrap.NavItem} the new current navitem
4393     */
4394     setActiveItem : function(item)
4395     {
4396         var prev = false;
4397         Roo.each(this.navItems, function(v){
4398             if (v == item) {
4399                 return ;
4400             }
4401             if (v.isActive()) {
4402                 v.setActive(false, true);
4403                 prev = v;
4404                 
4405             }
4406             
4407         });
4408
4409         item.setActive(true, true);
4410         this.fireEvent('changed', this, item, prev);
4411         
4412         
4413     },
4414     /**
4415     * gets the active Navigation item
4416     * @return {Roo.bootstrap.NavItem} the current navitem
4417     */
4418     getActive : function()
4419     {
4420         
4421         var prev = false;
4422         Roo.each(this.navItems, function(v){
4423             
4424             if (v.isActive()) {
4425                 prev = v;
4426                 
4427             }
4428             
4429         });
4430         return prev;
4431     },
4432     
4433     indexOfNav : function()
4434     {
4435         
4436         var prev = false;
4437         Roo.each(this.navItems, function(v,i){
4438             
4439             if (v.isActive()) {
4440                 prev = i;
4441                 
4442             }
4443             
4444         });
4445         return prev;
4446     },
4447     /**
4448     * adds a Navigation item
4449     * @param {Roo.bootstrap.NavItem} the navitem to add
4450     */
4451     addItem : function(cfg)
4452     {
4453         var cn = new Roo.bootstrap.NavItem(cfg);
4454         this.register(cn);
4455         cn.parentId = this.id;
4456         cn.onRender(this.el, null);
4457         return cn;
4458     },
4459     /**
4460     * register a Navigation item
4461     * @param {Roo.bootstrap.NavItem} the navitem to add
4462     */
4463     register : function(item)
4464     {
4465         this.navItems.push( item);
4466         item.navId = this.navId;
4467     
4468     },
4469     
4470     /**
4471     * clear all the Navigation item
4472     */
4473    
4474     clearAll : function()
4475     {
4476         this.navItems = [];
4477         this.el.dom.innerHTML = '';
4478     },
4479     
4480     getNavItem: function(tabId)
4481     {
4482         var ret = false;
4483         Roo.each(this.navItems, function(e) {
4484             if (e.tabId == tabId) {
4485                ret =  e;
4486                return false;
4487             }
4488             return true;
4489             
4490         });
4491         return ret;
4492     },
4493     
4494     setActiveNext : function()
4495     {
4496         var i = this.indexOfNav(this.getActive());
4497         if (i > this.navItems.length) {
4498             return;
4499         }
4500         this.setActiveItem(this.navItems[i+1]);
4501     },
4502     setActivePrev : function()
4503     {
4504         var i = this.indexOfNav(this.getActive());
4505         if (i  < 1) {
4506             return;
4507         }
4508         this.setActiveItem(this.navItems[i-1]);
4509     },
4510     clearWasActive : function(except) {
4511         Roo.each(this.navItems, function(e) {
4512             if (e.tabId != except.tabId && e.was_active) {
4513                e.was_active = false;
4514                return false;
4515             }
4516             return true;
4517             
4518         });
4519     },
4520     getWasActive : function ()
4521     {
4522         var r = false;
4523         Roo.each(this.navItems, function(e) {
4524             if (e.was_active) {
4525                r = e;
4526                return false;
4527             }
4528             return true;
4529             
4530         });
4531         return r;
4532     }
4533     
4534     
4535 });
4536
4537  
4538 Roo.apply(Roo.bootstrap.NavGroup, {
4539     
4540     groups: {},
4541      /**
4542     * register a Navigation Group
4543     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4544     */
4545     register : function(navgrp)
4546     {
4547         this.groups[navgrp.navId] = navgrp;
4548         
4549     },
4550     /**
4551     * fetch a Navigation Group based on the navigation ID
4552     * @param {string} the navgroup to add
4553     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4554     */
4555     get: function(navId) {
4556         if (typeof(this.groups[navId]) == 'undefined') {
4557             return false;
4558             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4559         }
4560         return this.groups[navId] ;
4561     }
4562     
4563     
4564     
4565 });
4566
4567  /*
4568  * - LGPL
4569  *
4570  * row
4571  * 
4572  */
4573
4574 /**
4575  * @class Roo.bootstrap.NavItem
4576  * @extends Roo.bootstrap.Component
4577  * Bootstrap Navbar.NavItem class
4578  * @cfg {String} href  link to
4579  * @cfg {String} html content of button
4580  * @cfg {String} badge text inside badge
4581  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4582  * @cfg {String} glyphicon DEPRICATED - use fa
4583  * @cfg {String} icon DEPRICATED - use fa
4584  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4585  * @cfg {Boolean} active Is item active
4586  * @cfg {Boolean} disabled Is item disabled
4587  
4588  * @cfg {Boolean} preventDefault (true | false) default false
4589  * @cfg {String} tabId the tab that this item activates.
4590  * @cfg {String} tagtype (a|span) render as a href or span?
4591  * @cfg {Boolean} animateRef (true|false) link to element default false  
4592   
4593  * @constructor
4594  * Create a new Navbar Item
4595  * @param {Object} config The config object
4596  */
4597 Roo.bootstrap.NavItem = function(config){
4598     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4599     this.addEvents({
4600         // raw events
4601         /**
4602          * @event click
4603          * The raw click event for the entire grid.
4604          * @param {Roo.EventObject} e
4605          */
4606         "click" : true,
4607          /**
4608             * @event changed
4609             * Fires when the active item active state changes
4610             * @param {Roo.bootstrap.NavItem} this
4611             * @param {boolean} state the new state
4612              
4613          */
4614         'changed': true,
4615         /**
4616             * @event scrollto
4617             * Fires when scroll to element
4618             * @param {Roo.bootstrap.NavItem} this
4619             * @param {Object} options
4620             * @param {Roo.EventObject} e
4621              
4622          */
4623         'scrollto': true
4624     });
4625    
4626 };
4627
4628 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4629     
4630     href: false,
4631     html: '',
4632     badge: '',
4633     icon: false,
4634     fa : false,
4635     glyphicon: false,
4636     active: false,
4637     preventDefault : false,
4638     tabId : false,
4639     tagtype : 'a',
4640     disabled : false,
4641     animateRef : false,
4642     was_active : false,
4643     
4644     getAutoCreate : function(){
4645          
4646         var cfg = {
4647             tag: 'li',
4648             cls: 'nav-item'
4649             
4650         };
4651         
4652         if (this.active) {
4653             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4654         }
4655         if (this.disabled) {
4656             cfg.cls += ' disabled';
4657         }
4658         
4659         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4660             cfg.cn = [
4661                 {
4662                     tag: this.tagtype,
4663                     href : this.href || "#",
4664                     html: this.html || ''
4665                 }
4666             ];
4667             if (this.tagtype == 'a') {
4668                 cfg.cn[0].cls = 'nav-link';
4669             }
4670             if (this.icon) {
4671                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4672             }
4673             if (this.fa) {
4674                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4675             }
4676             if(this.glyphicon) {
4677                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4678             }
4679             
4680             if (this.menu) {
4681                 
4682                 cfg.cn[0].html += " <span class='caret'></span>";
4683              
4684             }
4685             
4686             if (this.badge !== '') {
4687                  
4688                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4689             }
4690         }
4691         
4692         
4693         
4694         return cfg;
4695     },
4696     initEvents: function() 
4697     {
4698         if (typeof (this.menu) != 'undefined') {
4699             this.menu.parentType = this.xtype;
4700             this.menu.triggerEl = this.el;
4701             this.menu = this.addxtype(Roo.apply({}, this.menu));
4702         }
4703         
4704         this.el.select('a',true).on('click', this.onClick, this);
4705         
4706         if(this.tagtype == 'span'){
4707             this.el.select('span',true).on('click', this.onClick, this);
4708         }
4709        
4710         // at this point parent should be available..
4711         this.parent().register(this);
4712     },
4713     
4714     onClick : function(e)
4715     {
4716         if (e.getTarget('.dropdown-menu-item')) {
4717             // did you click on a menu itemm.... - then don't trigger onclick..
4718             return;
4719         }
4720         
4721         if(
4722                 this.preventDefault || 
4723                 this.href == '#' 
4724         ){
4725             Roo.log("NavItem - prevent Default?");
4726             e.preventDefault();
4727         }
4728         
4729         if (this.disabled) {
4730             return;
4731         }
4732         
4733         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4734         if (tg && tg.transition) {
4735             Roo.log("waiting for the transitionend");
4736             return;
4737         }
4738         
4739         
4740         
4741         //Roo.log("fire event clicked");
4742         if(this.fireEvent('click', this, e) === false){
4743             return;
4744         };
4745         
4746         if(this.tagtype == 'span'){
4747             return;
4748         }
4749         
4750         //Roo.log(this.href);
4751         var ael = this.el.select('a',true).first();
4752         //Roo.log(ael);
4753         
4754         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4755             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4756             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4757                 return; // ignore... - it's a 'hash' to another page.
4758             }
4759             Roo.log("NavItem - prevent Default?");
4760             e.preventDefault();
4761             this.scrollToElement(e);
4762         }
4763         
4764         
4765         var p =  this.parent();
4766    
4767         if (['tabs','pills'].indexOf(p.type)!==-1) {
4768             if (typeof(p.setActiveItem) !== 'undefined') {
4769                 p.setActiveItem(this);
4770             }
4771         }
4772         
4773         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4774         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4775             // remove the collapsed menu expand...
4776             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4777         }
4778     },
4779     
4780     isActive: function () {
4781         return this.active
4782     },
4783     setActive : function(state, fire, is_was_active)
4784     {
4785         if (this.active && !state && this.navId) {
4786             this.was_active = true;
4787             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4788             if (nv) {
4789                 nv.clearWasActive(this);
4790             }
4791             
4792         }
4793         this.active = state;
4794         
4795         if (!state ) {
4796             this.el.removeClass('active');
4797         } else if (!this.el.hasClass('active')) {
4798             this.el.addClass('active');
4799         }
4800         if (fire) {
4801             this.fireEvent('changed', this, state);
4802         }
4803         
4804         // show a panel if it's registered and related..
4805         
4806         if (!this.navId || !this.tabId || !state || is_was_active) {
4807             return;
4808         }
4809         
4810         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4811         if (!tg) {
4812             return;
4813         }
4814         var pan = tg.getPanelByName(this.tabId);
4815         if (!pan) {
4816             return;
4817         }
4818         // if we can not flip to new panel - go back to old nav highlight..
4819         if (false == tg.showPanel(pan)) {
4820             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4821             if (nv) {
4822                 var onav = nv.getWasActive();
4823                 if (onav) {
4824                     onav.setActive(true, false, true);
4825                 }
4826             }
4827             
4828         }
4829         
4830         
4831         
4832     },
4833      // this should not be here...
4834     setDisabled : function(state)
4835     {
4836         this.disabled = state;
4837         if (!state ) {
4838             this.el.removeClass('disabled');
4839         } else if (!this.el.hasClass('disabled')) {
4840             this.el.addClass('disabled');
4841         }
4842         
4843     },
4844     
4845     /**
4846      * Fetch the element to display the tooltip on.
4847      * @return {Roo.Element} defaults to this.el
4848      */
4849     tooltipEl : function()
4850     {
4851         return this.el.select('' + this.tagtype + '', true).first();
4852     },
4853     
4854     scrollToElement : function(e)
4855     {
4856         var c = document.body;
4857         
4858         /*
4859          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4860          */
4861         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4862             c = document.documentElement;
4863         }
4864         
4865         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4866         
4867         if(!target){
4868             return;
4869         }
4870
4871         var o = target.calcOffsetsTo(c);
4872         
4873         var options = {
4874             target : target,
4875             value : o[1]
4876         };
4877         
4878         this.fireEvent('scrollto', this, options, e);
4879         
4880         Roo.get(c).scrollTo('top', options.value, true);
4881         
4882         return;
4883     }
4884 });
4885  
4886
4887  /*
4888  * - LGPL
4889  *
4890  * sidebar item
4891  *
4892  *  li
4893  *    <span> icon </span>
4894  *    <span> text </span>
4895  *    <span>badge </span>
4896  */
4897
4898 /**
4899  * @class Roo.bootstrap.NavSidebarItem
4900  * @extends Roo.bootstrap.NavItem
4901  * Bootstrap Navbar.NavSidebarItem class
4902  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4903  * {Boolean} open is the menu open
4904  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4905  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4906  * {String} buttonSize (sm|md|lg)the extra classes for the button
4907  * {Boolean} showArrow show arrow next to the text (default true)
4908  * @constructor
4909  * Create a new Navbar Button
4910  * @param {Object} config The config object
4911  */
4912 Roo.bootstrap.NavSidebarItem = function(config){
4913     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4914     this.addEvents({
4915         // raw events
4916         /**
4917          * @event click
4918          * The raw click event for the entire grid.
4919          * @param {Roo.EventObject} e
4920          */
4921         "click" : true,
4922          /**
4923             * @event changed
4924             * Fires when the active item active state changes
4925             * @param {Roo.bootstrap.NavSidebarItem} this
4926             * @param {boolean} state the new state
4927              
4928          */
4929         'changed': true
4930     });
4931    
4932 };
4933
4934 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4935     
4936     badgeWeight : 'default',
4937     
4938     open: false,
4939     
4940     buttonView : false,
4941     
4942     buttonWeight : 'default',
4943     
4944     buttonSize : 'md',
4945     
4946     showArrow : true,
4947     
4948     getAutoCreate : function(){
4949         
4950         
4951         var a = {
4952                 tag: 'a',
4953                 href : this.href || '#',
4954                 cls: '',
4955                 html : '',
4956                 cn : []
4957         };
4958         
4959         if(this.buttonView){
4960             a = {
4961                 tag: 'button',
4962                 href : this.href || '#',
4963                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4964                 html : this.html,
4965                 cn : []
4966             };
4967         }
4968         
4969         var cfg = {
4970             tag: 'li',
4971             cls: '',
4972             cn: [ a ]
4973         };
4974         
4975         if (this.active) {
4976             cfg.cls += ' active';
4977         }
4978         
4979         if (this.disabled) {
4980             cfg.cls += ' disabled';
4981         }
4982         if (this.open) {
4983             cfg.cls += ' open x-open';
4984         }
4985         // left icon..
4986         if (this.glyphicon || this.icon) {
4987             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4988             a.cn.push({ tag : 'i', cls : c }) ;
4989         }
4990         
4991         if(!this.buttonView){
4992             var span = {
4993                 tag: 'span',
4994                 html : this.html || ''
4995             };
4996
4997             a.cn.push(span);
4998             
4999         }
5000         
5001         if (this.badge !== '') {
5002             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5003         }
5004         
5005         if (this.menu) {
5006             
5007             if(this.showArrow){
5008                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5009             }
5010             
5011             a.cls += ' dropdown-toggle treeview' ;
5012         }
5013         
5014         return cfg;
5015     },
5016     
5017     initEvents : function()
5018     { 
5019         if (typeof (this.menu) != 'undefined') {
5020             this.menu.parentType = this.xtype;
5021             this.menu.triggerEl = this.el;
5022             this.menu = this.addxtype(Roo.apply({}, this.menu));
5023         }
5024         
5025         this.el.on('click', this.onClick, this);
5026         
5027         if(this.badge !== ''){
5028             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5029         }
5030         
5031     },
5032     
5033     onClick : function(e)
5034     {
5035         if(this.disabled){
5036             e.preventDefault();
5037             return;
5038         }
5039         
5040         if(this.preventDefault){
5041             e.preventDefault();
5042         }
5043         
5044         this.fireEvent('click', this);
5045     },
5046     
5047     disable : function()
5048     {
5049         this.setDisabled(true);
5050     },
5051     
5052     enable : function()
5053     {
5054         this.setDisabled(false);
5055     },
5056     
5057     setDisabled : function(state)
5058     {
5059         if(this.disabled == state){
5060             return;
5061         }
5062         
5063         this.disabled = state;
5064         
5065         if (state) {
5066             this.el.addClass('disabled');
5067             return;
5068         }
5069         
5070         this.el.removeClass('disabled');
5071         
5072         return;
5073     },
5074     
5075     setActive : function(state)
5076     {
5077         if(this.active == state){
5078             return;
5079         }
5080         
5081         this.active = state;
5082         
5083         if (state) {
5084             this.el.addClass('active');
5085             return;
5086         }
5087         
5088         this.el.removeClass('active');
5089         
5090         return;
5091     },
5092     
5093     isActive: function () 
5094     {
5095         return this.active;
5096     },
5097     
5098     setBadge : function(str)
5099     {
5100         if(!this.badgeEl){
5101             return;
5102         }
5103         
5104         this.badgeEl.dom.innerHTML = str;
5105     }
5106     
5107    
5108      
5109  
5110 });
5111  
5112
5113  /*
5114  * - LGPL
5115  *
5116  * row
5117  * 
5118  */
5119
5120 /**
5121  * @class Roo.bootstrap.Row
5122  * @extends Roo.bootstrap.Component
5123  * Bootstrap Row class (contains columns...)
5124  * 
5125  * @constructor
5126  * Create a new Row
5127  * @param {Object} config The config object
5128  */
5129
5130 Roo.bootstrap.Row = function(config){
5131     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5132 };
5133
5134 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5135     
5136     getAutoCreate : function(){
5137        return {
5138             cls: 'row clearfix'
5139        };
5140     }
5141     
5142     
5143 });
5144
5145  
5146
5147  /*
5148  * - LGPL
5149  *
5150  * element
5151  * 
5152  */
5153
5154 /**
5155  * @class Roo.bootstrap.Element
5156  * @extends Roo.bootstrap.Component
5157  * Bootstrap Element class
5158  * @cfg {String} html contents of the element
5159  * @cfg {String} tag tag of the element
5160  * @cfg {String} cls class of the element
5161  * @cfg {Boolean} preventDefault (true|false) default false
5162  * @cfg {Boolean} clickable (true|false) default false
5163  * 
5164  * @constructor
5165  * Create a new Element
5166  * @param {Object} config The config object
5167  */
5168
5169 Roo.bootstrap.Element = function(config){
5170     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5171     
5172     this.addEvents({
5173         // raw events
5174         /**
5175          * @event click
5176          * When a element is chick
5177          * @param {Roo.bootstrap.Element} this
5178          * @param {Roo.EventObject} e
5179          */
5180         "click" : true
5181     });
5182 };
5183
5184 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5185     
5186     tag: 'div',
5187     cls: '',
5188     html: '',
5189     preventDefault: false, 
5190     clickable: false,
5191     
5192     getAutoCreate : function(){
5193         
5194         var cfg = {
5195             tag: this.tag,
5196             // cls: this.cls, double assign in parent class Component.js :: onRender
5197             html: this.html
5198         };
5199         
5200         return cfg;
5201     },
5202     
5203     initEvents: function() 
5204     {
5205         Roo.bootstrap.Element.superclass.initEvents.call(this);
5206         
5207         if(this.clickable){
5208             this.el.on('click', this.onClick, this);
5209         }
5210         
5211     },
5212     
5213     onClick : function(e)
5214     {
5215         if(this.preventDefault){
5216             e.preventDefault();
5217         }
5218         
5219         this.fireEvent('click', this, e);
5220     },
5221     
5222     getValue : function()
5223     {
5224         return this.el.dom.innerHTML;
5225     },
5226     
5227     setValue : function(value)
5228     {
5229         this.el.dom.innerHTML = value;
5230     }
5231    
5232 });
5233
5234  
5235
5236  /*
5237  * - LGPL
5238  *
5239  * pagination
5240  * 
5241  */
5242
5243 /**
5244  * @class Roo.bootstrap.Pagination
5245  * @extends Roo.bootstrap.Component
5246  * Bootstrap Pagination class
5247  * @cfg {String} size xs | sm | md | lg
5248  * @cfg {Boolean} inverse false | true
5249  * 
5250  * @constructor
5251  * Create a new Pagination
5252  * @param {Object} config The config object
5253  */
5254
5255 Roo.bootstrap.Pagination = function(config){
5256     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5257 };
5258
5259 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5260     
5261     cls: false,
5262     size: false,
5263     inverse: false,
5264     
5265     getAutoCreate : function(){
5266         var cfg = {
5267             tag: 'ul',
5268                 cls: 'pagination'
5269         };
5270         if (this.inverse) {
5271             cfg.cls += ' inverse';
5272         }
5273         if (this.html) {
5274             cfg.html=this.html;
5275         }
5276         if (this.cls) {
5277             cfg.cls += " " + this.cls;
5278         }
5279         return cfg;
5280     }
5281    
5282 });
5283
5284  
5285
5286  /*
5287  * - LGPL
5288  *
5289  * Pagination item
5290  * 
5291  */
5292
5293
5294 /**
5295  * @class Roo.bootstrap.PaginationItem
5296  * @extends Roo.bootstrap.Component
5297  * Bootstrap PaginationItem class
5298  * @cfg {String} html text
5299  * @cfg {String} href the link
5300  * @cfg {Boolean} preventDefault (true | false) default true
5301  * @cfg {Boolean} active (true | false) default false
5302  * @cfg {Boolean} disabled default false
5303  * 
5304  * 
5305  * @constructor
5306  * Create a new PaginationItem
5307  * @param {Object} config The config object
5308  */
5309
5310
5311 Roo.bootstrap.PaginationItem = function(config){
5312     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5313     this.addEvents({
5314         // raw events
5315         /**
5316          * @event click
5317          * The raw click event for the entire grid.
5318          * @param {Roo.EventObject} e
5319          */
5320         "click" : true
5321     });
5322 };
5323
5324 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5325     
5326     href : false,
5327     html : false,
5328     preventDefault: true,
5329     active : false,
5330     cls : false,
5331     disabled: false,
5332     
5333     getAutoCreate : function(){
5334         var cfg= {
5335             tag: 'li',
5336             cn: [
5337                 {
5338                     tag : 'a',
5339                     href : this.href ? this.href : '#',
5340                     html : this.html ? this.html : ''
5341                 }
5342             ]
5343         };
5344         
5345         if(this.cls){
5346             cfg.cls = this.cls;
5347         }
5348         
5349         if(this.disabled){
5350             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5351         }
5352         
5353         if(this.active){
5354             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5355         }
5356         
5357         return cfg;
5358     },
5359     
5360     initEvents: function() {
5361         
5362         this.el.on('click', this.onClick, this);
5363         
5364     },
5365     onClick : function(e)
5366     {
5367         Roo.log('PaginationItem on click ');
5368         if(this.preventDefault){
5369             e.preventDefault();
5370         }
5371         
5372         if(this.disabled){
5373             return;
5374         }
5375         
5376         this.fireEvent('click', this, e);
5377     }
5378    
5379 });
5380
5381  
5382
5383  /*
5384  * - LGPL
5385  *
5386  * slider
5387  * 
5388  */
5389
5390
5391 /**
5392  * @class Roo.bootstrap.Slider
5393  * @extends Roo.bootstrap.Component
5394  * Bootstrap Slider class
5395  *    
5396  * @constructor
5397  * Create a new Slider
5398  * @param {Object} config The config object
5399  */
5400
5401 Roo.bootstrap.Slider = function(config){
5402     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5403 };
5404
5405 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5406     
5407     getAutoCreate : function(){
5408         
5409         var cfg = {
5410             tag: 'div',
5411             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5412             cn: [
5413                 {
5414                     tag: 'a',
5415                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5416                 }
5417             ]
5418         };
5419         
5420         return cfg;
5421     }
5422    
5423 });
5424
5425  /*
5426  * Based on:
5427  * Ext JS Library 1.1.1
5428  * Copyright(c) 2006-2007, Ext JS, LLC.
5429  *
5430  * Originally Released Under LGPL - original licence link has changed is not relivant.
5431  *
5432  * Fork - LGPL
5433  * <script type="text/javascript">
5434  */
5435  
5436
5437 /**
5438  * @class Roo.grid.ColumnModel
5439  * @extends Roo.util.Observable
5440  * This is the default implementation of a ColumnModel used by the Grid. It defines
5441  * the columns in the grid.
5442  * <br>Usage:<br>
5443  <pre><code>
5444  var colModel = new Roo.grid.ColumnModel([
5445         {header: "Ticker", width: 60, sortable: true, locked: true},
5446         {header: "Company Name", width: 150, sortable: true},
5447         {header: "Market Cap.", width: 100, sortable: true},
5448         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5449         {header: "Employees", width: 100, sortable: true, resizable: false}
5450  ]);
5451  </code></pre>
5452  * <p>
5453  
5454  * The config options listed for this class are options which may appear in each
5455  * individual column definition.
5456  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5457  * @constructor
5458  * @param {Object} config An Array of column config objects. See this class's
5459  * config objects for details.
5460 */
5461 Roo.grid.ColumnModel = function(config){
5462         /**
5463      * The config passed into the constructor
5464      */
5465     this.config = config;
5466     this.lookup = {};
5467
5468     // if no id, create one
5469     // if the column does not have a dataIndex mapping,
5470     // map it to the order it is in the config
5471     for(var i = 0, len = config.length; i < len; i++){
5472         var c = config[i];
5473         if(typeof c.dataIndex == "undefined"){
5474             c.dataIndex = i;
5475         }
5476         if(typeof c.renderer == "string"){
5477             c.renderer = Roo.util.Format[c.renderer];
5478         }
5479         if(typeof c.id == "undefined"){
5480             c.id = Roo.id();
5481         }
5482         if(c.editor && c.editor.xtype){
5483             c.editor  = Roo.factory(c.editor, Roo.grid);
5484         }
5485         if(c.editor && c.editor.isFormField){
5486             c.editor = new Roo.grid.GridEditor(c.editor);
5487         }
5488         this.lookup[c.id] = c;
5489     }
5490
5491     /**
5492      * The width of columns which have no width specified (defaults to 100)
5493      * @type Number
5494      */
5495     this.defaultWidth = 100;
5496
5497     /**
5498      * Default sortable of columns which have no sortable specified (defaults to false)
5499      * @type Boolean
5500      */
5501     this.defaultSortable = false;
5502
5503     this.addEvents({
5504         /**
5505              * @event widthchange
5506              * Fires when the width of a column changes.
5507              * @param {ColumnModel} this
5508              * @param {Number} columnIndex The column index
5509              * @param {Number} newWidth The new width
5510              */
5511             "widthchange": true,
5512         /**
5513              * @event headerchange
5514              * Fires when the text of a header changes.
5515              * @param {ColumnModel} this
5516              * @param {Number} columnIndex The column index
5517              * @param {Number} newText The new header text
5518              */
5519             "headerchange": true,
5520         /**
5521              * @event hiddenchange
5522              * Fires when a column is hidden or "unhidden".
5523              * @param {ColumnModel} this
5524              * @param {Number} columnIndex The column index
5525              * @param {Boolean} hidden true if hidden, false otherwise
5526              */
5527             "hiddenchange": true,
5528             /**
5529          * @event columnmoved
5530          * Fires when a column is moved.
5531          * @param {ColumnModel} this
5532          * @param {Number} oldIndex
5533          * @param {Number} newIndex
5534          */
5535         "columnmoved" : true,
5536         /**
5537          * @event columlockchange
5538          * Fires when a column's locked state is changed
5539          * @param {ColumnModel} this
5540          * @param {Number} colIndex
5541          * @param {Boolean} locked true if locked
5542          */
5543         "columnlockchange" : true
5544     });
5545     Roo.grid.ColumnModel.superclass.constructor.call(this);
5546 };
5547 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5548     /**
5549      * @cfg {String} header The header text to display in the Grid view.
5550      */
5551     /**
5552      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5553      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5554      * specified, the column's index is used as an index into the Record's data Array.
5555      */
5556     /**
5557      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5558      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5559      */
5560     /**
5561      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5562      * Defaults to the value of the {@link #defaultSortable} property.
5563      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5564      */
5565     /**
5566      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5567      */
5568     /**
5569      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5570      */
5571     /**
5572      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5573      */
5574     /**
5575      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5576      */
5577     /**
5578      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5579      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5580      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5581      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5582      */
5583        /**
5584      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5585      */
5586     /**
5587      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5588      */
5589     /**
5590      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5591      */
5592     /**
5593      * @cfg {String} cursor (Optional)
5594      */
5595     /**
5596      * @cfg {String} tooltip (Optional)
5597      */
5598     /**
5599      * @cfg {Number} xs (Optional)
5600      */
5601     /**
5602      * @cfg {Number} sm (Optional)
5603      */
5604     /**
5605      * @cfg {Number} md (Optional)
5606      */
5607     /**
5608      * @cfg {Number} lg (Optional)
5609      */
5610     /**
5611      * Returns the id of the column at the specified index.
5612      * @param {Number} index The column index
5613      * @return {String} the id
5614      */
5615     getColumnId : function(index){
5616         return this.config[index].id;
5617     },
5618
5619     /**
5620      * Returns the column for a specified id.
5621      * @param {String} id The column id
5622      * @return {Object} the column
5623      */
5624     getColumnById : function(id){
5625         return this.lookup[id];
5626     },
5627
5628     
5629     /**
5630      * Returns the column for a specified dataIndex.
5631      * @param {String} dataIndex The column dataIndex
5632      * @return {Object|Boolean} the column or false if not found
5633      */
5634     getColumnByDataIndex: function(dataIndex){
5635         var index = this.findColumnIndex(dataIndex);
5636         return index > -1 ? this.config[index] : false;
5637     },
5638     
5639     /**
5640      * Returns the index for a specified column id.
5641      * @param {String} id The column id
5642      * @return {Number} the index, or -1 if not found
5643      */
5644     getIndexById : function(id){
5645         for(var i = 0, len = this.config.length; i < len; i++){
5646             if(this.config[i].id == id){
5647                 return i;
5648             }
5649         }
5650         return -1;
5651     },
5652     
5653     /**
5654      * Returns the index for a specified column dataIndex.
5655      * @param {String} dataIndex The column dataIndex
5656      * @return {Number} the index, or -1 if not found
5657      */
5658     
5659     findColumnIndex : function(dataIndex){
5660         for(var i = 0, len = this.config.length; i < len; i++){
5661             if(this.config[i].dataIndex == dataIndex){
5662                 return i;
5663             }
5664         }
5665         return -1;
5666     },
5667     
5668     
5669     moveColumn : function(oldIndex, newIndex){
5670         var c = this.config[oldIndex];
5671         this.config.splice(oldIndex, 1);
5672         this.config.splice(newIndex, 0, c);
5673         this.dataMap = null;
5674         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5675     },
5676
5677     isLocked : function(colIndex){
5678         return this.config[colIndex].locked === true;
5679     },
5680
5681     setLocked : function(colIndex, value, suppressEvent){
5682         if(this.isLocked(colIndex) == value){
5683             return;
5684         }
5685         this.config[colIndex].locked = value;
5686         if(!suppressEvent){
5687             this.fireEvent("columnlockchange", this, colIndex, value);
5688         }
5689     },
5690
5691     getTotalLockedWidth : function(){
5692         var totalWidth = 0;
5693         for(var i = 0; i < this.config.length; i++){
5694             if(this.isLocked(i) && !this.isHidden(i)){
5695                 this.totalWidth += this.getColumnWidth(i);
5696             }
5697         }
5698         return totalWidth;
5699     },
5700
5701     getLockedCount : function(){
5702         for(var i = 0, len = this.config.length; i < len; i++){
5703             if(!this.isLocked(i)){
5704                 return i;
5705             }
5706         }
5707         
5708         return this.config.length;
5709     },
5710
5711     /**
5712      * Returns the number of columns.
5713      * @return {Number}
5714      */
5715     getColumnCount : function(visibleOnly){
5716         if(visibleOnly === true){
5717             var c = 0;
5718             for(var i = 0, len = this.config.length; i < len; i++){
5719                 if(!this.isHidden(i)){
5720                     c++;
5721                 }
5722             }
5723             return c;
5724         }
5725         return this.config.length;
5726     },
5727
5728     /**
5729      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5730      * @param {Function} fn
5731      * @param {Object} scope (optional)
5732      * @return {Array} result
5733      */
5734     getColumnsBy : function(fn, scope){
5735         var r = [];
5736         for(var i = 0, len = this.config.length; i < len; i++){
5737             var c = this.config[i];
5738             if(fn.call(scope||this, c, i) === true){
5739                 r[r.length] = c;
5740             }
5741         }
5742         return r;
5743     },
5744
5745     /**
5746      * Returns true if the specified column is sortable.
5747      * @param {Number} col The column index
5748      * @return {Boolean}
5749      */
5750     isSortable : function(col){
5751         if(typeof this.config[col].sortable == "undefined"){
5752             return this.defaultSortable;
5753         }
5754         return this.config[col].sortable;
5755     },
5756
5757     /**
5758      * Returns the rendering (formatting) function defined for the column.
5759      * @param {Number} col The column index.
5760      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5761      */
5762     getRenderer : function(col){
5763         if(!this.config[col].renderer){
5764             return Roo.grid.ColumnModel.defaultRenderer;
5765         }
5766         return this.config[col].renderer;
5767     },
5768
5769     /**
5770      * Sets the rendering (formatting) function for a column.
5771      * @param {Number} col The column index
5772      * @param {Function} fn The function to use to process the cell's raw data
5773      * to return HTML markup for the grid view. The render function is called with
5774      * the following parameters:<ul>
5775      * <li>Data value.</li>
5776      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5777      * <li>css A CSS style string to apply to the table cell.</li>
5778      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5779      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5780      * <li>Row index</li>
5781      * <li>Column index</li>
5782      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5783      */
5784     setRenderer : function(col, fn){
5785         this.config[col].renderer = fn;
5786     },
5787
5788     /**
5789      * Returns the width for the specified column.
5790      * @param {Number} col The column index
5791      * @return {Number}
5792      */
5793     getColumnWidth : function(col){
5794         return this.config[col].width * 1 || this.defaultWidth;
5795     },
5796
5797     /**
5798      * Sets the width for a column.
5799      * @param {Number} col The column index
5800      * @param {Number} width The new width
5801      */
5802     setColumnWidth : function(col, width, suppressEvent){
5803         this.config[col].width = width;
5804         this.totalWidth = null;
5805         if(!suppressEvent){
5806              this.fireEvent("widthchange", this, col, width);
5807         }
5808     },
5809
5810     /**
5811      * Returns the total width of all columns.
5812      * @param {Boolean} includeHidden True to include hidden column widths
5813      * @return {Number}
5814      */
5815     getTotalWidth : function(includeHidden){
5816         if(!this.totalWidth){
5817             this.totalWidth = 0;
5818             for(var i = 0, len = this.config.length; i < len; i++){
5819                 if(includeHidden || !this.isHidden(i)){
5820                     this.totalWidth += this.getColumnWidth(i);
5821                 }
5822             }
5823         }
5824         return this.totalWidth;
5825     },
5826
5827     /**
5828      * Returns the header for the specified column.
5829      * @param {Number} col The column index
5830      * @return {String}
5831      */
5832     getColumnHeader : function(col){
5833         return this.config[col].header;
5834     },
5835
5836     /**
5837      * Sets the header for a column.
5838      * @param {Number} col The column index
5839      * @param {String} header The new header
5840      */
5841     setColumnHeader : function(col, header){
5842         this.config[col].header = header;
5843         this.fireEvent("headerchange", this, col, header);
5844     },
5845
5846     /**
5847      * Returns the tooltip for the specified column.
5848      * @param {Number} col The column index
5849      * @return {String}
5850      */
5851     getColumnTooltip : function(col){
5852             return this.config[col].tooltip;
5853     },
5854     /**
5855      * Sets the tooltip for a column.
5856      * @param {Number} col The column index
5857      * @param {String} tooltip The new tooltip
5858      */
5859     setColumnTooltip : function(col, tooltip){
5860             this.config[col].tooltip = tooltip;
5861     },
5862
5863     /**
5864      * Returns the dataIndex for the specified column.
5865      * @param {Number} col The column index
5866      * @return {Number}
5867      */
5868     getDataIndex : function(col){
5869         return this.config[col].dataIndex;
5870     },
5871
5872     /**
5873      * Sets the dataIndex for a column.
5874      * @param {Number} col The column index
5875      * @param {Number} dataIndex The new dataIndex
5876      */
5877     setDataIndex : function(col, dataIndex){
5878         this.config[col].dataIndex = dataIndex;
5879     },
5880
5881     
5882     
5883     /**
5884      * Returns true if the cell is editable.
5885      * @param {Number} colIndex The column index
5886      * @param {Number} rowIndex The row index - this is nto actually used..?
5887      * @return {Boolean}
5888      */
5889     isCellEditable : function(colIndex, rowIndex){
5890         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5891     },
5892
5893     /**
5894      * Returns the editor defined for the cell/column.
5895      * return false or null to disable editing.
5896      * @param {Number} colIndex The column index
5897      * @param {Number} rowIndex The row index
5898      * @return {Object}
5899      */
5900     getCellEditor : function(colIndex, rowIndex){
5901         return this.config[colIndex].editor;
5902     },
5903
5904     /**
5905      * Sets if a column is editable.
5906      * @param {Number} col The column index
5907      * @param {Boolean} editable True if the column is editable
5908      */
5909     setEditable : function(col, editable){
5910         this.config[col].editable = editable;
5911     },
5912
5913
5914     /**
5915      * Returns true if the column is hidden.
5916      * @param {Number} colIndex The column index
5917      * @return {Boolean}
5918      */
5919     isHidden : function(colIndex){
5920         return this.config[colIndex].hidden;
5921     },
5922
5923
5924     /**
5925      * Returns true if the column width cannot be changed
5926      */
5927     isFixed : function(colIndex){
5928         return this.config[colIndex].fixed;
5929     },
5930
5931     /**
5932      * Returns true if the column can be resized
5933      * @return {Boolean}
5934      */
5935     isResizable : function(colIndex){
5936         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5937     },
5938     /**
5939      * Sets if a column is hidden.
5940      * @param {Number} colIndex The column index
5941      * @param {Boolean} hidden True if the column is hidden
5942      */
5943     setHidden : function(colIndex, hidden){
5944         this.config[colIndex].hidden = hidden;
5945         this.totalWidth = null;
5946         this.fireEvent("hiddenchange", this, colIndex, hidden);
5947     },
5948
5949     /**
5950      * Sets the editor for a column.
5951      * @param {Number} col The column index
5952      * @param {Object} editor The editor object
5953      */
5954     setEditor : function(col, editor){
5955         this.config[col].editor = editor;
5956     }
5957 });
5958
5959 Roo.grid.ColumnModel.defaultRenderer = function(value)
5960 {
5961     if(typeof value == "object") {
5962         return value;
5963     }
5964         if(typeof value == "string" && value.length < 1){
5965             return "&#160;";
5966         }
5967     
5968         return String.format("{0}", value);
5969 };
5970
5971 // Alias for backwards compatibility
5972 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5973 /*
5974  * Based on:
5975  * Ext JS Library 1.1.1
5976  * Copyright(c) 2006-2007, Ext JS, LLC.
5977  *
5978  * Originally Released Under LGPL - original licence link has changed is not relivant.
5979  *
5980  * Fork - LGPL
5981  * <script type="text/javascript">
5982  */
5983  
5984 /**
5985  * @class Roo.LoadMask
5986  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5987  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5988  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5989  * element's UpdateManager load indicator and will be destroyed after the initial load.
5990  * @constructor
5991  * Create a new LoadMask
5992  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5993  * @param {Object} config The config object
5994  */
5995 Roo.LoadMask = function(el, config){
5996     this.el = Roo.get(el);
5997     Roo.apply(this, config);
5998     if(this.store){
5999         this.store.on('beforeload', this.onBeforeLoad, this);
6000         this.store.on('load', this.onLoad, this);
6001         this.store.on('loadexception', this.onLoadException, this);
6002         this.removeMask = false;
6003     }else{
6004         var um = this.el.getUpdateManager();
6005         um.showLoadIndicator = false; // disable the default indicator
6006         um.on('beforeupdate', this.onBeforeLoad, this);
6007         um.on('update', this.onLoad, this);
6008         um.on('failure', this.onLoad, this);
6009         this.removeMask = true;
6010     }
6011 };
6012
6013 Roo.LoadMask.prototype = {
6014     /**
6015      * @cfg {Boolean} removeMask
6016      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6017      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6018      */
6019     /**
6020      * @cfg {String} msg
6021      * The text to display in a centered loading message box (defaults to 'Loading...')
6022      */
6023     msg : 'Loading...',
6024     /**
6025      * @cfg {String} msgCls
6026      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6027      */
6028     msgCls : 'x-mask-loading',
6029
6030     /**
6031      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6032      * @type Boolean
6033      */
6034     disabled: false,
6035
6036     /**
6037      * Disables the mask to prevent it from being displayed
6038      */
6039     disable : function(){
6040        this.disabled = true;
6041     },
6042
6043     /**
6044      * Enables the mask so that it can be displayed
6045      */
6046     enable : function(){
6047         this.disabled = false;
6048     },
6049     
6050     onLoadException : function()
6051     {
6052         Roo.log(arguments);
6053         
6054         if (typeof(arguments[3]) != 'undefined') {
6055             Roo.MessageBox.alert("Error loading",arguments[3]);
6056         } 
6057         /*
6058         try {
6059             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6060                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6061             }   
6062         } catch(e) {
6063             
6064         }
6065         */
6066     
6067         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6068     },
6069     // private
6070     onLoad : function()
6071     {
6072         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6073     },
6074
6075     // private
6076     onBeforeLoad : function(){
6077         if(!this.disabled){
6078             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6079         }
6080     },
6081
6082     // private
6083     destroy : function(){
6084         if(this.store){
6085             this.store.un('beforeload', this.onBeforeLoad, this);
6086             this.store.un('load', this.onLoad, this);
6087             this.store.un('loadexception', this.onLoadException, this);
6088         }else{
6089             var um = this.el.getUpdateManager();
6090             um.un('beforeupdate', this.onBeforeLoad, this);
6091             um.un('update', this.onLoad, this);
6092             um.un('failure', this.onLoad, this);
6093         }
6094     }
6095 };/*
6096  * - LGPL
6097  *
6098  * table
6099  * 
6100  */
6101
6102 /**
6103  * @class Roo.bootstrap.Table
6104  * @extends Roo.bootstrap.Component
6105  * Bootstrap Table class
6106  * @cfg {String} cls table class
6107  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6108  * @cfg {String} bgcolor Specifies the background color for a table
6109  * @cfg {Number} border Specifies whether the table cells should have borders or not
6110  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6111  * @cfg {Number} cellspacing Specifies the space between cells
6112  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6113  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6114  * @cfg {String} sortable Specifies that the table should be sortable
6115  * @cfg {String} summary Specifies a summary of the content of a table
6116  * @cfg {Number} width Specifies the width of a table
6117  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6118  * 
6119  * @cfg {boolean} striped Should the rows be alternative striped
6120  * @cfg {boolean} bordered Add borders to the table
6121  * @cfg {boolean} hover Add hover highlighting
6122  * @cfg {boolean} condensed Format condensed
6123  * @cfg {boolean} responsive Format condensed
6124  * @cfg {Boolean} loadMask (true|false) default false
6125  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6126  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6127  * @cfg {Boolean} rowSelection (true|false) default false
6128  * @cfg {Boolean} cellSelection (true|false) default false
6129  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6130  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6131  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6132  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6133  
6134  * 
6135  * @constructor
6136  * Create a new Table
6137  * @param {Object} config The config object
6138  */
6139
6140 Roo.bootstrap.Table = function(config){
6141     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6142     
6143   
6144     
6145     // BC...
6146     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6147     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6148     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6149     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6150     
6151     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6152     if (this.sm) {
6153         this.sm.grid = this;
6154         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6155         this.sm = this.selModel;
6156         this.sm.xmodule = this.xmodule || false;
6157     }
6158     
6159     if (this.cm && typeof(this.cm.config) == 'undefined') {
6160         this.colModel = new Roo.grid.ColumnModel(this.cm);
6161         this.cm = this.colModel;
6162         this.cm.xmodule = this.xmodule || false;
6163     }
6164     if (this.store) {
6165         this.store= Roo.factory(this.store, Roo.data);
6166         this.ds = this.store;
6167         this.ds.xmodule = this.xmodule || false;
6168          
6169     }
6170     if (this.footer && this.store) {
6171         this.footer.dataSource = this.ds;
6172         this.footer = Roo.factory(this.footer);
6173     }
6174     
6175     /** @private */
6176     this.addEvents({
6177         /**
6178          * @event cellclick
6179          * Fires when a cell is clicked
6180          * @param {Roo.bootstrap.Table} this
6181          * @param {Roo.Element} el
6182          * @param {Number} rowIndex
6183          * @param {Number} columnIndex
6184          * @param {Roo.EventObject} e
6185          */
6186         "cellclick" : true,
6187         /**
6188          * @event celldblclick
6189          * Fires when a cell is double clicked
6190          * @param {Roo.bootstrap.Table} this
6191          * @param {Roo.Element} el
6192          * @param {Number} rowIndex
6193          * @param {Number} columnIndex
6194          * @param {Roo.EventObject} e
6195          */
6196         "celldblclick" : true,
6197         /**
6198          * @event rowclick
6199          * Fires when a row is clicked
6200          * @param {Roo.bootstrap.Table} this
6201          * @param {Roo.Element} el
6202          * @param {Number} rowIndex
6203          * @param {Roo.EventObject} e
6204          */
6205         "rowclick" : true,
6206         /**
6207          * @event rowdblclick
6208          * Fires when a row is double clicked
6209          * @param {Roo.bootstrap.Table} this
6210          * @param {Roo.Element} el
6211          * @param {Number} rowIndex
6212          * @param {Roo.EventObject} e
6213          */
6214         "rowdblclick" : true,
6215         /**
6216          * @event mouseover
6217          * Fires when a mouseover occur
6218          * @param {Roo.bootstrap.Table} this
6219          * @param {Roo.Element} el
6220          * @param {Number} rowIndex
6221          * @param {Number} columnIndex
6222          * @param {Roo.EventObject} e
6223          */
6224         "mouseover" : true,
6225         /**
6226          * @event mouseout
6227          * Fires when a mouseout occur
6228          * @param {Roo.bootstrap.Table} this
6229          * @param {Roo.Element} el
6230          * @param {Number} rowIndex
6231          * @param {Number} columnIndex
6232          * @param {Roo.EventObject} e
6233          */
6234         "mouseout" : true,
6235         /**
6236          * @event rowclass
6237          * Fires when a row is rendered, so you can change add a style to it.
6238          * @param {Roo.bootstrap.Table} this
6239          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6240          */
6241         'rowclass' : true,
6242           /**
6243          * @event rowsrendered
6244          * Fires when all the  rows have been rendered
6245          * @param {Roo.bootstrap.Table} this
6246          */
6247         'rowsrendered' : true,
6248         /**
6249          * @event contextmenu
6250          * The raw contextmenu event for the entire grid.
6251          * @param {Roo.EventObject} e
6252          */
6253         "contextmenu" : true,
6254         /**
6255          * @event rowcontextmenu
6256          * Fires when a row is right clicked
6257          * @param {Roo.bootstrap.Table} this
6258          * @param {Number} rowIndex
6259          * @param {Roo.EventObject} e
6260          */
6261         "rowcontextmenu" : true,
6262         /**
6263          * @event cellcontextmenu
6264          * Fires when a cell is right clicked
6265          * @param {Roo.bootstrap.Table} this
6266          * @param {Number} rowIndex
6267          * @param {Number} cellIndex
6268          * @param {Roo.EventObject} e
6269          */
6270          "cellcontextmenu" : true,
6271          /**
6272          * @event headercontextmenu
6273          * Fires when a header is right clicked
6274          * @param {Roo.bootstrap.Table} this
6275          * @param {Number} columnIndex
6276          * @param {Roo.EventObject} e
6277          */
6278         "headercontextmenu" : true
6279     });
6280 };
6281
6282 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6283     
6284     cls: false,
6285     align: false,
6286     bgcolor: false,
6287     border: false,
6288     cellpadding: false,
6289     cellspacing: false,
6290     frame: false,
6291     rules: false,
6292     sortable: false,
6293     summary: false,
6294     width: false,
6295     striped : false,
6296     scrollBody : false,
6297     bordered: false,
6298     hover:  false,
6299     condensed : false,
6300     responsive : false,
6301     sm : false,
6302     cm : false,
6303     store : false,
6304     loadMask : false,
6305     footerShow : true,
6306     headerShow : true,
6307   
6308     rowSelection : false,
6309     cellSelection : false,
6310     layout : false,
6311     
6312     // Roo.Element - the tbody
6313     mainBody: false,
6314     // Roo.Element - thead element
6315     mainHead: false,
6316     
6317     container: false, // used by gridpanel...
6318     
6319     lazyLoad : false,
6320     
6321     CSS : Roo.util.CSS,
6322     
6323     auto_hide_footer : false,
6324     
6325     getAutoCreate : function()
6326     {
6327         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6328         
6329         cfg = {
6330             tag: 'table',
6331             cls : 'table',
6332             cn : []
6333         };
6334         if (this.scrollBody) {
6335             cfg.cls += ' table-body-fixed';
6336         }    
6337         if (this.striped) {
6338             cfg.cls += ' table-striped';
6339         }
6340         
6341         if (this.hover) {
6342             cfg.cls += ' table-hover';
6343         }
6344         if (this.bordered) {
6345             cfg.cls += ' table-bordered';
6346         }
6347         if (this.condensed) {
6348             cfg.cls += ' table-condensed';
6349         }
6350         if (this.responsive) {
6351             cfg.cls += ' table-responsive';
6352         }
6353         
6354         if (this.cls) {
6355             cfg.cls+=  ' ' +this.cls;
6356         }
6357         
6358         // this lot should be simplifed...
6359         var _t = this;
6360         var cp = [
6361             'align',
6362             'bgcolor',
6363             'border',
6364             'cellpadding',
6365             'cellspacing',
6366             'frame',
6367             'rules',
6368             'sortable',
6369             'summary',
6370             'width'
6371         ].forEach(function(k) {
6372             if (_t[k]) {
6373                 cfg[k] = _t[k];
6374             }
6375         });
6376         
6377         
6378         if (this.layout) {
6379             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6380         }
6381         
6382         if(this.store || this.cm){
6383             if(this.headerShow){
6384                 cfg.cn.push(this.renderHeader());
6385             }
6386             
6387             cfg.cn.push(this.renderBody());
6388             
6389             if(this.footerShow){
6390                 cfg.cn.push(this.renderFooter());
6391             }
6392             // where does this come from?
6393             //cfg.cls+=  ' TableGrid';
6394         }
6395         
6396         return { cn : [ cfg ] };
6397     },
6398     
6399     initEvents : function()
6400     {   
6401         if(!this.store || !this.cm){
6402             return;
6403         }
6404         if (this.selModel) {
6405             this.selModel.initEvents();
6406         }
6407         
6408         
6409         //Roo.log('initEvents with ds!!!!');
6410         
6411         this.mainBody = this.el.select('tbody', true).first();
6412         this.mainHead = this.el.select('thead', true).first();
6413         this.mainFoot = this.el.select('tfoot', true).first();
6414         
6415         
6416         
6417         var _this = this;
6418         
6419         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6420             e.on('click', _this.sort, _this);
6421         });
6422         
6423         this.mainBody.on("click", this.onClick, this);
6424         this.mainBody.on("dblclick", this.onDblClick, this);
6425         
6426         // why is this done????? = it breaks dialogs??
6427         //this.parent().el.setStyle('position', 'relative');
6428         
6429         
6430         if (this.footer) {
6431             this.footer.parentId = this.id;
6432             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6433             
6434             if(this.lazyLoad){
6435                 this.el.select('tfoot tr td').first().addClass('hide');
6436             }
6437         } 
6438         
6439         if(this.loadMask) {
6440             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6441         }
6442         
6443         this.store.on('load', this.onLoad, this);
6444         this.store.on('beforeload', this.onBeforeLoad, this);
6445         this.store.on('update', this.onUpdate, this);
6446         this.store.on('add', this.onAdd, this);
6447         this.store.on("clear", this.clear, this);
6448         
6449         this.el.on("contextmenu", this.onContextMenu, this);
6450         
6451         this.mainBody.on('scroll', this.onBodyScroll, this);
6452         
6453         this.cm.on("headerchange", this.onHeaderChange, this);
6454         
6455         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6456         
6457     },
6458     
6459     onContextMenu : function(e, t)
6460     {
6461         this.processEvent("contextmenu", e);
6462     },
6463     
6464     processEvent : function(name, e)
6465     {
6466         if (name != 'touchstart' ) {
6467             this.fireEvent(name, e);    
6468         }
6469         
6470         var t = e.getTarget();
6471         
6472         var cell = Roo.get(t);
6473         
6474         if(!cell){
6475             return;
6476         }
6477         
6478         if(cell.findParent('tfoot', false, true)){
6479             return;
6480         }
6481         
6482         if(cell.findParent('thead', false, true)){
6483             
6484             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6485                 cell = Roo.get(t).findParent('th', false, true);
6486                 if (!cell) {
6487                     Roo.log("failed to find th in thead?");
6488                     Roo.log(e.getTarget());
6489                     return;
6490                 }
6491             }
6492             
6493             var cellIndex = cell.dom.cellIndex;
6494             
6495             var ename = name == 'touchstart' ? 'click' : name;
6496             this.fireEvent("header" + ename, this, cellIndex, e);
6497             
6498             return;
6499         }
6500         
6501         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6502             cell = Roo.get(t).findParent('td', false, true);
6503             if (!cell) {
6504                 Roo.log("failed to find th in tbody?");
6505                 Roo.log(e.getTarget());
6506                 return;
6507             }
6508         }
6509         
6510         var row = cell.findParent('tr', false, true);
6511         var cellIndex = cell.dom.cellIndex;
6512         var rowIndex = row.dom.rowIndex - 1;
6513         
6514         if(row !== false){
6515             
6516             this.fireEvent("row" + name, this, rowIndex, e);
6517             
6518             if(cell !== false){
6519             
6520                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6521             }
6522         }
6523         
6524     },
6525     
6526     onMouseover : function(e, el)
6527     {
6528         var cell = Roo.get(el);
6529         
6530         if(!cell){
6531             return;
6532         }
6533         
6534         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6535             cell = cell.findParent('td', false, true);
6536         }
6537         
6538         var row = cell.findParent('tr', false, true);
6539         var cellIndex = cell.dom.cellIndex;
6540         var rowIndex = row.dom.rowIndex - 1; // start from 0
6541         
6542         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6543         
6544     },
6545     
6546     onMouseout : function(e, el)
6547     {
6548         var cell = Roo.get(el);
6549         
6550         if(!cell){
6551             return;
6552         }
6553         
6554         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6555             cell = cell.findParent('td', false, true);
6556         }
6557         
6558         var row = cell.findParent('tr', false, true);
6559         var cellIndex = cell.dom.cellIndex;
6560         var rowIndex = row.dom.rowIndex - 1; // start from 0
6561         
6562         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6563         
6564     },
6565     
6566     onClick : function(e, el)
6567     {
6568         var cell = Roo.get(el);
6569         
6570         if(!cell || (!this.cellSelection && !this.rowSelection)){
6571             return;
6572         }
6573         
6574         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6575             cell = cell.findParent('td', false, true);
6576         }
6577         
6578         if(!cell || typeof(cell) == 'undefined'){
6579             return;
6580         }
6581         
6582         var row = cell.findParent('tr', false, true);
6583         
6584         if(!row || typeof(row) == 'undefined'){
6585             return;
6586         }
6587         
6588         var cellIndex = cell.dom.cellIndex;
6589         var rowIndex = this.getRowIndex(row);
6590         
6591         // why??? - should these not be based on SelectionModel?
6592         if(this.cellSelection){
6593             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6594         }
6595         
6596         if(this.rowSelection){
6597             this.fireEvent('rowclick', this, row, rowIndex, e);
6598         }
6599         
6600         
6601     },
6602         
6603     onDblClick : function(e,el)
6604     {
6605         var cell = Roo.get(el);
6606         
6607         if(!cell || (!this.cellSelection && !this.rowSelection)){
6608             return;
6609         }
6610         
6611         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6612             cell = cell.findParent('td', false, true);
6613         }
6614         
6615         if(!cell || typeof(cell) == 'undefined'){
6616             return;
6617         }
6618         
6619         var row = cell.findParent('tr', false, true);
6620         
6621         if(!row || typeof(row) == 'undefined'){
6622             return;
6623         }
6624         
6625         var cellIndex = cell.dom.cellIndex;
6626         var rowIndex = this.getRowIndex(row);
6627         
6628         if(this.cellSelection){
6629             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6630         }
6631         
6632         if(this.rowSelection){
6633             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6634         }
6635     },
6636     
6637     sort : function(e,el)
6638     {
6639         var col = Roo.get(el);
6640         
6641         if(!col.hasClass('sortable')){
6642             return;
6643         }
6644         
6645         var sort = col.attr('sort');
6646         var dir = 'ASC';
6647         
6648         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6649             dir = 'DESC';
6650         }
6651         
6652         this.store.sortInfo = {field : sort, direction : dir};
6653         
6654         if (this.footer) {
6655             Roo.log("calling footer first");
6656             this.footer.onClick('first');
6657         } else {
6658         
6659             this.store.load({ params : { start : 0 } });
6660         }
6661     },
6662     
6663     renderHeader : function()
6664     {
6665         var header = {
6666             tag: 'thead',
6667             cn : []
6668         };
6669         
6670         var cm = this.cm;
6671         this.totalWidth = 0;
6672         
6673         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6674             
6675             var config = cm.config[i];
6676             
6677             var c = {
6678                 tag: 'th',
6679                 cls : 'x-hcol-' + i,
6680                 style : '',
6681                 html: cm.getColumnHeader(i)
6682             };
6683             
6684             var hh = '';
6685             
6686             if(typeof(config.sortable) != 'undefined' && config.sortable){
6687                 c.cls = 'sortable';
6688                 c.html = '<i class="glyphicon"></i>' + c.html;
6689             }
6690             
6691             if(typeof(config.lgHeader) != 'undefined'){
6692                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6693             }
6694             
6695             if(typeof(config.mdHeader) != 'undefined'){
6696                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6697             }
6698             
6699             if(typeof(config.smHeader) != 'undefined'){
6700                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6701             }
6702             
6703             if(typeof(config.xsHeader) != 'undefined'){
6704                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6705             }
6706             
6707             if(hh.length){
6708                 c.html = hh;
6709             }
6710             
6711             if(typeof(config.tooltip) != 'undefined'){
6712                 c.tooltip = config.tooltip;
6713             }
6714             
6715             if(typeof(config.colspan) != 'undefined'){
6716                 c.colspan = config.colspan;
6717             }
6718             
6719             if(typeof(config.hidden) != 'undefined' && config.hidden){
6720                 c.style += ' display:none;';
6721             }
6722             
6723             if(typeof(config.dataIndex) != 'undefined'){
6724                 c.sort = config.dataIndex;
6725             }
6726             
6727            
6728             
6729             if(typeof(config.align) != 'undefined' && config.align.length){
6730                 c.style += ' text-align:' + config.align + ';';
6731             }
6732             
6733             if(typeof(config.width) != 'undefined'){
6734                 c.style += ' width:' + config.width + 'px;';
6735                 this.totalWidth += config.width;
6736             } else {
6737                 this.totalWidth += 100; // assume minimum of 100 per column?
6738             }
6739             
6740             if(typeof(config.cls) != 'undefined'){
6741                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6742             }
6743             
6744             ['xs','sm','md','lg'].map(function(size){
6745                 
6746                 if(typeof(config[size]) == 'undefined'){
6747                     return;
6748                 }
6749                 
6750                 if (!config[size]) { // 0 = hidden
6751                     c.cls += ' hidden-' + size;
6752                     return;
6753                 }
6754                 
6755                 c.cls += ' col-' + size + '-' + config[size];
6756
6757             });
6758             
6759             header.cn.push(c)
6760         }
6761         
6762         return header;
6763     },
6764     
6765     renderBody : function()
6766     {
6767         var body = {
6768             tag: 'tbody',
6769             cn : [
6770                 {
6771                     tag: 'tr',
6772                     cn : [
6773                         {
6774                             tag : 'td',
6775                             colspan :  this.cm.getColumnCount()
6776                         }
6777                     ]
6778                 }
6779             ]
6780         };
6781         
6782         return body;
6783     },
6784     
6785     renderFooter : function()
6786     {
6787         var footer = {
6788             tag: 'tfoot',
6789             cn : [
6790                 {
6791                     tag: 'tr',
6792                     cn : [
6793                         {
6794                             tag : 'td',
6795                             colspan :  this.cm.getColumnCount()
6796                         }
6797                     ]
6798                 }
6799             ]
6800         };
6801         
6802         return footer;
6803     },
6804     
6805     
6806     
6807     onLoad : function()
6808     {
6809 //        Roo.log('ds onload');
6810         this.clear();
6811         
6812         var _this = this;
6813         var cm = this.cm;
6814         var ds = this.store;
6815         
6816         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6817             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6818             if (_this.store.sortInfo) {
6819                     
6820                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6821                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6822                 }
6823                 
6824                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6825                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6826                 }
6827             }
6828         });
6829         
6830         var tbody =  this.mainBody;
6831               
6832         if(ds.getCount() > 0){
6833             ds.data.each(function(d,rowIndex){
6834                 var row =  this.renderRow(cm, ds, rowIndex);
6835                 
6836                 tbody.createChild(row);
6837                 
6838                 var _this = this;
6839                 
6840                 if(row.cellObjects.length){
6841                     Roo.each(row.cellObjects, function(r){
6842                         _this.renderCellObject(r);
6843                     })
6844                 }
6845                 
6846             }, this);
6847         }
6848         
6849         var tfoot = this.el.select('tfoot', true).first();
6850         
6851         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6852             
6853             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6854             
6855             var total = this.ds.getTotalCount();
6856             
6857             if(this.footer.pageSize < total){
6858                 this.mainFoot.show();
6859             }
6860         }
6861         
6862         Roo.each(this.el.select('tbody td', true).elements, function(e){
6863             e.on('mouseover', _this.onMouseover, _this);
6864         });
6865         
6866         Roo.each(this.el.select('tbody td', true).elements, function(e){
6867             e.on('mouseout', _this.onMouseout, _this);
6868         });
6869         this.fireEvent('rowsrendered', this);
6870         
6871         this.autoSize();
6872     },
6873     
6874     
6875     onUpdate : function(ds,record)
6876     {
6877         this.refreshRow(record);
6878         this.autoSize();
6879     },
6880     
6881     onRemove : function(ds, record, index, isUpdate){
6882         if(isUpdate !== true){
6883             this.fireEvent("beforerowremoved", this, index, record);
6884         }
6885         var bt = this.mainBody.dom;
6886         
6887         var rows = this.el.select('tbody > tr', true).elements;
6888         
6889         if(typeof(rows[index]) != 'undefined'){
6890             bt.removeChild(rows[index].dom);
6891         }
6892         
6893 //        if(bt.rows[index]){
6894 //            bt.removeChild(bt.rows[index]);
6895 //        }
6896         
6897         if(isUpdate !== true){
6898             //this.stripeRows(index);
6899             //this.syncRowHeights(index, index);
6900             //this.layout();
6901             this.fireEvent("rowremoved", this, index, record);
6902         }
6903     },
6904     
6905     onAdd : function(ds, records, rowIndex)
6906     {
6907         //Roo.log('on Add called');
6908         // - note this does not handle multiple adding very well..
6909         var bt = this.mainBody.dom;
6910         for (var i =0 ; i < records.length;i++) {
6911             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6912             //Roo.log(records[i]);
6913             //Roo.log(this.store.getAt(rowIndex+i));
6914             this.insertRow(this.store, rowIndex + i, false);
6915             return;
6916         }
6917         
6918     },
6919     
6920     
6921     refreshRow : function(record){
6922         var ds = this.store, index;
6923         if(typeof record == 'number'){
6924             index = record;
6925             record = ds.getAt(index);
6926         }else{
6927             index = ds.indexOf(record);
6928         }
6929         this.insertRow(ds, index, true);
6930         this.autoSize();
6931         this.onRemove(ds, record, index+1, true);
6932         this.autoSize();
6933         //this.syncRowHeights(index, index);
6934         //this.layout();
6935         this.fireEvent("rowupdated", this, index, record);
6936     },
6937     
6938     insertRow : function(dm, rowIndex, isUpdate){
6939         
6940         if(!isUpdate){
6941             this.fireEvent("beforerowsinserted", this, rowIndex);
6942         }
6943             //var s = this.getScrollState();
6944         var row = this.renderRow(this.cm, this.store, rowIndex);
6945         // insert before rowIndex..
6946         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6947         
6948         var _this = this;
6949                 
6950         if(row.cellObjects.length){
6951             Roo.each(row.cellObjects, function(r){
6952                 _this.renderCellObject(r);
6953             })
6954         }
6955             
6956         if(!isUpdate){
6957             this.fireEvent("rowsinserted", this, rowIndex);
6958             //this.syncRowHeights(firstRow, lastRow);
6959             //this.stripeRows(firstRow);
6960             //this.layout();
6961         }
6962         
6963     },
6964     
6965     
6966     getRowDom : function(rowIndex)
6967     {
6968         var rows = this.el.select('tbody > tr', true).elements;
6969         
6970         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6971         
6972     },
6973     // returns the object tree for a tr..
6974   
6975     
6976     renderRow : function(cm, ds, rowIndex) 
6977     {
6978         var d = ds.getAt(rowIndex);
6979         
6980         var row = {
6981             tag : 'tr',
6982             cls : 'x-row-' + rowIndex,
6983             cn : []
6984         };
6985             
6986         var cellObjects = [];
6987         
6988         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6989             var config = cm.config[i];
6990             
6991             var renderer = cm.getRenderer(i);
6992             var value = '';
6993             var id = false;
6994             
6995             if(typeof(renderer) !== 'undefined'){
6996                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6997             }
6998             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6999             // and are rendered into the cells after the row is rendered - using the id for the element.
7000             
7001             if(typeof(value) === 'object'){
7002                 id = Roo.id();
7003                 cellObjects.push({
7004                     container : id,
7005                     cfg : value 
7006                 })
7007             }
7008             
7009             var rowcfg = {
7010                 record: d,
7011                 rowIndex : rowIndex,
7012                 colIndex : i,
7013                 rowClass : ''
7014             };
7015
7016             this.fireEvent('rowclass', this, rowcfg);
7017             
7018             var td = {
7019                 tag: 'td',
7020                 cls : rowcfg.rowClass + ' x-col-' + i,
7021                 style: '',
7022                 html: (typeof(value) === 'object') ? '' : value
7023             };
7024             
7025             if (id) {
7026                 td.id = id;
7027             }
7028             
7029             if(typeof(config.colspan) != 'undefined'){
7030                 td.colspan = config.colspan;
7031             }
7032             
7033             if(typeof(config.hidden) != 'undefined' && config.hidden){
7034                 td.style += ' display:none;';
7035             }
7036             
7037             if(typeof(config.align) != 'undefined' && config.align.length){
7038                 td.style += ' text-align:' + config.align + ';';
7039             }
7040             if(typeof(config.valign) != 'undefined' && config.valign.length){
7041                 td.style += ' vertical-align:' + config.valign + ';';
7042             }
7043             
7044             if(typeof(config.width) != 'undefined'){
7045                 td.style += ' width:' +  config.width + 'px;';
7046             }
7047             
7048             if(typeof(config.cursor) != 'undefined'){
7049                 td.style += ' cursor:' +  config.cursor + ';';
7050             }
7051             
7052             if(typeof(config.cls) != 'undefined'){
7053                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7054             }
7055             
7056             ['xs','sm','md','lg'].map(function(size){
7057                 
7058                 if(typeof(config[size]) == 'undefined'){
7059                     return;
7060                 }
7061                 
7062                 if (!config[size]) { // 0 = hidden
7063                     td.cls += ' hidden-' + size;
7064                     return;
7065                 }
7066                 
7067                 td.cls += ' col-' + size + '-' + config[size];
7068
7069             });
7070             
7071             row.cn.push(td);
7072            
7073         }
7074         
7075         row.cellObjects = cellObjects;
7076         
7077         return row;
7078           
7079     },
7080     
7081     
7082     
7083     onBeforeLoad : function()
7084     {
7085         
7086     },
7087      /**
7088      * Remove all rows
7089      */
7090     clear : function()
7091     {
7092         this.el.select('tbody', true).first().dom.innerHTML = '';
7093     },
7094     /**
7095      * Show or hide a row.
7096      * @param {Number} rowIndex to show or hide
7097      * @param {Boolean} state hide
7098      */
7099     setRowVisibility : function(rowIndex, state)
7100     {
7101         var bt = this.mainBody.dom;
7102         
7103         var rows = this.el.select('tbody > tr', true).elements;
7104         
7105         if(typeof(rows[rowIndex]) == 'undefined'){
7106             return;
7107         }
7108         rows[rowIndex].dom.style.display = state ? '' : 'none';
7109     },
7110     
7111     
7112     getSelectionModel : function(){
7113         if(!this.selModel){
7114             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7115         }
7116         return this.selModel;
7117     },
7118     /*
7119      * Render the Roo.bootstrap object from renderder
7120      */
7121     renderCellObject : function(r)
7122     {
7123         var _this = this;
7124         
7125         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7126         
7127         var t = r.cfg.render(r.container);
7128         
7129         if(r.cfg.cn){
7130             Roo.each(r.cfg.cn, function(c){
7131                 var child = {
7132                     container: t.getChildContainer(),
7133                     cfg: c
7134                 };
7135                 _this.renderCellObject(child);
7136             })
7137         }
7138     },
7139     
7140     getRowIndex : function(row)
7141     {
7142         var rowIndex = -1;
7143         
7144         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7145             if(el != row){
7146                 return;
7147             }
7148             
7149             rowIndex = index;
7150         });
7151         
7152         return rowIndex;
7153     },
7154      /**
7155      * Returns the grid's underlying element = used by panel.Grid
7156      * @return {Element} The element
7157      */
7158     getGridEl : function(){
7159         return this.el;
7160     },
7161      /**
7162      * Forces a resize - used by panel.Grid
7163      * @return {Element} The element
7164      */
7165     autoSize : function()
7166     {
7167         //var ctr = Roo.get(this.container.dom.parentElement);
7168         var ctr = Roo.get(this.el.dom);
7169         
7170         var thd = this.getGridEl().select('thead',true).first();
7171         var tbd = this.getGridEl().select('tbody', true).first();
7172         var tfd = this.getGridEl().select('tfoot', true).first();
7173         
7174         var cw = ctr.getWidth();
7175         
7176         if (tbd) {
7177             
7178             tbd.setSize(ctr.getWidth(),
7179                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7180             );
7181             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7182             cw -= barsize;
7183         }
7184         cw = Math.max(cw, this.totalWidth);
7185         this.getGridEl().select('tr',true).setWidth(cw);
7186         // resize 'expandable coloumn?
7187         
7188         return; // we doe not have a view in this design..
7189         
7190     },
7191     onBodyScroll: function()
7192     {
7193         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7194         if(this.mainHead){
7195             this.mainHead.setStyle({
7196                 'position' : 'relative',
7197                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7198             });
7199         }
7200         
7201         if(this.lazyLoad){
7202             
7203             var scrollHeight = this.mainBody.dom.scrollHeight;
7204             
7205             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7206             
7207             var height = this.mainBody.getHeight();
7208             
7209             if(scrollHeight - height == scrollTop) {
7210                 
7211                 var total = this.ds.getTotalCount();
7212                 
7213                 if(this.footer.cursor + this.footer.pageSize < total){
7214                     
7215                     this.footer.ds.load({
7216                         params : {
7217                             start : this.footer.cursor + this.footer.pageSize,
7218                             limit : this.footer.pageSize
7219                         },
7220                         add : true
7221                     });
7222                 }
7223             }
7224             
7225         }
7226     },
7227     
7228     onHeaderChange : function()
7229     {
7230         var header = this.renderHeader();
7231         var table = this.el.select('table', true).first();
7232         
7233         this.mainHead.remove();
7234         this.mainHead = table.createChild(header, this.mainBody, false);
7235     },
7236     
7237     onHiddenChange : function(colModel, colIndex, hidden)
7238     {
7239         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7240         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7241         
7242         this.CSS.updateRule(thSelector, "display", "");
7243         this.CSS.updateRule(tdSelector, "display", "");
7244         
7245         if(hidden){
7246             this.CSS.updateRule(thSelector, "display", "none");
7247             this.CSS.updateRule(tdSelector, "display", "none");
7248         }
7249         
7250         this.onHeaderChange();
7251         this.onLoad();
7252     },
7253     
7254     setColumnWidth: function(col_index, width)
7255     {
7256         // width = "md-2 xs-2..."
7257         if(!this.colModel.config[col_index]) {
7258             return;
7259         }
7260         
7261         var w = width.split(" ");
7262         
7263         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7264         
7265         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7266         
7267         
7268         for(var j = 0; j < w.length; j++) {
7269             
7270             if(!w[j]) {
7271                 continue;
7272             }
7273             
7274             var size_cls = w[j].split("-");
7275             
7276             if(!Number.isInteger(size_cls[1] * 1)) {
7277                 continue;
7278             }
7279             
7280             if(!this.colModel.config[col_index][size_cls[0]]) {
7281                 continue;
7282             }
7283             
7284             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7285                 continue;
7286             }
7287             
7288             h_row[0].classList.replace(
7289                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7290                 "col-"+size_cls[0]+"-"+size_cls[1]
7291             );
7292             
7293             for(var i = 0; i < rows.length; i++) {
7294                 
7295                 var size_cls = w[j].split("-");
7296                 
7297                 if(!Number.isInteger(size_cls[1] * 1)) {
7298                     continue;
7299                 }
7300                 
7301                 if(!this.colModel.config[col_index][size_cls[0]]) {
7302                     continue;
7303                 }
7304                 
7305                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7306                     continue;
7307                 }
7308                 
7309                 rows[i].classList.replace(
7310                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7311                     "col-"+size_cls[0]+"-"+size_cls[1]
7312                 );
7313             }
7314             
7315             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7316         }
7317     }
7318 });
7319
7320  
7321
7322  /*
7323  * - LGPL
7324  *
7325  * table cell
7326  * 
7327  */
7328
7329 /**
7330  * @class Roo.bootstrap.TableCell
7331  * @extends Roo.bootstrap.Component
7332  * Bootstrap TableCell class
7333  * @cfg {String} html cell contain text
7334  * @cfg {String} cls cell class
7335  * @cfg {String} tag cell tag (td|th) default td
7336  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7337  * @cfg {String} align Aligns the content in a cell
7338  * @cfg {String} axis Categorizes cells
7339  * @cfg {String} bgcolor Specifies the background color of a cell
7340  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7341  * @cfg {Number} colspan Specifies the number of columns a cell should span
7342  * @cfg {String} headers Specifies one or more header cells a cell is related to
7343  * @cfg {Number} height Sets the height of a cell
7344  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7345  * @cfg {Number} rowspan Sets the number of rows a cell should span
7346  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7347  * @cfg {String} valign Vertical aligns the content in a cell
7348  * @cfg {Number} width Specifies the width of a cell
7349  * 
7350  * @constructor
7351  * Create a new TableCell
7352  * @param {Object} config The config object
7353  */
7354
7355 Roo.bootstrap.TableCell = function(config){
7356     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7357 };
7358
7359 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7360     
7361     html: false,
7362     cls: false,
7363     tag: false,
7364     abbr: false,
7365     align: false,
7366     axis: false,
7367     bgcolor: false,
7368     charoff: false,
7369     colspan: false,
7370     headers: false,
7371     height: false,
7372     nowrap: false,
7373     rowspan: false,
7374     scope: false,
7375     valign: false,
7376     width: false,
7377     
7378     
7379     getAutoCreate : function(){
7380         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7381         
7382         cfg = {
7383             tag: 'td'
7384         };
7385         
7386         if(this.tag){
7387             cfg.tag = this.tag;
7388         }
7389         
7390         if (this.html) {
7391             cfg.html=this.html
7392         }
7393         if (this.cls) {
7394             cfg.cls=this.cls
7395         }
7396         if (this.abbr) {
7397             cfg.abbr=this.abbr
7398         }
7399         if (this.align) {
7400             cfg.align=this.align
7401         }
7402         if (this.axis) {
7403             cfg.axis=this.axis
7404         }
7405         if (this.bgcolor) {
7406             cfg.bgcolor=this.bgcolor
7407         }
7408         if (this.charoff) {
7409             cfg.charoff=this.charoff
7410         }
7411         if (this.colspan) {
7412             cfg.colspan=this.colspan
7413         }
7414         if (this.headers) {
7415             cfg.headers=this.headers
7416         }
7417         if (this.height) {
7418             cfg.height=this.height
7419         }
7420         if (this.nowrap) {
7421             cfg.nowrap=this.nowrap
7422         }
7423         if (this.rowspan) {
7424             cfg.rowspan=this.rowspan
7425         }
7426         if (this.scope) {
7427             cfg.scope=this.scope
7428         }
7429         if (this.valign) {
7430             cfg.valign=this.valign
7431         }
7432         if (this.width) {
7433             cfg.width=this.width
7434         }
7435         
7436         
7437         return cfg;
7438     }
7439    
7440 });
7441
7442  
7443
7444  /*
7445  * - LGPL
7446  *
7447  * table row
7448  * 
7449  */
7450
7451 /**
7452  * @class Roo.bootstrap.TableRow
7453  * @extends Roo.bootstrap.Component
7454  * Bootstrap TableRow class
7455  * @cfg {String} cls row class
7456  * @cfg {String} align Aligns the content in a table row
7457  * @cfg {String} bgcolor Specifies a background color for a table row
7458  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7459  * @cfg {String} valign Vertical aligns the content in a table row
7460  * 
7461  * @constructor
7462  * Create a new TableRow
7463  * @param {Object} config The config object
7464  */
7465
7466 Roo.bootstrap.TableRow = function(config){
7467     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7468 };
7469
7470 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7471     
7472     cls: false,
7473     align: false,
7474     bgcolor: false,
7475     charoff: false,
7476     valign: false,
7477     
7478     getAutoCreate : function(){
7479         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7480         
7481         cfg = {
7482             tag: 'tr'
7483         };
7484             
7485         if(this.cls){
7486             cfg.cls = this.cls;
7487         }
7488         if(this.align){
7489             cfg.align = this.align;
7490         }
7491         if(this.bgcolor){
7492             cfg.bgcolor = this.bgcolor;
7493         }
7494         if(this.charoff){
7495             cfg.charoff = this.charoff;
7496         }
7497         if(this.valign){
7498             cfg.valign = this.valign;
7499         }
7500         
7501         return cfg;
7502     }
7503    
7504 });
7505
7506  
7507
7508  /*
7509  * - LGPL
7510  *
7511  * table body
7512  * 
7513  */
7514
7515 /**
7516  * @class Roo.bootstrap.TableBody
7517  * @extends Roo.bootstrap.Component
7518  * Bootstrap TableBody class
7519  * @cfg {String} cls element class
7520  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7521  * @cfg {String} align Aligns the content inside the element
7522  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7523  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7524  * 
7525  * @constructor
7526  * Create a new TableBody
7527  * @param {Object} config The config object
7528  */
7529
7530 Roo.bootstrap.TableBody = function(config){
7531     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7532 };
7533
7534 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7535     
7536     cls: false,
7537     tag: false,
7538     align: false,
7539     charoff: false,
7540     valign: false,
7541     
7542     getAutoCreate : function(){
7543         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7544         
7545         cfg = {
7546             tag: 'tbody'
7547         };
7548             
7549         if (this.cls) {
7550             cfg.cls=this.cls
7551         }
7552         if(this.tag){
7553             cfg.tag = this.tag;
7554         }
7555         
7556         if(this.align){
7557             cfg.align = this.align;
7558         }
7559         if(this.charoff){
7560             cfg.charoff = this.charoff;
7561         }
7562         if(this.valign){
7563             cfg.valign = this.valign;
7564         }
7565         
7566         return cfg;
7567     }
7568     
7569     
7570 //    initEvents : function()
7571 //    {
7572 //        
7573 //        if(!this.store){
7574 //            return;
7575 //        }
7576 //        
7577 //        this.store = Roo.factory(this.store, Roo.data);
7578 //        this.store.on('load', this.onLoad, this);
7579 //        
7580 //        this.store.load();
7581 //        
7582 //    },
7583 //    
7584 //    onLoad: function () 
7585 //    {   
7586 //        this.fireEvent('load', this);
7587 //    }
7588 //    
7589 //   
7590 });
7591
7592  
7593
7594  /*
7595  * Based on:
7596  * Ext JS Library 1.1.1
7597  * Copyright(c) 2006-2007, Ext JS, LLC.
7598  *
7599  * Originally Released Under LGPL - original licence link has changed is not relivant.
7600  *
7601  * Fork - LGPL
7602  * <script type="text/javascript">
7603  */
7604
7605 // as we use this in bootstrap.
7606 Roo.namespace('Roo.form');
7607  /**
7608  * @class Roo.form.Action
7609  * Internal Class used to handle form actions
7610  * @constructor
7611  * @param {Roo.form.BasicForm} el The form element or its id
7612  * @param {Object} config Configuration options
7613  */
7614
7615  
7616  
7617 // define the action interface
7618 Roo.form.Action = function(form, options){
7619     this.form = form;
7620     this.options = options || {};
7621 };
7622 /**
7623  * Client Validation Failed
7624  * @const 
7625  */
7626 Roo.form.Action.CLIENT_INVALID = 'client';
7627 /**
7628  * Server Validation Failed
7629  * @const 
7630  */
7631 Roo.form.Action.SERVER_INVALID = 'server';
7632  /**
7633  * Connect to Server Failed
7634  * @const 
7635  */
7636 Roo.form.Action.CONNECT_FAILURE = 'connect';
7637 /**
7638  * Reading Data from Server Failed
7639  * @const 
7640  */
7641 Roo.form.Action.LOAD_FAILURE = 'load';
7642
7643 Roo.form.Action.prototype = {
7644     type : 'default',
7645     failureType : undefined,
7646     response : undefined,
7647     result : undefined,
7648
7649     // interface method
7650     run : function(options){
7651
7652     },
7653
7654     // interface method
7655     success : function(response){
7656
7657     },
7658
7659     // interface method
7660     handleResponse : function(response){
7661
7662     },
7663
7664     // default connection failure
7665     failure : function(response){
7666         
7667         this.response = response;
7668         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7669         this.form.afterAction(this, false);
7670     },
7671
7672     processResponse : function(response){
7673         this.response = response;
7674         if(!response.responseText){
7675             return true;
7676         }
7677         this.result = this.handleResponse(response);
7678         return this.result;
7679     },
7680
7681     // utility functions used internally
7682     getUrl : function(appendParams){
7683         var url = this.options.url || this.form.url || this.form.el.dom.action;
7684         if(appendParams){
7685             var p = this.getParams();
7686             if(p){
7687                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7688             }
7689         }
7690         return url;
7691     },
7692
7693     getMethod : function(){
7694         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7695     },
7696
7697     getParams : function(){
7698         var bp = this.form.baseParams;
7699         var p = this.options.params;
7700         if(p){
7701             if(typeof p == "object"){
7702                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7703             }else if(typeof p == 'string' && bp){
7704                 p += '&' + Roo.urlEncode(bp);
7705             }
7706         }else if(bp){
7707             p = Roo.urlEncode(bp);
7708         }
7709         return p;
7710     },
7711
7712     createCallback : function(){
7713         return {
7714             success: this.success,
7715             failure: this.failure,
7716             scope: this,
7717             timeout: (this.form.timeout*1000),
7718             upload: this.form.fileUpload ? this.success : undefined
7719         };
7720     }
7721 };
7722
7723 Roo.form.Action.Submit = function(form, options){
7724     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7725 };
7726
7727 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7728     type : 'submit',
7729
7730     haveProgress : false,
7731     uploadComplete : false,
7732     
7733     // uploadProgress indicator.
7734     uploadProgress : function()
7735     {
7736         if (!this.form.progressUrl) {
7737             return;
7738         }
7739         
7740         if (!this.haveProgress) {
7741             Roo.MessageBox.progress("Uploading", "Uploading");
7742         }
7743         if (this.uploadComplete) {
7744            Roo.MessageBox.hide();
7745            return;
7746         }
7747         
7748         this.haveProgress = true;
7749    
7750         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7751         
7752         var c = new Roo.data.Connection();
7753         c.request({
7754             url : this.form.progressUrl,
7755             params: {
7756                 id : uid
7757             },
7758             method: 'GET',
7759             success : function(req){
7760                //console.log(data);
7761                 var rdata = false;
7762                 var edata;
7763                 try  {
7764                    rdata = Roo.decode(req.responseText)
7765                 } catch (e) {
7766                     Roo.log("Invalid data from server..");
7767                     Roo.log(edata);
7768                     return;
7769                 }
7770                 if (!rdata || !rdata.success) {
7771                     Roo.log(rdata);
7772                     Roo.MessageBox.alert(Roo.encode(rdata));
7773                     return;
7774                 }
7775                 var data = rdata.data;
7776                 
7777                 if (this.uploadComplete) {
7778                    Roo.MessageBox.hide();
7779                    return;
7780                 }
7781                    
7782                 if (data){
7783                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7784                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7785                     );
7786                 }
7787                 this.uploadProgress.defer(2000,this);
7788             },
7789        
7790             failure: function(data) {
7791                 Roo.log('progress url failed ');
7792                 Roo.log(data);
7793             },
7794             scope : this
7795         });
7796            
7797     },
7798     
7799     
7800     run : function()
7801     {
7802         // run get Values on the form, so it syncs any secondary forms.
7803         this.form.getValues();
7804         
7805         var o = this.options;
7806         var method = this.getMethod();
7807         var isPost = method == 'POST';
7808         if(o.clientValidation === false || this.form.isValid()){
7809             
7810             if (this.form.progressUrl) {
7811                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7812                     (new Date() * 1) + '' + Math.random());
7813                     
7814             } 
7815             
7816             
7817             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7818                 form:this.form.el.dom,
7819                 url:this.getUrl(!isPost),
7820                 method: method,
7821                 params:isPost ? this.getParams() : null,
7822                 isUpload: this.form.fileUpload
7823             }));
7824             
7825             this.uploadProgress();
7826
7827         }else if (o.clientValidation !== false){ // client validation failed
7828             this.failureType = Roo.form.Action.CLIENT_INVALID;
7829             this.form.afterAction(this, false);
7830         }
7831     },
7832
7833     success : function(response)
7834     {
7835         this.uploadComplete= true;
7836         if (this.haveProgress) {
7837             Roo.MessageBox.hide();
7838         }
7839         
7840         
7841         var result = this.processResponse(response);
7842         if(result === true || result.success){
7843             this.form.afterAction(this, true);
7844             return;
7845         }
7846         if(result.errors){
7847             this.form.markInvalid(result.errors);
7848             this.failureType = Roo.form.Action.SERVER_INVALID;
7849         }
7850         this.form.afterAction(this, false);
7851     },
7852     failure : function(response)
7853     {
7854         this.uploadComplete= true;
7855         if (this.haveProgress) {
7856             Roo.MessageBox.hide();
7857         }
7858         
7859         this.response = response;
7860         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7861         this.form.afterAction(this, false);
7862     },
7863     
7864     handleResponse : function(response){
7865         if(this.form.errorReader){
7866             var rs = this.form.errorReader.read(response);
7867             var errors = [];
7868             if(rs.records){
7869                 for(var i = 0, len = rs.records.length; i < len; i++) {
7870                     var r = rs.records[i];
7871                     errors[i] = r.data;
7872                 }
7873             }
7874             if(errors.length < 1){
7875                 errors = null;
7876             }
7877             return {
7878                 success : rs.success,
7879                 errors : errors
7880             };
7881         }
7882         var ret = false;
7883         try {
7884             ret = Roo.decode(response.responseText);
7885         } catch (e) {
7886             ret = {
7887                 success: false,
7888                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7889                 errors : []
7890             };
7891         }
7892         return ret;
7893         
7894     }
7895 });
7896
7897
7898 Roo.form.Action.Load = function(form, options){
7899     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7900     this.reader = this.form.reader;
7901 };
7902
7903 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7904     type : 'load',
7905
7906     run : function(){
7907         
7908         Roo.Ajax.request(Roo.apply(
7909                 this.createCallback(), {
7910                     method:this.getMethod(),
7911                     url:this.getUrl(false),
7912                     params:this.getParams()
7913         }));
7914     },
7915
7916     success : function(response){
7917         
7918         var result = this.processResponse(response);
7919         if(result === true || !result.success || !result.data){
7920             this.failureType = Roo.form.Action.LOAD_FAILURE;
7921             this.form.afterAction(this, false);
7922             return;
7923         }
7924         this.form.clearInvalid();
7925         this.form.setValues(result.data);
7926         this.form.afterAction(this, true);
7927     },
7928
7929     handleResponse : function(response){
7930         if(this.form.reader){
7931             var rs = this.form.reader.read(response);
7932             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7933             return {
7934                 success : rs.success,
7935                 data : data
7936             };
7937         }
7938         return Roo.decode(response.responseText);
7939     }
7940 });
7941
7942 Roo.form.Action.ACTION_TYPES = {
7943     'load' : Roo.form.Action.Load,
7944     'submit' : Roo.form.Action.Submit
7945 };/*
7946  * - LGPL
7947  *
7948  * form
7949  *
7950  */
7951
7952 /**
7953  * @class Roo.bootstrap.Form
7954  * @extends Roo.bootstrap.Component
7955  * Bootstrap Form class
7956  * @cfg {String} method  GET | POST (default POST)
7957  * @cfg {String} labelAlign top | left (default top)
7958  * @cfg {String} align left  | right - for navbars
7959  * @cfg {Boolean} loadMask load mask when submit (default true)
7960
7961  *
7962  * @constructor
7963  * Create a new Form
7964  * @param {Object} config The config object
7965  */
7966
7967
7968 Roo.bootstrap.Form = function(config){
7969     
7970     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7971     
7972     Roo.bootstrap.Form.popover.apply();
7973     
7974     this.addEvents({
7975         /**
7976          * @event clientvalidation
7977          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7978          * @param {Form} this
7979          * @param {Boolean} valid true if the form has passed client-side validation
7980          */
7981         clientvalidation: true,
7982         /**
7983          * @event beforeaction
7984          * Fires before any action is performed. Return false to cancel the action.
7985          * @param {Form} this
7986          * @param {Action} action The action to be performed
7987          */
7988         beforeaction: true,
7989         /**
7990          * @event actionfailed
7991          * Fires when an action fails.
7992          * @param {Form} this
7993          * @param {Action} action The action that failed
7994          */
7995         actionfailed : true,
7996         /**
7997          * @event actioncomplete
7998          * Fires when an action is completed.
7999          * @param {Form} this
8000          * @param {Action} action The action that completed
8001          */
8002         actioncomplete : true
8003     });
8004 };
8005
8006 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8007
8008      /**
8009      * @cfg {String} method
8010      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8011      */
8012     method : 'POST',
8013     /**
8014      * @cfg {String} url
8015      * The URL to use for form actions if one isn't supplied in the action options.
8016      */
8017     /**
8018      * @cfg {Boolean} fileUpload
8019      * Set to true if this form is a file upload.
8020      */
8021
8022     /**
8023      * @cfg {Object} baseParams
8024      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8025      */
8026
8027     /**
8028      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8029      */
8030     timeout: 30,
8031     /**
8032      * @cfg {Sting} align (left|right) for navbar forms
8033      */
8034     align : 'left',
8035
8036     // private
8037     activeAction : null,
8038
8039     /**
8040      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8041      * element by passing it or its id or mask the form itself by passing in true.
8042      * @type Mixed
8043      */
8044     waitMsgTarget : false,
8045
8046     loadMask : true,
8047     
8048     /**
8049      * @cfg {Boolean} errorMask (true|false) default false
8050      */
8051     errorMask : false,
8052     
8053     /**
8054      * @cfg {Number} maskOffset Default 100
8055      */
8056     maskOffset : 100,
8057     
8058     /**
8059      * @cfg {Boolean} maskBody
8060      */
8061     maskBody : false,
8062
8063     getAutoCreate : function(){
8064
8065         var cfg = {
8066             tag: 'form',
8067             method : this.method || 'POST',
8068             id : this.id || Roo.id(),
8069             cls : ''
8070         };
8071         if (this.parent().xtype.match(/^Nav/)) {
8072             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8073
8074         }
8075
8076         if (this.labelAlign == 'left' ) {
8077             cfg.cls += ' form-horizontal';
8078         }
8079
8080
8081         return cfg;
8082     },
8083     initEvents : function()
8084     {
8085         this.el.on('submit', this.onSubmit, this);
8086         // this was added as random key presses on the form where triggering form submit.
8087         this.el.on('keypress', function(e) {
8088             if (e.getCharCode() != 13) {
8089                 return true;
8090             }
8091             // we might need to allow it for textareas.. and some other items.
8092             // check e.getTarget().
8093
8094             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8095                 return true;
8096             }
8097
8098             Roo.log("keypress blocked");
8099
8100             e.preventDefault();
8101             return false;
8102         });
8103         
8104     },
8105     // private
8106     onSubmit : function(e){
8107         e.stopEvent();
8108     },
8109
8110      /**
8111      * Returns true if client-side validation on the form is successful.
8112      * @return Boolean
8113      */
8114     isValid : function(){
8115         var items = this.getItems();
8116         var valid = true;
8117         var target = false;
8118         
8119         items.each(function(f){
8120             
8121             if(f.validate()){
8122                 return;
8123             }
8124             
8125             Roo.log('invalid field: ' + f.name);
8126             
8127             valid = false;
8128
8129             if(!target && f.el.isVisible(true)){
8130                 target = f;
8131             }
8132            
8133         });
8134         
8135         if(this.errorMask && !valid){
8136             Roo.bootstrap.Form.popover.mask(this, target);
8137         }
8138         
8139         return valid;
8140     },
8141     
8142     /**
8143      * Returns true if any fields in this form have changed since their original load.
8144      * @return Boolean
8145      */
8146     isDirty : function(){
8147         var dirty = false;
8148         var items = this.getItems();
8149         items.each(function(f){
8150            if(f.isDirty()){
8151                dirty = true;
8152                return false;
8153            }
8154            return true;
8155         });
8156         return dirty;
8157     },
8158      /**
8159      * Performs a predefined action (submit or load) or custom actions you define on this form.
8160      * @param {String} actionName The name of the action type
8161      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8162      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8163      * accept other config options):
8164      * <pre>
8165 Property          Type             Description
8166 ----------------  ---------------  ----------------------------------------------------------------------------------
8167 url               String           The url for the action (defaults to the form's url)
8168 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8169 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8170 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8171                                    validate the form on the client (defaults to false)
8172      * </pre>
8173      * @return {BasicForm} this
8174      */
8175     doAction : function(action, options){
8176         if(typeof action == 'string'){
8177             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8178         }
8179         if(this.fireEvent('beforeaction', this, action) !== false){
8180             this.beforeAction(action);
8181             action.run.defer(100, action);
8182         }
8183         return this;
8184     },
8185
8186     // private
8187     beforeAction : function(action){
8188         var o = action.options;
8189         
8190         if(this.loadMask){
8191             
8192             if(this.maskBody){
8193                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8194             } else {
8195                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8196             }
8197         }
8198         // not really supported yet.. ??
8199
8200         //if(this.waitMsgTarget === true){
8201         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8202         //}else if(this.waitMsgTarget){
8203         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8204         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8205         //}else {
8206         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8207        // }
8208
8209     },
8210
8211     // private
8212     afterAction : function(action, success){
8213         this.activeAction = null;
8214         var o = action.options;
8215
8216         if(this.loadMask){
8217             
8218             if(this.maskBody){
8219                 Roo.get(document.body).unmask();
8220             } else {
8221                 this.el.unmask();
8222             }
8223         }
8224         
8225         //if(this.waitMsgTarget === true){
8226 //            this.el.unmask();
8227         //}else if(this.waitMsgTarget){
8228         //    this.waitMsgTarget.unmask();
8229         //}else{
8230         //    Roo.MessageBox.updateProgress(1);
8231         //    Roo.MessageBox.hide();
8232        // }
8233         //
8234         if(success){
8235             if(o.reset){
8236                 this.reset();
8237             }
8238             Roo.callback(o.success, o.scope, [this, action]);
8239             this.fireEvent('actioncomplete', this, action);
8240
8241         }else{
8242
8243             // failure condition..
8244             // we have a scenario where updates need confirming.
8245             // eg. if a locking scenario exists..
8246             // we look for { errors : { needs_confirm : true }} in the response.
8247             if (
8248                 (typeof(action.result) != 'undefined')  &&
8249                 (typeof(action.result.errors) != 'undefined')  &&
8250                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8251            ){
8252                 var _t = this;
8253                 Roo.log("not supported yet");
8254                  /*
8255
8256                 Roo.MessageBox.confirm(
8257                     "Change requires confirmation",
8258                     action.result.errorMsg,
8259                     function(r) {
8260                         if (r != 'yes') {
8261                             return;
8262                         }
8263                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8264                     }
8265
8266                 );
8267                 */
8268
8269
8270                 return;
8271             }
8272
8273             Roo.callback(o.failure, o.scope, [this, action]);
8274             // show an error message if no failed handler is set..
8275             if (!this.hasListener('actionfailed')) {
8276                 Roo.log("need to add dialog support");
8277                 /*
8278                 Roo.MessageBox.alert("Error",
8279                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8280                         action.result.errorMsg :
8281                         "Saving Failed, please check your entries or try again"
8282                 );
8283                 */
8284             }
8285
8286             this.fireEvent('actionfailed', this, action);
8287         }
8288
8289     },
8290     /**
8291      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8292      * @param {String} id The value to search for
8293      * @return Field
8294      */
8295     findField : function(id){
8296         var items = this.getItems();
8297         var field = items.get(id);
8298         if(!field){
8299              items.each(function(f){
8300                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8301                     field = f;
8302                     return false;
8303                 }
8304                 return true;
8305             });
8306         }
8307         return field || null;
8308     },
8309      /**
8310      * Mark fields in this form invalid in bulk.
8311      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8312      * @return {BasicForm} this
8313      */
8314     markInvalid : function(errors){
8315         if(errors instanceof Array){
8316             for(var i = 0, len = errors.length; i < len; i++){
8317                 var fieldError = errors[i];
8318                 var f = this.findField(fieldError.id);
8319                 if(f){
8320                     f.markInvalid(fieldError.msg);
8321                 }
8322             }
8323         }else{
8324             var field, id;
8325             for(id in errors){
8326                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8327                     field.markInvalid(errors[id]);
8328                 }
8329             }
8330         }
8331         //Roo.each(this.childForms || [], function (f) {
8332         //    f.markInvalid(errors);
8333         //});
8334
8335         return this;
8336     },
8337
8338     /**
8339      * Set values for fields in this form in bulk.
8340      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8341      * @return {BasicForm} this
8342      */
8343     setValues : function(values){
8344         if(values instanceof Array){ // array of objects
8345             for(var i = 0, len = values.length; i < len; i++){
8346                 var v = values[i];
8347                 var f = this.findField(v.id);
8348                 if(f){
8349                     f.setValue(v.value);
8350                     if(this.trackResetOnLoad){
8351                         f.originalValue = f.getValue();
8352                     }
8353                 }
8354             }
8355         }else{ // object hash
8356             var field, id;
8357             for(id in values){
8358                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8359
8360                     if (field.setFromData &&
8361                         field.valueField &&
8362                         field.displayField &&
8363                         // combos' with local stores can
8364                         // be queried via setValue()
8365                         // to set their value..
8366                         (field.store && !field.store.isLocal)
8367                         ) {
8368                         // it's a combo
8369                         var sd = { };
8370                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8371                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8372                         field.setFromData(sd);
8373
8374                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8375                         
8376                         field.setFromData(values);
8377                         
8378                     } else {
8379                         field.setValue(values[id]);
8380                     }
8381
8382
8383                     if(this.trackResetOnLoad){
8384                         field.originalValue = field.getValue();
8385                     }
8386                 }
8387             }
8388         }
8389
8390         //Roo.each(this.childForms || [], function (f) {
8391         //    f.setValues(values);
8392         //});
8393
8394         return this;
8395     },
8396
8397     /**
8398      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8399      * they are returned as an array.
8400      * @param {Boolean} asString
8401      * @return {Object}
8402      */
8403     getValues : function(asString){
8404         //if (this.childForms) {
8405             // copy values from the child forms
8406         //    Roo.each(this.childForms, function (f) {
8407         //        this.setValues(f.getValues());
8408         //    }, this);
8409         //}
8410
8411
8412
8413         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8414         if(asString === true){
8415             return fs;
8416         }
8417         return Roo.urlDecode(fs);
8418     },
8419
8420     /**
8421      * Returns the fields in this form as an object with key/value pairs.
8422      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8423      * @return {Object}
8424      */
8425     getFieldValues : function(with_hidden)
8426     {
8427         var items = this.getItems();
8428         var ret = {};
8429         items.each(function(f){
8430             
8431             if (!f.getName()) {
8432                 return;
8433             }
8434             
8435             var v = f.getValue();
8436             
8437             if (f.inputType =='radio') {
8438                 if (typeof(ret[f.getName()]) == 'undefined') {
8439                     ret[f.getName()] = ''; // empty..
8440                 }
8441
8442                 if (!f.el.dom.checked) {
8443                     return;
8444
8445                 }
8446                 v = f.el.dom.value;
8447
8448             }
8449             
8450             if(f.xtype == 'MoneyField'){
8451                 ret[f.currencyName] = f.getCurrency();
8452             }
8453
8454             // not sure if this supported any more..
8455             if ((typeof(v) == 'object') && f.getRawValue) {
8456                 v = f.getRawValue() ; // dates..
8457             }
8458             // combo boxes where name != hiddenName...
8459             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8460                 ret[f.name] = f.getRawValue();
8461             }
8462             ret[f.getName()] = v;
8463         });
8464
8465         return ret;
8466     },
8467
8468     /**
8469      * Clears all invalid messages in this form.
8470      * @return {BasicForm} this
8471      */
8472     clearInvalid : function(){
8473         var items = this.getItems();
8474
8475         items.each(function(f){
8476            f.clearInvalid();
8477         });
8478
8479         return this;
8480     },
8481
8482     /**
8483      * Resets this form.
8484      * @return {BasicForm} this
8485      */
8486     reset : function(){
8487         var items = this.getItems();
8488         items.each(function(f){
8489             f.reset();
8490         });
8491
8492         Roo.each(this.childForms || [], function (f) {
8493             f.reset();
8494         });
8495
8496
8497         return this;
8498     },
8499     
8500     getItems : function()
8501     {
8502         var r=new Roo.util.MixedCollection(false, function(o){
8503             return o.id || (o.id = Roo.id());
8504         });
8505         var iter = function(el) {
8506             if (el.inputEl) {
8507                 r.add(el);
8508             }
8509             if (!el.items) {
8510                 return;
8511             }
8512             Roo.each(el.items,function(e) {
8513                 iter(e);
8514             });
8515         };
8516
8517         iter(this);
8518         return r;
8519     },
8520     
8521     hideFields : function(items)
8522     {
8523         Roo.each(items, function(i){
8524             
8525             var f = this.findField(i);
8526             
8527             if(!f){
8528                 return;
8529             }
8530             
8531             f.hide();
8532             
8533         }, this);
8534     },
8535     
8536     showFields : function(items)
8537     {
8538         Roo.each(items, function(i){
8539             
8540             var f = this.findField(i);
8541             
8542             if(!f){
8543                 return;
8544             }
8545             
8546             f.show();
8547             
8548         }, this);
8549     }
8550
8551 });
8552
8553 Roo.apply(Roo.bootstrap.Form, {
8554     
8555     popover : {
8556         
8557         padding : 5,
8558         
8559         isApplied : false,
8560         
8561         isMasked : false,
8562         
8563         form : false,
8564         
8565         target : false,
8566         
8567         toolTip : false,
8568         
8569         intervalID : false,
8570         
8571         maskEl : false,
8572         
8573         apply : function()
8574         {
8575             if(this.isApplied){
8576                 return;
8577             }
8578             
8579             this.maskEl = {
8580                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8581                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8582                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8583                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8584             };
8585             
8586             this.maskEl.top.enableDisplayMode("block");
8587             this.maskEl.left.enableDisplayMode("block");
8588             this.maskEl.bottom.enableDisplayMode("block");
8589             this.maskEl.right.enableDisplayMode("block");
8590             
8591             this.toolTip = new Roo.bootstrap.Tooltip({
8592                 cls : 'roo-form-error-popover',
8593                 alignment : {
8594                     'left' : ['r-l', [-2,0], 'right'],
8595                     'right' : ['l-r', [2,0], 'left'],
8596                     'bottom' : ['tl-bl', [0,2], 'top'],
8597                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8598                 }
8599             });
8600             
8601             this.toolTip.render(Roo.get(document.body));
8602
8603             this.toolTip.el.enableDisplayMode("block");
8604             
8605             Roo.get(document.body).on('click', function(){
8606                 this.unmask();
8607             }, this);
8608             
8609             Roo.get(document.body).on('touchstart', function(){
8610                 this.unmask();
8611             }, this);
8612             
8613             this.isApplied = true
8614         },
8615         
8616         mask : function(form, target)
8617         {
8618             this.form = form;
8619             
8620             this.target = target;
8621             
8622             if(!this.form.errorMask || !target.el){
8623                 return;
8624             }
8625             
8626             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8627             
8628             Roo.log(scrollable);
8629             
8630             var ot = this.target.el.calcOffsetsTo(scrollable);
8631             
8632             var scrollTo = ot[1] - this.form.maskOffset;
8633             
8634             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8635             
8636             scrollable.scrollTo('top', scrollTo);
8637             
8638             var box = this.target.el.getBox();
8639             Roo.log(box);
8640             var zIndex = Roo.bootstrap.Modal.zIndex++;
8641
8642             
8643             this.maskEl.top.setStyle('position', 'absolute');
8644             this.maskEl.top.setStyle('z-index', zIndex);
8645             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8646             this.maskEl.top.setLeft(0);
8647             this.maskEl.top.setTop(0);
8648             this.maskEl.top.show();
8649             
8650             this.maskEl.left.setStyle('position', 'absolute');
8651             this.maskEl.left.setStyle('z-index', zIndex);
8652             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8653             this.maskEl.left.setLeft(0);
8654             this.maskEl.left.setTop(box.y - this.padding);
8655             this.maskEl.left.show();
8656
8657             this.maskEl.bottom.setStyle('position', 'absolute');
8658             this.maskEl.bottom.setStyle('z-index', zIndex);
8659             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8660             this.maskEl.bottom.setLeft(0);
8661             this.maskEl.bottom.setTop(box.bottom + this.padding);
8662             this.maskEl.bottom.show();
8663
8664             this.maskEl.right.setStyle('position', 'absolute');
8665             this.maskEl.right.setStyle('z-index', zIndex);
8666             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8667             this.maskEl.right.setLeft(box.right + this.padding);
8668             this.maskEl.right.setTop(box.y - this.padding);
8669             this.maskEl.right.show();
8670
8671             this.toolTip.bindEl = this.target.el;
8672
8673             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8674
8675             var tip = this.target.blankText;
8676
8677             if(this.target.getValue() !== '' ) {
8678                 
8679                 if (this.target.invalidText.length) {
8680                     tip = this.target.invalidText;
8681                 } else if (this.target.regexText.length){
8682                     tip = this.target.regexText;
8683                 }
8684             }
8685
8686             this.toolTip.show(tip);
8687
8688             this.intervalID = window.setInterval(function() {
8689                 Roo.bootstrap.Form.popover.unmask();
8690             }, 10000);
8691
8692             window.onwheel = function(){ return false;};
8693             
8694             (function(){ this.isMasked = true; }).defer(500, this);
8695             
8696         },
8697         
8698         unmask : function()
8699         {
8700             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8701                 return;
8702             }
8703             
8704             this.maskEl.top.setStyle('position', 'absolute');
8705             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8706             this.maskEl.top.hide();
8707
8708             this.maskEl.left.setStyle('position', 'absolute');
8709             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8710             this.maskEl.left.hide();
8711
8712             this.maskEl.bottom.setStyle('position', 'absolute');
8713             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8714             this.maskEl.bottom.hide();
8715
8716             this.maskEl.right.setStyle('position', 'absolute');
8717             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8718             this.maskEl.right.hide();
8719             
8720             this.toolTip.hide();
8721             
8722             this.toolTip.el.hide();
8723             
8724             window.onwheel = function(){ return true;};
8725             
8726             if(this.intervalID){
8727                 window.clearInterval(this.intervalID);
8728                 this.intervalID = false;
8729             }
8730             
8731             this.isMasked = false;
8732             
8733         }
8734         
8735     }
8736     
8737 });
8738
8739 /*
8740  * Based on:
8741  * Ext JS Library 1.1.1
8742  * Copyright(c) 2006-2007, Ext JS, LLC.
8743  *
8744  * Originally Released Under LGPL - original licence link has changed is not relivant.
8745  *
8746  * Fork - LGPL
8747  * <script type="text/javascript">
8748  */
8749 /**
8750  * @class Roo.form.VTypes
8751  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8752  * @singleton
8753  */
8754 Roo.form.VTypes = function(){
8755     // closure these in so they are only created once.
8756     var alpha = /^[a-zA-Z_]+$/;
8757     var alphanum = /^[a-zA-Z0-9_]+$/;
8758     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8759     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8760
8761     // All these messages and functions are configurable
8762     return {
8763         /**
8764          * The function used to validate email addresses
8765          * @param {String} value The email address
8766          */
8767         'email' : function(v){
8768             return email.test(v);
8769         },
8770         /**
8771          * The error text to display when the email validation function returns false
8772          * @type String
8773          */
8774         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8775         /**
8776          * The keystroke filter mask to be applied on email input
8777          * @type RegExp
8778          */
8779         'emailMask' : /[a-z0-9_\.\-@]/i,
8780
8781         /**
8782          * The function used to validate URLs
8783          * @param {String} value The URL
8784          */
8785         'url' : function(v){
8786             return url.test(v);
8787         },
8788         /**
8789          * The error text to display when the url validation function returns false
8790          * @type String
8791          */
8792         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8793         
8794         /**
8795          * The function used to validate alpha values
8796          * @param {String} value The value
8797          */
8798         'alpha' : function(v){
8799             return alpha.test(v);
8800         },
8801         /**
8802          * The error text to display when the alpha validation function returns false
8803          * @type String
8804          */
8805         'alphaText' : 'This field should only contain letters and _',
8806         /**
8807          * The keystroke filter mask to be applied on alpha input
8808          * @type RegExp
8809          */
8810         'alphaMask' : /[a-z_]/i,
8811
8812         /**
8813          * The function used to validate alphanumeric values
8814          * @param {String} value The value
8815          */
8816         'alphanum' : function(v){
8817             return alphanum.test(v);
8818         },
8819         /**
8820          * The error text to display when the alphanumeric validation function returns false
8821          * @type String
8822          */
8823         'alphanumText' : 'This field should only contain letters, numbers and _',
8824         /**
8825          * The keystroke filter mask to be applied on alphanumeric input
8826          * @type RegExp
8827          */
8828         'alphanumMask' : /[a-z0-9_]/i
8829     };
8830 }();/*
8831  * - LGPL
8832  *
8833  * Input
8834  * 
8835  */
8836
8837 /**
8838  * @class Roo.bootstrap.Input
8839  * @extends Roo.bootstrap.Component
8840  * Bootstrap Input class
8841  * @cfg {Boolean} disabled is it disabled
8842  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8843  * @cfg {String} name name of the input
8844  * @cfg {string} fieldLabel - the label associated
8845  * @cfg {string} placeholder - placeholder to put in text.
8846  * @cfg {string}  before - input group add on before
8847  * @cfg {string} after - input group add on after
8848  * @cfg {string} size - (lg|sm) or leave empty..
8849  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8850  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8851  * @cfg {Number} md colspan out of 12 for computer-sized screens
8852  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8853  * @cfg {string} value default value of the input
8854  * @cfg {Number} labelWidth set the width of label 
8855  * @cfg {Number} labellg set the width of label (1-12)
8856  * @cfg {Number} labelmd set the width of label (1-12)
8857  * @cfg {Number} labelsm set the width of label (1-12)
8858  * @cfg {Number} labelxs set the width of label (1-12)
8859  * @cfg {String} labelAlign (top|left)
8860  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8861  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8862  * @cfg {String} indicatorpos (left|right) default left
8863  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8864  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8865
8866  * @cfg {String} align (left|center|right) Default left
8867  * @cfg {Boolean} forceFeedback (true|false) Default false
8868  * 
8869  * @constructor
8870  * Create a new Input
8871  * @param {Object} config The config object
8872  */
8873
8874 Roo.bootstrap.Input = function(config){
8875     
8876     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8877     
8878     this.addEvents({
8879         /**
8880          * @event focus
8881          * Fires when this field receives input focus.
8882          * @param {Roo.form.Field} this
8883          */
8884         focus : true,
8885         /**
8886          * @event blur
8887          * Fires when this field loses input focus.
8888          * @param {Roo.form.Field} this
8889          */
8890         blur : true,
8891         /**
8892          * @event specialkey
8893          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8894          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8895          * @param {Roo.form.Field} this
8896          * @param {Roo.EventObject} e The event object
8897          */
8898         specialkey : true,
8899         /**
8900          * @event change
8901          * Fires just before the field blurs if the field value has changed.
8902          * @param {Roo.form.Field} this
8903          * @param {Mixed} newValue The new value
8904          * @param {Mixed} oldValue The original value
8905          */
8906         change : true,
8907         /**
8908          * @event invalid
8909          * Fires after the field has been marked as invalid.
8910          * @param {Roo.form.Field} this
8911          * @param {String} msg The validation message
8912          */
8913         invalid : true,
8914         /**
8915          * @event valid
8916          * Fires after the field has been validated with no errors.
8917          * @param {Roo.form.Field} this
8918          */
8919         valid : true,
8920          /**
8921          * @event keyup
8922          * Fires after the key up
8923          * @param {Roo.form.Field} this
8924          * @param {Roo.EventObject}  e The event Object
8925          */
8926         keyup : true
8927     });
8928 };
8929
8930 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8931      /**
8932      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8933       automatic validation (defaults to "keyup").
8934      */
8935     validationEvent : "keyup",
8936      /**
8937      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8938      */
8939     validateOnBlur : true,
8940     /**
8941      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8942      */
8943     validationDelay : 250,
8944      /**
8945      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8946      */
8947     focusClass : "x-form-focus",  // not needed???
8948     
8949        
8950     /**
8951      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8952      */
8953     invalidClass : "has-warning",
8954     
8955     /**
8956      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8957      */
8958     validClass : "has-success",
8959     
8960     /**
8961      * @cfg {Boolean} hasFeedback (true|false) default true
8962      */
8963     hasFeedback : true,
8964     
8965     /**
8966      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8967      */
8968     invalidFeedbackClass : "glyphicon-warning-sign",
8969     
8970     /**
8971      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8972      */
8973     validFeedbackClass : "glyphicon-ok",
8974     
8975     /**
8976      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8977      */
8978     selectOnFocus : false,
8979     
8980      /**
8981      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8982      */
8983     maskRe : null,
8984        /**
8985      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8986      */
8987     vtype : null,
8988     
8989       /**
8990      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8991      */
8992     disableKeyFilter : false,
8993     
8994        /**
8995      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8996      */
8997     disabled : false,
8998      /**
8999      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9000      */
9001     allowBlank : true,
9002     /**
9003      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9004      */
9005     blankText : "Please complete this mandatory field",
9006     
9007      /**
9008      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9009      */
9010     minLength : 0,
9011     /**
9012      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9013      */
9014     maxLength : Number.MAX_VALUE,
9015     /**
9016      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9017      */
9018     minLengthText : "The minimum length for this field is {0}",
9019     /**
9020      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9021      */
9022     maxLengthText : "The maximum length for this field is {0}",
9023   
9024     
9025     /**
9026      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9027      * If available, this function will be called only after the basic validators all return true, and will be passed the
9028      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9029      */
9030     validator : null,
9031     /**
9032      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9033      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9034      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9035      */
9036     regex : null,
9037     /**
9038      * @cfg {String} regexText -- Depricated - use Invalid Text
9039      */
9040     regexText : "",
9041     
9042     /**
9043      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9044      */
9045     invalidText : "",
9046     
9047     
9048     
9049     autocomplete: false,
9050     
9051     
9052     fieldLabel : '',
9053     inputType : 'text',
9054     
9055     name : false,
9056     placeholder: false,
9057     before : false,
9058     after : false,
9059     size : false,
9060     hasFocus : false,
9061     preventMark: false,
9062     isFormField : true,
9063     value : '',
9064     labelWidth : 2,
9065     labelAlign : false,
9066     readOnly : false,
9067     align : false,
9068     formatedValue : false,
9069     forceFeedback : false,
9070     
9071     indicatorpos : 'left',
9072     
9073     labellg : 0,
9074     labelmd : 0,
9075     labelsm : 0,
9076     labelxs : 0,
9077     
9078     capture : '',
9079     accept : '',
9080     
9081     parentLabelAlign : function()
9082     {
9083         var parent = this;
9084         while (parent.parent()) {
9085             parent = parent.parent();
9086             if (typeof(parent.labelAlign) !='undefined') {
9087                 return parent.labelAlign;
9088             }
9089         }
9090         return 'left';
9091         
9092     },
9093     
9094     getAutoCreate : function()
9095     {
9096         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9097         
9098         var id = Roo.id();
9099         
9100         var cfg = {};
9101         
9102         if(this.inputType != 'hidden'){
9103             cfg.cls = 'form-group' //input-group
9104         }
9105         
9106         var input =  {
9107             tag: 'input',
9108             id : id,
9109             type : this.inputType,
9110             value : this.value,
9111             cls : 'form-control',
9112             placeholder : this.placeholder || '',
9113             autocomplete : this.autocomplete || 'new-password'
9114         };
9115         
9116         if(this.capture.length){
9117             input.capture = this.capture;
9118         }
9119         
9120         if(this.accept.length){
9121             input.accept = this.accept + "/*";
9122         }
9123         
9124         if(this.align){
9125             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9126         }
9127         
9128         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9129             input.maxLength = this.maxLength;
9130         }
9131         
9132         if (this.disabled) {
9133             input.disabled=true;
9134         }
9135         
9136         if (this.readOnly) {
9137             input.readonly=true;
9138         }
9139         
9140         if (this.name) {
9141             input.name = this.name;
9142         }
9143         
9144         if (this.size) {
9145             input.cls += ' input-' + this.size;
9146         }
9147         
9148         var settings=this;
9149         ['xs','sm','md','lg'].map(function(size){
9150             if (settings[size]) {
9151                 cfg.cls += ' col-' + size + '-' + settings[size];
9152             }
9153         });
9154         
9155         var inputblock = input;
9156         
9157         var feedback = {
9158             tag: 'span',
9159             cls: 'glyphicon form-control-feedback'
9160         };
9161             
9162         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9163             
9164             inputblock = {
9165                 cls : 'has-feedback',
9166                 cn :  [
9167                     input,
9168                     feedback
9169                 ] 
9170             };  
9171         }
9172         
9173         if (this.before || this.after) {
9174             
9175             inputblock = {
9176                 cls : 'input-group',
9177                 cn :  [] 
9178             };
9179             
9180             if (this.before && typeof(this.before) == 'string') {
9181                 
9182                 inputblock.cn.push({
9183                     tag :'span',
9184                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9185                     html : this.before
9186                 });
9187             }
9188             if (this.before && typeof(this.before) == 'object') {
9189                 this.before = Roo.factory(this.before);
9190                 
9191                 inputblock.cn.push({
9192                     tag :'span',
9193                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9194                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9195                 });
9196             }
9197             
9198             inputblock.cn.push(input);
9199             
9200             if (this.after && typeof(this.after) == 'string') {
9201                 inputblock.cn.push({
9202                     tag :'span',
9203                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9204                     html : this.after
9205                 });
9206             }
9207             if (this.after && typeof(this.after) == 'object') {
9208                 this.after = Roo.factory(this.after);
9209                 
9210                 inputblock.cn.push({
9211                     tag :'span',
9212                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9213                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9214                 });
9215             }
9216             
9217             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9218                 inputblock.cls += ' has-feedback';
9219                 inputblock.cn.push(feedback);
9220             }
9221         };
9222         var indicator = {
9223             tag : 'i',
9224             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9225             tooltip : 'This field is required'
9226         };
9227         if (Roo.bootstrap.version == 4) {
9228             indicator = {
9229                 tag : 'i',
9230                 style : 'display-none'
9231             };
9232         }
9233         if (align ==='left' && this.fieldLabel.length) {
9234             
9235             cfg.cls += ' roo-form-group-label-left row';
9236             
9237             cfg.cn = [
9238                 indicator,
9239                 {
9240                     tag: 'label',
9241                     'for' :  id,
9242                     cls : 'control-label col-form-label',
9243                     html : this.fieldLabel
9244
9245                 },
9246                 {
9247                     cls : "", 
9248                     cn: [
9249                         inputblock
9250                     ]
9251                 }
9252             ];
9253             
9254             var labelCfg = cfg.cn[1];
9255             var contentCfg = cfg.cn[2];
9256             
9257             if(this.indicatorpos == 'right'){
9258                 cfg.cn = [
9259                     {
9260                         tag: 'label',
9261                         'for' :  id,
9262                         cls : 'control-label col-form-label',
9263                         cn : [
9264                             {
9265                                 tag : 'span',
9266                                 html : this.fieldLabel
9267                             },
9268                             indicator
9269                         ]
9270                     },
9271                     {
9272                         cls : "",
9273                         cn: [
9274                             inputblock
9275                         ]
9276                     }
9277
9278                 ];
9279                 
9280                 labelCfg = cfg.cn[0];
9281                 contentCfg = cfg.cn[1];
9282             
9283             }
9284             
9285             if(this.labelWidth > 12){
9286                 labelCfg.style = "width: " + this.labelWidth + 'px';
9287             }
9288             
9289             if(this.labelWidth < 13 && this.labelmd == 0){
9290                 this.labelmd = this.labelWidth;
9291             }
9292             
9293             if(this.labellg > 0){
9294                 labelCfg.cls += ' col-lg-' + this.labellg;
9295                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9296             }
9297             
9298             if(this.labelmd > 0){
9299                 labelCfg.cls += ' col-md-' + this.labelmd;
9300                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9301             }
9302             
9303             if(this.labelsm > 0){
9304                 labelCfg.cls += ' col-sm-' + this.labelsm;
9305                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9306             }
9307             
9308             if(this.labelxs > 0){
9309                 labelCfg.cls += ' col-xs-' + this.labelxs;
9310                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9311             }
9312             
9313             
9314         } else if ( this.fieldLabel.length) {
9315                 
9316             cfg.cn = [
9317                 {
9318                     tag : 'i',
9319                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9320                     tooltip : 'This field is required'
9321                 },
9322                 {
9323                     tag: 'label',
9324                    //cls : 'input-group-addon',
9325                     html : this.fieldLabel
9326
9327                 },
9328
9329                inputblock
9330
9331            ];
9332            
9333            if(this.indicatorpos == 'right'){
9334                 
9335                 cfg.cn = [
9336                     {
9337                         tag: 'label',
9338                        //cls : 'input-group-addon',
9339                         html : this.fieldLabel
9340
9341                     },
9342                     {
9343                         tag : 'i',
9344                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9345                         tooltip : 'This field is required'
9346                     },
9347
9348                    inputblock
9349
9350                ];
9351
9352             }
9353
9354         } else {
9355             
9356             cfg.cn = [
9357
9358                     inputblock
9359
9360             ];
9361                 
9362                 
9363         };
9364         
9365         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9366            cfg.cls += ' navbar-form';
9367         }
9368         
9369         if (this.parentType === 'NavGroup') {
9370            cfg.cls += ' navbar-form';
9371            cfg.tag = 'li';
9372         }
9373         
9374         return cfg;
9375         
9376     },
9377     /**
9378      * return the real input element.
9379      */
9380     inputEl: function ()
9381     {
9382         return this.el.select('input.form-control',true).first();
9383     },
9384     
9385     tooltipEl : function()
9386     {
9387         return this.inputEl();
9388     },
9389     
9390     indicatorEl : function()
9391     {
9392         if (Roo.bootstrap.version == 4) {
9393             return false; // not enabled in v4 yet.
9394         }
9395         
9396         var indicator = this.el.select('i.roo-required-indicator',true).first();
9397         
9398         if(!indicator){
9399             return false;
9400         }
9401         
9402         return indicator;
9403         
9404     },
9405     
9406     setDisabled : function(v)
9407     {
9408         var i  = this.inputEl().dom;
9409         if (!v) {
9410             i.removeAttribute('disabled');
9411             return;
9412             
9413         }
9414         i.setAttribute('disabled','true');
9415     },
9416     initEvents : function()
9417     {
9418           
9419         this.inputEl().on("keydown" , this.fireKey,  this);
9420         this.inputEl().on("focus", this.onFocus,  this);
9421         this.inputEl().on("blur", this.onBlur,  this);
9422         
9423         this.inputEl().relayEvent('keyup', this);
9424         
9425         this.indicator = this.indicatorEl();
9426         
9427         if(this.indicator){
9428             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9429         }
9430  
9431         // reference to original value for reset
9432         this.originalValue = this.getValue();
9433         //Roo.form.TextField.superclass.initEvents.call(this);
9434         if(this.validationEvent == 'keyup'){
9435             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9436             this.inputEl().on('keyup', this.filterValidation, this);
9437         }
9438         else if(this.validationEvent !== false){
9439             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9440         }
9441         
9442         if(this.selectOnFocus){
9443             this.on("focus", this.preFocus, this);
9444             
9445         }
9446         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9447             this.inputEl().on("keypress", this.filterKeys, this);
9448         } else {
9449             this.inputEl().relayEvent('keypress', this);
9450         }
9451        /* if(this.grow){
9452             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9453             this.el.on("click", this.autoSize,  this);
9454         }
9455         */
9456         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9457             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9458         }
9459         
9460         if (typeof(this.before) == 'object') {
9461             this.before.render(this.el.select('.roo-input-before',true).first());
9462         }
9463         if (typeof(this.after) == 'object') {
9464             this.after.render(this.el.select('.roo-input-after',true).first());
9465         }
9466         
9467         this.inputEl().on('change', this.onChange, this);
9468         
9469     },
9470     filterValidation : function(e){
9471         if(!e.isNavKeyPress()){
9472             this.validationTask.delay(this.validationDelay);
9473         }
9474     },
9475      /**
9476      * Validates the field value
9477      * @return {Boolean} True if the value is valid, else false
9478      */
9479     validate : function(){
9480         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9481         if(this.disabled || this.validateValue(this.getRawValue())){
9482             this.markValid();
9483             return true;
9484         }
9485         
9486         this.markInvalid();
9487         return false;
9488     },
9489     
9490     
9491     /**
9492      * Validates a value according to the field's validation rules and marks the field as invalid
9493      * if the validation fails
9494      * @param {Mixed} value The value to validate
9495      * @return {Boolean} True if the value is valid, else false
9496      */
9497     validateValue : function(value)
9498     {
9499         if(this.getVisibilityEl().hasClass('hidden')){
9500             return true;
9501         }
9502         
9503         if(value.length < 1)  { // if it's blank
9504             if(this.allowBlank){
9505                 return true;
9506             }
9507             return false;
9508         }
9509         
9510         if(value.length < this.minLength){
9511             return false;
9512         }
9513         if(value.length > this.maxLength){
9514             return false;
9515         }
9516         if(this.vtype){
9517             var vt = Roo.form.VTypes;
9518             if(!vt[this.vtype](value, this)){
9519                 return false;
9520             }
9521         }
9522         if(typeof this.validator == "function"){
9523             var msg = this.validator(value);
9524             if(msg !== true){
9525                 return false;
9526             }
9527             if (typeof(msg) == 'string') {
9528                 this.invalidText = msg;
9529             }
9530         }
9531         
9532         if(this.regex && !this.regex.test(value)){
9533             return false;
9534         }
9535         
9536         return true;
9537     },
9538     
9539      // private
9540     fireKey : function(e){
9541         //Roo.log('field ' + e.getKey());
9542         if(e.isNavKeyPress()){
9543             this.fireEvent("specialkey", this, e);
9544         }
9545     },
9546     focus : function (selectText){
9547         if(this.rendered){
9548             this.inputEl().focus();
9549             if(selectText === true){
9550                 this.inputEl().dom.select();
9551             }
9552         }
9553         return this;
9554     } ,
9555     
9556     onFocus : function(){
9557         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9558            // this.el.addClass(this.focusClass);
9559         }
9560         if(!this.hasFocus){
9561             this.hasFocus = true;
9562             this.startValue = this.getValue();
9563             this.fireEvent("focus", this);
9564         }
9565     },
9566     
9567     beforeBlur : Roo.emptyFn,
9568
9569     
9570     // private
9571     onBlur : function(){
9572         this.beforeBlur();
9573         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9574             //this.el.removeClass(this.focusClass);
9575         }
9576         this.hasFocus = false;
9577         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9578             this.validate();
9579         }
9580         var v = this.getValue();
9581         if(String(v) !== String(this.startValue)){
9582             this.fireEvent('change', this, v, this.startValue);
9583         }
9584         this.fireEvent("blur", this);
9585     },
9586     
9587     onChange : function(e)
9588     {
9589         var v = this.getValue();
9590         if(String(v) !== String(this.startValue)){
9591             this.fireEvent('change', this, v, this.startValue);
9592         }
9593         
9594     },
9595     
9596     /**
9597      * Resets the current field value to the originally loaded value and clears any validation messages
9598      */
9599     reset : function(){
9600         this.setValue(this.originalValue);
9601         this.validate();
9602     },
9603      /**
9604      * Returns the name of the field
9605      * @return {Mixed} name The name field
9606      */
9607     getName: function(){
9608         return this.name;
9609     },
9610      /**
9611      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9612      * @return {Mixed} value The field value
9613      */
9614     getValue : function(){
9615         
9616         var v = this.inputEl().getValue();
9617         
9618         return v;
9619     },
9620     /**
9621      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9622      * @return {Mixed} value The field value
9623      */
9624     getRawValue : function(){
9625         var v = this.inputEl().getValue();
9626         
9627         return v;
9628     },
9629     
9630     /**
9631      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9632      * @param {Mixed} value The value to set
9633      */
9634     setRawValue : function(v){
9635         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9636     },
9637     
9638     selectText : function(start, end){
9639         var v = this.getRawValue();
9640         if(v.length > 0){
9641             start = start === undefined ? 0 : start;
9642             end = end === undefined ? v.length : end;
9643             var d = this.inputEl().dom;
9644             if(d.setSelectionRange){
9645                 d.setSelectionRange(start, end);
9646             }else if(d.createTextRange){
9647                 var range = d.createTextRange();
9648                 range.moveStart("character", start);
9649                 range.moveEnd("character", v.length-end);
9650                 range.select();
9651             }
9652         }
9653     },
9654     
9655     /**
9656      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9657      * @param {Mixed} value The value to set
9658      */
9659     setValue : function(v){
9660         this.value = v;
9661         if(this.rendered){
9662             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9663             this.validate();
9664         }
9665     },
9666     
9667     /*
9668     processValue : function(value){
9669         if(this.stripCharsRe){
9670             var newValue = value.replace(this.stripCharsRe, '');
9671             if(newValue !== value){
9672                 this.setRawValue(newValue);
9673                 return newValue;
9674             }
9675         }
9676         return value;
9677     },
9678   */
9679     preFocus : function(){
9680         
9681         if(this.selectOnFocus){
9682             this.inputEl().dom.select();
9683         }
9684     },
9685     filterKeys : function(e){
9686         var k = e.getKey();
9687         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9688             return;
9689         }
9690         var c = e.getCharCode(), cc = String.fromCharCode(c);
9691         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9692             return;
9693         }
9694         if(!this.maskRe.test(cc)){
9695             e.stopEvent();
9696         }
9697     },
9698      /**
9699      * Clear any invalid styles/messages for this field
9700      */
9701     clearInvalid : function(){
9702         
9703         if(!this.el || this.preventMark){ // not rendered
9704             return;
9705         }
9706         
9707      
9708         this.el.removeClass(this.invalidClass);
9709         
9710         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9711             
9712             var feedback = this.el.select('.form-control-feedback', true).first();
9713             
9714             if(feedback){
9715                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9716             }
9717             
9718         }
9719         
9720         if(this.indicator){
9721             this.indicator.removeClass('visible');
9722             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9723         }
9724         
9725         this.fireEvent('valid', this);
9726     },
9727     
9728      /**
9729      * Mark this field as valid
9730      */
9731     markValid : function()
9732     {
9733         if(!this.el  || this.preventMark){ // not rendered...
9734             return;
9735         }
9736         
9737         this.el.removeClass([this.invalidClass, this.validClass]);
9738         
9739         var feedback = this.el.select('.form-control-feedback', true).first();
9740             
9741         if(feedback){
9742             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9743         }
9744         
9745         if(this.indicator){
9746             this.indicator.removeClass('visible');
9747             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9748         }
9749         
9750         if(this.disabled){
9751             return;
9752         }
9753         
9754         if(this.allowBlank && !this.getRawValue().length){
9755             return;
9756         }
9757         
9758         this.el.addClass(this.validClass);
9759         
9760         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9761             
9762             var feedback = this.el.select('.form-control-feedback', true).first();
9763             
9764             if(feedback){
9765                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9766                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9767             }
9768             
9769         }
9770         
9771         this.fireEvent('valid', this);
9772     },
9773     
9774      /**
9775      * Mark this field as invalid
9776      * @param {String} msg The validation message
9777      */
9778     markInvalid : function(msg)
9779     {
9780         if(!this.el  || this.preventMark){ // not rendered
9781             return;
9782         }
9783         
9784         this.el.removeClass([this.invalidClass, this.validClass]);
9785         
9786         var feedback = this.el.select('.form-control-feedback', true).first();
9787             
9788         if(feedback){
9789             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9790         }
9791
9792         if(this.disabled){
9793             return;
9794         }
9795         
9796         if(this.allowBlank && !this.getRawValue().length){
9797             return;
9798         }
9799         
9800         if(this.indicator){
9801             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9802             this.indicator.addClass('visible');
9803         }
9804         
9805         this.el.addClass(this.invalidClass);
9806         
9807         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9808             
9809             var feedback = this.el.select('.form-control-feedback', true).first();
9810             
9811             if(feedback){
9812                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9813                 
9814                 if(this.getValue().length || this.forceFeedback){
9815                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9816                 }
9817                 
9818             }
9819             
9820         }
9821         
9822         this.fireEvent('invalid', this, msg);
9823     },
9824     // private
9825     SafariOnKeyDown : function(event)
9826     {
9827         // this is a workaround for a password hang bug on chrome/ webkit.
9828         if (this.inputEl().dom.type != 'password') {
9829             return;
9830         }
9831         
9832         var isSelectAll = false;
9833         
9834         if(this.inputEl().dom.selectionEnd > 0){
9835             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9836         }
9837         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9838             event.preventDefault();
9839             this.setValue('');
9840             return;
9841         }
9842         
9843         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9844             
9845             event.preventDefault();
9846             // this is very hacky as keydown always get's upper case.
9847             //
9848             var cc = String.fromCharCode(event.getCharCode());
9849             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9850             
9851         }
9852     },
9853     adjustWidth : function(tag, w){
9854         tag = tag.toLowerCase();
9855         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9856             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9857                 if(tag == 'input'){
9858                     return w + 2;
9859                 }
9860                 if(tag == 'textarea'){
9861                     return w-2;
9862                 }
9863             }else if(Roo.isOpera){
9864                 if(tag == 'input'){
9865                     return w + 2;
9866                 }
9867                 if(tag == 'textarea'){
9868                     return w-2;
9869                 }
9870             }
9871         }
9872         return w;
9873     },
9874     
9875     setFieldLabel : function(v)
9876     {
9877         if(!this.rendered){
9878             return;
9879         }
9880         
9881         if(this.indicatorEl()){
9882             var ar = this.el.select('label > span',true);
9883             
9884             if (ar.elements.length) {
9885                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9886                 this.fieldLabel = v;
9887                 return;
9888             }
9889             
9890             var br = this.el.select('label',true);
9891             
9892             if(br.elements.length) {
9893                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9894                 this.fieldLabel = v;
9895                 return;
9896             }
9897             
9898             Roo.log('Cannot Found any of label > span || label in input');
9899             return;
9900         }
9901         
9902         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9903         this.fieldLabel = v;
9904         
9905         
9906     }
9907 });
9908
9909  
9910 /*
9911  * - LGPL
9912  *
9913  * Input
9914  * 
9915  */
9916
9917 /**
9918  * @class Roo.bootstrap.TextArea
9919  * @extends Roo.bootstrap.Input
9920  * Bootstrap TextArea class
9921  * @cfg {Number} cols Specifies the visible width of a text area
9922  * @cfg {Number} rows Specifies the visible number of lines in a text area
9923  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9924  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9925  * @cfg {string} html text
9926  * 
9927  * @constructor
9928  * Create a new TextArea
9929  * @param {Object} config The config object
9930  */
9931
9932 Roo.bootstrap.TextArea = function(config){
9933     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9934    
9935 };
9936
9937 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9938      
9939     cols : false,
9940     rows : 5,
9941     readOnly : false,
9942     warp : 'soft',
9943     resize : false,
9944     value: false,
9945     html: false,
9946     
9947     getAutoCreate : function(){
9948         
9949         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9950         
9951         var id = Roo.id();
9952         
9953         var cfg = {};
9954         
9955         if(this.inputType != 'hidden'){
9956             cfg.cls = 'form-group' //input-group
9957         }
9958         
9959         var input =  {
9960             tag: 'textarea',
9961             id : id,
9962             warp : this.warp,
9963             rows : this.rows,
9964             value : this.value || '',
9965             html: this.html || '',
9966             cls : 'form-control',
9967             placeholder : this.placeholder || '' 
9968             
9969         };
9970         
9971         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9972             input.maxLength = this.maxLength;
9973         }
9974         
9975         if(this.resize){
9976             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9977         }
9978         
9979         if(this.cols){
9980             input.cols = this.cols;
9981         }
9982         
9983         if (this.readOnly) {
9984             input.readonly = true;
9985         }
9986         
9987         if (this.name) {
9988             input.name = this.name;
9989         }
9990         
9991         if (this.size) {
9992             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9993         }
9994         
9995         var settings=this;
9996         ['xs','sm','md','lg'].map(function(size){
9997             if (settings[size]) {
9998                 cfg.cls += ' col-' + size + '-' + settings[size];
9999             }
10000         });
10001         
10002         var inputblock = input;
10003         
10004         if(this.hasFeedback && !this.allowBlank){
10005             
10006             var feedback = {
10007                 tag: 'span',
10008                 cls: 'glyphicon form-control-feedback'
10009             };
10010
10011             inputblock = {
10012                 cls : 'has-feedback',
10013                 cn :  [
10014                     input,
10015                     feedback
10016                 ] 
10017             };  
10018         }
10019         
10020         
10021         if (this.before || this.after) {
10022             
10023             inputblock = {
10024                 cls : 'input-group',
10025                 cn :  [] 
10026             };
10027             if (this.before) {
10028                 inputblock.cn.push({
10029                     tag :'span',
10030                     cls : 'input-group-addon',
10031                     html : this.before
10032                 });
10033             }
10034             
10035             inputblock.cn.push(input);
10036             
10037             if(this.hasFeedback && !this.allowBlank){
10038                 inputblock.cls += ' has-feedback';
10039                 inputblock.cn.push(feedback);
10040             }
10041             
10042             if (this.after) {
10043                 inputblock.cn.push({
10044                     tag :'span',
10045                     cls : 'input-group-addon',
10046                     html : this.after
10047                 });
10048             }
10049             
10050         }
10051         
10052         if (align ==='left' && this.fieldLabel.length) {
10053             cfg.cn = [
10054                 {
10055                     tag: 'label',
10056                     'for' :  id,
10057                     cls : 'control-label',
10058                     html : this.fieldLabel
10059                 },
10060                 {
10061                     cls : "",
10062                     cn: [
10063                         inputblock
10064                     ]
10065                 }
10066
10067             ];
10068             
10069             if(this.labelWidth > 12){
10070                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10071             }
10072
10073             if(this.labelWidth < 13 && this.labelmd == 0){
10074                 this.labelmd = this.labelWidth;
10075             }
10076
10077             if(this.labellg > 0){
10078                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10079                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10080             }
10081
10082             if(this.labelmd > 0){
10083                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10084                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10085             }
10086
10087             if(this.labelsm > 0){
10088                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10089                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10090             }
10091
10092             if(this.labelxs > 0){
10093                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10094                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10095             }
10096             
10097         } else if ( this.fieldLabel.length) {
10098             cfg.cn = [
10099
10100                {
10101                    tag: 'label',
10102                    //cls : 'input-group-addon',
10103                    html : this.fieldLabel
10104
10105                },
10106
10107                inputblock
10108
10109            ];
10110
10111         } else {
10112
10113             cfg.cn = [
10114
10115                 inputblock
10116
10117             ];
10118                 
10119         }
10120         
10121         if (this.disabled) {
10122             input.disabled=true;
10123         }
10124         
10125         return cfg;
10126         
10127     },
10128     /**
10129      * return the real textarea element.
10130      */
10131     inputEl: function ()
10132     {
10133         return this.el.select('textarea.form-control',true).first();
10134     },
10135     
10136     /**
10137      * Clear any invalid styles/messages for this field
10138      */
10139     clearInvalid : function()
10140     {
10141         
10142         if(!this.el || this.preventMark){ // not rendered
10143             return;
10144         }
10145         
10146         var label = this.el.select('label', true).first();
10147         var icon = this.el.select('i.fa-star', true).first();
10148         
10149         if(label && icon){
10150             icon.remove();
10151         }
10152         
10153         this.el.removeClass(this.invalidClass);
10154         
10155         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10156             
10157             var feedback = this.el.select('.form-control-feedback', true).first();
10158             
10159             if(feedback){
10160                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10161             }
10162             
10163         }
10164         
10165         this.fireEvent('valid', this);
10166     },
10167     
10168      /**
10169      * Mark this field as valid
10170      */
10171     markValid : function()
10172     {
10173         if(!this.el  || this.preventMark){ // not rendered
10174             return;
10175         }
10176         
10177         this.el.removeClass([this.invalidClass, this.validClass]);
10178         
10179         var feedback = this.el.select('.form-control-feedback', true).first();
10180             
10181         if(feedback){
10182             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10183         }
10184
10185         if(this.disabled || this.allowBlank){
10186             return;
10187         }
10188         
10189         var label = this.el.select('label', true).first();
10190         var icon = this.el.select('i.fa-star', true).first();
10191         
10192         if(label && icon){
10193             icon.remove();
10194         }
10195         
10196         this.el.addClass(this.validClass);
10197         
10198         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10199             
10200             var feedback = this.el.select('.form-control-feedback', true).first();
10201             
10202             if(feedback){
10203                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10204                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10205             }
10206             
10207         }
10208         
10209         this.fireEvent('valid', this);
10210     },
10211     
10212      /**
10213      * Mark this field as invalid
10214      * @param {String} msg The validation message
10215      */
10216     markInvalid : function(msg)
10217     {
10218         if(!this.el  || this.preventMark){ // not rendered
10219             return;
10220         }
10221         
10222         this.el.removeClass([this.invalidClass, this.validClass]);
10223         
10224         var feedback = this.el.select('.form-control-feedback', true).first();
10225             
10226         if(feedback){
10227             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10228         }
10229
10230         if(this.disabled || this.allowBlank){
10231             return;
10232         }
10233         
10234         var label = this.el.select('label', true).first();
10235         var icon = this.el.select('i.fa-star', true).first();
10236         
10237         if(!this.getValue().length && label && !icon){
10238             this.el.createChild({
10239                 tag : 'i',
10240                 cls : 'text-danger fa fa-lg fa-star',
10241                 tooltip : 'This field is required',
10242                 style : 'margin-right:5px;'
10243             }, label, true);
10244         }
10245
10246         this.el.addClass(this.invalidClass);
10247         
10248         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10249             
10250             var feedback = this.el.select('.form-control-feedback', true).first();
10251             
10252             if(feedback){
10253                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10254                 
10255                 if(this.getValue().length || this.forceFeedback){
10256                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10257                 }
10258                 
10259             }
10260             
10261         }
10262         
10263         this.fireEvent('invalid', this, msg);
10264     }
10265 });
10266
10267  
10268 /*
10269  * - LGPL
10270  *
10271  * trigger field - base class for combo..
10272  * 
10273  */
10274  
10275 /**
10276  * @class Roo.bootstrap.TriggerField
10277  * @extends Roo.bootstrap.Input
10278  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10279  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10280  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10281  * for which you can provide a custom implementation.  For example:
10282  * <pre><code>
10283 var trigger = new Roo.bootstrap.TriggerField();
10284 trigger.onTriggerClick = myTriggerFn;
10285 trigger.applyTo('my-field');
10286 </code></pre>
10287  *
10288  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10289  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10290  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10291  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10292  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10293
10294  * @constructor
10295  * Create a new TriggerField.
10296  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10297  * to the base TextField)
10298  */
10299 Roo.bootstrap.TriggerField = function(config){
10300     this.mimicing = false;
10301     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10302 };
10303
10304 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10305     /**
10306      * @cfg {String} triggerClass A CSS class to apply to the trigger
10307      */
10308      /**
10309      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10310      */
10311     hideTrigger:false,
10312
10313     /**
10314      * @cfg {Boolean} removable (true|false) special filter default false
10315      */
10316     removable : false,
10317     
10318     /** @cfg {Boolean} grow @hide */
10319     /** @cfg {Number} growMin @hide */
10320     /** @cfg {Number} growMax @hide */
10321
10322     /**
10323      * @hide 
10324      * @method
10325      */
10326     autoSize: Roo.emptyFn,
10327     // private
10328     monitorTab : true,
10329     // private
10330     deferHeight : true,
10331
10332     
10333     actionMode : 'wrap',
10334     
10335     caret : false,
10336     
10337     
10338     getAutoCreate : function(){
10339        
10340         var align = this.labelAlign || this.parentLabelAlign();
10341         
10342         var id = Roo.id();
10343         
10344         var cfg = {
10345             cls: 'form-group' //input-group
10346         };
10347         
10348         
10349         var input =  {
10350             tag: 'input',
10351             id : id,
10352             type : this.inputType,
10353             cls : 'form-control',
10354             autocomplete: 'new-password',
10355             placeholder : this.placeholder || '' 
10356             
10357         };
10358         if (this.name) {
10359             input.name = this.name;
10360         }
10361         if (this.size) {
10362             input.cls += ' input-' + this.size;
10363         }
10364         
10365         if (this.disabled) {
10366             input.disabled=true;
10367         }
10368         
10369         var inputblock = input;
10370         
10371         if(this.hasFeedback && !this.allowBlank){
10372             
10373             var feedback = {
10374                 tag: 'span',
10375                 cls: 'glyphicon form-control-feedback'
10376             };
10377             
10378             if(this.removable && !this.editable && !this.tickable){
10379                 inputblock = {
10380                     cls : 'has-feedback',
10381                     cn :  [
10382                         inputblock,
10383                         {
10384                             tag: 'button',
10385                             html : 'x',
10386                             cls : 'roo-combo-removable-btn close'
10387                         },
10388                         feedback
10389                     ] 
10390                 };
10391             } else {
10392                 inputblock = {
10393                     cls : 'has-feedback',
10394                     cn :  [
10395                         inputblock,
10396                         feedback
10397                     ] 
10398                 };
10399             }
10400
10401         } else {
10402             if(this.removable && !this.editable && !this.tickable){
10403                 inputblock = {
10404                     cls : 'roo-removable',
10405                     cn :  [
10406                         inputblock,
10407                         {
10408                             tag: 'button',
10409                             html : 'x',
10410                             cls : 'roo-combo-removable-btn close'
10411                         }
10412                     ] 
10413                 };
10414             }
10415         }
10416         
10417         if (this.before || this.after) {
10418             
10419             inputblock = {
10420                 cls : 'input-group',
10421                 cn :  [] 
10422             };
10423             if (this.before) {
10424                 inputblock.cn.push({
10425                     tag :'span',
10426                     cls : 'input-group-addon input-group-prepend input-group-text',
10427                     html : this.before
10428                 });
10429             }
10430             
10431             inputblock.cn.push(input);
10432             
10433             if(this.hasFeedback && !this.allowBlank){
10434                 inputblock.cls += ' has-feedback';
10435                 inputblock.cn.push(feedback);
10436             }
10437             
10438             if (this.after) {
10439                 inputblock.cn.push({
10440                     tag :'span',
10441                     cls : 'input-group-addon input-group-append input-group-text',
10442                     html : this.after
10443                 });
10444             }
10445             
10446         };
10447         
10448       
10449         
10450         var ibwrap = inputblock;
10451         
10452         if(this.multiple){
10453             ibwrap = {
10454                 tag: 'ul',
10455                 cls: 'roo-select2-choices',
10456                 cn:[
10457                     {
10458                         tag: 'li',
10459                         cls: 'roo-select2-search-field',
10460                         cn: [
10461
10462                             inputblock
10463                         ]
10464                     }
10465                 ]
10466             };
10467                 
10468         }
10469         
10470         var combobox = {
10471             cls: 'roo-select2-container input-group',
10472             cn: [
10473                  {
10474                     tag: 'input',
10475                     type : 'hidden',
10476                     cls: 'form-hidden-field'
10477                 },
10478                 ibwrap
10479             ]
10480         };
10481         
10482         if(!this.multiple && this.showToggleBtn){
10483             
10484             var caret = {
10485                         tag: 'span',
10486                         cls: 'caret'
10487              };
10488             if (this.caret != false) {
10489                 caret = {
10490                      tag: 'i',
10491                      cls: 'fa fa-' + this.caret
10492                 };
10493                 
10494             }
10495             
10496             combobox.cn.push({
10497                 tag :'span',
10498                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10499                 cn : [
10500                     caret,
10501                     {
10502                         tag: 'span',
10503                         cls: 'combobox-clear',
10504                         cn  : [
10505                             {
10506                                 tag : 'i',
10507                                 cls: 'icon-remove'
10508                             }
10509                         ]
10510                     }
10511                 ]
10512
10513             })
10514         }
10515         
10516         if(this.multiple){
10517             combobox.cls += ' roo-select2-container-multi';
10518         }
10519          var indicator = {
10520             tag : 'i',
10521             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10522             tooltip : 'This field is required'
10523         };
10524         if (Roo.bootstrap.version == 4) {
10525             indicator = {
10526                 tag : 'i',
10527                 style : 'display:none'
10528             };
10529         }
10530         
10531         
10532         if (align ==='left' && this.fieldLabel.length) {
10533             
10534             cfg.cls += ' roo-form-group-label-left row';
10535
10536             cfg.cn = [
10537                 indicator,
10538                 {
10539                     tag: 'label',
10540                     'for' :  id,
10541                     cls : 'control-label',
10542                     html : this.fieldLabel
10543
10544                 },
10545                 {
10546                     cls : "", 
10547                     cn: [
10548                         combobox
10549                     ]
10550                 }
10551
10552             ];
10553             
10554             var labelCfg = cfg.cn[1];
10555             var contentCfg = cfg.cn[2];
10556             
10557             if(this.indicatorpos == 'right'){
10558                 cfg.cn = [
10559                     {
10560                         tag: 'label',
10561                         'for' :  id,
10562                         cls : 'control-label',
10563                         cn : [
10564                             {
10565                                 tag : 'span',
10566                                 html : this.fieldLabel
10567                             },
10568                             indicator
10569                         ]
10570                     },
10571                     {
10572                         cls : "", 
10573                         cn: [
10574                             combobox
10575                         ]
10576                     }
10577
10578                 ];
10579                 
10580                 labelCfg = cfg.cn[0];
10581                 contentCfg = cfg.cn[1];
10582             }
10583             
10584             if(this.labelWidth > 12){
10585                 labelCfg.style = "width: " + this.labelWidth + 'px';
10586             }
10587             
10588             if(this.labelWidth < 13 && this.labelmd == 0){
10589                 this.labelmd = this.labelWidth;
10590             }
10591             
10592             if(this.labellg > 0){
10593                 labelCfg.cls += ' col-lg-' + this.labellg;
10594                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10595             }
10596             
10597             if(this.labelmd > 0){
10598                 labelCfg.cls += ' col-md-' + this.labelmd;
10599                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10600             }
10601             
10602             if(this.labelsm > 0){
10603                 labelCfg.cls += ' col-sm-' + this.labelsm;
10604                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10605             }
10606             
10607             if(this.labelxs > 0){
10608                 labelCfg.cls += ' col-xs-' + this.labelxs;
10609                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10610             }
10611             
10612         } else if ( this.fieldLabel.length) {
10613 //                Roo.log(" label");
10614             cfg.cn = [
10615                 indicator,
10616                {
10617                    tag: 'label',
10618                    //cls : 'input-group-addon',
10619                    html : this.fieldLabel
10620
10621                },
10622
10623                combobox
10624
10625             ];
10626             
10627             if(this.indicatorpos == 'right'){
10628                 
10629                 cfg.cn = [
10630                     {
10631                        tag: 'label',
10632                        cn : [
10633                            {
10634                                tag : 'span',
10635                                html : this.fieldLabel
10636                            },
10637                            indicator
10638                        ]
10639
10640                     },
10641                     combobox
10642
10643                 ];
10644
10645             }
10646
10647         } else {
10648             
10649 //                Roo.log(" no label && no align");
10650                 cfg = combobox
10651                      
10652                 
10653         }
10654         
10655         var settings=this;
10656         ['xs','sm','md','lg'].map(function(size){
10657             if (settings[size]) {
10658                 cfg.cls += ' col-' + size + '-' + settings[size];
10659             }
10660         });
10661         
10662         return cfg;
10663         
10664     },
10665     
10666     
10667     
10668     // private
10669     onResize : function(w, h){
10670 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10671 //        if(typeof w == 'number'){
10672 //            var x = w - this.trigger.getWidth();
10673 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10674 //            this.trigger.setStyle('left', x+'px');
10675 //        }
10676     },
10677
10678     // private
10679     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10680
10681     // private
10682     getResizeEl : function(){
10683         return this.inputEl();
10684     },
10685
10686     // private
10687     getPositionEl : function(){
10688         return this.inputEl();
10689     },
10690
10691     // private
10692     alignErrorIcon : function(){
10693         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10694     },
10695
10696     // private
10697     initEvents : function(){
10698         
10699         this.createList();
10700         
10701         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10702         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10703         if(!this.multiple && this.showToggleBtn){
10704             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10705             if(this.hideTrigger){
10706                 this.trigger.setDisplayed(false);
10707             }
10708             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10709         }
10710         
10711         if(this.multiple){
10712             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10713         }
10714         
10715         if(this.removable && !this.editable && !this.tickable){
10716             var close = this.closeTriggerEl();
10717             
10718             if(close){
10719                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10720                 close.on('click', this.removeBtnClick, this, close);
10721             }
10722         }
10723         
10724         //this.trigger.addClassOnOver('x-form-trigger-over');
10725         //this.trigger.addClassOnClick('x-form-trigger-click');
10726         
10727         //if(!this.width){
10728         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10729         //}
10730     },
10731     
10732     closeTriggerEl : function()
10733     {
10734         var close = this.el.select('.roo-combo-removable-btn', true).first();
10735         return close ? close : false;
10736     },
10737     
10738     removeBtnClick : function(e, h, el)
10739     {
10740         e.preventDefault();
10741         
10742         if(this.fireEvent("remove", this) !== false){
10743             this.reset();
10744             this.fireEvent("afterremove", this)
10745         }
10746     },
10747     
10748     createList : function()
10749     {
10750         this.list = Roo.get(document.body).createChild({
10751             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10752             cls: 'typeahead typeahead-long dropdown-menu',
10753             style: 'display:none'
10754         });
10755         
10756         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10757         
10758     },
10759
10760     // private
10761     initTrigger : function(){
10762        
10763     },
10764
10765     // private
10766     onDestroy : function(){
10767         if(this.trigger){
10768             this.trigger.removeAllListeners();
10769           //  this.trigger.remove();
10770         }
10771         //if(this.wrap){
10772         //    this.wrap.remove();
10773         //}
10774         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10775     },
10776
10777     // private
10778     onFocus : function(){
10779         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10780         /*
10781         if(!this.mimicing){
10782             this.wrap.addClass('x-trigger-wrap-focus');
10783             this.mimicing = true;
10784             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10785             if(this.monitorTab){
10786                 this.el.on("keydown", this.checkTab, this);
10787             }
10788         }
10789         */
10790     },
10791
10792     // private
10793     checkTab : function(e){
10794         if(e.getKey() == e.TAB){
10795             this.triggerBlur();
10796         }
10797     },
10798
10799     // private
10800     onBlur : function(){
10801         // do nothing
10802     },
10803
10804     // private
10805     mimicBlur : function(e, t){
10806         /*
10807         if(!this.wrap.contains(t) && this.validateBlur()){
10808             this.triggerBlur();
10809         }
10810         */
10811     },
10812
10813     // private
10814     triggerBlur : function(){
10815         this.mimicing = false;
10816         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10817         if(this.monitorTab){
10818             this.el.un("keydown", this.checkTab, this);
10819         }
10820         //this.wrap.removeClass('x-trigger-wrap-focus');
10821         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10822     },
10823
10824     // private
10825     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10826     validateBlur : function(e, t){
10827         return true;
10828     },
10829
10830     // private
10831     onDisable : function(){
10832         this.inputEl().dom.disabled = true;
10833         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10834         //if(this.wrap){
10835         //    this.wrap.addClass('x-item-disabled');
10836         //}
10837     },
10838
10839     // private
10840     onEnable : function(){
10841         this.inputEl().dom.disabled = false;
10842         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10843         //if(this.wrap){
10844         //    this.el.removeClass('x-item-disabled');
10845         //}
10846     },
10847
10848     // private
10849     onShow : function(){
10850         var ae = this.getActionEl();
10851         
10852         if(ae){
10853             ae.dom.style.display = '';
10854             ae.dom.style.visibility = 'visible';
10855         }
10856     },
10857
10858     // private
10859     
10860     onHide : function(){
10861         var ae = this.getActionEl();
10862         ae.dom.style.display = 'none';
10863     },
10864
10865     /**
10866      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10867      * by an implementing function.
10868      * @method
10869      * @param {EventObject} e
10870      */
10871     onTriggerClick : Roo.emptyFn
10872 });
10873  /*
10874  * Based on:
10875  * Ext JS Library 1.1.1
10876  * Copyright(c) 2006-2007, Ext JS, LLC.
10877  *
10878  * Originally Released Under LGPL - original licence link has changed is not relivant.
10879  *
10880  * Fork - LGPL
10881  * <script type="text/javascript">
10882  */
10883
10884
10885 /**
10886  * @class Roo.data.SortTypes
10887  * @singleton
10888  * Defines the default sorting (casting?) comparison functions used when sorting data.
10889  */
10890 Roo.data.SortTypes = {
10891     /**
10892      * Default sort that does nothing
10893      * @param {Mixed} s The value being converted
10894      * @return {Mixed} The comparison value
10895      */
10896     none : function(s){
10897         return s;
10898     },
10899     
10900     /**
10901      * The regular expression used to strip tags
10902      * @type {RegExp}
10903      * @property
10904      */
10905     stripTagsRE : /<\/?[^>]+>/gi,
10906     
10907     /**
10908      * Strips all HTML tags to sort on text only
10909      * @param {Mixed} s The value being converted
10910      * @return {String} The comparison value
10911      */
10912     asText : function(s){
10913         return String(s).replace(this.stripTagsRE, "");
10914     },
10915     
10916     /**
10917      * Strips all HTML tags to sort on text only - Case insensitive
10918      * @param {Mixed} s The value being converted
10919      * @return {String} The comparison value
10920      */
10921     asUCText : function(s){
10922         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10923     },
10924     
10925     /**
10926      * Case insensitive string
10927      * @param {Mixed} s The value being converted
10928      * @return {String} The comparison value
10929      */
10930     asUCString : function(s) {
10931         return String(s).toUpperCase();
10932     },
10933     
10934     /**
10935      * Date sorting
10936      * @param {Mixed} s The value being converted
10937      * @return {Number} The comparison value
10938      */
10939     asDate : function(s) {
10940         if(!s){
10941             return 0;
10942         }
10943         if(s instanceof Date){
10944             return s.getTime();
10945         }
10946         return Date.parse(String(s));
10947     },
10948     
10949     /**
10950      * Float sorting
10951      * @param {Mixed} s The value being converted
10952      * @return {Float} The comparison value
10953      */
10954     asFloat : function(s) {
10955         var val = parseFloat(String(s).replace(/,/g, ""));
10956         if(isNaN(val)) {
10957             val = 0;
10958         }
10959         return val;
10960     },
10961     
10962     /**
10963      * Integer sorting
10964      * @param {Mixed} s The value being converted
10965      * @return {Number} The comparison value
10966      */
10967     asInt : function(s) {
10968         var val = parseInt(String(s).replace(/,/g, ""));
10969         if(isNaN(val)) {
10970             val = 0;
10971         }
10972         return val;
10973     }
10974 };/*
10975  * Based on:
10976  * Ext JS Library 1.1.1
10977  * Copyright(c) 2006-2007, Ext JS, LLC.
10978  *
10979  * Originally Released Under LGPL - original licence link has changed is not relivant.
10980  *
10981  * Fork - LGPL
10982  * <script type="text/javascript">
10983  */
10984
10985 /**
10986 * @class Roo.data.Record
10987  * Instances of this class encapsulate both record <em>definition</em> information, and record
10988  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10989  * to access Records cached in an {@link Roo.data.Store} object.<br>
10990  * <p>
10991  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10992  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10993  * objects.<br>
10994  * <p>
10995  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10996  * @constructor
10997  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10998  * {@link #create}. The parameters are the same.
10999  * @param {Array} data An associative Array of data values keyed by the field name.
11000  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11001  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11002  * not specified an integer id is generated.
11003  */
11004 Roo.data.Record = function(data, id){
11005     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11006     this.data = data;
11007 };
11008
11009 /**
11010  * Generate a constructor for a specific record layout.
11011  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11012  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11013  * Each field definition object may contain the following properties: <ul>
11014  * <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,
11015  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11016  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11017  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11018  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11019  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11020  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11021  * this may be omitted.</p></li>
11022  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11023  * <ul><li>auto (Default, implies no conversion)</li>
11024  * <li>string</li>
11025  * <li>int</li>
11026  * <li>float</li>
11027  * <li>boolean</li>
11028  * <li>date</li></ul></p></li>
11029  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11030  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11031  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11032  * by the Reader into an object that will be stored in the Record. It is passed the
11033  * following parameters:<ul>
11034  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11035  * </ul></p></li>
11036  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11037  * </ul>
11038  * <br>usage:<br><pre><code>
11039 var TopicRecord = Roo.data.Record.create(
11040     {name: 'title', mapping: 'topic_title'},
11041     {name: 'author', mapping: 'username'},
11042     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11043     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11044     {name: 'lastPoster', mapping: 'user2'},
11045     {name: 'excerpt', mapping: 'post_text'}
11046 );
11047
11048 var myNewRecord = new TopicRecord({
11049     title: 'Do my job please',
11050     author: 'noobie',
11051     totalPosts: 1,
11052     lastPost: new Date(),
11053     lastPoster: 'Animal',
11054     excerpt: 'No way dude!'
11055 });
11056 myStore.add(myNewRecord);
11057 </code></pre>
11058  * @method create
11059  * @static
11060  */
11061 Roo.data.Record.create = function(o){
11062     var f = function(){
11063         f.superclass.constructor.apply(this, arguments);
11064     };
11065     Roo.extend(f, Roo.data.Record);
11066     var p = f.prototype;
11067     p.fields = new Roo.util.MixedCollection(false, function(field){
11068         return field.name;
11069     });
11070     for(var i = 0, len = o.length; i < len; i++){
11071         p.fields.add(new Roo.data.Field(o[i]));
11072     }
11073     f.getField = function(name){
11074         return p.fields.get(name);  
11075     };
11076     return f;
11077 };
11078
11079 Roo.data.Record.AUTO_ID = 1000;
11080 Roo.data.Record.EDIT = 'edit';
11081 Roo.data.Record.REJECT = 'reject';
11082 Roo.data.Record.COMMIT = 'commit';
11083
11084 Roo.data.Record.prototype = {
11085     /**
11086      * Readonly flag - true if this record has been modified.
11087      * @type Boolean
11088      */
11089     dirty : false,
11090     editing : false,
11091     error: null,
11092     modified: null,
11093
11094     // private
11095     join : function(store){
11096         this.store = store;
11097     },
11098
11099     /**
11100      * Set the named field to the specified value.
11101      * @param {String} name The name of the field to set.
11102      * @param {Object} value The value to set the field to.
11103      */
11104     set : function(name, value){
11105         if(this.data[name] == value){
11106             return;
11107         }
11108         this.dirty = true;
11109         if(!this.modified){
11110             this.modified = {};
11111         }
11112         if(typeof this.modified[name] == 'undefined'){
11113             this.modified[name] = this.data[name];
11114         }
11115         this.data[name] = value;
11116         if(!this.editing && this.store){
11117             this.store.afterEdit(this);
11118         }       
11119     },
11120
11121     /**
11122      * Get the value of the named field.
11123      * @param {String} name The name of the field to get the value of.
11124      * @return {Object} The value of the field.
11125      */
11126     get : function(name){
11127         return this.data[name]; 
11128     },
11129
11130     // private
11131     beginEdit : function(){
11132         this.editing = true;
11133         this.modified = {}; 
11134     },
11135
11136     // private
11137     cancelEdit : function(){
11138         this.editing = false;
11139         delete this.modified;
11140     },
11141
11142     // private
11143     endEdit : function(){
11144         this.editing = false;
11145         if(this.dirty && this.store){
11146             this.store.afterEdit(this);
11147         }
11148     },
11149
11150     /**
11151      * Usually called by the {@link Roo.data.Store} which owns the Record.
11152      * Rejects all changes made to the Record since either creation, or the last commit operation.
11153      * Modified fields are reverted to their original values.
11154      * <p>
11155      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11156      * of reject operations.
11157      */
11158     reject : function(){
11159         var m = this.modified;
11160         for(var n in m){
11161             if(typeof m[n] != "function"){
11162                 this.data[n] = m[n];
11163             }
11164         }
11165         this.dirty = false;
11166         delete this.modified;
11167         this.editing = false;
11168         if(this.store){
11169             this.store.afterReject(this);
11170         }
11171     },
11172
11173     /**
11174      * Usually called by the {@link Roo.data.Store} which owns the Record.
11175      * Commits all changes made to the Record since either creation, or the last commit operation.
11176      * <p>
11177      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11178      * of commit operations.
11179      */
11180     commit : function(){
11181         this.dirty = false;
11182         delete this.modified;
11183         this.editing = false;
11184         if(this.store){
11185             this.store.afterCommit(this);
11186         }
11187     },
11188
11189     // private
11190     hasError : function(){
11191         return this.error != null;
11192     },
11193
11194     // private
11195     clearError : function(){
11196         this.error = null;
11197     },
11198
11199     /**
11200      * Creates a copy of this record.
11201      * @param {String} id (optional) A new record id if you don't want to use this record's id
11202      * @return {Record}
11203      */
11204     copy : function(newId) {
11205         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11206     }
11207 };/*
11208  * Based on:
11209  * Ext JS Library 1.1.1
11210  * Copyright(c) 2006-2007, Ext JS, LLC.
11211  *
11212  * Originally Released Under LGPL - original licence link has changed is not relivant.
11213  *
11214  * Fork - LGPL
11215  * <script type="text/javascript">
11216  */
11217
11218
11219
11220 /**
11221  * @class Roo.data.Store
11222  * @extends Roo.util.Observable
11223  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11224  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11225  * <p>
11226  * 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
11227  * has no knowledge of the format of the data returned by the Proxy.<br>
11228  * <p>
11229  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11230  * instances from the data object. These records are cached and made available through accessor functions.
11231  * @constructor
11232  * Creates a new Store.
11233  * @param {Object} config A config object containing the objects needed for the Store to access data,
11234  * and read the data into Records.
11235  */
11236 Roo.data.Store = function(config){
11237     this.data = new Roo.util.MixedCollection(false);
11238     this.data.getKey = function(o){
11239         return o.id;
11240     };
11241     this.baseParams = {};
11242     // private
11243     this.paramNames = {
11244         "start" : "start",
11245         "limit" : "limit",
11246         "sort" : "sort",
11247         "dir" : "dir",
11248         "multisort" : "_multisort"
11249     };
11250
11251     if(config && config.data){
11252         this.inlineData = config.data;
11253         delete config.data;
11254     }
11255
11256     Roo.apply(this, config);
11257     
11258     if(this.reader){ // reader passed
11259         this.reader = Roo.factory(this.reader, Roo.data);
11260         this.reader.xmodule = this.xmodule || false;
11261         if(!this.recordType){
11262             this.recordType = this.reader.recordType;
11263         }
11264         if(this.reader.onMetaChange){
11265             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11266         }
11267     }
11268
11269     if(this.recordType){
11270         this.fields = this.recordType.prototype.fields;
11271     }
11272     this.modified = [];
11273
11274     this.addEvents({
11275         /**
11276          * @event datachanged
11277          * Fires when the data cache has changed, and a widget which is using this Store
11278          * as a Record cache should refresh its view.
11279          * @param {Store} this
11280          */
11281         datachanged : true,
11282         /**
11283          * @event metachange
11284          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11285          * @param {Store} this
11286          * @param {Object} meta The JSON metadata
11287          */
11288         metachange : true,
11289         /**
11290          * @event add
11291          * Fires when Records have been added to the Store
11292          * @param {Store} this
11293          * @param {Roo.data.Record[]} records The array of Records added
11294          * @param {Number} index The index at which the record(s) were added
11295          */
11296         add : true,
11297         /**
11298          * @event remove
11299          * Fires when a Record has been removed from the Store
11300          * @param {Store} this
11301          * @param {Roo.data.Record} record The Record that was removed
11302          * @param {Number} index The index at which the record was removed
11303          */
11304         remove : true,
11305         /**
11306          * @event update
11307          * Fires when a Record has been updated
11308          * @param {Store} this
11309          * @param {Roo.data.Record} record The Record that was updated
11310          * @param {String} operation The update operation being performed.  Value may be one of:
11311          * <pre><code>
11312  Roo.data.Record.EDIT
11313  Roo.data.Record.REJECT
11314  Roo.data.Record.COMMIT
11315          * </code></pre>
11316          */
11317         update : true,
11318         /**
11319          * @event clear
11320          * Fires when the data cache has been cleared.
11321          * @param {Store} this
11322          */
11323         clear : true,
11324         /**
11325          * @event beforeload
11326          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11327          * the load action will be canceled.
11328          * @param {Store} this
11329          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11330          */
11331         beforeload : true,
11332         /**
11333          * @event beforeloadadd
11334          * Fires after a new set of Records has been loaded.
11335          * @param {Store} this
11336          * @param {Roo.data.Record[]} records The Records that were loaded
11337          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11338          */
11339         beforeloadadd : true,
11340         /**
11341          * @event load
11342          * Fires after a new set of Records has been loaded, before they are added to the store.
11343          * @param {Store} this
11344          * @param {Roo.data.Record[]} records The Records that were loaded
11345          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11346          * @params {Object} return from reader
11347          */
11348         load : true,
11349         /**
11350          * @event loadexception
11351          * Fires if an exception occurs in the Proxy during loading.
11352          * Called with the signature of the Proxy's "loadexception" event.
11353          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11354          * 
11355          * @param {Proxy} 
11356          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11357          * @param {Object} load options 
11358          * @param {Object} jsonData from your request (normally this contains the Exception)
11359          */
11360         loadexception : true
11361     });
11362     
11363     if(this.proxy){
11364         this.proxy = Roo.factory(this.proxy, Roo.data);
11365         this.proxy.xmodule = this.xmodule || false;
11366         this.relayEvents(this.proxy,  ["loadexception"]);
11367     }
11368     this.sortToggle = {};
11369     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11370
11371     Roo.data.Store.superclass.constructor.call(this);
11372
11373     if(this.inlineData){
11374         this.loadData(this.inlineData);
11375         delete this.inlineData;
11376     }
11377 };
11378
11379 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11380      /**
11381     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11382     * without a remote query - used by combo/forms at present.
11383     */
11384     
11385     /**
11386     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11387     */
11388     /**
11389     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11390     */
11391     /**
11392     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11393     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11394     */
11395     /**
11396     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11397     * on any HTTP request
11398     */
11399     /**
11400     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11401     */
11402     /**
11403     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11404     */
11405     multiSort: false,
11406     /**
11407     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11408     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11409     */
11410     remoteSort : false,
11411
11412     /**
11413     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11414      * loaded or when a record is removed. (defaults to false).
11415     */
11416     pruneModifiedRecords : false,
11417
11418     // private
11419     lastOptions : null,
11420
11421     /**
11422      * Add Records to the Store and fires the add event.
11423      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11424      */
11425     add : function(records){
11426         records = [].concat(records);
11427         for(var i = 0, len = records.length; i < len; i++){
11428             records[i].join(this);
11429         }
11430         var index = this.data.length;
11431         this.data.addAll(records);
11432         this.fireEvent("add", this, records, index);
11433     },
11434
11435     /**
11436      * Remove a Record from the Store and fires the remove event.
11437      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11438      */
11439     remove : function(record){
11440         var index = this.data.indexOf(record);
11441         this.data.removeAt(index);
11442  
11443         if(this.pruneModifiedRecords){
11444             this.modified.remove(record);
11445         }
11446         this.fireEvent("remove", this, record, index);
11447     },
11448
11449     /**
11450      * Remove all Records from the Store and fires the clear event.
11451      */
11452     removeAll : function(){
11453         this.data.clear();
11454         if(this.pruneModifiedRecords){
11455             this.modified = [];
11456         }
11457         this.fireEvent("clear", this);
11458     },
11459
11460     /**
11461      * Inserts Records to the Store at the given index and fires the add event.
11462      * @param {Number} index The start index at which to insert the passed Records.
11463      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11464      */
11465     insert : function(index, records){
11466         records = [].concat(records);
11467         for(var i = 0, len = records.length; i < len; i++){
11468             this.data.insert(index, records[i]);
11469             records[i].join(this);
11470         }
11471         this.fireEvent("add", this, records, index);
11472     },
11473
11474     /**
11475      * Get the index within the cache of the passed Record.
11476      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11477      * @return {Number} The index of the passed Record. Returns -1 if not found.
11478      */
11479     indexOf : function(record){
11480         return this.data.indexOf(record);
11481     },
11482
11483     /**
11484      * Get the index within the cache of the Record with the passed id.
11485      * @param {String} id The id of the Record to find.
11486      * @return {Number} The index of the Record. Returns -1 if not found.
11487      */
11488     indexOfId : function(id){
11489         return this.data.indexOfKey(id);
11490     },
11491
11492     /**
11493      * Get the Record with the specified id.
11494      * @param {String} id The id of the Record to find.
11495      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11496      */
11497     getById : function(id){
11498         return this.data.key(id);
11499     },
11500
11501     /**
11502      * Get the Record at the specified index.
11503      * @param {Number} index The index of the Record to find.
11504      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11505      */
11506     getAt : function(index){
11507         return this.data.itemAt(index);
11508     },
11509
11510     /**
11511      * Returns a range of Records between specified indices.
11512      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11513      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11514      * @return {Roo.data.Record[]} An array of Records
11515      */
11516     getRange : function(start, end){
11517         return this.data.getRange(start, end);
11518     },
11519
11520     // private
11521     storeOptions : function(o){
11522         o = Roo.apply({}, o);
11523         delete o.callback;
11524         delete o.scope;
11525         this.lastOptions = o;
11526     },
11527
11528     /**
11529      * Loads the Record cache from the configured Proxy using the configured Reader.
11530      * <p>
11531      * If using remote paging, then the first load call must specify the <em>start</em>
11532      * and <em>limit</em> properties in the options.params property to establish the initial
11533      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11534      * <p>
11535      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11536      * and this call will return before the new data has been loaded. Perform any post-processing
11537      * in a callback function, or in a "load" event handler.</strong>
11538      * <p>
11539      * @param {Object} options An object containing properties which control loading options:<ul>
11540      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11541      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11542      * passed the following arguments:<ul>
11543      * <li>r : Roo.data.Record[]</li>
11544      * <li>options: Options object from the load call</li>
11545      * <li>success: Boolean success indicator</li></ul></li>
11546      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11547      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11548      * </ul>
11549      */
11550     load : function(options){
11551         options = options || {};
11552         if(this.fireEvent("beforeload", this, options) !== false){
11553             this.storeOptions(options);
11554             var p = Roo.apply(options.params || {}, this.baseParams);
11555             // if meta was not loaded from remote source.. try requesting it.
11556             if (!this.reader.metaFromRemote) {
11557                 p._requestMeta = 1;
11558             }
11559             if(this.sortInfo && this.remoteSort){
11560                 var pn = this.paramNames;
11561                 p[pn["sort"]] = this.sortInfo.field;
11562                 p[pn["dir"]] = this.sortInfo.direction;
11563             }
11564             if (this.multiSort) {
11565                 var pn = this.paramNames;
11566                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11567             }
11568             
11569             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11570         }
11571     },
11572
11573     /**
11574      * Reloads the Record cache from the configured Proxy using the configured Reader and
11575      * the options from the last load operation performed.
11576      * @param {Object} options (optional) An object containing properties which may override the options
11577      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11578      * the most recently used options are reused).
11579      */
11580     reload : function(options){
11581         this.load(Roo.applyIf(options||{}, this.lastOptions));
11582     },
11583
11584     // private
11585     // Called as a callback by the Reader during a load operation.
11586     loadRecords : function(o, options, success){
11587         if(!o || success === false){
11588             if(success !== false){
11589                 this.fireEvent("load", this, [], options, o);
11590             }
11591             if(options.callback){
11592                 options.callback.call(options.scope || this, [], options, false);
11593             }
11594             return;
11595         }
11596         // if data returned failure - throw an exception.
11597         if (o.success === false) {
11598             // show a message if no listener is registered.
11599             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11600                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11601             }
11602             // loadmask wil be hooked into this..
11603             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11604             return;
11605         }
11606         var r = o.records, t = o.totalRecords || r.length;
11607         
11608         this.fireEvent("beforeloadadd", this, r, options, o);
11609         
11610         if(!options || options.add !== true){
11611             if(this.pruneModifiedRecords){
11612                 this.modified = [];
11613             }
11614             for(var i = 0, len = r.length; i < len; i++){
11615                 r[i].join(this);
11616             }
11617             if(this.snapshot){
11618                 this.data = this.snapshot;
11619                 delete this.snapshot;
11620             }
11621             this.data.clear();
11622             this.data.addAll(r);
11623             this.totalLength = t;
11624             this.applySort();
11625             this.fireEvent("datachanged", this);
11626         }else{
11627             this.totalLength = Math.max(t, this.data.length+r.length);
11628             this.add(r);
11629         }
11630         
11631         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11632                 
11633             var e = new Roo.data.Record({});
11634
11635             e.set(this.parent.displayField, this.parent.emptyTitle);
11636             e.set(this.parent.valueField, '');
11637
11638             this.insert(0, e);
11639         }
11640             
11641         this.fireEvent("load", this, r, options, o);
11642         if(options.callback){
11643             options.callback.call(options.scope || this, r, options, true);
11644         }
11645     },
11646
11647
11648     /**
11649      * Loads data from a passed data block. A Reader which understands the format of the data
11650      * must have been configured in the constructor.
11651      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11652      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11653      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11654      */
11655     loadData : function(o, append){
11656         var r = this.reader.readRecords(o);
11657         this.loadRecords(r, {add: append}, true);
11658     },
11659
11660     /**
11661      * Gets the number of cached records.
11662      * <p>
11663      * <em>If using paging, this may not be the total size of the dataset. If the data object
11664      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11665      * the data set size</em>
11666      */
11667     getCount : function(){
11668         return this.data.length || 0;
11669     },
11670
11671     /**
11672      * Gets the total number of records in the dataset as returned by the server.
11673      * <p>
11674      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11675      * the dataset size</em>
11676      */
11677     getTotalCount : function(){
11678         return this.totalLength || 0;
11679     },
11680
11681     /**
11682      * Returns the sort state of the Store as an object with two properties:
11683      * <pre><code>
11684  field {String} The name of the field by which the Records are sorted
11685  direction {String} The sort order, "ASC" or "DESC"
11686      * </code></pre>
11687      */
11688     getSortState : function(){
11689         return this.sortInfo;
11690     },
11691
11692     // private
11693     applySort : function(){
11694         if(this.sortInfo && !this.remoteSort){
11695             var s = this.sortInfo, f = s.field;
11696             var st = this.fields.get(f).sortType;
11697             var fn = function(r1, r2){
11698                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11699                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11700             };
11701             this.data.sort(s.direction, fn);
11702             if(this.snapshot && this.snapshot != this.data){
11703                 this.snapshot.sort(s.direction, fn);
11704             }
11705         }
11706     },
11707
11708     /**
11709      * Sets the default sort column and order to be used by the next load operation.
11710      * @param {String} fieldName The name of the field to sort by.
11711      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11712      */
11713     setDefaultSort : function(field, dir){
11714         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11715     },
11716
11717     /**
11718      * Sort the Records.
11719      * If remote sorting is used, the sort is performed on the server, and the cache is
11720      * reloaded. If local sorting is used, the cache is sorted internally.
11721      * @param {String} fieldName The name of the field to sort by.
11722      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11723      */
11724     sort : function(fieldName, dir){
11725         var f = this.fields.get(fieldName);
11726         if(!dir){
11727             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11728             
11729             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11730                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11731             }else{
11732                 dir = f.sortDir;
11733             }
11734         }
11735         this.sortToggle[f.name] = dir;
11736         this.sortInfo = {field: f.name, direction: dir};
11737         if(!this.remoteSort){
11738             this.applySort();
11739             this.fireEvent("datachanged", this);
11740         }else{
11741             this.load(this.lastOptions);
11742         }
11743     },
11744
11745     /**
11746      * Calls the specified function for each of the Records in the cache.
11747      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11748      * Returning <em>false</em> aborts and exits the iteration.
11749      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11750      */
11751     each : function(fn, scope){
11752         this.data.each(fn, scope);
11753     },
11754
11755     /**
11756      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11757      * (e.g., during paging).
11758      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11759      */
11760     getModifiedRecords : function(){
11761         return this.modified;
11762     },
11763
11764     // private
11765     createFilterFn : function(property, value, anyMatch){
11766         if(!value.exec){ // not a regex
11767             value = String(value);
11768             if(value.length == 0){
11769                 return false;
11770             }
11771             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11772         }
11773         return function(r){
11774             return value.test(r.data[property]);
11775         };
11776     },
11777
11778     /**
11779      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11780      * @param {String} property A field on your records
11781      * @param {Number} start The record index to start at (defaults to 0)
11782      * @param {Number} end The last record index to include (defaults to length - 1)
11783      * @return {Number} The sum
11784      */
11785     sum : function(property, start, end){
11786         var rs = this.data.items, v = 0;
11787         start = start || 0;
11788         end = (end || end === 0) ? end : rs.length-1;
11789
11790         for(var i = start; i <= end; i++){
11791             v += (rs[i].data[property] || 0);
11792         }
11793         return v;
11794     },
11795
11796     /**
11797      * Filter the records by a specified property.
11798      * @param {String} field A field on your records
11799      * @param {String/RegExp} value Either a string that the field
11800      * should start with or a RegExp to test against the field
11801      * @param {Boolean} anyMatch True to match any part not just the beginning
11802      */
11803     filter : function(property, value, anyMatch){
11804         var fn = this.createFilterFn(property, value, anyMatch);
11805         return fn ? this.filterBy(fn) : this.clearFilter();
11806     },
11807
11808     /**
11809      * Filter by a function. The specified function will be called with each
11810      * record in this data source. If the function returns true the record is included,
11811      * otherwise it is filtered.
11812      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11813      * @param {Object} scope (optional) The scope of the function (defaults to this)
11814      */
11815     filterBy : function(fn, scope){
11816         this.snapshot = this.snapshot || this.data;
11817         this.data = this.queryBy(fn, scope||this);
11818         this.fireEvent("datachanged", this);
11819     },
11820
11821     /**
11822      * Query the records by a specified property.
11823      * @param {String} field A field on your records
11824      * @param {String/RegExp} value Either a string that the field
11825      * should start with or a RegExp to test against the field
11826      * @param {Boolean} anyMatch True to match any part not just the beginning
11827      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11828      */
11829     query : function(property, value, anyMatch){
11830         var fn = this.createFilterFn(property, value, anyMatch);
11831         return fn ? this.queryBy(fn) : this.data.clone();
11832     },
11833
11834     /**
11835      * Query by a function. The specified function will be called with each
11836      * record in this data source. If the function returns true the record is included
11837      * in the results.
11838      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11839      * @param {Object} scope (optional) The scope of the function (defaults to this)
11840       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11841      **/
11842     queryBy : function(fn, scope){
11843         var data = this.snapshot || this.data;
11844         return data.filterBy(fn, scope||this);
11845     },
11846
11847     /**
11848      * Collects unique values for a particular dataIndex from this store.
11849      * @param {String} dataIndex The property to collect
11850      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11851      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11852      * @return {Array} An array of the unique values
11853      **/
11854     collect : function(dataIndex, allowNull, bypassFilter){
11855         var d = (bypassFilter === true && this.snapshot) ?
11856                 this.snapshot.items : this.data.items;
11857         var v, sv, r = [], l = {};
11858         for(var i = 0, len = d.length; i < len; i++){
11859             v = d[i].data[dataIndex];
11860             sv = String(v);
11861             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11862                 l[sv] = true;
11863                 r[r.length] = v;
11864             }
11865         }
11866         return r;
11867     },
11868
11869     /**
11870      * Revert to a view of the Record cache with no filtering applied.
11871      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11872      */
11873     clearFilter : function(suppressEvent){
11874         if(this.snapshot && this.snapshot != this.data){
11875             this.data = this.snapshot;
11876             delete this.snapshot;
11877             if(suppressEvent !== true){
11878                 this.fireEvent("datachanged", this);
11879             }
11880         }
11881     },
11882
11883     // private
11884     afterEdit : function(record){
11885         if(this.modified.indexOf(record) == -1){
11886             this.modified.push(record);
11887         }
11888         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11889     },
11890     
11891     // private
11892     afterReject : function(record){
11893         this.modified.remove(record);
11894         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11895     },
11896
11897     // private
11898     afterCommit : function(record){
11899         this.modified.remove(record);
11900         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11901     },
11902
11903     /**
11904      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11905      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11906      */
11907     commitChanges : function(){
11908         var m = this.modified.slice(0);
11909         this.modified = [];
11910         for(var i = 0, len = m.length; i < len; i++){
11911             m[i].commit();
11912         }
11913     },
11914
11915     /**
11916      * Cancel outstanding changes on all changed records.
11917      */
11918     rejectChanges : function(){
11919         var m = this.modified.slice(0);
11920         this.modified = [];
11921         for(var i = 0, len = m.length; i < len; i++){
11922             m[i].reject();
11923         }
11924     },
11925
11926     onMetaChange : function(meta, rtype, o){
11927         this.recordType = rtype;
11928         this.fields = rtype.prototype.fields;
11929         delete this.snapshot;
11930         this.sortInfo = meta.sortInfo || this.sortInfo;
11931         this.modified = [];
11932         this.fireEvent('metachange', this, this.reader.meta);
11933     },
11934     
11935     moveIndex : function(data, type)
11936     {
11937         var index = this.indexOf(data);
11938         
11939         var newIndex = index + type;
11940         
11941         this.remove(data);
11942         
11943         this.insert(newIndex, data);
11944         
11945     }
11946 });/*
11947  * Based on:
11948  * Ext JS Library 1.1.1
11949  * Copyright(c) 2006-2007, Ext JS, LLC.
11950  *
11951  * Originally Released Under LGPL - original licence link has changed is not relivant.
11952  *
11953  * Fork - LGPL
11954  * <script type="text/javascript">
11955  */
11956
11957 /**
11958  * @class Roo.data.SimpleStore
11959  * @extends Roo.data.Store
11960  * Small helper class to make creating Stores from Array data easier.
11961  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11962  * @cfg {Array} fields An array of field definition objects, or field name strings.
11963  * @cfg {Array} data The multi-dimensional array of data
11964  * @constructor
11965  * @param {Object} config
11966  */
11967 Roo.data.SimpleStore = function(config){
11968     Roo.data.SimpleStore.superclass.constructor.call(this, {
11969         isLocal : true,
11970         reader: new Roo.data.ArrayReader({
11971                 id: config.id
11972             },
11973             Roo.data.Record.create(config.fields)
11974         ),
11975         proxy : new Roo.data.MemoryProxy(config.data)
11976     });
11977     this.load();
11978 };
11979 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11980  * Based on:
11981  * Ext JS Library 1.1.1
11982  * Copyright(c) 2006-2007, Ext JS, LLC.
11983  *
11984  * Originally Released Under LGPL - original licence link has changed is not relivant.
11985  *
11986  * Fork - LGPL
11987  * <script type="text/javascript">
11988  */
11989
11990 /**
11991 /**
11992  * @extends Roo.data.Store
11993  * @class Roo.data.JsonStore
11994  * Small helper class to make creating Stores for JSON data easier. <br/>
11995 <pre><code>
11996 var store = new Roo.data.JsonStore({
11997     url: 'get-images.php',
11998     root: 'images',
11999     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12000 });
12001 </code></pre>
12002  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12003  * JsonReader and HttpProxy (unless inline data is provided).</b>
12004  * @cfg {Array} fields An array of field definition objects, or field name strings.
12005  * @constructor
12006  * @param {Object} config
12007  */
12008 Roo.data.JsonStore = function(c){
12009     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12010         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12011         reader: new Roo.data.JsonReader(c, c.fields)
12012     }));
12013 };
12014 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12015  * Based on:
12016  * Ext JS Library 1.1.1
12017  * Copyright(c) 2006-2007, Ext JS, LLC.
12018  *
12019  * Originally Released Under LGPL - original licence link has changed is not relivant.
12020  *
12021  * Fork - LGPL
12022  * <script type="text/javascript">
12023  */
12024
12025  
12026 Roo.data.Field = function(config){
12027     if(typeof config == "string"){
12028         config = {name: config};
12029     }
12030     Roo.apply(this, config);
12031     
12032     if(!this.type){
12033         this.type = "auto";
12034     }
12035     
12036     var st = Roo.data.SortTypes;
12037     // named sortTypes are supported, here we look them up
12038     if(typeof this.sortType == "string"){
12039         this.sortType = st[this.sortType];
12040     }
12041     
12042     // set default sortType for strings and dates
12043     if(!this.sortType){
12044         switch(this.type){
12045             case "string":
12046                 this.sortType = st.asUCString;
12047                 break;
12048             case "date":
12049                 this.sortType = st.asDate;
12050                 break;
12051             default:
12052                 this.sortType = st.none;
12053         }
12054     }
12055
12056     // define once
12057     var stripRe = /[\$,%]/g;
12058
12059     // prebuilt conversion function for this field, instead of
12060     // switching every time we're reading a value
12061     if(!this.convert){
12062         var cv, dateFormat = this.dateFormat;
12063         switch(this.type){
12064             case "":
12065             case "auto":
12066             case undefined:
12067                 cv = function(v){ return v; };
12068                 break;
12069             case "string":
12070                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12071                 break;
12072             case "int":
12073                 cv = function(v){
12074                     return v !== undefined && v !== null && v !== '' ?
12075                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12076                     };
12077                 break;
12078             case "float":
12079                 cv = function(v){
12080                     return v !== undefined && v !== null && v !== '' ?
12081                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12082                     };
12083                 break;
12084             case "bool":
12085             case "boolean":
12086                 cv = function(v){ return v === true || v === "true" || v == 1; };
12087                 break;
12088             case "date":
12089                 cv = function(v){
12090                     if(!v){
12091                         return '';
12092                     }
12093                     if(v instanceof Date){
12094                         return v;
12095                     }
12096                     if(dateFormat){
12097                         if(dateFormat == "timestamp"){
12098                             return new Date(v*1000);
12099                         }
12100                         return Date.parseDate(v, dateFormat);
12101                     }
12102                     var parsed = Date.parse(v);
12103                     return parsed ? new Date(parsed) : null;
12104                 };
12105              break;
12106             
12107         }
12108         this.convert = cv;
12109     }
12110 };
12111
12112 Roo.data.Field.prototype = {
12113     dateFormat: null,
12114     defaultValue: "",
12115     mapping: null,
12116     sortType : null,
12117     sortDir : "ASC"
12118 };/*
12119  * Based on:
12120  * Ext JS Library 1.1.1
12121  * Copyright(c) 2006-2007, Ext JS, LLC.
12122  *
12123  * Originally Released Under LGPL - original licence link has changed is not relivant.
12124  *
12125  * Fork - LGPL
12126  * <script type="text/javascript">
12127  */
12128  
12129 // Base class for reading structured data from a data source.  This class is intended to be
12130 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12131
12132 /**
12133  * @class Roo.data.DataReader
12134  * Base class for reading structured data from a data source.  This class is intended to be
12135  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12136  */
12137
12138 Roo.data.DataReader = function(meta, recordType){
12139     
12140     this.meta = meta;
12141     
12142     this.recordType = recordType instanceof Array ? 
12143         Roo.data.Record.create(recordType) : recordType;
12144 };
12145
12146 Roo.data.DataReader.prototype = {
12147      /**
12148      * Create an empty record
12149      * @param {Object} data (optional) - overlay some values
12150      * @return {Roo.data.Record} record created.
12151      */
12152     newRow :  function(d) {
12153         var da =  {};
12154         this.recordType.prototype.fields.each(function(c) {
12155             switch( c.type) {
12156                 case 'int' : da[c.name] = 0; break;
12157                 case 'date' : da[c.name] = new Date(); break;
12158                 case 'float' : da[c.name] = 0.0; break;
12159                 case 'boolean' : da[c.name] = false; break;
12160                 default : da[c.name] = ""; break;
12161             }
12162             
12163         });
12164         return new this.recordType(Roo.apply(da, d));
12165     }
12166     
12167 };/*
12168  * Based on:
12169  * Ext JS Library 1.1.1
12170  * Copyright(c) 2006-2007, Ext JS, LLC.
12171  *
12172  * Originally Released Under LGPL - original licence link has changed is not relivant.
12173  *
12174  * Fork - LGPL
12175  * <script type="text/javascript">
12176  */
12177
12178 /**
12179  * @class Roo.data.DataProxy
12180  * @extends Roo.data.Observable
12181  * This class is an abstract base class for implementations which provide retrieval of
12182  * unformatted data objects.<br>
12183  * <p>
12184  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12185  * (of the appropriate type which knows how to parse the data object) to provide a block of
12186  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12187  * <p>
12188  * Custom implementations must implement the load method as described in
12189  * {@link Roo.data.HttpProxy#load}.
12190  */
12191 Roo.data.DataProxy = function(){
12192     this.addEvents({
12193         /**
12194          * @event beforeload
12195          * Fires before a network request is made to retrieve a data object.
12196          * @param {Object} This DataProxy object.
12197          * @param {Object} params The params parameter to the load function.
12198          */
12199         beforeload : true,
12200         /**
12201          * @event load
12202          * Fires before the load method's callback is called.
12203          * @param {Object} This DataProxy object.
12204          * @param {Object} o The data object.
12205          * @param {Object} arg The callback argument object passed to the load function.
12206          */
12207         load : true,
12208         /**
12209          * @event loadexception
12210          * Fires if an Exception occurs during data retrieval.
12211          * @param {Object} This DataProxy object.
12212          * @param {Object} o The data object.
12213          * @param {Object} arg The callback argument object passed to the load function.
12214          * @param {Object} e The Exception.
12215          */
12216         loadexception : true
12217     });
12218     Roo.data.DataProxy.superclass.constructor.call(this);
12219 };
12220
12221 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12222
12223     /**
12224      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12225      */
12226 /*
12227  * Based on:
12228  * Ext JS Library 1.1.1
12229  * Copyright(c) 2006-2007, Ext JS, LLC.
12230  *
12231  * Originally Released Under LGPL - original licence link has changed is not relivant.
12232  *
12233  * Fork - LGPL
12234  * <script type="text/javascript">
12235  */
12236 /**
12237  * @class Roo.data.MemoryProxy
12238  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12239  * to the Reader when its load method is called.
12240  * @constructor
12241  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12242  */
12243 Roo.data.MemoryProxy = function(data){
12244     if (data.data) {
12245         data = data.data;
12246     }
12247     Roo.data.MemoryProxy.superclass.constructor.call(this);
12248     this.data = data;
12249 };
12250
12251 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12252     
12253     /**
12254      * Load data from the requested source (in this case an in-memory
12255      * data object passed to the constructor), read the data object into
12256      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12257      * process that block using the passed callback.
12258      * @param {Object} params This parameter is not used by the MemoryProxy class.
12259      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12260      * object into a block of Roo.data.Records.
12261      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12262      * The function must be passed <ul>
12263      * <li>The Record block object</li>
12264      * <li>The "arg" argument from the load function</li>
12265      * <li>A boolean success indicator</li>
12266      * </ul>
12267      * @param {Object} scope The scope in which to call the callback
12268      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12269      */
12270     load : function(params, reader, callback, scope, arg){
12271         params = params || {};
12272         var result;
12273         try {
12274             result = reader.readRecords(this.data);
12275         }catch(e){
12276             this.fireEvent("loadexception", this, arg, null, e);
12277             callback.call(scope, null, arg, false);
12278             return;
12279         }
12280         callback.call(scope, result, arg, true);
12281     },
12282     
12283     // private
12284     update : function(params, records){
12285         
12286     }
12287 });/*
12288  * Based on:
12289  * Ext JS Library 1.1.1
12290  * Copyright(c) 2006-2007, Ext JS, LLC.
12291  *
12292  * Originally Released Under LGPL - original licence link has changed is not relivant.
12293  *
12294  * Fork - LGPL
12295  * <script type="text/javascript">
12296  */
12297 /**
12298  * @class Roo.data.HttpProxy
12299  * @extends Roo.data.DataProxy
12300  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12301  * configured to reference a certain URL.<br><br>
12302  * <p>
12303  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12304  * from which the running page was served.<br><br>
12305  * <p>
12306  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12307  * <p>
12308  * Be aware that to enable the browser to parse an XML document, the server must set
12309  * the Content-Type header in the HTTP response to "text/xml".
12310  * @constructor
12311  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12312  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12313  * will be used to make the request.
12314  */
12315 Roo.data.HttpProxy = function(conn){
12316     Roo.data.HttpProxy.superclass.constructor.call(this);
12317     // is conn a conn config or a real conn?
12318     this.conn = conn;
12319     this.useAjax = !conn || !conn.events;
12320   
12321 };
12322
12323 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12324     // thse are take from connection...
12325     
12326     /**
12327      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12328      */
12329     /**
12330      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12331      * extra parameters to each request made by this object. (defaults to undefined)
12332      */
12333     /**
12334      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12335      *  to each request made by this object. (defaults to undefined)
12336      */
12337     /**
12338      * @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)
12339      */
12340     /**
12341      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12342      */
12343      /**
12344      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12345      * @type Boolean
12346      */
12347   
12348
12349     /**
12350      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12351      * @type Boolean
12352      */
12353     /**
12354      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12355      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12356      * a finer-grained basis than the DataProxy events.
12357      */
12358     getConnection : function(){
12359         return this.useAjax ? Roo.Ajax : this.conn;
12360     },
12361
12362     /**
12363      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12364      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12365      * process that block using the passed callback.
12366      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12367      * for the request to the remote server.
12368      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12369      * object into a block of Roo.data.Records.
12370      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12371      * The function must be passed <ul>
12372      * <li>The Record block object</li>
12373      * <li>The "arg" argument from the load function</li>
12374      * <li>A boolean success indicator</li>
12375      * </ul>
12376      * @param {Object} scope The scope in which to call the callback
12377      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12378      */
12379     load : function(params, reader, callback, scope, arg){
12380         if(this.fireEvent("beforeload", this, params) !== false){
12381             var  o = {
12382                 params : params || {},
12383                 request: {
12384                     callback : callback,
12385                     scope : scope,
12386                     arg : arg
12387                 },
12388                 reader: reader,
12389                 callback : this.loadResponse,
12390                 scope: this
12391             };
12392             if(this.useAjax){
12393                 Roo.applyIf(o, this.conn);
12394                 if(this.activeRequest){
12395                     Roo.Ajax.abort(this.activeRequest);
12396                 }
12397                 this.activeRequest = Roo.Ajax.request(o);
12398             }else{
12399                 this.conn.request(o);
12400             }
12401         }else{
12402             callback.call(scope||this, null, arg, false);
12403         }
12404     },
12405
12406     // private
12407     loadResponse : function(o, success, response){
12408         delete this.activeRequest;
12409         if(!success){
12410             this.fireEvent("loadexception", this, o, response);
12411             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12412             return;
12413         }
12414         var result;
12415         try {
12416             result = o.reader.read(response);
12417         }catch(e){
12418             this.fireEvent("loadexception", this, o, response, e);
12419             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12420             return;
12421         }
12422         
12423         this.fireEvent("load", this, o, o.request.arg);
12424         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12425     },
12426
12427     // private
12428     update : function(dataSet){
12429
12430     },
12431
12432     // private
12433     updateResponse : function(dataSet){
12434
12435     }
12436 });/*
12437  * Based on:
12438  * Ext JS Library 1.1.1
12439  * Copyright(c) 2006-2007, Ext JS, LLC.
12440  *
12441  * Originally Released Under LGPL - original licence link has changed is not relivant.
12442  *
12443  * Fork - LGPL
12444  * <script type="text/javascript">
12445  */
12446
12447 /**
12448  * @class Roo.data.ScriptTagProxy
12449  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12450  * other than the originating domain of the running page.<br><br>
12451  * <p>
12452  * <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
12453  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12454  * <p>
12455  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12456  * source code that is used as the source inside a &lt;script> tag.<br><br>
12457  * <p>
12458  * In order for the browser to process the returned data, the server must wrap the data object
12459  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12460  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12461  * depending on whether the callback name was passed:
12462  * <p>
12463  * <pre><code>
12464 boolean scriptTag = false;
12465 String cb = request.getParameter("callback");
12466 if (cb != null) {
12467     scriptTag = true;
12468     response.setContentType("text/javascript");
12469 } else {
12470     response.setContentType("application/x-json");
12471 }
12472 Writer out = response.getWriter();
12473 if (scriptTag) {
12474     out.write(cb + "(");
12475 }
12476 out.print(dataBlock.toJsonString());
12477 if (scriptTag) {
12478     out.write(");");
12479 }
12480 </pre></code>
12481  *
12482  * @constructor
12483  * @param {Object} config A configuration object.
12484  */
12485 Roo.data.ScriptTagProxy = function(config){
12486     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12487     Roo.apply(this, config);
12488     this.head = document.getElementsByTagName("head")[0];
12489 };
12490
12491 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12492
12493 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12494     /**
12495      * @cfg {String} url The URL from which to request the data object.
12496      */
12497     /**
12498      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12499      */
12500     timeout : 30000,
12501     /**
12502      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12503      * the server the name of the callback function set up by the load call to process the returned data object.
12504      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12505      * javascript output which calls this named function passing the data object as its only parameter.
12506      */
12507     callbackParam : "callback",
12508     /**
12509      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12510      * name to the request.
12511      */
12512     nocache : true,
12513
12514     /**
12515      * Load data from the configured URL, read the data object into
12516      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12517      * process that block using the passed callback.
12518      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12519      * for the request to the remote server.
12520      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12521      * object into a block of Roo.data.Records.
12522      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12523      * The function must be passed <ul>
12524      * <li>The Record block object</li>
12525      * <li>The "arg" argument from the load function</li>
12526      * <li>A boolean success indicator</li>
12527      * </ul>
12528      * @param {Object} scope The scope in which to call the callback
12529      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12530      */
12531     load : function(params, reader, callback, scope, arg){
12532         if(this.fireEvent("beforeload", this, params) !== false){
12533
12534             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12535
12536             var url = this.url;
12537             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12538             if(this.nocache){
12539                 url += "&_dc=" + (new Date().getTime());
12540             }
12541             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12542             var trans = {
12543                 id : transId,
12544                 cb : "stcCallback"+transId,
12545                 scriptId : "stcScript"+transId,
12546                 params : params,
12547                 arg : arg,
12548                 url : url,
12549                 callback : callback,
12550                 scope : scope,
12551                 reader : reader
12552             };
12553             var conn = this;
12554
12555             window[trans.cb] = function(o){
12556                 conn.handleResponse(o, trans);
12557             };
12558
12559             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12560
12561             if(this.autoAbort !== false){
12562                 this.abort();
12563             }
12564
12565             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12566
12567             var script = document.createElement("script");
12568             script.setAttribute("src", url);
12569             script.setAttribute("type", "text/javascript");
12570             script.setAttribute("id", trans.scriptId);
12571             this.head.appendChild(script);
12572
12573             this.trans = trans;
12574         }else{
12575             callback.call(scope||this, null, arg, false);
12576         }
12577     },
12578
12579     // private
12580     isLoading : function(){
12581         return this.trans ? true : false;
12582     },
12583
12584     /**
12585      * Abort the current server request.
12586      */
12587     abort : function(){
12588         if(this.isLoading()){
12589             this.destroyTrans(this.trans);
12590         }
12591     },
12592
12593     // private
12594     destroyTrans : function(trans, isLoaded){
12595         this.head.removeChild(document.getElementById(trans.scriptId));
12596         clearTimeout(trans.timeoutId);
12597         if(isLoaded){
12598             window[trans.cb] = undefined;
12599             try{
12600                 delete window[trans.cb];
12601             }catch(e){}
12602         }else{
12603             // if hasn't been loaded, wait for load to remove it to prevent script error
12604             window[trans.cb] = function(){
12605                 window[trans.cb] = undefined;
12606                 try{
12607                     delete window[trans.cb];
12608                 }catch(e){}
12609             };
12610         }
12611     },
12612
12613     // private
12614     handleResponse : function(o, trans){
12615         this.trans = false;
12616         this.destroyTrans(trans, true);
12617         var result;
12618         try {
12619             result = trans.reader.readRecords(o);
12620         }catch(e){
12621             this.fireEvent("loadexception", this, o, trans.arg, e);
12622             trans.callback.call(trans.scope||window, null, trans.arg, false);
12623             return;
12624         }
12625         this.fireEvent("load", this, o, trans.arg);
12626         trans.callback.call(trans.scope||window, result, trans.arg, true);
12627     },
12628
12629     // private
12630     handleFailure : function(trans){
12631         this.trans = false;
12632         this.destroyTrans(trans, false);
12633         this.fireEvent("loadexception", this, null, trans.arg);
12634         trans.callback.call(trans.scope||window, null, trans.arg, false);
12635     }
12636 });/*
12637  * Based on:
12638  * Ext JS Library 1.1.1
12639  * Copyright(c) 2006-2007, Ext JS, LLC.
12640  *
12641  * Originally Released Under LGPL - original licence link has changed is not relivant.
12642  *
12643  * Fork - LGPL
12644  * <script type="text/javascript">
12645  */
12646
12647 /**
12648  * @class Roo.data.JsonReader
12649  * @extends Roo.data.DataReader
12650  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12651  * based on mappings in a provided Roo.data.Record constructor.
12652  * 
12653  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12654  * in the reply previously. 
12655  * 
12656  * <p>
12657  * Example code:
12658  * <pre><code>
12659 var RecordDef = Roo.data.Record.create([
12660     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12661     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12662 ]);
12663 var myReader = new Roo.data.JsonReader({
12664     totalProperty: "results",    // The property which contains the total dataset size (optional)
12665     root: "rows",                // The property which contains an Array of row objects
12666     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12667 }, RecordDef);
12668 </code></pre>
12669  * <p>
12670  * This would consume a JSON file like this:
12671  * <pre><code>
12672 { 'results': 2, 'rows': [
12673     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12674     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12675 }
12676 </code></pre>
12677  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12678  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12679  * paged from the remote server.
12680  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12681  * @cfg {String} root name of the property which contains the Array of row objects.
12682  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12683  * @cfg {Array} fields Array of field definition objects
12684  * @constructor
12685  * Create a new JsonReader
12686  * @param {Object} meta Metadata configuration options
12687  * @param {Object} recordType Either an Array of field definition objects,
12688  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12689  */
12690 Roo.data.JsonReader = function(meta, recordType){
12691     
12692     meta = meta || {};
12693     // set some defaults:
12694     Roo.applyIf(meta, {
12695         totalProperty: 'total',
12696         successProperty : 'success',
12697         root : 'data',
12698         id : 'id'
12699     });
12700     
12701     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12702 };
12703 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12704     
12705     /**
12706      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12707      * Used by Store query builder to append _requestMeta to params.
12708      * 
12709      */
12710     metaFromRemote : false,
12711     /**
12712      * This method is only used by a DataProxy which has retrieved data from a remote server.
12713      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12714      * @return {Object} data A data block which is used by an Roo.data.Store object as
12715      * a cache of Roo.data.Records.
12716      */
12717     read : function(response){
12718         var json = response.responseText;
12719        
12720         var o = /* eval:var:o */ eval("("+json+")");
12721         if(!o) {
12722             throw {message: "JsonReader.read: Json object not found"};
12723         }
12724         
12725         if(o.metaData){
12726             
12727             delete this.ef;
12728             this.metaFromRemote = true;
12729             this.meta = o.metaData;
12730             this.recordType = Roo.data.Record.create(o.metaData.fields);
12731             this.onMetaChange(this.meta, this.recordType, o);
12732         }
12733         return this.readRecords(o);
12734     },
12735
12736     // private function a store will implement
12737     onMetaChange : function(meta, recordType, o){
12738
12739     },
12740
12741     /**
12742          * @ignore
12743          */
12744     simpleAccess: function(obj, subsc) {
12745         return obj[subsc];
12746     },
12747
12748         /**
12749          * @ignore
12750          */
12751     getJsonAccessor: function(){
12752         var re = /[\[\.]/;
12753         return function(expr) {
12754             try {
12755                 return(re.test(expr))
12756                     ? new Function("obj", "return obj." + expr)
12757                     : function(obj){
12758                         return obj[expr];
12759                     };
12760             } catch(e){}
12761             return Roo.emptyFn;
12762         };
12763     }(),
12764
12765     /**
12766      * Create a data block containing Roo.data.Records from an XML document.
12767      * @param {Object} o An object which contains an Array of row objects in the property specified
12768      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12769      * which contains the total size of the dataset.
12770      * @return {Object} data A data block which is used by an Roo.data.Store object as
12771      * a cache of Roo.data.Records.
12772      */
12773     readRecords : function(o){
12774         /**
12775          * After any data loads, the raw JSON data is available for further custom processing.
12776          * @type Object
12777          */
12778         this.o = o;
12779         var s = this.meta, Record = this.recordType,
12780             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12781
12782 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12783         if (!this.ef) {
12784             if(s.totalProperty) {
12785                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12786                 }
12787                 if(s.successProperty) {
12788                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12789                 }
12790                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12791                 if (s.id) {
12792                         var g = this.getJsonAccessor(s.id);
12793                         this.getId = function(rec) {
12794                                 var r = g(rec);  
12795                                 return (r === undefined || r === "") ? null : r;
12796                         };
12797                 } else {
12798                         this.getId = function(){return null;};
12799                 }
12800             this.ef = [];
12801             for(var jj = 0; jj < fl; jj++){
12802                 f = fi[jj];
12803                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12804                 this.ef[jj] = this.getJsonAccessor(map);
12805             }
12806         }
12807
12808         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12809         if(s.totalProperty){
12810             var vt = parseInt(this.getTotal(o), 10);
12811             if(!isNaN(vt)){
12812                 totalRecords = vt;
12813             }
12814         }
12815         if(s.successProperty){
12816             var vs = this.getSuccess(o);
12817             if(vs === false || vs === 'false'){
12818                 success = false;
12819             }
12820         }
12821         var records = [];
12822         for(var i = 0; i < c; i++){
12823                 var n = root[i];
12824             var values = {};
12825             var id = this.getId(n);
12826             for(var j = 0; j < fl; j++){
12827                 f = fi[j];
12828             var v = this.ef[j](n);
12829             if (!f.convert) {
12830                 Roo.log('missing convert for ' + f.name);
12831                 Roo.log(f);
12832                 continue;
12833             }
12834             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12835             }
12836             var record = new Record(values, id);
12837             record.json = n;
12838             records[i] = record;
12839         }
12840         return {
12841             raw : o,
12842             success : success,
12843             records : records,
12844             totalRecords : totalRecords
12845         };
12846     }
12847 });/*
12848  * Based on:
12849  * Ext JS Library 1.1.1
12850  * Copyright(c) 2006-2007, Ext JS, LLC.
12851  *
12852  * Originally Released Under LGPL - original licence link has changed is not relivant.
12853  *
12854  * Fork - LGPL
12855  * <script type="text/javascript">
12856  */
12857
12858 /**
12859  * @class Roo.data.ArrayReader
12860  * @extends Roo.data.DataReader
12861  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12862  * Each element of that Array represents a row of data fields. The
12863  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12864  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12865  * <p>
12866  * Example code:.
12867  * <pre><code>
12868 var RecordDef = Roo.data.Record.create([
12869     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12870     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12871 ]);
12872 var myReader = new Roo.data.ArrayReader({
12873     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12874 }, RecordDef);
12875 </code></pre>
12876  * <p>
12877  * This would consume an Array like this:
12878  * <pre><code>
12879 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12880   </code></pre>
12881  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12882  * @constructor
12883  * Create a new JsonReader
12884  * @param {Object} meta Metadata configuration options.
12885  * @param {Object} recordType Either an Array of field definition objects
12886  * as specified to {@link Roo.data.Record#create},
12887  * or an {@link Roo.data.Record} object
12888  * created using {@link Roo.data.Record#create}.
12889  */
12890 Roo.data.ArrayReader = function(meta, recordType){
12891     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12892 };
12893
12894 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12895     /**
12896      * Create a data block containing Roo.data.Records from an XML document.
12897      * @param {Object} o An Array of row objects which represents the dataset.
12898      * @return {Object} data A data block which is used by an Roo.data.Store object as
12899      * a cache of Roo.data.Records.
12900      */
12901     readRecords : function(o){
12902         var sid = this.meta ? this.meta.id : null;
12903         var recordType = this.recordType, fields = recordType.prototype.fields;
12904         var records = [];
12905         var root = o;
12906             for(var i = 0; i < root.length; i++){
12907                     var n = root[i];
12908                 var values = {};
12909                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12910                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12911                 var f = fields.items[j];
12912                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12913                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12914                 v = f.convert(v);
12915                 values[f.name] = v;
12916             }
12917                 var record = new recordType(values, id);
12918                 record.json = n;
12919                 records[records.length] = record;
12920             }
12921             return {
12922                 records : records,
12923                 totalRecords : records.length
12924             };
12925     }
12926 });/*
12927  * - LGPL
12928  * * 
12929  */
12930
12931 /**
12932  * @class Roo.bootstrap.ComboBox
12933  * @extends Roo.bootstrap.TriggerField
12934  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12935  * @cfg {Boolean} append (true|false) default false
12936  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12937  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12938  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12939  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12940  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12941  * @cfg {Boolean} animate default true
12942  * @cfg {Boolean} emptyResultText only for touch device
12943  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12944  * @cfg {String} emptyTitle default ''
12945  * @constructor
12946  * Create a new ComboBox.
12947  * @param {Object} config Configuration options
12948  */
12949 Roo.bootstrap.ComboBox = function(config){
12950     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12951     this.addEvents({
12952         /**
12953          * @event expand
12954          * Fires when the dropdown list is expanded
12955         * @param {Roo.bootstrap.ComboBox} combo This combo box
12956         */
12957         'expand' : true,
12958         /**
12959          * @event collapse
12960          * Fires when the dropdown list is collapsed
12961         * @param {Roo.bootstrap.ComboBox} combo This combo box
12962         */
12963         'collapse' : true,
12964         /**
12965          * @event beforeselect
12966          * Fires before a list item is selected. Return false to cancel the selection.
12967         * @param {Roo.bootstrap.ComboBox} combo This combo box
12968         * @param {Roo.data.Record} record The data record returned from the underlying store
12969         * @param {Number} index The index of the selected item in the dropdown list
12970         */
12971         'beforeselect' : true,
12972         /**
12973          * @event select
12974          * Fires when a list item is selected
12975         * @param {Roo.bootstrap.ComboBox} combo This combo box
12976         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12977         * @param {Number} index The index of the selected item in the dropdown list
12978         */
12979         'select' : true,
12980         /**
12981          * @event beforequery
12982          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12983          * The event object passed has these properties:
12984         * @param {Roo.bootstrap.ComboBox} combo This combo box
12985         * @param {String} query The query
12986         * @param {Boolean} forceAll true to force "all" query
12987         * @param {Boolean} cancel true to cancel the query
12988         * @param {Object} e The query event object
12989         */
12990         'beforequery': true,
12991          /**
12992          * @event add
12993          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12994         * @param {Roo.bootstrap.ComboBox} combo This combo box
12995         */
12996         'add' : true,
12997         /**
12998          * @event edit
12999          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13000         * @param {Roo.bootstrap.ComboBox} combo This combo box
13001         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13002         */
13003         'edit' : true,
13004         /**
13005          * @event remove
13006          * Fires when the remove value from the combobox array
13007         * @param {Roo.bootstrap.ComboBox} combo This combo box
13008         */
13009         'remove' : true,
13010         /**
13011          * @event afterremove
13012          * Fires when the remove value from the combobox array
13013         * @param {Roo.bootstrap.ComboBox} combo This combo box
13014         */
13015         'afterremove' : true,
13016         /**
13017          * @event specialfilter
13018          * Fires when specialfilter
13019             * @param {Roo.bootstrap.ComboBox} combo This combo box
13020             */
13021         'specialfilter' : true,
13022         /**
13023          * @event tick
13024          * Fires when tick the element
13025             * @param {Roo.bootstrap.ComboBox} combo This combo box
13026             */
13027         'tick' : true,
13028         /**
13029          * @event touchviewdisplay
13030          * Fires when touch view require special display (default is using displayField)
13031             * @param {Roo.bootstrap.ComboBox} combo This combo box
13032             * @param {Object} cfg set html .
13033             */
13034         'touchviewdisplay' : true
13035         
13036     });
13037     
13038     this.item = [];
13039     this.tickItems = [];
13040     
13041     this.selectedIndex = -1;
13042     if(this.mode == 'local'){
13043         if(config.queryDelay === undefined){
13044             this.queryDelay = 10;
13045         }
13046         if(config.minChars === undefined){
13047             this.minChars = 0;
13048         }
13049     }
13050 };
13051
13052 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13053      
13054     /**
13055      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13056      * rendering into an Roo.Editor, defaults to false)
13057      */
13058     /**
13059      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13060      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13061      */
13062     /**
13063      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13064      */
13065     /**
13066      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13067      * the dropdown list (defaults to undefined, with no header element)
13068      */
13069
13070      /**
13071      * @cfg {String/Roo.Template} tpl The template to use to render the output
13072      */
13073      
13074      /**
13075      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13076      */
13077     listWidth: undefined,
13078     /**
13079      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13080      * mode = 'remote' or 'text' if mode = 'local')
13081      */
13082     displayField: undefined,
13083     
13084     /**
13085      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13086      * mode = 'remote' or 'value' if mode = 'local'). 
13087      * Note: use of a valueField requires the user make a selection
13088      * in order for a value to be mapped.
13089      */
13090     valueField: undefined,
13091     /**
13092      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13093      */
13094     modalTitle : '',
13095     
13096     /**
13097      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13098      * field's data value (defaults to the underlying DOM element's name)
13099      */
13100     hiddenName: undefined,
13101     /**
13102      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13103      */
13104     listClass: '',
13105     /**
13106      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13107      */
13108     selectedClass: 'active',
13109     
13110     /**
13111      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13112      */
13113     shadow:'sides',
13114     /**
13115      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13116      * anchor positions (defaults to 'tl-bl')
13117      */
13118     listAlign: 'tl-bl?',
13119     /**
13120      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13121      */
13122     maxHeight: 300,
13123     /**
13124      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13125      * query specified by the allQuery config option (defaults to 'query')
13126      */
13127     triggerAction: 'query',
13128     /**
13129      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13130      * (defaults to 4, does not apply if editable = false)
13131      */
13132     minChars : 4,
13133     /**
13134      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13135      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13136      */
13137     typeAhead: false,
13138     /**
13139      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13140      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13141      */
13142     queryDelay: 500,
13143     /**
13144      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13145      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13146      */
13147     pageSize: 0,
13148     /**
13149      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13150      * when editable = true (defaults to false)
13151      */
13152     selectOnFocus:false,
13153     /**
13154      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13155      */
13156     queryParam: 'query',
13157     /**
13158      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13159      * when mode = 'remote' (defaults to 'Loading...')
13160      */
13161     loadingText: 'Loading...',
13162     /**
13163      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13164      */
13165     resizable: false,
13166     /**
13167      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13168      */
13169     handleHeight : 8,
13170     /**
13171      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13172      * traditional select (defaults to true)
13173      */
13174     editable: true,
13175     /**
13176      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13177      */
13178     allQuery: '',
13179     /**
13180      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13181      */
13182     mode: 'remote',
13183     /**
13184      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13185      * listWidth has a higher value)
13186      */
13187     minListWidth : 70,
13188     /**
13189      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13190      * allow the user to set arbitrary text into the field (defaults to false)
13191      */
13192     forceSelection:false,
13193     /**
13194      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13195      * if typeAhead = true (defaults to 250)
13196      */
13197     typeAheadDelay : 250,
13198     /**
13199      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13200      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13201      */
13202     valueNotFoundText : undefined,
13203     /**
13204      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13205      */
13206     blockFocus : false,
13207     
13208     /**
13209      * @cfg {Boolean} disableClear Disable showing of clear button.
13210      */
13211     disableClear : false,
13212     /**
13213      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13214      */
13215     alwaysQuery : false,
13216     
13217     /**
13218      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13219      */
13220     multiple : false,
13221     
13222     /**
13223      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13224      */
13225     invalidClass : "has-warning",
13226     
13227     /**
13228      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13229      */
13230     validClass : "has-success",
13231     
13232     /**
13233      * @cfg {Boolean} specialFilter (true|false) special filter default false
13234      */
13235     specialFilter : false,
13236     
13237     /**
13238      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13239      */
13240     mobileTouchView : true,
13241     
13242     /**
13243      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13244      */
13245     useNativeIOS : false,
13246     
13247     /**
13248      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13249      */
13250     mobile_restrict_height : false,
13251     
13252     ios_options : false,
13253     
13254     //private
13255     addicon : false,
13256     editicon: false,
13257     
13258     page: 0,
13259     hasQuery: false,
13260     append: false,
13261     loadNext: false,
13262     autoFocus : true,
13263     tickable : false,
13264     btnPosition : 'right',
13265     triggerList : true,
13266     showToggleBtn : true,
13267     animate : true,
13268     emptyResultText: 'Empty',
13269     triggerText : 'Select',
13270     emptyTitle : '',
13271     
13272     // element that contains real text value.. (when hidden is used..)
13273     
13274     getAutoCreate : function()
13275     {   
13276         var cfg = false;
13277         //render
13278         /*
13279          * Render classic select for iso
13280          */
13281         
13282         if(Roo.isIOS && this.useNativeIOS){
13283             cfg = this.getAutoCreateNativeIOS();
13284             return cfg;
13285         }
13286         
13287         /*
13288          * Touch Devices
13289          */
13290         
13291         if(Roo.isTouch && this.mobileTouchView){
13292             cfg = this.getAutoCreateTouchView();
13293             return cfg;;
13294         }
13295         
13296         /*
13297          *  Normal ComboBox
13298          */
13299         if(!this.tickable){
13300             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13301             return cfg;
13302         }
13303         
13304         /*
13305          *  ComboBox with tickable selections
13306          */
13307              
13308         var align = this.labelAlign || this.parentLabelAlign();
13309         
13310         cfg = {
13311             cls : 'form-group roo-combobox-tickable' //input-group
13312         };
13313         
13314         var btn_text_select = '';
13315         var btn_text_done = '';
13316         var btn_text_cancel = '';
13317         
13318         if (this.btn_text_show) {
13319             btn_text_select = 'Select';
13320             btn_text_done = 'Done';
13321             btn_text_cancel = 'Cancel'; 
13322         }
13323         
13324         var buttons = {
13325             tag : 'div',
13326             cls : 'tickable-buttons',
13327             cn : [
13328                 {
13329                     tag : 'button',
13330                     type : 'button',
13331                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13332                     //html : this.triggerText
13333                     html: btn_text_select
13334                 },
13335                 {
13336                     tag : 'button',
13337                     type : 'button',
13338                     name : 'ok',
13339                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13340                     //html : 'Done'
13341                     html: btn_text_done
13342                 },
13343                 {
13344                     tag : 'button',
13345                     type : 'button',
13346                     name : 'cancel',
13347                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13348                     //html : 'Cancel'
13349                     html: btn_text_cancel
13350                 }
13351             ]
13352         };
13353         
13354         if(this.editable){
13355             buttons.cn.unshift({
13356                 tag: 'input',
13357                 cls: 'roo-select2-search-field-input'
13358             });
13359         }
13360         
13361         var _this = this;
13362         
13363         Roo.each(buttons.cn, function(c){
13364             if (_this.size) {
13365                 c.cls += ' btn-' + _this.size;
13366             }
13367
13368             if (_this.disabled) {
13369                 c.disabled = true;
13370             }
13371         });
13372         
13373         var box = {
13374             tag: 'div',
13375             cn: [
13376                 {
13377                     tag: 'input',
13378                     type : 'hidden',
13379                     cls: 'form-hidden-field'
13380                 },
13381                 {
13382                     tag: 'ul',
13383                     cls: 'roo-select2-choices',
13384                     cn:[
13385                         {
13386                             tag: 'li',
13387                             cls: 'roo-select2-search-field',
13388                             cn: [
13389                                 buttons
13390                             ]
13391                         }
13392                     ]
13393                 }
13394             ]
13395         };
13396         
13397         var combobox = {
13398             cls: 'roo-select2-container input-group roo-select2-container-multi',
13399             cn: [
13400                 
13401                 box
13402 //                {
13403 //                    tag: 'ul',
13404 //                    cls: 'typeahead typeahead-long dropdown-menu',
13405 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13406 //                }
13407             ]
13408         };
13409         
13410         if(this.hasFeedback && !this.allowBlank){
13411             
13412             var feedback = {
13413                 tag: 'span',
13414                 cls: 'glyphicon form-control-feedback'
13415             };
13416
13417             combobox.cn.push(feedback);
13418         }
13419         
13420         var indicator = {
13421             tag : 'i',
13422             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13423             tooltip : 'This field is required'
13424         };
13425         if (Roo.bootstrap.version == 4) {
13426             indicator = {
13427                 tag : 'i',
13428                 style : 'display:none'
13429             };
13430         }
13431         if (align ==='left' && this.fieldLabel.length) {
13432             
13433             cfg.cls += ' roo-form-group-label-left row';
13434             
13435             cfg.cn = [
13436                 indicator,
13437                 {
13438                     tag: 'label',
13439                     'for' :  id,
13440                     cls : 'control-label col-form-label',
13441                     html : this.fieldLabel
13442
13443                 },
13444                 {
13445                     cls : "", 
13446                     cn: [
13447                         combobox
13448                     ]
13449                 }
13450
13451             ];
13452             
13453             var labelCfg = cfg.cn[1];
13454             var contentCfg = cfg.cn[2];
13455             
13456
13457             if(this.indicatorpos == 'right'){
13458                 
13459                 cfg.cn = [
13460                     {
13461                         tag: 'label',
13462                         'for' :  id,
13463                         cls : 'control-label col-form-label',
13464                         cn : [
13465                             {
13466                                 tag : 'span',
13467                                 html : this.fieldLabel
13468                             },
13469                             indicator
13470                         ]
13471                     },
13472                     {
13473                         cls : "",
13474                         cn: [
13475                             combobox
13476                         ]
13477                     }
13478
13479                 ];
13480                 
13481                 
13482                 
13483                 labelCfg = cfg.cn[0];
13484                 contentCfg = cfg.cn[1];
13485             
13486             }
13487             
13488             if(this.labelWidth > 12){
13489                 labelCfg.style = "width: " + this.labelWidth + 'px';
13490             }
13491             
13492             if(this.labelWidth < 13 && this.labelmd == 0){
13493                 this.labelmd = this.labelWidth;
13494             }
13495             
13496             if(this.labellg > 0){
13497                 labelCfg.cls += ' col-lg-' + this.labellg;
13498                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13499             }
13500             
13501             if(this.labelmd > 0){
13502                 labelCfg.cls += ' col-md-' + this.labelmd;
13503                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13504             }
13505             
13506             if(this.labelsm > 0){
13507                 labelCfg.cls += ' col-sm-' + this.labelsm;
13508                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13509             }
13510             
13511             if(this.labelxs > 0){
13512                 labelCfg.cls += ' col-xs-' + this.labelxs;
13513                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13514             }
13515                 
13516                 
13517         } else if ( this.fieldLabel.length) {
13518 //                Roo.log(" label");
13519                  cfg.cn = [
13520                    indicator,
13521                     {
13522                         tag: 'label',
13523                         //cls : 'input-group-addon',
13524                         html : this.fieldLabel
13525                     },
13526                     combobox
13527                 ];
13528                 
13529                 if(this.indicatorpos == 'right'){
13530                     cfg.cn = [
13531                         {
13532                             tag: 'label',
13533                             //cls : 'input-group-addon',
13534                             html : this.fieldLabel
13535                         },
13536                         indicator,
13537                         combobox
13538                     ];
13539                     
13540                 }
13541
13542         } else {
13543             
13544 //                Roo.log(" no label && no align");
13545                 cfg = combobox
13546                      
13547                 
13548         }
13549          
13550         var settings=this;
13551         ['xs','sm','md','lg'].map(function(size){
13552             if (settings[size]) {
13553                 cfg.cls += ' col-' + size + '-' + settings[size];
13554             }
13555         });
13556         
13557         return cfg;
13558         
13559     },
13560     
13561     _initEventsCalled : false,
13562     
13563     // private
13564     initEvents: function()
13565     {   
13566         if (this._initEventsCalled) { // as we call render... prevent looping...
13567             return;
13568         }
13569         this._initEventsCalled = true;
13570         
13571         if (!this.store) {
13572             throw "can not find store for combo";
13573         }
13574         
13575         this.indicator = this.indicatorEl();
13576         
13577         this.store = Roo.factory(this.store, Roo.data);
13578         this.store.parent = this;
13579         
13580         // if we are building from html. then this element is so complex, that we can not really
13581         // use the rendered HTML.
13582         // so we have to trash and replace the previous code.
13583         if (Roo.XComponent.build_from_html) {
13584             // remove this element....
13585             var e = this.el.dom, k=0;
13586             while (e ) { e = e.previousSibling;  ++k;}
13587
13588             this.el.remove();
13589             
13590             this.el=false;
13591             this.rendered = false;
13592             
13593             this.render(this.parent().getChildContainer(true), k);
13594         }
13595         
13596         if(Roo.isIOS && this.useNativeIOS){
13597             this.initIOSView();
13598             return;
13599         }
13600         
13601         /*
13602          * Touch Devices
13603          */
13604         
13605         if(Roo.isTouch && this.mobileTouchView){
13606             this.initTouchView();
13607             return;
13608         }
13609         
13610         if(this.tickable){
13611             this.initTickableEvents();
13612             return;
13613         }
13614         
13615         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13616         
13617         if(this.hiddenName){
13618             
13619             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13620             
13621             this.hiddenField.dom.value =
13622                 this.hiddenValue !== undefined ? this.hiddenValue :
13623                 this.value !== undefined ? this.value : '';
13624
13625             // prevent input submission
13626             this.el.dom.removeAttribute('name');
13627             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13628              
13629              
13630         }
13631         //if(Roo.isGecko){
13632         //    this.el.dom.setAttribute('autocomplete', 'off');
13633         //}
13634         
13635         var cls = 'x-combo-list';
13636         
13637         //this.list = new Roo.Layer({
13638         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13639         //});
13640         
13641         var _this = this;
13642         
13643         (function(){
13644             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13645             _this.list.setWidth(lw);
13646         }).defer(100);
13647         
13648         this.list.on('mouseover', this.onViewOver, this);
13649         this.list.on('mousemove', this.onViewMove, this);
13650         this.list.on('scroll', this.onViewScroll, this);
13651         
13652         /*
13653         this.list.swallowEvent('mousewheel');
13654         this.assetHeight = 0;
13655
13656         if(this.title){
13657             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13658             this.assetHeight += this.header.getHeight();
13659         }
13660
13661         this.innerList = this.list.createChild({cls:cls+'-inner'});
13662         this.innerList.on('mouseover', this.onViewOver, this);
13663         this.innerList.on('mousemove', this.onViewMove, this);
13664         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13665         
13666         if(this.allowBlank && !this.pageSize && !this.disableClear){
13667             this.footer = this.list.createChild({cls:cls+'-ft'});
13668             this.pageTb = new Roo.Toolbar(this.footer);
13669            
13670         }
13671         if(this.pageSize){
13672             this.footer = this.list.createChild({cls:cls+'-ft'});
13673             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13674                     {pageSize: this.pageSize});
13675             
13676         }
13677         
13678         if (this.pageTb && this.allowBlank && !this.disableClear) {
13679             var _this = this;
13680             this.pageTb.add(new Roo.Toolbar.Fill(), {
13681                 cls: 'x-btn-icon x-btn-clear',
13682                 text: '&#160;',
13683                 handler: function()
13684                 {
13685                     _this.collapse();
13686                     _this.clearValue();
13687                     _this.onSelect(false, -1);
13688                 }
13689             });
13690         }
13691         if (this.footer) {
13692             this.assetHeight += this.footer.getHeight();
13693         }
13694         */
13695             
13696         if(!this.tpl){
13697             this.tpl = Roo.bootstrap.version == 4 ?
13698                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13699                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13700         }
13701
13702         this.view = new Roo.View(this.list, this.tpl, {
13703             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13704         });
13705         //this.view.wrapEl.setDisplayed(false);
13706         this.view.on('click', this.onViewClick, this);
13707         
13708         
13709         this.store.on('beforeload', this.onBeforeLoad, this);
13710         this.store.on('load', this.onLoad, this);
13711         this.store.on('loadexception', this.onLoadException, this);
13712         /*
13713         if(this.resizable){
13714             this.resizer = new Roo.Resizable(this.list,  {
13715                pinned:true, handles:'se'
13716             });
13717             this.resizer.on('resize', function(r, w, h){
13718                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13719                 this.listWidth = w;
13720                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13721                 this.restrictHeight();
13722             }, this);
13723             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13724         }
13725         */
13726         if(!this.editable){
13727             this.editable = true;
13728             this.setEditable(false);
13729         }
13730         
13731         /*
13732         
13733         if (typeof(this.events.add.listeners) != 'undefined') {
13734             
13735             this.addicon = this.wrap.createChild(
13736                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13737        
13738             this.addicon.on('click', function(e) {
13739                 this.fireEvent('add', this);
13740             }, this);
13741         }
13742         if (typeof(this.events.edit.listeners) != 'undefined') {
13743             
13744             this.editicon = this.wrap.createChild(
13745                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13746             if (this.addicon) {
13747                 this.editicon.setStyle('margin-left', '40px');
13748             }
13749             this.editicon.on('click', function(e) {
13750                 
13751                 // we fire even  if inothing is selected..
13752                 this.fireEvent('edit', this, this.lastData );
13753                 
13754             }, this);
13755         }
13756         */
13757         
13758         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13759             "up" : function(e){
13760                 this.inKeyMode = true;
13761                 this.selectPrev();
13762             },
13763
13764             "down" : function(e){
13765                 if(!this.isExpanded()){
13766                     this.onTriggerClick();
13767                 }else{
13768                     this.inKeyMode = true;
13769                     this.selectNext();
13770                 }
13771             },
13772
13773             "enter" : function(e){
13774 //                this.onViewClick();
13775                 //return true;
13776                 this.collapse();
13777                 
13778                 if(this.fireEvent("specialkey", this, e)){
13779                     this.onViewClick(false);
13780                 }
13781                 
13782                 return true;
13783             },
13784
13785             "esc" : function(e){
13786                 this.collapse();
13787             },
13788
13789             "tab" : function(e){
13790                 this.collapse();
13791                 
13792                 if(this.fireEvent("specialkey", this, e)){
13793                     this.onViewClick(false);
13794                 }
13795                 
13796                 return true;
13797             },
13798
13799             scope : this,
13800
13801             doRelay : function(foo, bar, hname){
13802                 if(hname == 'down' || this.scope.isExpanded()){
13803                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13804                 }
13805                 return true;
13806             },
13807
13808             forceKeyDown: true
13809         });
13810         
13811         
13812         this.queryDelay = Math.max(this.queryDelay || 10,
13813                 this.mode == 'local' ? 10 : 250);
13814         
13815         
13816         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13817         
13818         if(this.typeAhead){
13819             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13820         }
13821         if(this.editable !== false){
13822             this.inputEl().on("keyup", this.onKeyUp, this);
13823         }
13824         if(this.forceSelection){
13825             this.inputEl().on('blur', this.doForce, this);
13826         }
13827         
13828         if(this.multiple){
13829             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13830             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13831         }
13832     },
13833     
13834     initTickableEvents: function()
13835     {   
13836         this.createList();
13837         
13838         if(this.hiddenName){
13839             
13840             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13841             
13842             this.hiddenField.dom.value =
13843                 this.hiddenValue !== undefined ? this.hiddenValue :
13844                 this.value !== undefined ? this.value : '';
13845
13846             // prevent input submission
13847             this.el.dom.removeAttribute('name');
13848             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13849              
13850              
13851         }
13852         
13853 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13854         
13855         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13856         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13857         if(this.triggerList){
13858             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13859         }
13860          
13861         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13862         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13863         
13864         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13865         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13866         
13867         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13868         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13869         
13870         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13871         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13872         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13873         
13874         this.okBtn.hide();
13875         this.cancelBtn.hide();
13876         
13877         var _this = this;
13878         
13879         (function(){
13880             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13881             _this.list.setWidth(lw);
13882         }).defer(100);
13883         
13884         this.list.on('mouseover', this.onViewOver, this);
13885         this.list.on('mousemove', this.onViewMove, this);
13886         
13887         this.list.on('scroll', this.onViewScroll, this);
13888         
13889         if(!this.tpl){
13890             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13891                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13892         }
13893
13894         this.view = new Roo.View(this.list, this.tpl, {
13895             singleSelect:true,
13896             tickable:true,
13897             parent:this,
13898             store: this.store,
13899             selectedClass: this.selectedClass
13900         });
13901         
13902         //this.view.wrapEl.setDisplayed(false);
13903         this.view.on('click', this.onViewClick, this);
13904         
13905         
13906         
13907         this.store.on('beforeload', this.onBeforeLoad, this);
13908         this.store.on('load', this.onLoad, this);
13909         this.store.on('loadexception', this.onLoadException, this);
13910         
13911         if(this.editable){
13912             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13913                 "up" : function(e){
13914                     this.inKeyMode = true;
13915                     this.selectPrev();
13916                 },
13917
13918                 "down" : function(e){
13919                     this.inKeyMode = true;
13920                     this.selectNext();
13921                 },
13922
13923                 "enter" : function(e){
13924                     if(this.fireEvent("specialkey", this, e)){
13925                         this.onViewClick(false);
13926                     }
13927                     
13928                     return true;
13929                 },
13930
13931                 "esc" : function(e){
13932                     this.onTickableFooterButtonClick(e, false, false);
13933                 },
13934
13935                 "tab" : function(e){
13936                     this.fireEvent("specialkey", this, e);
13937                     
13938                     this.onTickableFooterButtonClick(e, false, false);
13939                     
13940                     return true;
13941                 },
13942
13943                 scope : this,
13944
13945                 doRelay : function(e, fn, key){
13946                     if(this.scope.isExpanded()){
13947                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13948                     }
13949                     return true;
13950                 },
13951
13952                 forceKeyDown: true
13953             });
13954         }
13955         
13956         this.queryDelay = Math.max(this.queryDelay || 10,
13957                 this.mode == 'local' ? 10 : 250);
13958         
13959         
13960         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13961         
13962         if(this.typeAhead){
13963             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13964         }
13965         
13966         if(this.editable !== false){
13967             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13968         }
13969         
13970         this.indicator = this.indicatorEl();
13971         
13972         if(this.indicator){
13973             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13974             this.indicator.hide();
13975         }
13976         
13977     },
13978
13979     onDestroy : function(){
13980         if(this.view){
13981             this.view.setStore(null);
13982             this.view.el.removeAllListeners();
13983             this.view.el.remove();
13984             this.view.purgeListeners();
13985         }
13986         if(this.list){
13987             this.list.dom.innerHTML  = '';
13988         }
13989         
13990         if(this.store){
13991             this.store.un('beforeload', this.onBeforeLoad, this);
13992             this.store.un('load', this.onLoad, this);
13993             this.store.un('loadexception', this.onLoadException, this);
13994         }
13995         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13996     },
13997
13998     // private
13999     fireKey : function(e){
14000         if(e.isNavKeyPress() && !this.list.isVisible()){
14001             this.fireEvent("specialkey", this, e);
14002         }
14003     },
14004
14005     // private
14006     onResize: function(w, h){
14007 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14008 //        
14009 //        if(typeof w != 'number'){
14010 //            // we do not handle it!?!?
14011 //            return;
14012 //        }
14013 //        var tw = this.trigger.getWidth();
14014 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14015 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14016 //        var x = w - tw;
14017 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14018 //            
14019 //        //this.trigger.setStyle('left', x+'px');
14020 //        
14021 //        if(this.list && this.listWidth === undefined){
14022 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14023 //            this.list.setWidth(lw);
14024 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14025 //        }
14026         
14027     
14028         
14029     },
14030
14031     /**
14032      * Allow or prevent the user from directly editing the field text.  If false is passed,
14033      * the user will only be able to select from the items defined in the dropdown list.  This method
14034      * is the runtime equivalent of setting the 'editable' config option at config time.
14035      * @param {Boolean} value True to allow the user to directly edit the field text
14036      */
14037     setEditable : function(value){
14038         if(value == this.editable){
14039             return;
14040         }
14041         this.editable = value;
14042         if(!value){
14043             this.inputEl().dom.setAttribute('readOnly', true);
14044             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14045             this.inputEl().addClass('x-combo-noedit');
14046         }else{
14047             this.inputEl().dom.setAttribute('readOnly', false);
14048             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14049             this.inputEl().removeClass('x-combo-noedit');
14050         }
14051     },
14052
14053     // private
14054     
14055     onBeforeLoad : function(combo,opts){
14056         if(!this.hasFocus){
14057             return;
14058         }
14059          if (!opts.add) {
14060             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14061          }
14062         this.restrictHeight();
14063         this.selectedIndex = -1;
14064     },
14065
14066     // private
14067     onLoad : function(){
14068         
14069         this.hasQuery = false;
14070         
14071         if(!this.hasFocus){
14072             return;
14073         }
14074         
14075         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14076             this.loading.hide();
14077         }
14078         
14079         if(this.store.getCount() > 0){
14080             
14081             this.expand();
14082             this.restrictHeight();
14083             if(this.lastQuery == this.allQuery){
14084                 if(this.editable && !this.tickable){
14085                     this.inputEl().dom.select();
14086                 }
14087                 
14088                 if(
14089                     !this.selectByValue(this.value, true) &&
14090                     this.autoFocus && 
14091                     (
14092                         !this.store.lastOptions ||
14093                         typeof(this.store.lastOptions.add) == 'undefined' || 
14094                         this.store.lastOptions.add != true
14095                     )
14096                 ){
14097                     this.select(0, true);
14098                 }
14099             }else{
14100                 if(this.autoFocus){
14101                     this.selectNext();
14102                 }
14103                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14104                     this.taTask.delay(this.typeAheadDelay);
14105                 }
14106             }
14107         }else{
14108             this.onEmptyResults();
14109         }
14110         
14111         //this.el.focus();
14112     },
14113     // private
14114     onLoadException : function()
14115     {
14116         this.hasQuery = false;
14117         
14118         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14119             this.loading.hide();
14120         }
14121         
14122         if(this.tickable && this.editable){
14123             return;
14124         }
14125         
14126         this.collapse();
14127         // only causes errors at present
14128         //Roo.log(this.store.reader.jsonData);
14129         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14130             // fixme
14131             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14132         //}
14133         
14134         
14135     },
14136     // private
14137     onTypeAhead : function(){
14138         if(this.store.getCount() > 0){
14139             var r = this.store.getAt(0);
14140             var newValue = r.data[this.displayField];
14141             var len = newValue.length;
14142             var selStart = this.getRawValue().length;
14143             
14144             if(selStart != len){
14145                 this.setRawValue(newValue);
14146                 this.selectText(selStart, newValue.length);
14147             }
14148         }
14149     },
14150
14151     // private
14152     onSelect : function(record, index){
14153         
14154         if(this.fireEvent('beforeselect', this, record, index) !== false){
14155         
14156             this.setFromData(index > -1 ? record.data : false);
14157             
14158             this.collapse();
14159             this.fireEvent('select', this, record, index);
14160         }
14161     },
14162
14163     /**
14164      * Returns the currently selected field value or empty string if no value is set.
14165      * @return {String} value The selected value
14166      */
14167     getValue : function()
14168     {
14169         if(Roo.isIOS && this.useNativeIOS){
14170             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14171         }
14172         
14173         if(this.multiple){
14174             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14175         }
14176         
14177         if(this.valueField){
14178             return typeof this.value != 'undefined' ? this.value : '';
14179         }else{
14180             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14181         }
14182     },
14183     
14184     getRawValue : function()
14185     {
14186         if(Roo.isIOS && this.useNativeIOS){
14187             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14188         }
14189         
14190         var v = this.inputEl().getValue();
14191         
14192         return v;
14193     },
14194
14195     /**
14196      * Clears any text/value currently set in the field
14197      */
14198     clearValue : function(){
14199         
14200         if(this.hiddenField){
14201             this.hiddenField.dom.value = '';
14202         }
14203         this.value = '';
14204         this.setRawValue('');
14205         this.lastSelectionText = '';
14206         this.lastData = false;
14207         
14208         var close = this.closeTriggerEl();
14209         
14210         if(close){
14211             close.hide();
14212         }
14213         
14214         this.validate();
14215         
14216     },
14217
14218     /**
14219      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14220      * will be displayed in the field.  If the value does not match the data value of an existing item,
14221      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14222      * Otherwise the field will be blank (although the value will still be set).
14223      * @param {String} value The value to match
14224      */
14225     setValue : function(v)
14226     {
14227         if(Roo.isIOS && this.useNativeIOS){
14228             this.setIOSValue(v);
14229             return;
14230         }
14231         
14232         if(this.multiple){
14233             this.syncValue();
14234             return;
14235         }
14236         
14237         var text = v;
14238         if(this.valueField){
14239             var r = this.findRecord(this.valueField, v);
14240             if(r){
14241                 text = r.data[this.displayField];
14242             }else if(this.valueNotFoundText !== undefined){
14243                 text = this.valueNotFoundText;
14244             }
14245         }
14246         this.lastSelectionText = text;
14247         if(this.hiddenField){
14248             this.hiddenField.dom.value = v;
14249         }
14250         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14251         this.value = v;
14252         
14253         var close = this.closeTriggerEl();
14254         
14255         if(close){
14256             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14257         }
14258         
14259         this.validate();
14260     },
14261     /**
14262      * @property {Object} the last set data for the element
14263      */
14264     
14265     lastData : false,
14266     /**
14267      * Sets the value of the field based on a object which is related to the record format for the store.
14268      * @param {Object} value the value to set as. or false on reset?
14269      */
14270     setFromData : function(o){
14271         
14272         if(this.multiple){
14273             this.addItem(o);
14274             return;
14275         }
14276             
14277         var dv = ''; // display value
14278         var vv = ''; // value value..
14279         this.lastData = o;
14280         if (this.displayField) {
14281             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14282         } else {
14283             // this is an error condition!!!
14284             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14285         }
14286         
14287         if(this.valueField){
14288             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14289         }
14290         
14291         var close = this.closeTriggerEl();
14292         
14293         if(close){
14294             if(dv.length || vv * 1 > 0){
14295                 close.show() ;
14296                 this.blockFocus=true;
14297             } else {
14298                 close.hide();
14299             }             
14300         }
14301         
14302         if(this.hiddenField){
14303             this.hiddenField.dom.value = vv;
14304             
14305             this.lastSelectionText = dv;
14306             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14307             this.value = vv;
14308             return;
14309         }
14310         // no hidden field.. - we store the value in 'value', but still display
14311         // display field!!!!
14312         this.lastSelectionText = dv;
14313         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14314         this.value = vv;
14315         
14316         
14317         
14318     },
14319     // private
14320     reset : function(){
14321         // overridden so that last data is reset..
14322         
14323         if(this.multiple){
14324             this.clearItem();
14325             return;
14326         }
14327         
14328         this.setValue(this.originalValue);
14329         //this.clearInvalid();
14330         this.lastData = false;
14331         if (this.view) {
14332             this.view.clearSelections();
14333         }
14334         
14335         this.validate();
14336     },
14337     // private
14338     findRecord : function(prop, value){
14339         var record;
14340         if(this.store.getCount() > 0){
14341             this.store.each(function(r){
14342                 if(r.data[prop] == value){
14343                     record = r;
14344                     return false;
14345                 }
14346                 return true;
14347             });
14348         }
14349         return record;
14350     },
14351     
14352     getName: function()
14353     {
14354         // returns hidden if it's set..
14355         if (!this.rendered) {return ''};
14356         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14357         
14358     },
14359     // private
14360     onViewMove : function(e, t){
14361         this.inKeyMode = false;
14362     },
14363
14364     // private
14365     onViewOver : function(e, t){
14366         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14367             return;
14368         }
14369         var item = this.view.findItemFromChild(t);
14370         
14371         if(item){
14372             var index = this.view.indexOf(item);
14373             this.select(index, false);
14374         }
14375     },
14376
14377     // private
14378     onViewClick : function(view, doFocus, el, e)
14379     {
14380         var index = this.view.getSelectedIndexes()[0];
14381         
14382         var r = this.store.getAt(index);
14383         
14384         if(this.tickable){
14385             
14386             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14387                 return;
14388             }
14389             
14390             var rm = false;
14391             var _this = this;
14392             
14393             Roo.each(this.tickItems, function(v,k){
14394                 
14395                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14396                     Roo.log(v);
14397                     _this.tickItems.splice(k, 1);
14398                     
14399                     if(typeof(e) == 'undefined' && view == false){
14400                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14401                     }
14402                     
14403                     rm = true;
14404                     return;
14405                 }
14406             });
14407             
14408             if(rm){
14409                 return;
14410             }
14411             
14412             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14413                 this.tickItems.push(r.data);
14414             }
14415             
14416             if(typeof(e) == 'undefined' && view == false){
14417                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14418             }
14419                     
14420             return;
14421         }
14422         
14423         if(r){
14424             this.onSelect(r, index);
14425         }
14426         if(doFocus !== false && !this.blockFocus){
14427             this.inputEl().focus();
14428         }
14429     },
14430
14431     // private
14432     restrictHeight : function(){
14433         //this.innerList.dom.style.height = '';
14434         //var inner = this.innerList.dom;
14435         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14436         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14437         //this.list.beginUpdate();
14438         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14439         this.list.alignTo(this.inputEl(), this.listAlign);
14440         this.list.alignTo(this.inputEl(), this.listAlign);
14441         //this.list.endUpdate();
14442     },
14443
14444     // private
14445     onEmptyResults : function(){
14446         
14447         if(this.tickable && this.editable){
14448             this.hasFocus = false;
14449             this.restrictHeight();
14450             return;
14451         }
14452         
14453         this.collapse();
14454     },
14455
14456     /**
14457      * Returns true if the dropdown list is expanded, else false.
14458      */
14459     isExpanded : function(){
14460         return this.list.isVisible();
14461     },
14462
14463     /**
14464      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14465      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14466      * @param {String} value The data value of the item to select
14467      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14468      * selected item if it is not currently in view (defaults to true)
14469      * @return {Boolean} True if the value matched an item in the list, else false
14470      */
14471     selectByValue : function(v, scrollIntoView){
14472         if(v !== undefined && v !== null){
14473             var r = this.findRecord(this.valueField || this.displayField, v);
14474             if(r){
14475                 this.select(this.store.indexOf(r), scrollIntoView);
14476                 return true;
14477             }
14478         }
14479         return false;
14480     },
14481
14482     /**
14483      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14484      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14485      * @param {Number} index The zero-based index of the list item to select
14486      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14487      * selected item if it is not currently in view (defaults to true)
14488      */
14489     select : function(index, scrollIntoView){
14490         this.selectedIndex = index;
14491         this.view.select(index);
14492         if(scrollIntoView !== false){
14493             var el = this.view.getNode(index);
14494             /*
14495              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14496              */
14497             if(el){
14498                 this.list.scrollChildIntoView(el, false);
14499             }
14500         }
14501     },
14502
14503     // private
14504     selectNext : function(){
14505         var ct = this.store.getCount();
14506         if(ct > 0){
14507             if(this.selectedIndex == -1){
14508                 this.select(0);
14509             }else if(this.selectedIndex < ct-1){
14510                 this.select(this.selectedIndex+1);
14511             }
14512         }
14513     },
14514
14515     // private
14516     selectPrev : function(){
14517         var ct = this.store.getCount();
14518         if(ct > 0){
14519             if(this.selectedIndex == -1){
14520                 this.select(0);
14521             }else if(this.selectedIndex != 0){
14522                 this.select(this.selectedIndex-1);
14523             }
14524         }
14525     },
14526
14527     // private
14528     onKeyUp : function(e){
14529         if(this.editable !== false && !e.isSpecialKey()){
14530             this.lastKey = e.getKey();
14531             this.dqTask.delay(this.queryDelay);
14532         }
14533     },
14534
14535     // private
14536     validateBlur : function(){
14537         return !this.list || !this.list.isVisible();   
14538     },
14539
14540     // private
14541     initQuery : function(){
14542         
14543         var v = this.getRawValue();
14544         
14545         if(this.tickable && this.editable){
14546             v = this.tickableInputEl().getValue();
14547         }
14548         
14549         this.doQuery(v);
14550     },
14551
14552     // private
14553     doForce : function(){
14554         if(this.inputEl().dom.value.length > 0){
14555             this.inputEl().dom.value =
14556                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14557              
14558         }
14559     },
14560
14561     /**
14562      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14563      * query allowing the query action to be canceled if needed.
14564      * @param {String} query The SQL query to execute
14565      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14566      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14567      * saved in the current store (defaults to false)
14568      */
14569     doQuery : function(q, forceAll){
14570         
14571         if(q === undefined || q === null){
14572             q = '';
14573         }
14574         var qe = {
14575             query: q,
14576             forceAll: forceAll,
14577             combo: this,
14578             cancel:false
14579         };
14580         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14581             return false;
14582         }
14583         q = qe.query;
14584         
14585         forceAll = qe.forceAll;
14586         if(forceAll === true || (q.length >= this.minChars)){
14587             
14588             this.hasQuery = true;
14589             
14590             if(this.lastQuery != q || this.alwaysQuery){
14591                 this.lastQuery = q;
14592                 if(this.mode == 'local'){
14593                     this.selectedIndex = -1;
14594                     if(forceAll){
14595                         this.store.clearFilter();
14596                     }else{
14597                         
14598                         if(this.specialFilter){
14599                             this.fireEvent('specialfilter', this);
14600                             this.onLoad();
14601                             return;
14602                         }
14603                         
14604                         this.store.filter(this.displayField, q);
14605                     }
14606                     
14607                     this.store.fireEvent("datachanged", this.store);
14608                     
14609                     this.onLoad();
14610                     
14611                     
14612                 }else{
14613                     
14614                     this.store.baseParams[this.queryParam] = q;
14615                     
14616                     var options = {params : this.getParams(q)};
14617                     
14618                     if(this.loadNext){
14619                         options.add = true;
14620                         options.params.start = this.page * this.pageSize;
14621                     }
14622                     
14623                     this.store.load(options);
14624                     
14625                     /*
14626                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14627                      *  we should expand the list on onLoad
14628                      *  so command out it
14629                      */
14630 //                    this.expand();
14631                 }
14632             }else{
14633                 this.selectedIndex = -1;
14634                 this.onLoad();   
14635             }
14636         }
14637         
14638         this.loadNext = false;
14639     },
14640     
14641     // private
14642     getParams : function(q){
14643         var p = {};
14644         //p[this.queryParam] = q;
14645         
14646         if(this.pageSize){
14647             p.start = 0;
14648             p.limit = this.pageSize;
14649         }
14650         return p;
14651     },
14652
14653     /**
14654      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14655      */
14656     collapse : function(){
14657         if(!this.isExpanded()){
14658             return;
14659         }
14660         
14661         this.list.hide();
14662         
14663         this.hasFocus = false;
14664         
14665         if(this.tickable){
14666             this.okBtn.hide();
14667             this.cancelBtn.hide();
14668             this.trigger.show();
14669             
14670             if(this.editable){
14671                 this.tickableInputEl().dom.value = '';
14672                 this.tickableInputEl().blur();
14673             }
14674             
14675         }
14676         
14677         Roo.get(document).un('mousedown', this.collapseIf, this);
14678         Roo.get(document).un('mousewheel', this.collapseIf, this);
14679         if (!this.editable) {
14680             Roo.get(document).un('keydown', this.listKeyPress, this);
14681         }
14682         this.fireEvent('collapse', this);
14683         
14684         this.validate();
14685     },
14686
14687     // private
14688     collapseIf : function(e){
14689         var in_combo  = e.within(this.el);
14690         var in_list =  e.within(this.list);
14691         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14692         
14693         if (in_combo || in_list || is_list) {
14694             //e.stopPropagation();
14695             return;
14696         }
14697         
14698         if(this.tickable){
14699             this.onTickableFooterButtonClick(e, false, false);
14700         }
14701
14702         this.collapse();
14703         
14704     },
14705
14706     /**
14707      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14708      */
14709     expand : function(){
14710        
14711         if(this.isExpanded() || !this.hasFocus){
14712             return;
14713         }
14714         
14715         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14716         this.list.setWidth(lw);
14717         
14718         Roo.log('expand');
14719         
14720         this.list.show();
14721         
14722         this.restrictHeight();
14723         
14724         if(this.tickable){
14725             
14726             this.tickItems = Roo.apply([], this.item);
14727             
14728             this.okBtn.show();
14729             this.cancelBtn.show();
14730             this.trigger.hide();
14731             
14732             if(this.editable){
14733                 this.tickableInputEl().focus();
14734             }
14735             
14736         }
14737         
14738         Roo.get(document).on('mousedown', this.collapseIf, this);
14739         Roo.get(document).on('mousewheel', this.collapseIf, this);
14740         if (!this.editable) {
14741             Roo.get(document).on('keydown', this.listKeyPress, this);
14742         }
14743         
14744         this.fireEvent('expand', this);
14745     },
14746
14747     // private
14748     // Implements the default empty TriggerField.onTriggerClick function
14749     onTriggerClick : function(e)
14750     {
14751         Roo.log('trigger click');
14752         
14753         if(this.disabled || !this.triggerList){
14754             return;
14755         }
14756         
14757         this.page = 0;
14758         this.loadNext = false;
14759         
14760         if(this.isExpanded()){
14761             this.collapse();
14762             if (!this.blockFocus) {
14763                 this.inputEl().focus();
14764             }
14765             
14766         }else {
14767             this.hasFocus = true;
14768             if(this.triggerAction == 'all') {
14769                 this.doQuery(this.allQuery, true);
14770             } else {
14771                 this.doQuery(this.getRawValue());
14772             }
14773             if (!this.blockFocus) {
14774                 this.inputEl().focus();
14775             }
14776         }
14777     },
14778     
14779     onTickableTriggerClick : function(e)
14780     {
14781         if(this.disabled){
14782             return;
14783         }
14784         
14785         this.page = 0;
14786         this.loadNext = false;
14787         this.hasFocus = true;
14788         
14789         if(this.triggerAction == 'all') {
14790             this.doQuery(this.allQuery, true);
14791         } else {
14792             this.doQuery(this.getRawValue());
14793         }
14794     },
14795     
14796     onSearchFieldClick : function(e)
14797     {
14798         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14799             this.onTickableFooterButtonClick(e, false, false);
14800             return;
14801         }
14802         
14803         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14804             return;
14805         }
14806         
14807         this.page = 0;
14808         this.loadNext = false;
14809         this.hasFocus = true;
14810         
14811         if(this.triggerAction == 'all') {
14812             this.doQuery(this.allQuery, true);
14813         } else {
14814             this.doQuery(this.getRawValue());
14815         }
14816     },
14817     
14818     listKeyPress : function(e)
14819     {
14820         //Roo.log('listkeypress');
14821         // scroll to first matching element based on key pres..
14822         if (e.isSpecialKey()) {
14823             return false;
14824         }
14825         var k = String.fromCharCode(e.getKey()).toUpperCase();
14826         //Roo.log(k);
14827         var match  = false;
14828         var csel = this.view.getSelectedNodes();
14829         var cselitem = false;
14830         if (csel.length) {
14831             var ix = this.view.indexOf(csel[0]);
14832             cselitem  = this.store.getAt(ix);
14833             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14834                 cselitem = false;
14835             }
14836             
14837         }
14838         
14839         this.store.each(function(v) { 
14840             if (cselitem) {
14841                 // start at existing selection.
14842                 if (cselitem.id == v.id) {
14843                     cselitem = false;
14844                 }
14845                 return true;
14846             }
14847                 
14848             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14849                 match = this.store.indexOf(v);
14850                 return false;
14851             }
14852             return true;
14853         }, this);
14854         
14855         if (match === false) {
14856             return true; // no more action?
14857         }
14858         // scroll to?
14859         this.view.select(match);
14860         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14861         sn.scrollIntoView(sn.dom.parentNode, false);
14862     },
14863     
14864     onViewScroll : function(e, t){
14865         
14866         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){
14867             return;
14868         }
14869         
14870         this.hasQuery = true;
14871         
14872         this.loading = this.list.select('.loading', true).first();
14873         
14874         if(this.loading === null){
14875             this.list.createChild({
14876                 tag: 'div',
14877                 cls: 'loading roo-select2-more-results roo-select2-active',
14878                 html: 'Loading more results...'
14879             });
14880             
14881             this.loading = this.list.select('.loading', true).first();
14882             
14883             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14884             
14885             this.loading.hide();
14886         }
14887         
14888         this.loading.show();
14889         
14890         var _combo = this;
14891         
14892         this.page++;
14893         this.loadNext = true;
14894         
14895         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14896         
14897         return;
14898     },
14899     
14900     addItem : function(o)
14901     {   
14902         var dv = ''; // display value
14903         
14904         if (this.displayField) {
14905             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14906         } else {
14907             // this is an error condition!!!
14908             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14909         }
14910         
14911         if(!dv.length){
14912             return;
14913         }
14914         
14915         var choice = this.choices.createChild({
14916             tag: 'li',
14917             cls: 'roo-select2-search-choice',
14918             cn: [
14919                 {
14920                     tag: 'div',
14921                     html: dv
14922                 },
14923                 {
14924                     tag: 'a',
14925                     href: '#',
14926                     cls: 'roo-select2-search-choice-close fa fa-times',
14927                     tabindex: '-1'
14928                 }
14929             ]
14930             
14931         }, this.searchField);
14932         
14933         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14934         
14935         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14936         
14937         this.item.push(o);
14938         
14939         this.lastData = o;
14940         
14941         this.syncValue();
14942         
14943         this.inputEl().dom.value = '';
14944         
14945         this.validate();
14946     },
14947     
14948     onRemoveItem : function(e, _self, o)
14949     {
14950         e.preventDefault();
14951         
14952         this.lastItem = Roo.apply([], this.item);
14953         
14954         var index = this.item.indexOf(o.data) * 1;
14955         
14956         if( index < 0){
14957             Roo.log('not this item?!');
14958             return;
14959         }
14960         
14961         this.item.splice(index, 1);
14962         o.item.remove();
14963         
14964         this.syncValue();
14965         
14966         this.fireEvent('remove', this, e);
14967         
14968         this.validate();
14969         
14970     },
14971     
14972     syncValue : function()
14973     {
14974         if(!this.item.length){
14975             this.clearValue();
14976             return;
14977         }
14978             
14979         var value = [];
14980         var _this = this;
14981         Roo.each(this.item, function(i){
14982             if(_this.valueField){
14983                 value.push(i[_this.valueField]);
14984                 return;
14985             }
14986
14987             value.push(i);
14988         });
14989
14990         this.value = value.join(',');
14991
14992         if(this.hiddenField){
14993             this.hiddenField.dom.value = this.value;
14994         }
14995         
14996         this.store.fireEvent("datachanged", this.store);
14997         
14998         this.validate();
14999     },
15000     
15001     clearItem : function()
15002     {
15003         if(!this.multiple){
15004             return;
15005         }
15006         
15007         this.item = [];
15008         
15009         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15010            c.remove();
15011         });
15012         
15013         this.syncValue();
15014         
15015         this.validate();
15016         
15017         if(this.tickable && !Roo.isTouch){
15018             this.view.refresh();
15019         }
15020     },
15021     
15022     inputEl: function ()
15023     {
15024         if(Roo.isIOS && this.useNativeIOS){
15025             return this.el.select('select.roo-ios-select', true).first();
15026         }
15027         
15028         if(Roo.isTouch && this.mobileTouchView){
15029             return this.el.select('input.form-control',true).first();
15030         }
15031         
15032         if(this.tickable){
15033             return this.searchField;
15034         }
15035         
15036         return this.el.select('input.form-control',true).first();
15037     },
15038     
15039     onTickableFooterButtonClick : function(e, btn, el)
15040     {
15041         e.preventDefault();
15042         
15043         this.lastItem = Roo.apply([], this.item);
15044         
15045         if(btn && btn.name == 'cancel'){
15046             this.tickItems = Roo.apply([], this.item);
15047             this.collapse();
15048             return;
15049         }
15050         
15051         this.clearItem();
15052         
15053         var _this = this;
15054         
15055         Roo.each(this.tickItems, function(o){
15056             _this.addItem(o);
15057         });
15058         
15059         this.collapse();
15060         
15061     },
15062     
15063     validate : function()
15064     {
15065         if(this.getVisibilityEl().hasClass('hidden')){
15066             return true;
15067         }
15068         
15069         var v = this.getRawValue();
15070         
15071         if(this.multiple){
15072             v = this.getValue();
15073         }
15074         
15075         if(this.disabled || this.allowBlank || v.length){
15076             this.markValid();
15077             return true;
15078         }
15079         
15080         this.markInvalid();
15081         return false;
15082     },
15083     
15084     tickableInputEl : function()
15085     {
15086         if(!this.tickable || !this.editable){
15087             return this.inputEl();
15088         }
15089         
15090         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15091     },
15092     
15093     
15094     getAutoCreateTouchView : function()
15095     {
15096         var id = Roo.id();
15097         
15098         var cfg = {
15099             cls: 'form-group' //input-group
15100         };
15101         
15102         var input =  {
15103             tag: 'input',
15104             id : id,
15105             type : this.inputType,
15106             cls : 'form-control x-combo-noedit',
15107             autocomplete: 'new-password',
15108             placeholder : this.placeholder || '',
15109             readonly : true
15110         };
15111         
15112         if (this.name) {
15113             input.name = this.name;
15114         }
15115         
15116         if (this.size) {
15117             input.cls += ' input-' + this.size;
15118         }
15119         
15120         if (this.disabled) {
15121             input.disabled = true;
15122         }
15123         
15124         var inputblock = {
15125             cls : '',
15126             cn : [
15127                 input
15128             ]
15129         };
15130         
15131         if(this.before){
15132             inputblock.cls += ' input-group';
15133             
15134             inputblock.cn.unshift({
15135                 tag :'span',
15136                 cls : 'input-group-addon input-group-prepend input-group-text',
15137                 html : this.before
15138             });
15139         }
15140         
15141         if(this.removable && !this.multiple){
15142             inputblock.cls += ' roo-removable';
15143             
15144             inputblock.cn.push({
15145                 tag: 'button',
15146                 html : 'x',
15147                 cls : 'roo-combo-removable-btn close'
15148             });
15149         }
15150
15151         if(this.hasFeedback && !this.allowBlank){
15152             
15153             inputblock.cls += ' has-feedback';
15154             
15155             inputblock.cn.push({
15156                 tag: 'span',
15157                 cls: 'glyphicon form-control-feedback'
15158             });
15159             
15160         }
15161         
15162         if (this.after) {
15163             
15164             inputblock.cls += (this.before) ? '' : ' input-group';
15165             
15166             inputblock.cn.push({
15167                 tag :'span',
15168                 cls : 'input-group-addon input-group-append input-group-text',
15169                 html : this.after
15170             });
15171         }
15172
15173         
15174         var ibwrap = inputblock;
15175         
15176         if(this.multiple){
15177             ibwrap = {
15178                 tag: 'ul',
15179                 cls: 'roo-select2-choices',
15180                 cn:[
15181                     {
15182                         tag: 'li',
15183                         cls: 'roo-select2-search-field',
15184                         cn: [
15185
15186                             inputblock
15187                         ]
15188                     }
15189                 ]
15190             };
15191         
15192             
15193         }
15194         
15195         var combobox = {
15196             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15197             cn: [
15198                 {
15199                     tag: 'input',
15200                     type : 'hidden',
15201                     cls: 'form-hidden-field'
15202                 },
15203                 ibwrap
15204             ]
15205         };
15206         
15207         if(!this.multiple && this.showToggleBtn){
15208             
15209             var caret = {
15210                         tag: 'span',
15211                         cls: 'caret'
15212             };
15213             
15214             if (this.caret != false) {
15215                 caret = {
15216                      tag: 'i',
15217                      cls: 'fa fa-' + this.caret
15218                 };
15219                 
15220             }
15221             
15222             combobox.cn.push({
15223                 tag :'span',
15224                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15225                 cn : [
15226                     caret,
15227                     {
15228                         tag: 'span',
15229                         cls: 'combobox-clear',
15230                         cn  : [
15231                             {
15232                                 tag : 'i',
15233                                 cls: 'icon-remove'
15234                             }
15235                         ]
15236                     }
15237                 ]
15238
15239             })
15240         }
15241         
15242         if(this.multiple){
15243             combobox.cls += ' roo-select2-container-multi';
15244         }
15245         
15246         var align = this.labelAlign || this.parentLabelAlign();
15247         
15248         if (align ==='left' && this.fieldLabel.length) {
15249
15250             cfg.cn = [
15251                 {
15252                    tag : 'i',
15253                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15254                    tooltip : 'This field is required'
15255                 },
15256                 {
15257                     tag: 'label',
15258                     cls : 'control-label col-form-label',
15259                     html : this.fieldLabel
15260
15261                 },
15262                 {
15263                     cls : '', 
15264                     cn: [
15265                         combobox
15266                     ]
15267                 }
15268             ];
15269             
15270             var labelCfg = cfg.cn[1];
15271             var contentCfg = cfg.cn[2];
15272             
15273
15274             if(this.indicatorpos == 'right'){
15275                 cfg.cn = [
15276                     {
15277                         tag: 'label',
15278                         'for' :  id,
15279                         cls : 'control-label col-form-label',
15280                         cn : [
15281                             {
15282                                 tag : 'span',
15283                                 html : this.fieldLabel
15284                             },
15285                             {
15286                                 tag : 'i',
15287                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15288                                 tooltip : 'This field is required'
15289                             }
15290                         ]
15291                     },
15292                     {
15293                         cls : "",
15294                         cn: [
15295                             combobox
15296                         ]
15297                     }
15298
15299                 ];
15300                 
15301                 labelCfg = cfg.cn[0];
15302                 contentCfg = cfg.cn[1];
15303             }
15304             
15305            
15306             
15307             if(this.labelWidth > 12){
15308                 labelCfg.style = "width: " + this.labelWidth + 'px';
15309             }
15310             
15311             if(this.labelWidth < 13 && this.labelmd == 0){
15312                 this.labelmd = this.labelWidth;
15313             }
15314             
15315             if(this.labellg > 0){
15316                 labelCfg.cls += ' col-lg-' + this.labellg;
15317                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15318             }
15319             
15320             if(this.labelmd > 0){
15321                 labelCfg.cls += ' col-md-' + this.labelmd;
15322                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15323             }
15324             
15325             if(this.labelsm > 0){
15326                 labelCfg.cls += ' col-sm-' + this.labelsm;
15327                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15328             }
15329             
15330             if(this.labelxs > 0){
15331                 labelCfg.cls += ' col-xs-' + this.labelxs;
15332                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15333             }
15334                 
15335                 
15336         } else if ( this.fieldLabel.length) {
15337             cfg.cn = [
15338                 {
15339                    tag : 'i',
15340                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15341                    tooltip : 'This field is required'
15342                 },
15343                 {
15344                     tag: 'label',
15345                     cls : 'control-label',
15346                     html : this.fieldLabel
15347
15348                 },
15349                 {
15350                     cls : '', 
15351                     cn: [
15352                         combobox
15353                     ]
15354                 }
15355             ];
15356             
15357             if(this.indicatorpos == 'right'){
15358                 cfg.cn = [
15359                     {
15360                         tag: 'label',
15361                         cls : 'control-label',
15362                         html : this.fieldLabel,
15363                         cn : [
15364                             {
15365                                tag : 'i',
15366                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15367                                tooltip : 'This field is required'
15368                             }
15369                         ]
15370                     },
15371                     {
15372                         cls : '', 
15373                         cn: [
15374                             combobox
15375                         ]
15376                     }
15377                 ];
15378             }
15379         } else {
15380             cfg.cn = combobox;    
15381         }
15382         
15383         
15384         var settings = this;
15385         
15386         ['xs','sm','md','lg'].map(function(size){
15387             if (settings[size]) {
15388                 cfg.cls += ' col-' + size + '-' + settings[size];
15389             }
15390         });
15391         
15392         return cfg;
15393     },
15394     
15395     initTouchView : function()
15396     {
15397         this.renderTouchView();
15398         
15399         this.touchViewEl.on('scroll', function(){
15400             this.el.dom.scrollTop = 0;
15401         }, this);
15402         
15403         this.originalValue = this.getValue();
15404         
15405         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15406         
15407         this.inputEl().on("click", this.showTouchView, this);
15408         if (this.triggerEl) {
15409             this.triggerEl.on("click", this.showTouchView, this);
15410         }
15411         
15412         
15413         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15414         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15415         
15416         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15417         
15418         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15419         this.store.on('load', this.onTouchViewLoad, this);
15420         this.store.on('loadexception', this.onTouchViewLoadException, this);
15421         
15422         if(this.hiddenName){
15423             
15424             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15425             
15426             this.hiddenField.dom.value =
15427                 this.hiddenValue !== undefined ? this.hiddenValue :
15428                 this.value !== undefined ? this.value : '';
15429         
15430             this.el.dom.removeAttribute('name');
15431             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15432         }
15433         
15434         if(this.multiple){
15435             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15436             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15437         }
15438         
15439         if(this.removable && !this.multiple){
15440             var close = this.closeTriggerEl();
15441             if(close){
15442                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15443                 close.on('click', this.removeBtnClick, this, close);
15444             }
15445         }
15446         /*
15447          * fix the bug in Safari iOS8
15448          */
15449         this.inputEl().on("focus", function(e){
15450             document.activeElement.blur();
15451         }, this);
15452         
15453         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15454         
15455         return;
15456         
15457         
15458     },
15459     
15460     renderTouchView : function()
15461     {
15462         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15463         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15464         
15465         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15466         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15467         
15468         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15469         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15470         this.touchViewBodyEl.setStyle('overflow', 'auto');
15471         
15472         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15473         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15474         
15475         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15476         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15477         
15478     },
15479     
15480     showTouchView : function()
15481     {
15482         if(this.disabled){
15483             return;
15484         }
15485         
15486         this.touchViewHeaderEl.hide();
15487
15488         if(this.modalTitle.length){
15489             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15490             this.touchViewHeaderEl.show();
15491         }
15492
15493         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15494         this.touchViewEl.show();
15495
15496         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15497         
15498         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15499         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15500
15501         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15502
15503         if(this.modalTitle.length){
15504             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15505         }
15506         
15507         this.touchViewBodyEl.setHeight(bodyHeight);
15508
15509         if(this.animate){
15510             var _this = this;
15511             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15512         }else{
15513             this.touchViewEl.addClass('in');
15514         }
15515         
15516         if(this._touchViewMask){
15517             Roo.get(document.body).addClass("x-body-masked");
15518             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15519             this._touchViewMask.setStyle('z-index', 10000);
15520             this._touchViewMask.addClass('show');
15521         }
15522         
15523         this.doTouchViewQuery();
15524         
15525     },
15526     
15527     hideTouchView : function()
15528     {
15529         this.touchViewEl.removeClass('in');
15530
15531         if(this.animate){
15532             var _this = this;
15533             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15534         }else{
15535             this.touchViewEl.setStyle('display', 'none');
15536         }
15537         
15538         if(this._touchViewMask){
15539             this._touchViewMask.removeClass('show');
15540             Roo.get(document.body).removeClass("x-body-masked");
15541         }
15542     },
15543     
15544     setTouchViewValue : function()
15545     {
15546         if(this.multiple){
15547             this.clearItem();
15548         
15549             var _this = this;
15550
15551             Roo.each(this.tickItems, function(o){
15552                 this.addItem(o);
15553             }, this);
15554         }
15555         
15556         this.hideTouchView();
15557     },
15558     
15559     doTouchViewQuery : function()
15560     {
15561         var qe = {
15562             query: '',
15563             forceAll: true,
15564             combo: this,
15565             cancel:false
15566         };
15567         
15568         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15569             return false;
15570         }
15571         
15572         if(!this.alwaysQuery || this.mode == 'local'){
15573             this.onTouchViewLoad();
15574             return;
15575         }
15576         
15577         this.store.load();
15578     },
15579     
15580     onTouchViewBeforeLoad : function(combo,opts)
15581     {
15582         return;
15583     },
15584
15585     // private
15586     onTouchViewLoad : function()
15587     {
15588         if(this.store.getCount() < 1){
15589             this.onTouchViewEmptyResults();
15590             return;
15591         }
15592         
15593         this.clearTouchView();
15594         
15595         var rawValue = this.getRawValue();
15596         
15597         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15598         
15599         this.tickItems = [];
15600         
15601         this.store.data.each(function(d, rowIndex){
15602             var row = this.touchViewListGroup.createChild(template);
15603             
15604             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15605                 row.addClass(d.data.cls);
15606             }
15607             
15608             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15609                 var cfg = {
15610                     data : d.data,
15611                     html : d.data[this.displayField]
15612                 };
15613                 
15614                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15615                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15616                 }
15617             }
15618             row.removeClass('selected');
15619             if(!this.multiple && this.valueField &&
15620                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15621             {
15622                 // radio buttons..
15623                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15624                 row.addClass('selected');
15625             }
15626             
15627             if(this.multiple && this.valueField &&
15628                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15629             {
15630                 
15631                 // checkboxes...
15632                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15633                 this.tickItems.push(d.data);
15634             }
15635             
15636             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15637             
15638         }, this);
15639         
15640         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15641         
15642         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15643
15644         if(this.modalTitle.length){
15645             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15646         }
15647
15648         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15649         
15650         if(this.mobile_restrict_height && listHeight < bodyHeight){
15651             this.touchViewBodyEl.setHeight(listHeight);
15652         }
15653         
15654         var _this = this;
15655         
15656         if(firstChecked && listHeight > bodyHeight){
15657             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15658         }
15659         
15660     },
15661     
15662     onTouchViewLoadException : function()
15663     {
15664         this.hideTouchView();
15665     },
15666     
15667     onTouchViewEmptyResults : function()
15668     {
15669         this.clearTouchView();
15670         
15671         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15672         
15673         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15674         
15675     },
15676     
15677     clearTouchView : function()
15678     {
15679         this.touchViewListGroup.dom.innerHTML = '';
15680     },
15681     
15682     onTouchViewClick : function(e, el, o)
15683     {
15684         e.preventDefault();
15685         
15686         var row = o.row;
15687         var rowIndex = o.rowIndex;
15688         
15689         var r = this.store.getAt(rowIndex);
15690         
15691         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15692             
15693             if(!this.multiple){
15694                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15695                     c.dom.removeAttribute('checked');
15696                 }, this);
15697
15698                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15699
15700                 this.setFromData(r.data);
15701
15702                 var close = this.closeTriggerEl();
15703
15704                 if(close){
15705                     close.show();
15706                 }
15707
15708                 this.hideTouchView();
15709
15710                 this.fireEvent('select', this, r, rowIndex);
15711
15712                 return;
15713             }
15714
15715             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15716                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15717                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15718                 return;
15719             }
15720
15721             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15722             this.addItem(r.data);
15723             this.tickItems.push(r.data);
15724         }
15725     },
15726     
15727     getAutoCreateNativeIOS : function()
15728     {
15729         var cfg = {
15730             cls: 'form-group' //input-group,
15731         };
15732         
15733         var combobox =  {
15734             tag: 'select',
15735             cls : 'roo-ios-select'
15736         };
15737         
15738         if (this.name) {
15739             combobox.name = this.name;
15740         }
15741         
15742         if (this.disabled) {
15743             combobox.disabled = true;
15744         }
15745         
15746         var settings = this;
15747         
15748         ['xs','sm','md','lg'].map(function(size){
15749             if (settings[size]) {
15750                 cfg.cls += ' col-' + size + '-' + settings[size];
15751             }
15752         });
15753         
15754         cfg.cn = combobox;
15755         
15756         return cfg;
15757         
15758     },
15759     
15760     initIOSView : function()
15761     {
15762         this.store.on('load', this.onIOSViewLoad, this);
15763         
15764         return;
15765     },
15766     
15767     onIOSViewLoad : function()
15768     {
15769         if(this.store.getCount() < 1){
15770             return;
15771         }
15772         
15773         this.clearIOSView();
15774         
15775         if(this.allowBlank) {
15776             
15777             var default_text = '-- SELECT --';
15778             
15779             if(this.placeholder.length){
15780                 default_text = this.placeholder;
15781             }
15782             
15783             if(this.emptyTitle.length){
15784                 default_text += ' - ' + this.emptyTitle + ' -';
15785             }
15786             
15787             var opt = this.inputEl().createChild({
15788                 tag: 'option',
15789                 value : 0,
15790                 html : default_text
15791             });
15792             
15793             var o = {};
15794             o[this.valueField] = 0;
15795             o[this.displayField] = default_text;
15796             
15797             this.ios_options.push({
15798                 data : o,
15799                 el : opt
15800             });
15801             
15802         }
15803         
15804         this.store.data.each(function(d, rowIndex){
15805             
15806             var html = '';
15807             
15808             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15809                 html = d.data[this.displayField];
15810             }
15811             
15812             var value = '';
15813             
15814             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15815                 value = d.data[this.valueField];
15816             }
15817             
15818             var option = {
15819                 tag: 'option',
15820                 value : value,
15821                 html : html
15822             };
15823             
15824             if(this.value == d.data[this.valueField]){
15825                 option['selected'] = true;
15826             }
15827             
15828             var opt = this.inputEl().createChild(option);
15829             
15830             this.ios_options.push({
15831                 data : d.data,
15832                 el : opt
15833             });
15834             
15835         }, this);
15836         
15837         this.inputEl().on('change', function(){
15838            this.fireEvent('select', this);
15839         }, this);
15840         
15841     },
15842     
15843     clearIOSView: function()
15844     {
15845         this.inputEl().dom.innerHTML = '';
15846         
15847         this.ios_options = [];
15848     },
15849     
15850     setIOSValue: function(v)
15851     {
15852         this.value = v;
15853         
15854         if(!this.ios_options){
15855             return;
15856         }
15857         
15858         Roo.each(this.ios_options, function(opts){
15859            
15860            opts.el.dom.removeAttribute('selected');
15861            
15862            if(opts.data[this.valueField] != v){
15863                return;
15864            }
15865            
15866            opts.el.dom.setAttribute('selected', true);
15867            
15868         }, this);
15869     }
15870
15871     /** 
15872     * @cfg {Boolean} grow 
15873     * @hide 
15874     */
15875     /** 
15876     * @cfg {Number} growMin 
15877     * @hide 
15878     */
15879     /** 
15880     * @cfg {Number} growMax 
15881     * @hide 
15882     */
15883     /**
15884      * @hide
15885      * @method autoSize
15886      */
15887 });
15888
15889 Roo.apply(Roo.bootstrap.ComboBox,  {
15890     
15891     header : {
15892         tag: 'div',
15893         cls: 'modal-header',
15894         cn: [
15895             {
15896                 tag: 'h4',
15897                 cls: 'modal-title'
15898             }
15899         ]
15900     },
15901     
15902     body : {
15903         tag: 'div',
15904         cls: 'modal-body',
15905         cn: [
15906             {
15907                 tag: 'ul',
15908                 cls: 'list-group'
15909             }
15910         ]
15911     },
15912     
15913     listItemRadio : {
15914         tag: 'li',
15915         cls: 'list-group-item',
15916         cn: [
15917             {
15918                 tag: 'span',
15919                 cls: 'roo-combobox-list-group-item-value'
15920             },
15921             {
15922                 tag: 'div',
15923                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15924                 cn: [
15925                     {
15926                         tag: 'input',
15927                         type: 'radio'
15928                     },
15929                     {
15930                         tag: 'label'
15931                     }
15932                 ]
15933             }
15934         ]
15935     },
15936     
15937     listItemCheckbox : {
15938         tag: 'li',
15939         cls: 'list-group-item',
15940         cn: [
15941             {
15942                 tag: 'span',
15943                 cls: 'roo-combobox-list-group-item-value'
15944             },
15945             {
15946                 tag: 'div',
15947                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15948                 cn: [
15949                     {
15950                         tag: 'input',
15951                         type: 'checkbox'
15952                     },
15953                     {
15954                         tag: 'label'
15955                     }
15956                 ]
15957             }
15958         ]
15959     },
15960     
15961     emptyResult : {
15962         tag: 'div',
15963         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15964     },
15965     
15966     footer : {
15967         tag: 'div',
15968         cls: 'modal-footer',
15969         cn: [
15970             {
15971                 tag: 'div',
15972                 cls: 'row',
15973                 cn: [
15974                     {
15975                         tag: 'div',
15976                         cls: 'col-xs-6 text-left',
15977                         cn: {
15978                             tag: 'button',
15979                             cls: 'btn btn-danger roo-touch-view-cancel',
15980                             html: 'Cancel'
15981                         }
15982                     },
15983                     {
15984                         tag: 'div',
15985                         cls: 'col-xs-6 text-right',
15986                         cn: {
15987                             tag: 'button',
15988                             cls: 'btn btn-success roo-touch-view-ok',
15989                             html: 'OK'
15990                         }
15991                     }
15992                 ]
15993             }
15994         ]
15995         
15996     }
15997 });
15998
15999 Roo.apply(Roo.bootstrap.ComboBox,  {
16000     
16001     touchViewTemplate : {
16002         tag: 'div',
16003         cls: 'modal fade roo-combobox-touch-view',
16004         cn: [
16005             {
16006                 tag: 'div',
16007                 cls: 'modal-dialog',
16008                 style : 'position:fixed', // we have to fix position....
16009                 cn: [
16010                     {
16011                         tag: 'div',
16012                         cls: 'modal-content',
16013                         cn: [
16014                             Roo.bootstrap.ComboBox.header,
16015                             Roo.bootstrap.ComboBox.body,
16016                             Roo.bootstrap.ComboBox.footer
16017                         ]
16018                     }
16019                 ]
16020             }
16021         ]
16022     }
16023 });/*
16024  * Based on:
16025  * Ext JS Library 1.1.1
16026  * Copyright(c) 2006-2007, Ext JS, LLC.
16027  *
16028  * Originally Released Under LGPL - original licence link has changed is not relivant.
16029  *
16030  * Fork - LGPL
16031  * <script type="text/javascript">
16032  */
16033
16034 /**
16035  * @class Roo.View
16036  * @extends Roo.util.Observable
16037  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16038  * This class also supports single and multi selection modes. <br>
16039  * Create a data model bound view:
16040  <pre><code>
16041  var store = new Roo.data.Store(...);
16042
16043  var view = new Roo.View({
16044     el : "my-element",
16045     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16046  
16047     singleSelect: true,
16048     selectedClass: "ydataview-selected",
16049     store: store
16050  });
16051
16052  // listen for node click?
16053  view.on("click", function(vw, index, node, e){
16054  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16055  });
16056
16057  // load XML data
16058  dataModel.load("foobar.xml");
16059  </code></pre>
16060  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16061  * <br><br>
16062  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16063  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16064  * 
16065  * Note: old style constructor is still suported (container, template, config)
16066  * 
16067  * @constructor
16068  * Create a new View
16069  * @param {Object} config The config object
16070  * 
16071  */
16072 Roo.View = function(config, depreciated_tpl, depreciated_config){
16073     
16074     this.parent = false;
16075     
16076     if (typeof(depreciated_tpl) == 'undefined') {
16077         // new way.. - universal constructor.
16078         Roo.apply(this, config);
16079         this.el  = Roo.get(this.el);
16080     } else {
16081         // old format..
16082         this.el  = Roo.get(config);
16083         this.tpl = depreciated_tpl;
16084         Roo.apply(this, depreciated_config);
16085     }
16086     this.wrapEl  = this.el.wrap().wrap();
16087     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16088     
16089     
16090     if(typeof(this.tpl) == "string"){
16091         this.tpl = new Roo.Template(this.tpl);
16092     } else {
16093         // support xtype ctors..
16094         this.tpl = new Roo.factory(this.tpl, Roo);
16095     }
16096     
16097     
16098     this.tpl.compile();
16099     
16100     /** @private */
16101     this.addEvents({
16102         /**
16103          * @event beforeclick
16104          * Fires before a click is processed. Returns false to cancel the default action.
16105          * @param {Roo.View} this
16106          * @param {Number} index The index of the target node
16107          * @param {HTMLElement} node The target node
16108          * @param {Roo.EventObject} e The raw event object
16109          */
16110             "beforeclick" : true,
16111         /**
16112          * @event click
16113          * Fires when a template node is clicked.
16114          * @param {Roo.View} this
16115          * @param {Number} index The index of the target node
16116          * @param {HTMLElement} node The target node
16117          * @param {Roo.EventObject} e The raw event object
16118          */
16119             "click" : true,
16120         /**
16121          * @event dblclick
16122          * Fires when a template node is double clicked.
16123          * @param {Roo.View} this
16124          * @param {Number} index The index of the target node
16125          * @param {HTMLElement} node The target node
16126          * @param {Roo.EventObject} e The raw event object
16127          */
16128             "dblclick" : true,
16129         /**
16130          * @event contextmenu
16131          * Fires when a template node is right clicked.
16132          * @param {Roo.View} this
16133          * @param {Number} index The index of the target node
16134          * @param {HTMLElement} node The target node
16135          * @param {Roo.EventObject} e The raw event object
16136          */
16137             "contextmenu" : true,
16138         /**
16139          * @event selectionchange
16140          * Fires when the selected nodes change.
16141          * @param {Roo.View} this
16142          * @param {Array} selections Array of the selected nodes
16143          */
16144             "selectionchange" : true,
16145     
16146         /**
16147          * @event beforeselect
16148          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16149          * @param {Roo.View} this
16150          * @param {HTMLElement} node The node to be selected
16151          * @param {Array} selections Array of currently selected nodes
16152          */
16153             "beforeselect" : true,
16154         /**
16155          * @event preparedata
16156          * Fires on every row to render, to allow you to change the data.
16157          * @param {Roo.View} this
16158          * @param {Object} data to be rendered (change this)
16159          */
16160           "preparedata" : true
16161           
16162           
16163         });
16164
16165
16166
16167     this.el.on({
16168         "click": this.onClick,
16169         "dblclick": this.onDblClick,
16170         "contextmenu": this.onContextMenu,
16171         scope:this
16172     });
16173
16174     this.selections = [];
16175     this.nodes = [];
16176     this.cmp = new Roo.CompositeElementLite([]);
16177     if(this.store){
16178         this.store = Roo.factory(this.store, Roo.data);
16179         this.setStore(this.store, true);
16180     }
16181     
16182     if ( this.footer && this.footer.xtype) {
16183            
16184          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16185         
16186         this.footer.dataSource = this.store;
16187         this.footer.container = fctr;
16188         this.footer = Roo.factory(this.footer, Roo);
16189         fctr.insertFirst(this.el);
16190         
16191         // this is a bit insane - as the paging toolbar seems to detach the el..
16192 //        dom.parentNode.parentNode.parentNode
16193          // they get detached?
16194     }
16195     
16196     
16197     Roo.View.superclass.constructor.call(this);
16198     
16199     
16200 };
16201
16202 Roo.extend(Roo.View, Roo.util.Observable, {
16203     
16204      /**
16205      * @cfg {Roo.data.Store} store Data store to load data from.
16206      */
16207     store : false,
16208     
16209     /**
16210      * @cfg {String|Roo.Element} el The container element.
16211      */
16212     el : '',
16213     
16214     /**
16215      * @cfg {String|Roo.Template} tpl The template used by this View 
16216      */
16217     tpl : false,
16218     /**
16219      * @cfg {String} dataName the named area of the template to use as the data area
16220      *                          Works with domtemplates roo-name="name"
16221      */
16222     dataName: false,
16223     /**
16224      * @cfg {String} selectedClass The css class to add to selected nodes
16225      */
16226     selectedClass : "x-view-selected",
16227      /**
16228      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16229      */
16230     emptyText : "",
16231     
16232     /**
16233      * @cfg {String} text to display on mask (default Loading)
16234      */
16235     mask : false,
16236     /**
16237      * @cfg {Boolean} multiSelect Allow multiple selection
16238      */
16239     multiSelect : false,
16240     /**
16241      * @cfg {Boolean} singleSelect Allow single selection
16242      */
16243     singleSelect:  false,
16244     
16245     /**
16246      * @cfg {Boolean} toggleSelect - selecting 
16247      */
16248     toggleSelect : false,
16249     
16250     /**
16251      * @cfg {Boolean} tickable - selecting 
16252      */
16253     tickable : false,
16254     
16255     /**
16256      * Returns the element this view is bound to.
16257      * @return {Roo.Element}
16258      */
16259     getEl : function(){
16260         return this.wrapEl;
16261     },
16262     
16263     
16264
16265     /**
16266      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16267      */
16268     refresh : function(){
16269         //Roo.log('refresh');
16270         var t = this.tpl;
16271         
16272         // if we are using something like 'domtemplate', then
16273         // the what gets used is:
16274         // t.applySubtemplate(NAME, data, wrapping data..)
16275         // the outer template then get' applied with
16276         //     the store 'extra data'
16277         // and the body get's added to the
16278         //      roo-name="data" node?
16279         //      <span class='roo-tpl-{name}'></span> ?????
16280         
16281         
16282         
16283         this.clearSelections();
16284         this.el.update("");
16285         var html = [];
16286         var records = this.store.getRange();
16287         if(records.length < 1) {
16288             
16289             // is this valid??  = should it render a template??
16290             
16291             this.el.update(this.emptyText);
16292             return;
16293         }
16294         var el = this.el;
16295         if (this.dataName) {
16296             this.el.update(t.apply(this.store.meta)); //????
16297             el = this.el.child('.roo-tpl-' + this.dataName);
16298         }
16299         
16300         for(var i = 0, len = records.length; i < len; i++){
16301             var data = this.prepareData(records[i].data, i, records[i]);
16302             this.fireEvent("preparedata", this, data, i, records[i]);
16303             
16304             var d = Roo.apply({}, data);
16305             
16306             if(this.tickable){
16307                 Roo.apply(d, {'roo-id' : Roo.id()});
16308                 
16309                 var _this = this;
16310             
16311                 Roo.each(this.parent.item, function(item){
16312                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16313                         return;
16314                     }
16315                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16316                 });
16317             }
16318             
16319             html[html.length] = Roo.util.Format.trim(
16320                 this.dataName ?
16321                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16322                     t.apply(d)
16323             );
16324         }
16325         
16326         
16327         
16328         el.update(html.join(""));
16329         this.nodes = el.dom.childNodes;
16330         this.updateIndexes(0);
16331     },
16332     
16333
16334     /**
16335      * Function to override to reformat the data that is sent to
16336      * the template for each node.
16337      * DEPRICATED - use the preparedata event handler.
16338      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16339      * a JSON object for an UpdateManager bound view).
16340      */
16341     prepareData : function(data, index, record)
16342     {
16343         this.fireEvent("preparedata", this, data, index, record);
16344         return data;
16345     },
16346
16347     onUpdate : function(ds, record){
16348         // Roo.log('on update');   
16349         this.clearSelections();
16350         var index = this.store.indexOf(record);
16351         var n = this.nodes[index];
16352         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16353         n.parentNode.removeChild(n);
16354         this.updateIndexes(index, index);
16355     },
16356
16357     
16358     
16359 // --------- FIXME     
16360     onAdd : function(ds, records, index)
16361     {
16362         //Roo.log(['on Add', ds, records, index] );        
16363         this.clearSelections();
16364         if(this.nodes.length == 0){
16365             this.refresh();
16366             return;
16367         }
16368         var n = this.nodes[index];
16369         for(var i = 0, len = records.length; i < len; i++){
16370             var d = this.prepareData(records[i].data, i, records[i]);
16371             if(n){
16372                 this.tpl.insertBefore(n, d);
16373             }else{
16374                 
16375                 this.tpl.append(this.el, d);
16376             }
16377         }
16378         this.updateIndexes(index);
16379     },
16380
16381     onRemove : function(ds, record, index){
16382        // Roo.log('onRemove');
16383         this.clearSelections();
16384         var el = this.dataName  ?
16385             this.el.child('.roo-tpl-' + this.dataName) :
16386             this.el; 
16387         
16388         el.dom.removeChild(this.nodes[index]);
16389         this.updateIndexes(index);
16390     },
16391
16392     /**
16393      * Refresh an individual node.
16394      * @param {Number} index
16395      */
16396     refreshNode : function(index){
16397         this.onUpdate(this.store, this.store.getAt(index));
16398     },
16399
16400     updateIndexes : function(startIndex, endIndex){
16401         var ns = this.nodes;
16402         startIndex = startIndex || 0;
16403         endIndex = endIndex || ns.length - 1;
16404         for(var i = startIndex; i <= endIndex; i++){
16405             ns[i].nodeIndex = i;
16406         }
16407     },
16408
16409     /**
16410      * Changes the data store this view uses and refresh the view.
16411      * @param {Store} store
16412      */
16413     setStore : function(store, initial){
16414         if(!initial && this.store){
16415             this.store.un("datachanged", this.refresh);
16416             this.store.un("add", this.onAdd);
16417             this.store.un("remove", this.onRemove);
16418             this.store.un("update", this.onUpdate);
16419             this.store.un("clear", this.refresh);
16420             this.store.un("beforeload", this.onBeforeLoad);
16421             this.store.un("load", this.onLoad);
16422             this.store.un("loadexception", this.onLoad);
16423         }
16424         if(store){
16425           
16426             store.on("datachanged", this.refresh, this);
16427             store.on("add", this.onAdd, this);
16428             store.on("remove", this.onRemove, this);
16429             store.on("update", this.onUpdate, this);
16430             store.on("clear", this.refresh, this);
16431             store.on("beforeload", this.onBeforeLoad, this);
16432             store.on("load", this.onLoad, this);
16433             store.on("loadexception", this.onLoad, this);
16434         }
16435         
16436         if(store){
16437             this.refresh();
16438         }
16439     },
16440     /**
16441      * onbeforeLoad - masks the loading area.
16442      *
16443      */
16444     onBeforeLoad : function(store,opts)
16445     {
16446          //Roo.log('onBeforeLoad');   
16447         if (!opts.add) {
16448             this.el.update("");
16449         }
16450         this.el.mask(this.mask ? this.mask : "Loading" ); 
16451     },
16452     onLoad : function ()
16453     {
16454         this.el.unmask();
16455     },
16456     
16457
16458     /**
16459      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16460      * @param {HTMLElement} node
16461      * @return {HTMLElement} The template node
16462      */
16463     findItemFromChild : function(node){
16464         var el = this.dataName  ?
16465             this.el.child('.roo-tpl-' + this.dataName,true) :
16466             this.el.dom; 
16467         
16468         if(!node || node.parentNode == el){
16469                     return node;
16470             }
16471             var p = node.parentNode;
16472             while(p && p != el){
16473             if(p.parentNode == el){
16474                 return p;
16475             }
16476             p = p.parentNode;
16477         }
16478             return null;
16479     },
16480
16481     /** @ignore */
16482     onClick : function(e){
16483         var item = this.findItemFromChild(e.getTarget());
16484         if(item){
16485             var index = this.indexOf(item);
16486             if(this.onItemClick(item, index, e) !== false){
16487                 this.fireEvent("click", this, index, item, e);
16488             }
16489         }else{
16490             this.clearSelections();
16491         }
16492     },
16493
16494     /** @ignore */
16495     onContextMenu : function(e){
16496         var item = this.findItemFromChild(e.getTarget());
16497         if(item){
16498             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16499         }
16500     },
16501
16502     /** @ignore */
16503     onDblClick : function(e){
16504         var item = this.findItemFromChild(e.getTarget());
16505         if(item){
16506             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16507         }
16508     },
16509
16510     onItemClick : function(item, index, e)
16511     {
16512         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16513             return false;
16514         }
16515         if (this.toggleSelect) {
16516             var m = this.isSelected(item) ? 'unselect' : 'select';
16517             //Roo.log(m);
16518             var _t = this;
16519             _t[m](item, true, false);
16520             return true;
16521         }
16522         if(this.multiSelect || this.singleSelect){
16523             if(this.multiSelect && e.shiftKey && this.lastSelection){
16524                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16525             }else{
16526                 this.select(item, this.multiSelect && e.ctrlKey);
16527                 this.lastSelection = item;
16528             }
16529             
16530             if(!this.tickable){
16531                 e.preventDefault();
16532             }
16533             
16534         }
16535         return true;
16536     },
16537
16538     /**
16539      * Get the number of selected nodes.
16540      * @return {Number}
16541      */
16542     getSelectionCount : function(){
16543         return this.selections.length;
16544     },
16545
16546     /**
16547      * Get the currently selected nodes.
16548      * @return {Array} An array of HTMLElements
16549      */
16550     getSelectedNodes : function(){
16551         return this.selections;
16552     },
16553
16554     /**
16555      * Get the indexes of the selected nodes.
16556      * @return {Array}
16557      */
16558     getSelectedIndexes : function(){
16559         var indexes = [], s = this.selections;
16560         for(var i = 0, len = s.length; i < len; i++){
16561             indexes.push(s[i].nodeIndex);
16562         }
16563         return indexes;
16564     },
16565
16566     /**
16567      * Clear all selections
16568      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16569      */
16570     clearSelections : function(suppressEvent){
16571         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16572             this.cmp.elements = this.selections;
16573             this.cmp.removeClass(this.selectedClass);
16574             this.selections = [];
16575             if(!suppressEvent){
16576                 this.fireEvent("selectionchange", this, this.selections);
16577             }
16578         }
16579     },
16580
16581     /**
16582      * Returns true if the passed node is selected
16583      * @param {HTMLElement/Number} node The node or node index
16584      * @return {Boolean}
16585      */
16586     isSelected : function(node){
16587         var s = this.selections;
16588         if(s.length < 1){
16589             return false;
16590         }
16591         node = this.getNode(node);
16592         return s.indexOf(node) !== -1;
16593     },
16594
16595     /**
16596      * Selects nodes.
16597      * @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
16598      * @param {Boolean} keepExisting (optional) true to keep existing selections
16599      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16600      */
16601     select : function(nodeInfo, keepExisting, suppressEvent){
16602         if(nodeInfo instanceof Array){
16603             if(!keepExisting){
16604                 this.clearSelections(true);
16605             }
16606             for(var i = 0, len = nodeInfo.length; i < len; i++){
16607                 this.select(nodeInfo[i], true, true);
16608             }
16609             return;
16610         } 
16611         var node = this.getNode(nodeInfo);
16612         if(!node || this.isSelected(node)){
16613             return; // already selected.
16614         }
16615         if(!keepExisting){
16616             this.clearSelections(true);
16617         }
16618         
16619         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16620             Roo.fly(node).addClass(this.selectedClass);
16621             this.selections.push(node);
16622             if(!suppressEvent){
16623                 this.fireEvent("selectionchange", this, this.selections);
16624             }
16625         }
16626         
16627         
16628     },
16629       /**
16630      * Unselects nodes.
16631      * @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
16632      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16633      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16634      */
16635     unselect : function(nodeInfo, keepExisting, suppressEvent)
16636     {
16637         if(nodeInfo instanceof Array){
16638             Roo.each(this.selections, function(s) {
16639                 this.unselect(s, nodeInfo);
16640             }, this);
16641             return;
16642         }
16643         var node = this.getNode(nodeInfo);
16644         if(!node || !this.isSelected(node)){
16645             //Roo.log("not selected");
16646             return; // not selected.
16647         }
16648         // fireevent???
16649         var ns = [];
16650         Roo.each(this.selections, function(s) {
16651             if (s == node ) {
16652                 Roo.fly(node).removeClass(this.selectedClass);
16653
16654                 return;
16655             }
16656             ns.push(s);
16657         },this);
16658         
16659         this.selections= ns;
16660         this.fireEvent("selectionchange", this, this.selections);
16661     },
16662
16663     /**
16664      * Gets a template node.
16665      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16666      * @return {HTMLElement} The node or null if it wasn't found
16667      */
16668     getNode : function(nodeInfo){
16669         if(typeof nodeInfo == "string"){
16670             return document.getElementById(nodeInfo);
16671         }else if(typeof nodeInfo == "number"){
16672             return this.nodes[nodeInfo];
16673         }
16674         return nodeInfo;
16675     },
16676
16677     /**
16678      * Gets a range template nodes.
16679      * @param {Number} startIndex
16680      * @param {Number} endIndex
16681      * @return {Array} An array of nodes
16682      */
16683     getNodes : function(start, end){
16684         var ns = this.nodes;
16685         start = start || 0;
16686         end = typeof end == "undefined" ? ns.length - 1 : end;
16687         var nodes = [];
16688         if(start <= end){
16689             for(var i = start; i <= end; i++){
16690                 nodes.push(ns[i]);
16691             }
16692         } else{
16693             for(var i = start; i >= end; i--){
16694                 nodes.push(ns[i]);
16695             }
16696         }
16697         return nodes;
16698     },
16699
16700     /**
16701      * Finds the index of the passed node
16702      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16703      * @return {Number} The index of the node or -1
16704      */
16705     indexOf : function(node){
16706         node = this.getNode(node);
16707         if(typeof node.nodeIndex == "number"){
16708             return node.nodeIndex;
16709         }
16710         var ns = this.nodes;
16711         for(var i = 0, len = ns.length; i < len; i++){
16712             if(ns[i] == node){
16713                 return i;
16714             }
16715         }
16716         return -1;
16717     }
16718 });
16719 /*
16720  * - LGPL
16721  *
16722  * based on jquery fullcalendar
16723  * 
16724  */
16725
16726 Roo.bootstrap = Roo.bootstrap || {};
16727 /**
16728  * @class Roo.bootstrap.Calendar
16729  * @extends Roo.bootstrap.Component
16730  * Bootstrap Calendar class
16731  * @cfg {Boolean} loadMask (true|false) default false
16732  * @cfg {Object} header generate the user specific header of the calendar, default false
16733
16734  * @constructor
16735  * Create a new Container
16736  * @param {Object} config The config object
16737  */
16738
16739
16740
16741 Roo.bootstrap.Calendar = function(config){
16742     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16743      this.addEvents({
16744         /**
16745              * @event select
16746              * Fires when a date is selected
16747              * @param {DatePicker} this
16748              * @param {Date} date The selected date
16749              */
16750         'select': true,
16751         /**
16752              * @event monthchange
16753              * Fires when the displayed month changes 
16754              * @param {DatePicker} this
16755              * @param {Date} date The selected month
16756              */
16757         'monthchange': true,
16758         /**
16759              * @event evententer
16760              * Fires when mouse over an event
16761              * @param {Calendar} this
16762              * @param {event} Event
16763              */
16764         'evententer': true,
16765         /**
16766              * @event eventleave
16767              * Fires when the mouse leaves an
16768              * @param {Calendar} this
16769              * @param {event}
16770              */
16771         'eventleave': true,
16772         /**
16773              * @event eventclick
16774              * Fires when the mouse click an
16775              * @param {Calendar} this
16776              * @param {event}
16777              */
16778         'eventclick': true
16779         
16780     });
16781
16782 };
16783
16784 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16785     
16786      /**
16787      * @cfg {Number} startDay
16788      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16789      */
16790     startDay : 0,
16791     
16792     loadMask : false,
16793     
16794     header : false,
16795       
16796     getAutoCreate : function(){
16797         
16798         
16799         var fc_button = function(name, corner, style, content ) {
16800             return Roo.apply({},{
16801                 tag : 'span',
16802                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16803                          (corner.length ?
16804                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16805                             ''
16806                         ),
16807                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16808                 unselectable: 'on'
16809             });
16810         };
16811         
16812         var header = {};
16813         
16814         if(!this.header){
16815             header = {
16816                 tag : 'table',
16817                 cls : 'fc-header',
16818                 style : 'width:100%',
16819                 cn : [
16820                     {
16821                         tag: 'tr',
16822                         cn : [
16823                             {
16824                                 tag : 'td',
16825                                 cls : 'fc-header-left',
16826                                 cn : [
16827                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16828                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16829                                     { tag: 'span', cls: 'fc-header-space' },
16830                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16831
16832
16833                                 ]
16834                             },
16835
16836                             {
16837                                 tag : 'td',
16838                                 cls : 'fc-header-center',
16839                                 cn : [
16840                                     {
16841                                         tag: 'span',
16842                                         cls: 'fc-header-title',
16843                                         cn : {
16844                                             tag: 'H2',
16845                                             html : 'month / year'
16846                                         }
16847                                     }
16848
16849                                 ]
16850                             },
16851                             {
16852                                 tag : 'td',
16853                                 cls : 'fc-header-right',
16854                                 cn : [
16855                               /*      fc_button('month', 'left', '', 'month' ),
16856                                     fc_button('week', '', '', 'week' ),
16857                                     fc_button('day', 'right', '', 'day' )
16858                                 */    
16859
16860                                 ]
16861                             }
16862
16863                         ]
16864                     }
16865                 ]
16866             };
16867         }
16868         
16869         header = this.header;
16870         
16871        
16872         var cal_heads = function() {
16873             var ret = [];
16874             // fixme - handle this.
16875             
16876             for (var i =0; i < Date.dayNames.length; i++) {
16877                 var d = Date.dayNames[i];
16878                 ret.push({
16879                     tag: 'th',
16880                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16881                     html : d.substring(0,3)
16882                 });
16883                 
16884             }
16885             ret[0].cls += ' fc-first';
16886             ret[6].cls += ' fc-last';
16887             return ret;
16888         };
16889         var cal_cell = function(n) {
16890             return  {
16891                 tag: 'td',
16892                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16893                 cn : [
16894                     {
16895                         cn : [
16896                             {
16897                                 cls: 'fc-day-number',
16898                                 html: 'D'
16899                             },
16900                             {
16901                                 cls: 'fc-day-content',
16902                              
16903                                 cn : [
16904                                      {
16905                                         style: 'position: relative;' // height: 17px;
16906                                     }
16907                                 ]
16908                             }
16909                             
16910                             
16911                         ]
16912                     }
16913                 ]
16914                 
16915             }
16916         };
16917         var cal_rows = function() {
16918             
16919             var ret = [];
16920             for (var r = 0; r < 6; r++) {
16921                 var row= {
16922                     tag : 'tr',
16923                     cls : 'fc-week',
16924                     cn : []
16925                 };
16926                 
16927                 for (var i =0; i < Date.dayNames.length; i++) {
16928                     var d = Date.dayNames[i];
16929                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16930
16931                 }
16932                 row.cn[0].cls+=' fc-first';
16933                 row.cn[0].cn[0].style = 'min-height:90px';
16934                 row.cn[6].cls+=' fc-last';
16935                 ret.push(row);
16936                 
16937             }
16938             ret[0].cls += ' fc-first';
16939             ret[4].cls += ' fc-prev-last';
16940             ret[5].cls += ' fc-last';
16941             return ret;
16942             
16943         };
16944         
16945         var cal_table = {
16946             tag: 'table',
16947             cls: 'fc-border-separate',
16948             style : 'width:100%',
16949             cellspacing  : 0,
16950             cn : [
16951                 { 
16952                     tag: 'thead',
16953                     cn : [
16954                         { 
16955                             tag: 'tr',
16956                             cls : 'fc-first fc-last',
16957                             cn : cal_heads()
16958                         }
16959                     ]
16960                 },
16961                 { 
16962                     tag: 'tbody',
16963                     cn : cal_rows()
16964                 }
16965                   
16966             ]
16967         };
16968          
16969          var cfg = {
16970             cls : 'fc fc-ltr',
16971             cn : [
16972                 header,
16973                 {
16974                     cls : 'fc-content',
16975                     style : "position: relative;",
16976                     cn : [
16977                         {
16978                             cls : 'fc-view fc-view-month fc-grid',
16979                             style : 'position: relative',
16980                             unselectable : 'on',
16981                             cn : [
16982                                 {
16983                                     cls : 'fc-event-container',
16984                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16985                                 },
16986                                 cal_table
16987                             ]
16988                         }
16989                     ]
16990     
16991                 }
16992            ] 
16993             
16994         };
16995         
16996          
16997         
16998         return cfg;
16999     },
17000     
17001     
17002     initEvents : function()
17003     {
17004         if(!this.store){
17005             throw "can not find store for calendar";
17006         }
17007         
17008         var mark = {
17009             tag: "div",
17010             cls:"x-dlg-mask",
17011             style: "text-align:center",
17012             cn: [
17013                 {
17014                     tag: "div",
17015                     style: "background-color:white;width:50%;margin:250 auto",
17016                     cn: [
17017                         {
17018                             tag: "img",
17019                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17020                         },
17021                         {
17022                             tag: "span",
17023                             html: "Loading"
17024                         }
17025                         
17026                     ]
17027                 }
17028             ]
17029         };
17030         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17031         
17032         var size = this.el.select('.fc-content', true).first().getSize();
17033         this.maskEl.setSize(size.width, size.height);
17034         this.maskEl.enableDisplayMode("block");
17035         if(!this.loadMask){
17036             this.maskEl.hide();
17037         }
17038         
17039         this.store = Roo.factory(this.store, Roo.data);
17040         this.store.on('load', this.onLoad, this);
17041         this.store.on('beforeload', this.onBeforeLoad, this);
17042         
17043         this.resize();
17044         
17045         this.cells = this.el.select('.fc-day',true);
17046         //Roo.log(this.cells);
17047         this.textNodes = this.el.query('.fc-day-number');
17048         this.cells.addClassOnOver('fc-state-hover');
17049         
17050         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17051         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17052         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17053         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17054         
17055         this.on('monthchange', this.onMonthChange, this);
17056         
17057         this.update(new Date().clearTime());
17058     },
17059     
17060     resize : function() {
17061         var sz  = this.el.getSize();
17062         
17063         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17064         this.el.select('.fc-day-content div',true).setHeight(34);
17065     },
17066     
17067     
17068     // private
17069     showPrevMonth : function(e){
17070         this.update(this.activeDate.add("mo", -1));
17071     },
17072     showToday : function(e){
17073         this.update(new Date().clearTime());
17074     },
17075     // private
17076     showNextMonth : function(e){
17077         this.update(this.activeDate.add("mo", 1));
17078     },
17079
17080     // private
17081     showPrevYear : function(){
17082         this.update(this.activeDate.add("y", -1));
17083     },
17084
17085     // private
17086     showNextYear : function(){
17087         this.update(this.activeDate.add("y", 1));
17088     },
17089
17090     
17091    // private
17092     update : function(date)
17093     {
17094         var vd = this.activeDate;
17095         this.activeDate = date;
17096 //        if(vd && this.el){
17097 //            var t = date.getTime();
17098 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17099 //                Roo.log('using add remove');
17100 //                
17101 //                this.fireEvent('monthchange', this, date);
17102 //                
17103 //                this.cells.removeClass("fc-state-highlight");
17104 //                this.cells.each(function(c){
17105 //                   if(c.dateValue == t){
17106 //                       c.addClass("fc-state-highlight");
17107 //                       setTimeout(function(){
17108 //                            try{c.dom.firstChild.focus();}catch(e){}
17109 //                       }, 50);
17110 //                       return false;
17111 //                   }
17112 //                   return true;
17113 //                });
17114 //                return;
17115 //            }
17116 //        }
17117         
17118         var days = date.getDaysInMonth();
17119         
17120         var firstOfMonth = date.getFirstDateOfMonth();
17121         var startingPos = firstOfMonth.getDay()-this.startDay;
17122         
17123         if(startingPos < this.startDay){
17124             startingPos += 7;
17125         }
17126         
17127         var pm = date.add(Date.MONTH, -1);
17128         var prevStart = pm.getDaysInMonth()-startingPos;
17129 //        
17130         this.cells = this.el.select('.fc-day',true);
17131         this.textNodes = this.el.query('.fc-day-number');
17132         this.cells.addClassOnOver('fc-state-hover');
17133         
17134         var cells = this.cells.elements;
17135         var textEls = this.textNodes;
17136         
17137         Roo.each(cells, function(cell){
17138             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17139         });
17140         
17141         days += startingPos;
17142
17143         // convert everything to numbers so it's fast
17144         var day = 86400000;
17145         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17146         //Roo.log(d);
17147         //Roo.log(pm);
17148         //Roo.log(prevStart);
17149         
17150         var today = new Date().clearTime().getTime();
17151         var sel = date.clearTime().getTime();
17152         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17153         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17154         var ddMatch = this.disabledDatesRE;
17155         var ddText = this.disabledDatesText;
17156         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17157         var ddaysText = this.disabledDaysText;
17158         var format = this.format;
17159         
17160         var setCellClass = function(cal, cell){
17161             cell.row = 0;
17162             cell.events = [];
17163             cell.more = [];
17164             //Roo.log('set Cell Class');
17165             cell.title = "";
17166             var t = d.getTime();
17167             
17168             //Roo.log(d);
17169             
17170             cell.dateValue = t;
17171             if(t == today){
17172                 cell.className += " fc-today";
17173                 cell.className += " fc-state-highlight";
17174                 cell.title = cal.todayText;
17175             }
17176             if(t == sel){
17177                 // disable highlight in other month..
17178                 //cell.className += " fc-state-highlight";
17179                 
17180             }
17181             // disabling
17182             if(t < min) {
17183                 cell.className = " fc-state-disabled";
17184                 cell.title = cal.minText;
17185                 return;
17186             }
17187             if(t > max) {
17188                 cell.className = " fc-state-disabled";
17189                 cell.title = cal.maxText;
17190                 return;
17191             }
17192             if(ddays){
17193                 if(ddays.indexOf(d.getDay()) != -1){
17194                     cell.title = ddaysText;
17195                     cell.className = " fc-state-disabled";
17196                 }
17197             }
17198             if(ddMatch && format){
17199                 var fvalue = d.dateFormat(format);
17200                 if(ddMatch.test(fvalue)){
17201                     cell.title = ddText.replace("%0", fvalue);
17202                     cell.className = " fc-state-disabled";
17203                 }
17204             }
17205             
17206             if (!cell.initialClassName) {
17207                 cell.initialClassName = cell.dom.className;
17208             }
17209             
17210             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17211         };
17212
17213         var i = 0;
17214         
17215         for(; i < startingPos; i++) {
17216             textEls[i].innerHTML = (++prevStart);
17217             d.setDate(d.getDate()+1);
17218             
17219             cells[i].className = "fc-past fc-other-month";
17220             setCellClass(this, cells[i]);
17221         }
17222         
17223         var intDay = 0;
17224         
17225         for(; i < days; i++){
17226             intDay = i - startingPos + 1;
17227             textEls[i].innerHTML = (intDay);
17228             d.setDate(d.getDate()+1);
17229             
17230             cells[i].className = ''; // "x-date-active";
17231             setCellClass(this, cells[i]);
17232         }
17233         var extraDays = 0;
17234         
17235         for(; i < 42; i++) {
17236             textEls[i].innerHTML = (++extraDays);
17237             d.setDate(d.getDate()+1);
17238             
17239             cells[i].className = "fc-future fc-other-month";
17240             setCellClass(this, cells[i]);
17241         }
17242         
17243         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17244         
17245         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17246         
17247         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17248         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17249         
17250         if(totalRows != 6){
17251             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17252             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17253         }
17254         
17255         this.fireEvent('monthchange', this, date);
17256         
17257         
17258         /*
17259         if(!this.internalRender){
17260             var main = this.el.dom.firstChild;
17261             var w = main.offsetWidth;
17262             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17263             Roo.fly(main).setWidth(w);
17264             this.internalRender = true;
17265             // opera does not respect the auto grow header center column
17266             // then, after it gets a width opera refuses to recalculate
17267             // without a second pass
17268             if(Roo.isOpera && !this.secondPass){
17269                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17270                 this.secondPass = true;
17271                 this.update.defer(10, this, [date]);
17272             }
17273         }
17274         */
17275         
17276     },
17277     
17278     findCell : function(dt) {
17279         dt = dt.clearTime().getTime();
17280         var ret = false;
17281         this.cells.each(function(c){
17282             //Roo.log("check " +c.dateValue + '?=' + dt);
17283             if(c.dateValue == dt){
17284                 ret = c;
17285                 return false;
17286             }
17287             return true;
17288         });
17289         
17290         return ret;
17291     },
17292     
17293     findCells : function(ev) {
17294         var s = ev.start.clone().clearTime().getTime();
17295        // Roo.log(s);
17296         var e= ev.end.clone().clearTime().getTime();
17297        // Roo.log(e);
17298         var ret = [];
17299         this.cells.each(function(c){
17300              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17301             
17302             if(c.dateValue > e){
17303                 return ;
17304             }
17305             if(c.dateValue < s){
17306                 return ;
17307             }
17308             ret.push(c);
17309         });
17310         
17311         return ret;    
17312     },
17313     
17314 //    findBestRow: function(cells)
17315 //    {
17316 //        var ret = 0;
17317 //        
17318 //        for (var i =0 ; i < cells.length;i++) {
17319 //            ret  = Math.max(cells[i].rows || 0,ret);
17320 //        }
17321 //        return ret;
17322 //        
17323 //    },
17324     
17325     
17326     addItem : function(ev)
17327     {
17328         // look for vertical location slot in
17329         var cells = this.findCells(ev);
17330         
17331 //        ev.row = this.findBestRow(cells);
17332         
17333         // work out the location.
17334         
17335         var crow = false;
17336         var rows = [];
17337         for(var i =0; i < cells.length; i++) {
17338             
17339             cells[i].row = cells[0].row;
17340             
17341             if(i == 0){
17342                 cells[i].row = cells[i].row + 1;
17343             }
17344             
17345             if (!crow) {
17346                 crow = {
17347                     start : cells[i],
17348                     end :  cells[i]
17349                 };
17350                 continue;
17351             }
17352             if (crow.start.getY() == cells[i].getY()) {
17353                 // on same row.
17354                 crow.end = cells[i];
17355                 continue;
17356             }
17357             // different row.
17358             rows.push(crow);
17359             crow = {
17360                 start: cells[i],
17361                 end : cells[i]
17362             };
17363             
17364         }
17365         
17366         rows.push(crow);
17367         ev.els = [];
17368         ev.rows = rows;
17369         ev.cells = cells;
17370         
17371         cells[0].events.push(ev);
17372         
17373         this.calevents.push(ev);
17374     },
17375     
17376     clearEvents: function() {
17377         
17378         if(!this.calevents){
17379             return;
17380         }
17381         
17382         Roo.each(this.cells.elements, function(c){
17383             c.row = 0;
17384             c.events = [];
17385             c.more = [];
17386         });
17387         
17388         Roo.each(this.calevents, function(e) {
17389             Roo.each(e.els, function(el) {
17390                 el.un('mouseenter' ,this.onEventEnter, this);
17391                 el.un('mouseleave' ,this.onEventLeave, this);
17392                 el.remove();
17393             },this);
17394         },this);
17395         
17396         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17397             e.remove();
17398         });
17399         
17400     },
17401     
17402     renderEvents: function()
17403     {   
17404         var _this = this;
17405         
17406         this.cells.each(function(c) {
17407             
17408             if(c.row < 5){
17409                 return;
17410             }
17411             
17412             var ev = c.events;
17413             
17414             var r = 4;
17415             if(c.row != c.events.length){
17416                 r = 4 - (4 - (c.row - c.events.length));
17417             }
17418             
17419             c.events = ev.slice(0, r);
17420             c.more = ev.slice(r);
17421             
17422             if(c.more.length && c.more.length == 1){
17423                 c.events.push(c.more.pop());
17424             }
17425             
17426             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17427             
17428         });
17429             
17430         this.cells.each(function(c) {
17431             
17432             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17433             
17434             
17435             for (var e = 0; e < c.events.length; e++){
17436                 var ev = c.events[e];
17437                 var rows = ev.rows;
17438                 
17439                 for(var i = 0; i < rows.length; i++) {
17440                 
17441                     // how many rows should it span..
17442
17443                     var  cfg = {
17444                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17445                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17446
17447                         unselectable : "on",
17448                         cn : [
17449                             {
17450                                 cls: 'fc-event-inner',
17451                                 cn : [
17452     //                                {
17453     //                                  tag:'span',
17454     //                                  cls: 'fc-event-time',
17455     //                                  html : cells.length > 1 ? '' : ev.time
17456     //                                },
17457                                     {
17458                                       tag:'span',
17459                                       cls: 'fc-event-title',
17460                                       html : String.format('{0}', ev.title)
17461                                     }
17462
17463
17464                                 ]
17465                             },
17466                             {
17467                                 cls: 'ui-resizable-handle ui-resizable-e',
17468                                 html : '&nbsp;&nbsp;&nbsp'
17469                             }
17470
17471                         ]
17472                     };
17473
17474                     if (i == 0) {
17475                         cfg.cls += ' fc-event-start';
17476                     }
17477                     if ((i+1) == rows.length) {
17478                         cfg.cls += ' fc-event-end';
17479                     }
17480
17481                     var ctr = _this.el.select('.fc-event-container',true).first();
17482                     var cg = ctr.createChild(cfg);
17483
17484                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17485                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17486
17487                     var r = (c.more.length) ? 1 : 0;
17488                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17489                     cg.setWidth(ebox.right - sbox.x -2);
17490
17491                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17492                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17493                     cg.on('click', _this.onEventClick, _this, ev);
17494
17495                     ev.els.push(cg);
17496                     
17497                 }
17498                 
17499             }
17500             
17501             
17502             if(c.more.length){
17503                 var  cfg = {
17504                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17505                     style : 'position: absolute',
17506                     unselectable : "on",
17507                     cn : [
17508                         {
17509                             cls: 'fc-event-inner',
17510                             cn : [
17511                                 {
17512                                   tag:'span',
17513                                   cls: 'fc-event-title',
17514                                   html : 'More'
17515                                 }
17516
17517
17518                             ]
17519                         },
17520                         {
17521                             cls: 'ui-resizable-handle ui-resizable-e',
17522                             html : '&nbsp;&nbsp;&nbsp'
17523                         }
17524
17525                     ]
17526                 };
17527
17528                 var ctr = _this.el.select('.fc-event-container',true).first();
17529                 var cg = ctr.createChild(cfg);
17530
17531                 var sbox = c.select('.fc-day-content',true).first().getBox();
17532                 var ebox = c.select('.fc-day-content',true).first().getBox();
17533                 //Roo.log(cg);
17534                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17535                 cg.setWidth(ebox.right - sbox.x -2);
17536
17537                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17538                 
17539             }
17540             
17541         });
17542         
17543         
17544         
17545     },
17546     
17547     onEventEnter: function (e, el,event,d) {
17548         this.fireEvent('evententer', this, el, event);
17549     },
17550     
17551     onEventLeave: function (e, el,event,d) {
17552         this.fireEvent('eventleave', this, el, event);
17553     },
17554     
17555     onEventClick: function (e, el,event,d) {
17556         this.fireEvent('eventclick', this, el, event);
17557     },
17558     
17559     onMonthChange: function () {
17560         this.store.load();
17561     },
17562     
17563     onMoreEventClick: function(e, el, more)
17564     {
17565         var _this = this;
17566         
17567         this.calpopover.placement = 'right';
17568         this.calpopover.setTitle('More');
17569         
17570         this.calpopover.setContent('');
17571         
17572         var ctr = this.calpopover.el.select('.popover-content', true).first();
17573         
17574         Roo.each(more, function(m){
17575             var cfg = {
17576                 cls : 'fc-event-hori fc-event-draggable',
17577                 html : m.title
17578             };
17579             var cg = ctr.createChild(cfg);
17580             
17581             cg.on('click', _this.onEventClick, _this, m);
17582         });
17583         
17584         this.calpopover.show(el);
17585         
17586         
17587     },
17588     
17589     onLoad: function () 
17590     {   
17591         this.calevents = [];
17592         var cal = this;
17593         
17594         if(this.store.getCount() > 0){
17595             this.store.data.each(function(d){
17596                cal.addItem({
17597                     id : d.data.id,
17598                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17599                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17600                     time : d.data.start_time,
17601                     title : d.data.title,
17602                     description : d.data.description,
17603                     venue : d.data.venue
17604                 });
17605             });
17606         }
17607         
17608         this.renderEvents();
17609         
17610         if(this.calevents.length && this.loadMask){
17611             this.maskEl.hide();
17612         }
17613     },
17614     
17615     onBeforeLoad: function()
17616     {
17617         this.clearEvents();
17618         if(this.loadMask){
17619             this.maskEl.show();
17620         }
17621     }
17622 });
17623
17624  
17625  /*
17626  * - LGPL
17627  *
17628  * element
17629  * 
17630  */
17631
17632 /**
17633  * @class Roo.bootstrap.Popover
17634  * @extends Roo.bootstrap.Component
17635  * Bootstrap Popover class
17636  * @cfg {String} html contents of the popover   (or false to use children..)
17637  * @cfg {String} title of popover (or false to hide)
17638  * @cfg {String} placement how it is placed
17639  * @cfg {String} trigger click || hover (or false to trigger manually)
17640  * @cfg {String} over what (parent or false to trigger manually.)
17641  * @cfg {Number} delay - delay before showing
17642  
17643  * @constructor
17644  * Create a new Popover
17645  * @param {Object} config The config object
17646  */
17647
17648 Roo.bootstrap.Popover = function(config){
17649     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17650     
17651     this.addEvents({
17652         // raw events
17653          /**
17654          * @event show
17655          * After the popover show
17656          * 
17657          * @param {Roo.bootstrap.Popover} this
17658          */
17659         "show" : true,
17660         /**
17661          * @event hide
17662          * After the popover hide
17663          * 
17664          * @param {Roo.bootstrap.Popover} this
17665          */
17666         "hide" : true
17667     });
17668 };
17669
17670 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17671     
17672     title: 'Fill in a title',
17673     html: false,
17674     
17675     placement : 'right',
17676     trigger : 'hover', // hover
17677     
17678     delay : 0,
17679     
17680     over: 'parent',
17681     
17682     can_build_overlaid : false,
17683     
17684     getChildContainer : function()
17685     {
17686         return this.el.select('.popover-content',true).first();
17687     },
17688     
17689     getAutoCreate : function(){
17690          
17691         var cfg = {
17692            cls : 'popover roo-dynamic',
17693            style: 'display:block',
17694            cn : [
17695                 {
17696                     cls : 'arrow'
17697                 },
17698                 {
17699                     cls : 'popover-inner',
17700                     cn : [
17701                         {
17702                             tag: 'h3',
17703                             cls: 'popover-title popover-header',
17704                             html : this.title
17705                         },
17706                         {
17707                             cls : 'popover-content popover-body',
17708                             html : this.html
17709                         }
17710                     ]
17711                     
17712                 }
17713            ]
17714         };
17715         
17716         return cfg;
17717     },
17718     setTitle: function(str)
17719     {
17720         this.title = str;
17721         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17722     },
17723     setContent: function(str)
17724     {
17725         this.html = str;
17726         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17727     },
17728     // as it get's added to the bottom of the page.
17729     onRender : function(ct, position)
17730     {
17731         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17732         if(!this.el){
17733             var cfg = Roo.apply({},  this.getAutoCreate());
17734             cfg.id = Roo.id();
17735             
17736             if (this.cls) {
17737                 cfg.cls += ' ' + this.cls;
17738             }
17739             if (this.style) {
17740                 cfg.style = this.style;
17741             }
17742             //Roo.log("adding to ");
17743             this.el = Roo.get(document.body).createChild(cfg, position);
17744 //            Roo.log(this.el);
17745         }
17746         this.initEvents();
17747     },
17748     
17749     initEvents : function()
17750     {
17751         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17752         this.el.enableDisplayMode('block');
17753         this.el.hide();
17754         if (this.over === false) {
17755             return; 
17756         }
17757         if (this.triggers === false) {
17758             return;
17759         }
17760         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17761         var triggers = this.trigger ? this.trigger.split(' ') : [];
17762         Roo.each(triggers, function(trigger) {
17763         
17764             if (trigger == 'click') {
17765                 on_el.on('click', this.toggle, this);
17766             } else if (trigger != 'manual') {
17767                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17768                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17769       
17770                 on_el.on(eventIn  ,this.enter, this);
17771                 on_el.on(eventOut, this.leave, this);
17772             }
17773         }, this);
17774         
17775     },
17776     
17777     
17778     // private
17779     timeout : null,
17780     hoverState : null,
17781     
17782     toggle : function () {
17783         this.hoverState == 'in' ? this.leave() : this.enter();
17784     },
17785     
17786     enter : function () {
17787         
17788         clearTimeout(this.timeout);
17789     
17790         this.hoverState = 'in';
17791     
17792         if (!this.delay || !this.delay.show) {
17793             this.show();
17794             return;
17795         }
17796         var _t = this;
17797         this.timeout = setTimeout(function () {
17798             if (_t.hoverState == 'in') {
17799                 _t.show();
17800             }
17801         }, this.delay.show)
17802     },
17803     
17804     leave : function() {
17805         clearTimeout(this.timeout);
17806     
17807         this.hoverState = 'out';
17808     
17809         if (!this.delay || !this.delay.hide) {
17810             this.hide();
17811             return;
17812         }
17813         var _t = this;
17814         this.timeout = setTimeout(function () {
17815             if (_t.hoverState == 'out') {
17816                 _t.hide();
17817             }
17818         }, this.delay.hide)
17819     },
17820     
17821     show : function (on_el)
17822     {
17823         if (!on_el) {
17824             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17825         }
17826         
17827         // set content.
17828         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17829         if (this.html !== false) {
17830             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17831         }
17832         this.el.removeClass([
17833             'fade','top','bottom', 'left', 'right','in',
17834             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17835         ]);
17836         if (!this.title.length) {
17837             this.el.select('.popover-title',true).hide();
17838         }
17839         
17840         var placement = typeof this.placement == 'function' ?
17841             this.placement.call(this, this.el, on_el) :
17842             this.placement;
17843             
17844         var autoToken = /\s?auto?\s?/i;
17845         var autoPlace = autoToken.test(placement);
17846         if (autoPlace) {
17847             placement = placement.replace(autoToken, '') || 'top';
17848         }
17849         
17850         //this.el.detach()
17851         //this.el.setXY([0,0]);
17852         this.el.show();
17853         this.el.dom.style.display='block';
17854         this.el.addClass(placement);
17855         
17856         //this.el.appendTo(on_el);
17857         
17858         var p = this.getPosition();
17859         var box = this.el.getBox();
17860         
17861         if (autoPlace) {
17862             // fixme..
17863         }
17864         var align = Roo.bootstrap.Popover.alignment[placement];
17865         
17866 //        Roo.log(align);
17867         this.el.alignTo(on_el, align[0],align[1]);
17868         //var arrow = this.el.select('.arrow',true).first();
17869         //arrow.set(align[2], 
17870         
17871         this.el.addClass('in');
17872         
17873         
17874         if (this.el.hasClass('fade')) {
17875             // fade it?
17876         }
17877         
17878         this.hoverState = 'in';
17879         
17880         this.fireEvent('show', this);
17881         
17882     },
17883     hide : function()
17884     {
17885         this.el.setXY([0,0]);
17886         this.el.removeClass('in');
17887         this.el.hide();
17888         this.hoverState = null;
17889         
17890         this.fireEvent('hide', this);
17891     }
17892     
17893 });
17894
17895 Roo.bootstrap.Popover.alignment = {
17896     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17897     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17898     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17899     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17900 };
17901
17902  /*
17903  * - LGPL
17904  *
17905  * Progress
17906  * 
17907  */
17908
17909 /**
17910  * @class Roo.bootstrap.Progress
17911  * @extends Roo.bootstrap.Component
17912  * Bootstrap Progress class
17913  * @cfg {Boolean} striped striped of the progress bar
17914  * @cfg {Boolean} active animated of the progress bar
17915  * 
17916  * 
17917  * @constructor
17918  * Create a new Progress
17919  * @param {Object} config The config object
17920  */
17921
17922 Roo.bootstrap.Progress = function(config){
17923     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17924 };
17925
17926 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17927     
17928     striped : false,
17929     active: false,
17930     
17931     getAutoCreate : function(){
17932         var cfg = {
17933             tag: 'div',
17934             cls: 'progress'
17935         };
17936         
17937         
17938         if(this.striped){
17939             cfg.cls += ' progress-striped';
17940         }
17941       
17942         if(this.active){
17943             cfg.cls += ' active';
17944         }
17945         
17946         
17947         return cfg;
17948     }
17949    
17950 });
17951
17952  
17953
17954  /*
17955  * - LGPL
17956  *
17957  * ProgressBar
17958  * 
17959  */
17960
17961 /**
17962  * @class Roo.bootstrap.ProgressBar
17963  * @extends Roo.bootstrap.Component
17964  * Bootstrap ProgressBar class
17965  * @cfg {Number} aria_valuenow aria-value now
17966  * @cfg {Number} aria_valuemin aria-value min
17967  * @cfg {Number} aria_valuemax aria-value max
17968  * @cfg {String} label label for the progress bar
17969  * @cfg {String} panel (success | info | warning | danger )
17970  * @cfg {String} role role of the progress bar
17971  * @cfg {String} sr_only text
17972  * 
17973  * 
17974  * @constructor
17975  * Create a new ProgressBar
17976  * @param {Object} config The config object
17977  */
17978
17979 Roo.bootstrap.ProgressBar = function(config){
17980     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17981 };
17982
17983 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17984     
17985     aria_valuenow : 0,
17986     aria_valuemin : 0,
17987     aria_valuemax : 100,
17988     label : false,
17989     panel : false,
17990     role : false,
17991     sr_only: false,
17992     
17993     getAutoCreate : function()
17994     {
17995         
17996         var cfg = {
17997             tag: 'div',
17998             cls: 'progress-bar',
17999             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18000         };
18001         
18002         if(this.sr_only){
18003             cfg.cn = {
18004                 tag: 'span',
18005                 cls: 'sr-only',
18006                 html: this.sr_only
18007             }
18008         }
18009         
18010         if(this.role){
18011             cfg.role = this.role;
18012         }
18013         
18014         if(this.aria_valuenow){
18015             cfg['aria-valuenow'] = this.aria_valuenow;
18016         }
18017         
18018         if(this.aria_valuemin){
18019             cfg['aria-valuemin'] = this.aria_valuemin;
18020         }
18021         
18022         if(this.aria_valuemax){
18023             cfg['aria-valuemax'] = this.aria_valuemax;
18024         }
18025         
18026         if(this.label && !this.sr_only){
18027             cfg.html = this.label;
18028         }
18029         
18030         if(this.panel){
18031             cfg.cls += ' progress-bar-' + this.panel;
18032         }
18033         
18034         return cfg;
18035     },
18036     
18037     update : function(aria_valuenow)
18038     {
18039         this.aria_valuenow = aria_valuenow;
18040         
18041         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18042     }
18043    
18044 });
18045
18046  
18047
18048  /*
18049  * - LGPL
18050  *
18051  * column
18052  * 
18053  */
18054
18055 /**
18056  * @class Roo.bootstrap.TabGroup
18057  * @extends Roo.bootstrap.Column
18058  * Bootstrap Column class
18059  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18060  * @cfg {Boolean} carousel true to make the group behave like a carousel
18061  * @cfg {Boolean} bullets show bullets for the panels
18062  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18063  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18064  * @cfg {Boolean} showarrow (true|false) show arrow default true
18065  * 
18066  * @constructor
18067  * Create a new TabGroup
18068  * @param {Object} config The config object
18069  */
18070
18071 Roo.bootstrap.TabGroup = function(config){
18072     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18073     if (!this.navId) {
18074         this.navId = Roo.id();
18075     }
18076     this.tabs = [];
18077     Roo.bootstrap.TabGroup.register(this);
18078     
18079 };
18080
18081 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18082     
18083     carousel : false,
18084     transition : false,
18085     bullets : 0,
18086     timer : 0,
18087     autoslide : false,
18088     slideFn : false,
18089     slideOnTouch : false,
18090     showarrow : true,
18091     
18092     getAutoCreate : function()
18093     {
18094         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18095         
18096         cfg.cls += ' tab-content';
18097         
18098         if (this.carousel) {
18099             cfg.cls += ' carousel slide';
18100             
18101             cfg.cn = [{
18102                cls : 'carousel-inner',
18103                cn : []
18104             }];
18105         
18106             if(this.bullets  && !Roo.isTouch){
18107                 
18108                 var bullets = {
18109                     cls : 'carousel-bullets',
18110                     cn : []
18111                 };
18112                
18113                 if(this.bullets_cls){
18114                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18115                 }
18116                 
18117                 bullets.cn.push({
18118                     cls : 'clear'
18119                 });
18120                 
18121                 cfg.cn[0].cn.push(bullets);
18122             }
18123             
18124             if(this.showarrow){
18125                 cfg.cn[0].cn.push({
18126                     tag : 'div',
18127                     class : 'carousel-arrow',
18128                     cn : [
18129                         {
18130                             tag : 'div',
18131                             class : 'carousel-prev',
18132                             cn : [
18133                                 {
18134                                     tag : 'i',
18135                                     class : 'fa fa-chevron-left'
18136                                 }
18137                             ]
18138                         },
18139                         {
18140                             tag : 'div',
18141                             class : 'carousel-next',
18142                             cn : [
18143                                 {
18144                                     tag : 'i',
18145                                     class : 'fa fa-chevron-right'
18146                                 }
18147                             ]
18148                         }
18149                     ]
18150                 });
18151             }
18152             
18153         }
18154         
18155         return cfg;
18156     },
18157     
18158     initEvents:  function()
18159     {
18160 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18161 //            this.el.on("touchstart", this.onTouchStart, this);
18162 //        }
18163         
18164         if(this.autoslide){
18165             var _this = this;
18166             
18167             this.slideFn = window.setInterval(function() {
18168                 _this.showPanelNext();
18169             }, this.timer);
18170         }
18171         
18172         if(this.showarrow){
18173             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18174             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18175         }
18176         
18177         
18178     },
18179     
18180 //    onTouchStart : function(e, el, o)
18181 //    {
18182 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18183 //            return;
18184 //        }
18185 //        
18186 //        this.showPanelNext();
18187 //    },
18188     
18189     
18190     getChildContainer : function()
18191     {
18192         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18193     },
18194     
18195     /**
18196     * register a Navigation item
18197     * @param {Roo.bootstrap.NavItem} the navitem to add
18198     */
18199     register : function(item)
18200     {
18201         this.tabs.push( item);
18202         item.navId = this.navId; // not really needed..
18203         this.addBullet();
18204     
18205     },
18206     
18207     getActivePanel : function()
18208     {
18209         var r = false;
18210         Roo.each(this.tabs, function(t) {
18211             if (t.active) {
18212                 r = t;
18213                 return false;
18214             }
18215             return null;
18216         });
18217         return r;
18218         
18219     },
18220     getPanelByName : function(n)
18221     {
18222         var r = false;
18223         Roo.each(this.tabs, function(t) {
18224             if (t.tabId == n) {
18225                 r = t;
18226                 return false;
18227             }
18228             return null;
18229         });
18230         return r;
18231     },
18232     indexOfPanel : function(p)
18233     {
18234         var r = false;
18235         Roo.each(this.tabs, function(t,i) {
18236             if (t.tabId == p.tabId) {
18237                 r = i;
18238                 return false;
18239             }
18240             return null;
18241         });
18242         return r;
18243     },
18244     /**
18245      * show a specific panel
18246      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18247      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18248      */
18249     showPanel : function (pan)
18250     {
18251         if(this.transition || typeof(pan) == 'undefined'){
18252             Roo.log("waiting for the transitionend");
18253             return;
18254         }
18255         
18256         if (typeof(pan) == 'number') {
18257             pan = this.tabs[pan];
18258         }
18259         
18260         if (typeof(pan) == 'string') {
18261             pan = this.getPanelByName(pan);
18262         }
18263         
18264         var cur = this.getActivePanel();
18265         
18266         if(!pan || !cur){
18267             Roo.log('pan or acitve pan is undefined');
18268             return false;
18269         }
18270         
18271         if (pan.tabId == this.getActivePanel().tabId) {
18272             return true;
18273         }
18274         
18275         if (false === cur.fireEvent('beforedeactivate')) {
18276             return false;
18277         }
18278         
18279         if(this.bullets > 0 && !Roo.isTouch){
18280             this.setActiveBullet(this.indexOfPanel(pan));
18281         }
18282         
18283         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18284             
18285             this.transition = true;
18286             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18287             var lr = dir == 'next' ? 'left' : 'right';
18288             pan.el.addClass(dir); // or prev
18289             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18290             cur.el.addClass(lr); // or right
18291             pan.el.addClass(lr);
18292             
18293             var _this = this;
18294             cur.el.on('transitionend', function() {
18295                 Roo.log("trans end?");
18296                 
18297                 pan.el.removeClass([lr,dir]);
18298                 pan.setActive(true);
18299                 
18300                 cur.el.removeClass([lr]);
18301                 cur.setActive(false);
18302                 
18303                 _this.transition = false;
18304                 
18305             }, this, { single:  true } );
18306             
18307             return true;
18308         }
18309         
18310         cur.setActive(false);
18311         pan.setActive(true);
18312         
18313         return true;
18314         
18315     },
18316     showPanelNext : function()
18317     {
18318         var i = this.indexOfPanel(this.getActivePanel());
18319         
18320         if (i >= this.tabs.length - 1 && !this.autoslide) {
18321             return;
18322         }
18323         
18324         if (i >= this.tabs.length - 1 && this.autoslide) {
18325             i = -1;
18326         }
18327         
18328         this.showPanel(this.tabs[i+1]);
18329     },
18330     
18331     showPanelPrev : function()
18332     {
18333         var i = this.indexOfPanel(this.getActivePanel());
18334         
18335         if (i  < 1 && !this.autoslide) {
18336             return;
18337         }
18338         
18339         if (i < 1 && this.autoslide) {
18340             i = this.tabs.length;
18341         }
18342         
18343         this.showPanel(this.tabs[i-1]);
18344     },
18345     
18346     
18347     addBullet: function()
18348     {
18349         if(!this.bullets || Roo.isTouch){
18350             return;
18351         }
18352         var ctr = this.el.select('.carousel-bullets',true).first();
18353         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18354         var bullet = ctr.createChild({
18355             cls : 'bullet bullet-' + i
18356         },ctr.dom.lastChild);
18357         
18358         
18359         var _this = this;
18360         
18361         bullet.on('click', (function(e, el, o, ii, t){
18362
18363             e.preventDefault();
18364
18365             this.showPanel(ii);
18366
18367             if(this.autoslide && this.slideFn){
18368                 clearInterval(this.slideFn);
18369                 this.slideFn = window.setInterval(function() {
18370                     _this.showPanelNext();
18371                 }, this.timer);
18372             }
18373
18374         }).createDelegate(this, [i, bullet], true));
18375                 
18376         
18377     },
18378      
18379     setActiveBullet : function(i)
18380     {
18381         if(Roo.isTouch){
18382             return;
18383         }
18384         
18385         Roo.each(this.el.select('.bullet', true).elements, function(el){
18386             el.removeClass('selected');
18387         });
18388
18389         var bullet = this.el.select('.bullet-' + i, true).first();
18390         
18391         if(!bullet){
18392             return;
18393         }
18394         
18395         bullet.addClass('selected');
18396     }
18397     
18398     
18399   
18400 });
18401
18402  
18403
18404  
18405  
18406 Roo.apply(Roo.bootstrap.TabGroup, {
18407     
18408     groups: {},
18409      /**
18410     * register a Navigation Group
18411     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18412     */
18413     register : function(navgrp)
18414     {
18415         this.groups[navgrp.navId] = navgrp;
18416         
18417     },
18418     /**
18419     * fetch a Navigation Group based on the navigation ID
18420     * if one does not exist , it will get created.
18421     * @param {string} the navgroup to add
18422     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18423     */
18424     get: function(navId) {
18425         if (typeof(this.groups[navId]) == 'undefined') {
18426             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18427         }
18428         return this.groups[navId] ;
18429     }
18430     
18431     
18432     
18433 });
18434
18435  /*
18436  * - LGPL
18437  *
18438  * TabPanel
18439  * 
18440  */
18441
18442 /**
18443  * @class Roo.bootstrap.TabPanel
18444  * @extends Roo.bootstrap.Component
18445  * Bootstrap TabPanel class
18446  * @cfg {Boolean} active panel active
18447  * @cfg {String} html panel content
18448  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18449  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18450  * @cfg {String} href click to link..
18451  * 
18452  * 
18453  * @constructor
18454  * Create a new TabPanel
18455  * @param {Object} config The config object
18456  */
18457
18458 Roo.bootstrap.TabPanel = function(config){
18459     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18460     this.addEvents({
18461         /**
18462              * @event changed
18463              * Fires when the active status changes
18464              * @param {Roo.bootstrap.TabPanel} this
18465              * @param {Boolean} state the new state
18466             
18467          */
18468         'changed': true,
18469         /**
18470              * @event beforedeactivate
18471              * Fires before a tab is de-activated - can be used to do validation on a form.
18472              * @param {Roo.bootstrap.TabPanel} this
18473              * @return {Boolean} false if there is an error
18474             
18475          */
18476         'beforedeactivate': true
18477      });
18478     
18479     this.tabId = this.tabId || Roo.id();
18480   
18481 };
18482
18483 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18484     
18485     active: false,
18486     html: false,
18487     tabId: false,
18488     navId : false,
18489     href : '',
18490     
18491     getAutoCreate : function(){
18492         var cfg = {
18493             tag: 'div',
18494             // item is needed for carousel - not sure if it has any effect otherwise
18495             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18496             html: this.html || ''
18497         };
18498         
18499         if(this.active){
18500             cfg.cls += ' active';
18501         }
18502         
18503         if(this.tabId){
18504             cfg.tabId = this.tabId;
18505         }
18506         
18507         
18508         return cfg;
18509     },
18510     
18511     initEvents:  function()
18512     {
18513         var p = this.parent();
18514         
18515         this.navId = this.navId || p.navId;
18516         
18517         if (typeof(this.navId) != 'undefined') {
18518             // not really needed.. but just in case.. parent should be a NavGroup.
18519             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18520             
18521             tg.register(this);
18522             
18523             var i = tg.tabs.length - 1;
18524             
18525             if(this.active && tg.bullets > 0 && i < tg.bullets){
18526                 tg.setActiveBullet(i);
18527             }
18528         }
18529         
18530         this.el.on('click', this.onClick, this);
18531         
18532         if(Roo.isTouch){
18533             this.el.on("touchstart", this.onTouchStart, this);
18534             this.el.on("touchmove", this.onTouchMove, this);
18535             this.el.on("touchend", this.onTouchEnd, this);
18536         }
18537         
18538     },
18539     
18540     onRender : function(ct, position)
18541     {
18542         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18543     },
18544     
18545     setActive : function(state)
18546     {
18547         Roo.log("panel - set active " + this.tabId + "=" + state);
18548         
18549         this.active = state;
18550         if (!state) {
18551             this.el.removeClass('active');
18552             
18553         } else  if (!this.el.hasClass('active')) {
18554             this.el.addClass('active');
18555         }
18556         
18557         this.fireEvent('changed', this, state);
18558     },
18559     
18560     onClick : function(e)
18561     {
18562         e.preventDefault();
18563         
18564         if(!this.href.length){
18565             return;
18566         }
18567         
18568         window.location.href = this.href;
18569     },
18570     
18571     startX : 0,
18572     startY : 0,
18573     endX : 0,
18574     endY : 0,
18575     swiping : false,
18576     
18577     onTouchStart : function(e)
18578     {
18579         this.swiping = false;
18580         
18581         this.startX = e.browserEvent.touches[0].clientX;
18582         this.startY = e.browserEvent.touches[0].clientY;
18583     },
18584     
18585     onTouchMove : function(e)
18586     {
18587         this.swiping = true;
18588         
18589         this.endX = e.browserEvent.touches[0].clientX;
18590         this.endY = e.browserEvent.touches[0].clientY;
18591     },
18592     
18593     onTouchEnd : function(e)
18594     {
18595         if(!this.swiping){
18596             this.onClick(e);
18597             return;
18598         }
18599         
18600         var tabGroup = this.parent();
18601         
18602         if(this.endX > this.startX){ // swiping right
18603             tabGroup.showPanelPrev();
18604             return;
18605         }
18606         
18607         if(this.startX > this.endX){ // swiping left
18608             tabGroup.showPanelNext();
18609             return;
18610         }
18611     }
18612     
18613     
18614 });
18615  
18616
18617  
18618
18619  /*
18620  * - LGPL
18621  *
18622  * DateField
18623  * 
18624  */
18625
18626 /**
18627  * @class Roo.bootstrap.DateField
18628  * @extends Roo.bootstrap.Input
18629  * Bootstrap DateField class
18630  * @cfg {Number} weekStart default 0
18631  * @cfg {String} viewMode default empty, (months|years)
18632  * @cfg {String} minViewMode default empty, (months|years)
18633  * @cfg {Number} startDate default -Infinity
18634  * @cfg {Number} endDate default Infinity
18635  * @cfg {Boolean} todayHighlight default false
18636  * @cfg {Boolean} todayBtn default false
18637  * @cfg {Boolean} calendarWeeks default false
18638  * @cfg {Object} daysOfWeekDisabled default empty
18639  * @cfg {Boolean} singleMode default false (true | false)
18640  * 
18641  * @cfg {Boolean} keyboardNavigation default true
18642  * @cfg {String} language default en
18643  * 
18644  * @constructor
18645  * Create a new DateField
18646  * @param {Object} config The config object
18647  */
18648
18649 Roo.bootstrap.DateField = function(config){
18650     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18651      this.addEvents({
18652             /**
18653              * @event show
18654              * Fires when this field show.
18655              * @param {Roo.bootstrap.DateField} this
18656              * @param {Mixed} date The date value
18657              */
18658             show : true,
18659             /**
18660              * @event show
18661              * Fires when this field hide.
18662              * @param {Roo.bootstrap.DateField} this
18663              * @param {Mixed} date The date value
18664              */
18665             hide : true,
18666             /**
18667              * @event select
18668              * Fires when select a date.
18669              * @param {Roo.bootstrap.DateField} this
18670              * @param {Mixed} date The date value
18671              */
18672             select : true,
18673             /**
18674              * @event beforeselect
18675              * Fires when before select a date.
18676              * @param {Roo.bootstrap.DateField} this
18677              * @param {Mixed} date The date value
18678              */
18679             beforeselect : true
18680         });
18681 };
18682
18683 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18684     
18685     /**
18686      * @cfg {String} format
18687      * The default date format string which can be overriden for localization support.  The format must be
18688      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18689      */
18690     format : "m/d/y",
18691     /**
18692      * @cfg {String} altFormats
18693      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18694      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18695      */
18696     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18697     
18698     weekStart : 0,
18699     
18700     viewMode : '',
18701     
18702     minViewMode : '',
18703     
18704     todayHighlight : false,
18705     
18706     todayBtn: false,
18707     
18708     language: 'en',
18709     
18710     keyboardNavigation: true,
18711     
18712     calendarWeeks: false,
18713     
18714     startDate: -Infinity,
18715     
18716     endDate: Infinity,
18717     
18718     daysOfWeekDisabled: [],
18719     
18720     _events: [],
18721     
18722     singleMode : false,
18723     
18724     UTCDate: function()
18725     {
18726         return new Date(Date.UTC.apply(Date, arguments));
18727     },
18728     
18729     UTCToday: function()
18730     {
18731         var today = new Date();
18732         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18733     },
18734     
18735     getDate: function() {
18736             var d = this.getUTCDate();
18737             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18738     },
18739     
18740     getUTCDate: function() {
18741             return this.date;
18742     },
18743     
18744     setDate: function(d) {
18745             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18746     },
18747     
18748     setUTCDate: function(d) {
18749             this.date = d;
18750             this.setValue(this.formatDate(this.date));
18751     },
18752         
18753     onRender: function(ct, position)
18754     {
18755         
18756         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18757         
18758         this.language = this.language || 'en';
18759         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18760         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18761         
18762         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18763         this.format = this.format || 'm/d/y';
18764         this.isInline = false;
18765         this.isInput = true;
18766         this.component = this.el.select('.add-on', true).first() || false;
18767         this.component = (this.component && this.component.length === 0) ? false : this.component;
18768         this.hasInput = this.component && this.inputEl().length;
18769         
18770         if (typeof(this.minViewMode === 'string')) {
18771             switch (this.minViewMode) {
18772                 case 'months':
18773                     this.minViewMode = 1;
18774                     break;
18775                 case 'years':
18776                     this.minViewMode = 2;
18777                     break;
18778                 default:
18779                     this.minViewMode = 0;
18780                     break;
18781             }
18782         }
18783         
18784         if (typeof(this.viewMode === 'string')) {
18785             switch (this.viewMode) {
18786                 case 'months':
18787                     this.viewMode = 1;
18788                     break;
18789                 case 'years':
18790                     this.viewMode = 2;
18791                     break;
18792                 default:
18793                     this.viewMode = 0;
18794                     break;
18795             }
18796         }
18797                 
18798         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18799         
18800 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18801         
18802         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18803         
18804         this.picker().on('mousedown', this.onMousedown, this);
18805         this.picker().on('click', this.onClick, this);
18806         
18807         this.picker().addClass('datepicker-dropdown');
18808         
18809         this.startViewMode = this.viewMode;
18810         
18811         if(this.singleMode){
18812             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18813                 v.setVisibilityMode(Roo.Element.DISPLAY);
18814                 v.hide();
18815             });
18816             
18817             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18818                 v.setStyle('width', '189px');
18819             });
18820         }
18821         
18822         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18823             if(!this.calendarWeeks){
18824                 v.remove();
18825                 return;
18826             }
18827             
18828             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18829             v.attr('colspan', function(i, val){
18830                 return parseInt(val) + 1;
18831             });
18832         });
18833                         
18834         
18835         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18836         
18837         this.setStartDate(this.startDate);
18838         this.setEndDate(this.endDate);
18839         
18840         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18841         
18842         this.fillDow();
18843         this.fillMonths();
18844         this.update();
18845         this.showMode();
18846         
18847         if(this.isInline) {
18848             this.showPopup();
18849         }
18850     },
18851     
18852     picker : function()
18853     {
18854         return this.pickerEl;
18855 //        return this.el.select('.datepicker', true).first();
18856     },
18857     
18858     fillDow: function()
18859     {
18860         var dowCnt = this.weekStart;
18861         
18862         var dow = {
18863             tag: 'tr',
18864             cn: [
18865                 
18866             ]
18867         };
18868         
18869         if(this.calendarWeeks){
18870             dow.cn.push({
18871                 tag: 'th',
18872                 cls: 'cw',
18873                 html: '&nbsp;'
18874             })
18875         }
18876         
18877         while (dowCnt < this.weekStart + 7) {
18878             dow.cn.push({
18879                 tag: 'th',
18880                 cls: 'dow',
18881                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18882             });
18883         }
18884         
18885         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18886     },
18887     
18888     fillMonths: function()
18889     {    
18890         var i = 0;
18891         var months = this.picker().select('>.datepicker-months td', true).first();
18892         
18893         months.dom.innerHTML = '';
18894         
18895         while (i < 12) {
18896             var month = {
18897                 tag: 'span',
18898                 cls: 'month',
18899                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18900             };
18901             
18902             months.createChild(month);
18903         }
18904         
18905     },
18906     
18907     update: function()
18908     {
18909         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;
18910         
18911         if (this.date < this.startDate) {
18912             this.viewDate = new Date(this.startDate);
18913         } else if (this.date > this.endDate) {
18914             this.viewDate = new Date(this.endDate);
18915         } else {
18916             this.viewDate = new Date(this.date);
18917         }
18918         
18919         this.fill();
18920     },
18921     
18922     fill: function() 
18923     {
18924         var d = new Date(this.viewDate),
18925                 year = d.getUTCFullYear(),
18926                 month = d.getUTCMonth(),
18927                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18928                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18929                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18930                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18931                 currentDate = this.date && this.date.valueOf(),
18932                 today = this.UTCToday();
18933         
18934         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18935         
18936 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18937         
18938 //        this.picker.select('>tfoot th.today').
18939 //                                              .text(dates[this.language].today)
18940 //                                              .toggle(this.todayBtn !== false);
18941     
18942         this.updateNavArrows();
18943         this.fillMonths();
18944                                                 
18945         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18946         
18947         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18948          
18949         prevMonth.setUTCDate(day);
18950         
18951         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18952         
18953         var nextMonth = new Date(prevMonth);
18954         
18955         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18956         
18957         nextMonth = nextMonth.valueOf();
18958         
18959         var fillMonths = false;
18960         
18961         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18962         
18963         while(prevMonth.valueOf() <= nextMonth) {
18964             var clsName = '';
18965             
18966             if (prevMonth.getUTCDay() === this.weekStart) {
18967                 if(fillMonths){
18968                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18969                 }
18970                     
18971                 fillMonths = {
18972                     tag: 'tr',
18973                     cn: []
18974                 };
18975                 
18976                 if(this.calendarWeeks){
18977                     // ISO 8601: First week contains first thursday.
18978                     // ISO also states week starts on Monday, but we can be more abstract here.
18979                     var
18980                     // Start of current week: based on weekstart/current date
18981                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18982                     // Thursday of this week
18983                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18984                     // First Thursday of year, year from thursday
18985                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18986                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18987                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18988                     
18989                     fillMonths.cn.push({
18990                         tag: 'td',
18991                         cls: 'cw',
18992                         html: calWeek
18993                     });
18994                 }
18995             }
18996             
18997             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18998                 clsName += ' old';
18999             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19000                 clsName += ' new';
19001             }
19002             if (this.todayHighlight &&
19003                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19004                 prevMonth.getUTCMonth() == today.getMonth() &&
19005                 prevMonth.getUTCDate() == today.getDate()) {
19006                 clsName += ' today';
19007             }
19008             
19009             if (currentDate && prevMonth.valueOf() === currentDate) {
19010                 clsName += ' active';
19011             }
19012             
19013             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19014                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19015                     clsName += ' disabled';
19016             }
19017             
19018             fillMonths.cn.push({
19019                 tag: 'td',
19020                 cls: 'day ' + clsName,
19021                 html: prevMonth.getDate()
19022             });
19023             
19024             prevMonth.setDate(prevMonth.getDate()+1);
19025         }
19026           
19027         var currentYear = this.date && this.date.getUTCFullYear();
19028         var currentMonth = this.date && this.date.getUTCMonth();
19029         
19030         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19031         
19032         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19033             v.removeClass('active');
19034             
19035             if(currentYear === year && k === currentMonth){
19036                 v.addClass('active');
19037             }
19038             
19039             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19040                 v.addClass('disabled');
19041             }
19042             
19043         });
19044         
19045         
19046         year = parseInt(year/10, 10) * 10;
19047         
19048         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19049         
19050         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19051         
19052         year -= 1;
19053         for (var i = -1; i < 11; i++) {
19054             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19055                 tag: 'span',
19056                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19057                 html: year
19058             });
19059             
19060             year += 1;
19061         }
19062     },
19063     
19064     showMode: function(dir) 
19065     {
19066         if (dir) {
19067             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19068         }
19069         
19070         Roo.each(this.picker().select('>div',true).elements, function(v){
19071             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19072             v.hide();
19073         });
19074         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19075     },
19076     
19077     place: function()
19078     {
19079         if(this.isInline) {
19080             return;
19081         }
19082         
19083         this.picker().removeClass(['bottom', 'top']);
19084         
19085         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19086             /*
19087              * place to the top of element!
19088              *
19089              */
19090             
19091             this.picker().addClass('top');
19092             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19093             
19094             return;
19095         }
19096         
19097         this.picker().addClass('bottom');
19098         
19099         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19100     },
19101     
19102     parseDate : function(value)
19103     {
19104         if(!value || value instanceof Date){
19105             return value;
19106         }
19107         var v = Date.parseDate(value, this.format);
19108         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19109             v = Date.parseDate(value, 'Y-m-d');
19110         }
19111         if(!v && this.altFormats){
19112             if(!this.altFormatsArray){
19113                 this.altFormatsArray = this.altFormats.split("|");
19114             }
19115             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19116                 v = Date.parseDate(value, this.altFormatsArray[i]);
19117             }
19118         }
19119         return v;
19120     },
19121     
19122     formatDate : function(date, fmt)
19123     {   
19124         return (!date || !(date instanceof Date)) ?
19125         date : date.dateFormat(fmt || this.format);
19126     },
19127     
19128     onFocus : function()
19129     {
19130         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19131         this.showPopup();
19132     },
19133     
19134     onBlur : function()
19135     {
19136         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19137         
19138         var d = this.inputEl().getValue();
19139         
19140         this.setValue(d);
19141                 
19142         this.hidePopup();
19143     },
19144     
19145     showPopup : function()
19146     {
19147         this.picker().show();
19148         this.update();
19149         this.place();
19150         
19151         this.fireEvent('showpopup', this, this.date);
19152     },
19153     
19154     hidePopup : function()
19155     {
19156         if(this.isInline) {
19157             return;
19158         }
19159         this.picker().hide();
19160         this.viewMode = this.startViewMode;
19161         this.showMode();
19162         
19163         this.fireEvent('hidepopup', this, this.date);
19164         
19165     },
19166     
19167     onMousedown: function(e)
19168     {
19169         e.stopPropagation();
19170         e.preventDefault();
19171     },
19172     
19173     keyup: function(e)
19174     {
19175         Roo.bootstrap.DateField.superclass.keyup.call(this);
19176         this.update();
19177     },
19178
19179     setValue: function(v)
19180     {
19181         if(this.fireEvent('beforeselect', this, v) !== false){
19182             var d = new Date(this.parseDate(v) ).clearTime();
19183         
19184             if(isNaN(d.getTime())){
19185                 this.date = this.viewDate = '';
19186                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19187                 return;
19188             }
19189
19190             v = this.formatDate(d);
19191
19192             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19193
19194             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19195
19196             this.update();
19197
19198             this.fireEvent('select', this, this.date);
19199         }
19200     },
19201     
19202     getValue: function()
19203     {
19204         return this.formatDate(this.date);
19205     },
19206     
19207     fireKey: function(e)
19208     {
19209         if (!this.picker().isVisible()){
19210             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19211                 this.showPopup();
19212             }
19213             return;
19214         }
19215         
19216         var dateChanged = false,
19217         dir, day, month,
19218         newDate, newViewDate;
19219         
19220         switch(e.keyCode){
19221             case 27: // escape
19222                 this.hidePopup();
19223                 e.preventDefault();
19224                 break;
19225             case 37: // left
19226             case 39: // right
19227                 if (!this.keyboardNavigation) {
19228                     break;
19229                 }
19230                 dir = e.keyCode == 37 ? -1 : 1;
19231                 
19232                 if (e.ctrlKey){
19233                     newDate = this.moveYear(this.date, dir);
19234                     newViewDate = this.moveYear(this.viewDate, dir);
19235                 } else if (e.shiftKey){
19236                     newDate = this.moveMonth(this.date, dir);
19237                     newViewDate = this.moveMonth(this.viewDate, dir);
19238                 } else {
19239                     newDate = new Date(this.date);
19240                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19241                     newViewDate = new Date(this.viewDate);
19242                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19243                 }
19244                 if (this.dateWithinRange(newDate)){
19245                     this.date = newDate;
19246                     this.viewDate = newViewDate;
19247                     this.setValue(this.formatDate(this.date));
19248 //                    this.update();
19249                     e.preventDefault();
19250                     dateChanged = true;
19251                 }
19252                 break;
19253             case 38: // up
19254             case 40: // down
19255                 if (!this.keyboardNavigation) {
19256                     break;
19257                 }
19258                 dir = e.keyCode == 38 ? -1 : 1;
19259                 if (e.ctrlKey){
19260                     newDate = this.moveYear(this.date, dir);
19261                     newViewDate = this.moveYear(this.viewDate, dir);
19262                 } else if (e.shiftKey){
19263                     newDate = this.moveMonth(this.date, dir);
19264                     newViewDate = this.moveMonth(this.viewDate, dir);
19265                 } else {
19266                     newDate = new Date(this.date);
19267                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19268                     newViewDate = new Date(this.viewDate);
19269                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19270                 }
19271                 if (this.dateWithinRange(newDate)){
19272                     this.date = newDate;
19273                     this.viewDate = newViewDate;
19274                     this.setValue(this.formatDate(this.date));
19275 //                    this.update();
19276                     e.preventDefault();
19277                     dateChanged = true;
19278                 }
19279                 break;
19280             case 13: // enter
19281                 this.setValue(this.formatDate(this.date));
19282                 this.hidePopup();
19283                 e.preventDefault();
19284                 break;
19285             case 9: // tab
19286                 this.setValue(this.formatDate(this.date));
19287                 this.hidePopup();
19288                 break;
19289             case 16: // shift
19290             case 17: // ctrl
19291             case 18: // alt
19292                 break;
19293             default :
19294                 this.hidePopup();
19295                 
19296         }
19297     },
19298     
19299     
19300     onClick: function(e) 
19301     {
19302         e.stopPropagation();
19303         e.preventDefault();
19304         
19305         var target = e.getTarget();
19306         
19307         if(target.nodeName.toLowerCase() === 'i'){
19308             target = Roo.get(target).dom.parentNode;
19309         }
19310         
19311         var nodeName = target.nodeName;
19312         var className = target.className;
19313         var html = target.innerHTML;
19314         //Roo.log(nodeName);
19315         
19316         switch(nodeName.toLowerCase()) {
19317             case 'th':
19318                 switch(className) {
19319                     case 'switch':
19320                         this.showMode(1);
19321                         break;
19322                     case 'prev':
19323                     case 'next':
19324                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19325                         switch(this.viewMode){
19326                                 case 0:
19327                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19328                                         break;
19329                                 case 1:
19330                                 case 2:
19331                                         this.viewDate = this.moveYear(this.viewDate, dir);
19332                                         break;
19333                         }
19334                         this.fill();
19335                         break;
19336                     case 'today':
19337                         var date = new Date();
19338                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19339 //                        this.fill()
19340                         this.setValue(this.formatDate(this.date));
19341                         
19342                         this.hidePopup();
19343                         break;
19344                 }
19345                 break;
19346             case 'span':
19347                 if (className.indexOf('disabled') < 0) {
19348                     this.viewDate.setUTCDate(1);
19349                     if (className.indexOf('month') > -1) {
19350                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19351                     } else {
19352                         var year = parseInt(html, 10) || 0;
19353                         this.viewDate.setUTCFullYear(year);
19354                         
19355                     }
19356                     
19357                     if(this.singleMode){
19358                         this.setValue(this.formatDate(this.viewDate));
19359                         this.hidePopup();
19360                         return;
19361                     }
19362                     
19363                     this.showMode(-1);
19364                     this.fill();
19365                 }
19366                 break;
19367                 
19368             case 'td':
19369                 //Roo.log(className);
19370                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19371                     var day = parseInt(html, 10) || 1;
19372                     var year = this.viewDate.getUTCFullYear(),
19373                         month = this.viewDate.getUTCMonth();
19374
19375                     if (className.indexOf('old') > -1) {
19376                         if(month === 0 ){
19377                             month = 11;
19378                             year -= 1;
19379                         }else{
19380                             month -= 1;
19381                         }
19382                     } else if (className.indexOf('new') > -1) {
19383                         if (month == 11) {
19384                             month = 0;
19385                             year += 1;
19386                         } else {
19387                             month += 1;
19388                         }
19389                     }
19390                     //Roo.log([year,month,day]);
19391                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19392                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19393 //                    this.fill();
19394                     //Roo.log(this.formatDate(this.date));
19395                     this.setValue(this.formatDate(this.date));
19396                     this.hidePopup();
19397                 }
19398                 break;
19399         }
19400     },
19401     
19402     setStartDate: function(startDate)
19403     {
19404         this.startDate = startDate || -Infinity;
19405         if (this.startDate !== -Infinity) {
19406             this.startDate = this.parseDate(this.startDate);
19407         }
19408         this.update();
19409         this.updateNavArrows();
19410     },
19411
19412     setEndDate: function(endDate)
19413     {
19414         this.endDate = endDate || Infinity;
19415         if (this.endDate !== Infinity) {
19416             this.endDate = this.parseDate(this.endDate);
19417         }
19418         this.update();
19419         this.updateNavArrows();
19420     },
19421     
19422     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19423     {
19424         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19425         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19426             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19427         }
19428         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19429             return parseInt(d, 10);
19430         });
19431         this.update();
19432         this.updateNavArrows();
19433     },
19434     
19435     updateNavArrows: function() 
19436     {
19437         if(this.singleMode){
19438             return;
19439         }
19440         
19441         var d = new Date(this.viewDate),
19442         year = d.getUTCFullYear(),
19443         month = d.getUTCMonth();
19444         
19445         Roo.each(this.picker().select('.prev', true).elements, function(v){
19446             v.show();
19447             switch (this.viewMode) {
19448                 case 0:
19449
19450                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19451                         v.hide();
19452                     }
19453                     break;
19454                 case 1:
19455                 case 2:
19456                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19457                         v.hide();
19458                     }
19459                     break;
19460             }
19461         });
19462         
19463         Roo.each(this.picker().select('.next', true).elements, function(v){
19464             v.show();
19465             switch (this.viewMode) {
19466                 case 0:
19467
19468                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19469                         v.hide();
19470                     }
19471                     break;
19472                 case 1:
19473                 case 2:
19474                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19475                         v.hide();
19476                     }
19477                     break;
19478             }
19479         })
19480     },
19481     
19482     moveMonth: function(date, dir)
19483     {
19484         if (!dir) {
19485             return date;
19486         }
19487         var new_date = new Date(date.valueOf()),
19488         day = new_date.getUTCDate(),
19489         month = new_date.getUTCMonth(),
19490         mag = Math.abs(dir),
19491         new_month, test;
19492         dir = dir > 0 ? 1 : -1;
19493         if (mag == 1){
19494             test = dir == -1
19495             // If going back one month, make sure month is not current month
19496             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19497             ? function(){
19498                 return new_date.getUTCMonth() == month;
19499             }
19500             // If going forward one month, make sure month is as expected
19501             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19502             : function(){
19503                 return new_date.getUTCMonth() != new_month;
19504             };
19505             new_month = month + dir;
19506             new_date.setUTCMonth(new_month);
19507             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19508             if (new_month < 0 || new_month > 11) {
19509                 new_month = (new_month + 12) % 12;
19510             }
19511         } else {
19512             // For magnitudes >1, move one month at a time...
19513             for (var i=0; i<mag; i++) {
19514                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19515                 new_date = this.moveMonth(new_date, dir);
19516             }
19517             // ...then reset the day, keeping it in the new month
19518             new_month = new_date.getUTCMonth();
19519             new_date.setUTCDate(day);
19520             test = function(){
19521                 return new_month != new_date.getUTCMonth();
19522             };
19523         }
19524         // Common date-resetting loop -- if date is beyond end of month, make it
19525         // end of month
19526         while (test()){
19527             new_date.setUTCDate(--day);
19528             new_date.setUTCMonth(new_month);
19529         }
19530         return new_date;
19531     },
19532
19533     moveYear: function(date, dir)
19534     {
19535         return this.moveMonth(date, dir*12);
19536     },
19537
19538     dateWithinRange: function(date)
19539     {
19540         return date >= this.startDate && date <= this.endDate;
19541     },
19542
19543     
19544     remove: function() 
19545     {
19546         this.picker().remove();
19547     },
19548     
19549     validateValue : function(value)
19550     {
19551         if(this.getVisibilityEl().hasClass('hidden')){
19552             return true;
19553         }
19554         
19555         if(value.length < 1)  {
19556             if(this.allowBlank){
19557                 return true;
19558             }
19559             return false;
19560         }
19561         
19562         if(value.length < this.minLength){
19563             return false;
19564         }
19565         if(value.length > this.maxLength){
19566             return false;
19567         }
19568         if(this.vtype){
19569             var vt = Roo.form.VTypes;
19570             if(!vt[this.vtype](value, this)){
19571                 return false;
19572             }
19573         }
19574         if(typeof this.validator == "function"){
19575             var msg = this.validator(value);
19576             if(msg !== true){
19577                 return false;
19578             }
19579         }
19580         
19581         if(this.regex && !this.regex.test(value)){
19582             return false;
19583         }
19584         
19585         if(typeof(this.parseDate(value)) == 'undefined'){
19586             return false;
19587         }
19588         
19589         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19590             return false;
19591         }      
19592         
19593         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19594             return false;
19595         } 
19596         
19597         
19598         return true;
19599     },
19600     
19601     reset : function()
19602     {
19603         this.date = this.viewDate = '';
19604         
19605         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19606     }
19607    
19608 });
19609
19610 Roo.apply(Roo.bootstrap.DateField,  {
19611     
19612     head : {
19613         tag: 'thead',
19614         cn: [
19615         {
19616             tag: 'tr',
19617             cn: [
19618             {
19619                 tag: 'th',
19620                 cls: 'prev',
19621                 html: '<i class="fa fa-arrow-left"/>'
19622             },
19623             {
19624                 tag: 'th',
19625                 cls: 'switch',
19626                 colspan: '5'
19627             },
19628             {
19629                 tag: 'th',
19630                 cls: 'next',
19631                 html: '<i class="fa fa-arrow-right"/>'
19632             }
19633
19634             ]
19635         }
19636         ]
19637     },
19638     
19639     content : {
19640         tag: 'tbody',
19641         cn: [
19642         {
19643             tag: 'tr',
19644             cn: [
19645             {
19646                 tag: 'td',
19647                 colspan: '7'
19648             }
19649             ]
19650         }
19651         ]
19652     },
19653     
19654     footer : {
19655         tag: 'tfoot',
19656         cn: [
19657         {
19658             tag: 'tr',
19659             cn: [
19660             {
19661                 tag: 'th',
19662                 colspan: '7',
19663                 cls: 'today'
19664             }
19665                     
19666             ]
19667         }
19668         ]
19669     },
19670     
19671     dates:{
19672         en: {
19673             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19674             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19675             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19676             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19677             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19678             today: "Today"
19679         }
19680     },
19681     
19682     modes: [
19683     {
19684         clsName: 'days',
19685         navFnc: 'Month',
19686         navStep: 1
19687     },
19688     {
19689         clsName: 'months',
19690         navFnc: 'FullYear',
19691         navStep: 1
19692     },
19693     {
19694         clsName: 'years',
19695         navFnc: 'FullYear',
19696         navStep: 10
19697     }]
19698 });
19699
19700 Roo.apply(Roo.bootstrap.DateField,  {
19701   
19702     template : {
19703         tag: 'div',
19704         cls: 'datepicker dropdown-menu roo-dynamic',
19705         cn: [
19706         {
19707             tag: 'div',
19708             cls: 'datepicker-days',
19709             cn: [
19710             {
19711                 tag: 'table',
19712                 cls: 'table-condensed',
19713                 cn:[
19714                 Roo.bootstrap.DateField.head,
19715                 {
19716                     tag: 'tbody'
19717                 },
19718                 Roo.bootstrap.DateField.footer
19719                 ]
19720             }
19721             ]
19722         },
19723         {
19724             tag: 'div',
19725             cls: 'datepicker-months',
19726             cn: [
19727             {
19728                 tag: 'table',
19729                 cls: 'table-condensed',
19730                 cn:[
19731                 Roo.bootstrap.DateField.head,
19732                 Roo.bootstrap.DateField.content,
19733                 Roo.bootstrap.DateField.footer
19734                 ]
19735             }
19736             ]
19737         },
19738         {
19739             tag: 'div',
19740             cls: 'datepicker-years',
19741             cn: [
19742             {
19743                 tag: 'table',
19744                 cls: 'table-condensed',
19745                 cn:[
19746                 Roo.bootstrap.DateField.head,
19747                 Roo.bootstrap.DateField.content,
19748                 Roo.bootstrap.DateField.footer
19749                 ]
19750             }
19751             ]
19752         }
19753         ]
19754     }
19755 });
19756
19757  
19758
19759  /*
19760  * - LGPL
19761  *
19762  * TimeField
19763  * 
19764  */
19765
19766 /**
19767  * @class Roo.bootstrap.TimeField
19768  * @extends Roo.bootstrap.Input
19769  * Bootstrap DateField class
19770  * 
19771  * 
19772  * @constructor
19773  * Create a new TimeField
19774  * @param {Object} config The config object
19775  */
19776
19777 Roo.bootstrap.TimeField = function(config){
19778     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19779     this.addEvents({
19780             /**
19781              * @event show
19782              * Fires when this field show.
19783              * @param {Roo.bootstrap.DateField} thisthis
19784              * @param {Mixed} date The date value
19785              */
19786             show : true,
19787             /**
19788              * @event show
19789              * Fires when this field hide.
19790              * @param {Roo.bootstrap.DateField} this
19791              * @param {Mixed} date The date value
19792              */
19793             hide : true,
19794             /**
19795              * @event select
19796              * Fires when select a date.
19797              * @param {Roo.bootstrap.DateField} this
19798              * @param {Mixed} date The date value
19799              */
19800             select : true
19801         });
19802 };
19803
19804 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19805     
19806     /**
19807      * @cfg {String} format
19808      * The default time format string which can be overriden for localization support.  The format must be
19809      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19810      */
19811     format : "H:i",
19812        
19813     onRender: function(ct, position)
19814     {
19815         
19816         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19817                 
19818         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19819         
19820         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19821         
19822         this.pop = this.picker().select('>.datepicker-time',true).first();
19823         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19824         
19825         this.picker().on('mousedown', this.onMousedown, this);
19826         this.picker().on('click', this.onClick, this);
19827         
19828         this.picker().addClass('datepicker-dropdown');
19829     
19830         this.fillTime();
19831         this.update();
19832             
19833         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19834         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19835         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19836         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19837         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19838         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19839
19840     },
19841     
19842     fireKey: function(e){
19843         if (!this.picker().isVisible()){
19844             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19845                 this.show();
19846             }
19847             return;
19848         }
19849
19850         e.preventDefault();
19851         
19852         switch(e.keyCode){
19853             case 27: // escape
19854                 this.hide();
19855                 break;
19856             case 37: // left
19857             case 39: // right
19858                 this.onTogglePeriod();
19859                 break;
19860             case 38: // up
19861                 this.onIncrementMinutes();
19862                 break;
19863             case 40: // down
19864                 this.onDecrementMinutes();
19865                 break;
19866             case 13: // enter
19867             case 9: // tab
19868                 this.setTime();
19869                 break;
19870         }
19871     },
19872     
19873     onClick: function(e) {
19874         e.stopPropagation();
19875         e.preventDefault();
19876     },
19877     
19878     picker : function()
19879     {
19880         return this.el.select('.datepicker', true).first();
19881     },
19882     
19883     fillTime: function()
19884     {    
19885         var time = this.pop.select('tbody', true).first();
19886         
19887         time.dom.innerHTML = '';
19888         
19889         time.createChild({
19890             tag: 'tr',
19891             cn: [
19892                 {
19893                     tag: 'td',
19894                     cn: [
19895                         {
19896                             tag: 'a',
19897                             href: '#',
19898                             cls: 'btn',
19899                             cn: [
19900                                 {
19901                                     tag: 'span',
19902                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19903                                 }
19904                             ]
19905                         } 
19906                     ]
19907                 },
19908                 {
19909                     tag: 'td',
19910                     cls: 'separator'
19911                 },
19912                 {
19913                     tag: 'td',
19914                     cn: [
19915                         {
19916                             tag: 'a',
19917                             href: '#',
19918                             cls: 'btn',
19919                             cn: [
19920                                 {
19921                                     tag: 'span',
19922                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19923                                 }
19924                             ]
19925                         }
19926                     ]
19927                 },
19928                 {
19929                     tag: 'td',
19930                     cls: 'separator'
19931                 }
19932             ]
19933         });
19934         
19935         time.createChild({
19936             tag: 'tr',
19937             cn: [
19938                 {
19939                     tag: 'td',
19940                     cn: [
19941                         {
19942                             tag: 'span',
19943                             cls: 'timepicker-hour',
19944                             html: '00'
19945                         }  
19946                     ]
19947                 },
19948                 {
19949                     tag: 'td',
19950                     cls: 'separator',
19951                     html: ':'
19952                 },
19953                 {
19954                     tag: 'td',
19955                     cn: [
19956                         {
19957                             tag: 'span',
19958                             cls: 'timepicker-minute',
19959                             html: '00'
19960                         }  
19961                     ]
19962                 },
19963                 {
19964                     tag: 'td',
19965                     cls: 'separator'
19966                 },
19967                 {
19968                     tag: 'td',
19969                     cn: [
19970                         {
19971                             tag: 'button',
19972                             type: 'button',
19973                             cls: 'btn btn-primary period',
19974                             html: 'AM'
19975                             
19976                         }
19977                     ]
19978                 }
19979             ]
19980         });
19981         
19982         time.createChild({
19983             tag: 'tr',
19984             cn: [
19985                 {
19986                     tag: 'td',
19987                     cn: [
19988                         {
19989                             tag: 'a',
19990                             href: '#',
19991                             cls: 'btn',
19992                             cn: [
19993                                 {
19994                                     tag: 'span',
19995                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19996                                 }
19997                             ]
19998                         }
19999                     ]
20000                 },
20001                 {
20002                     tag: 'td',
20003                     cls: 'separator'
20004                 },
20005                 {
20006                     tag: 'td',
20007                     cn: [
20008                         {
20009                             tag: 'a',
20010                             href: '#',
20011                             cls: 'btn',
20012                             cn: [
20013                                 {
20014                                     tag: 'span',
20015                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20016                                 }
20017                             ]
20018                         }
20019                     ]
20020                 },
20021                 {
20022                     tag: 'td',
20023                     cls: 'separator'
20024                 }
20025             ]
20026         });
20027         
20028     },
20029     
20030     update: function()
20031     {
20032         
20033         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20034         
20035         this.fill();
20036     },
20037     
20038     fill: function() 
20039     {
20040         var hours = this.time.getHours();
20041         var minutes = this.time.getMinutes();
20042         var period = 'AM';
20043         
20044         if(hours > 11){
20045             period = 'PM';
20046         }
20047         
20048         if(hours == 0){
20049             hours = 12;
20050         }
20051         
20052         
20053         if(hours > 12){
20054             hours = hours - 12;
20055         }
20056         
20057         if(hours < 10){
20058             hours = '0' + hours;
20059         }
20060         
20061         if(minutes < 10){
20062             minutes = '0' + minutes;
20063         }
20064         
20065         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20066         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20067         this.pop.select('button', true).first().dom.innerHTML = period;
20068         
20069     },
20070     
20071     place: function()
20072     {   
20073         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20074         
20075         var cls = ['bottom'];
20076         
20077         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20078             cls.pop();
20079             cls.push('top');
20080         }
20081         
20082         cls.push('right');
20083         
20084         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20085             cls.pop();
20086             cls.push('left');
20087         }
20088         
20089         this.picker().addClass(cls.join('-'));
20090         
20091         var _this = this;
20092         
20093         Roo.each(cls, function(c){
20094             if(c == 'bottom'){
20095                 _this.picker().setTop(_this.inputEl().getHeight());
20096                 return;
20097             }
20098             if(c == 'top'){
20099                 _this.picker().setTop(0 - _this.picker().getHeight());
20100                 return;
20101             }
20102             
20103             if(c == 'left'){
20104                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20105                 return;
20106             }
20107             if(c == 'right'){
20108                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20109                 return;
20110             }
20111         });
20112         
20113     },
20114   
20115     onFocus : function()
20116     {
20117         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20118         this.show();
20119     },
20120     
20121     onBlur : function()
20122     {
20123         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20124         this.hide();
20125     },
20126     
20127     show : function()
20128     {
20129         this.picker().show();
20130         this.pop.show();
20131         this.update();
20132         this.place();
20133         
20134         this.fireEvent('show', this, this.date);
20135     },
20136     
20137     hide : function()
20138     {
20139         this.picker().hide();
20140         this.pop.hide();
20141         
20142         this.fireEvent('hide', this, this.date);
20143     },
20144     
20145     setTime : function()
20146     {
20147         this.hide();
20148         this.setValue(this.time.format(this.format));
20149         
20150         this.fireEvent('select', this, this.date);
20151         
20152         
20153     },
20154     
20155     onMousedown: function(e){
20156         e.stopPropagation();
20157         e.preventDefault();
20158     },
20159     
20160     onIncrementHours: function()
20161     {
20162         Roo.log('onIncrementHours');
20163         this.time = this.time.add(Date.HOUR, 1);
20164         this.update();
20165         
20166     },
20167     
20168     onDecrementHours: function()
20169     {
20170         Roo.log('onDecrementHours');
20171         this.time = this.time.add(Date.HOUR, -1);
20172         this.update();
20173     },
20174     
20175     onIncrementMinutes: function()
20176     {
20177         Roo.log('onIncrementMinutes');
20178         this.time = this.time.add(Date.MINUTE, 1);
20179         this.update();
20180     },
20181     
20182     onDecrementMinutes: function()
20183     {
20184         Roo.log('onDecrementMinutes');
20185         this.time = this.time.add(Date.MINUTE, -1);
20186         this.update();
20187     },
20188     
20189     onTogglePeriod: function()
20190     {
20191         Roo.log('onTogglePeriod');
20192         this.time = this.time.add(Date.HOUR, 12);
20193         this.update();
20194     }
20195     
20196    
20197 });
20198
20199 Roo.apply(Roo.bootstrap.TimeField,  {
20200     
20201     content : {
20202         tag: 'tbody',
20203         cn: [
20204             {
20205                 tag: 'tr',
20206                 cn: [
20207                 {
20208                     tag: 'td',
20209                     colspan: '7'
20210                 }
20211                 ]
20212             }
20213         ]
20214     },
20215     
20216     footer : {
20217         tag: 'tfoot',
20218         cn: [
20219             {
20220                 tag: 'tr',
20221                 cn: [
20222                 {
20223                     tag: 'th',
20224                     colspan: '7',
20225                     cls: '',
20226                     cn: [
20227                         {
20228                             tag: 'button',
20229                             cls: 'btn btn-info ok',
20230                             html: 'OK'
20231                         }
20232                     ]
20233                 }
20234
20235                 ]
20236             }
20237         ]
20238     }
20239 });
20240
20241 Roo.apply(Roo.bootstrap.TimeField,  {
20242   
20243     template : {
20244         tag: 'div',
20245         cls: 'datepicker dropdown-menu',
20246         cn: [
20247             {
20248                 tag: 'div',
20249                 cls: 'datepicker-time',
20250                 cn: [
20251                 {
20252                     tag: 'table',
20253                     cls: 'table-condensed',
20254                     cn:[
20255                     Roo.bootstrap.TimeField.content,
20256                     Roo.bootstrap.TimeField.footer
20257                     ]
20258                 }
20259                 ]
20260             }
20261         ]
20262     }
20263 });
20264
20265  
20266
20267  /*
20268  * - LGPL
20269  *
20270  * MonthField
20271  * 
20272  */
20273
20274 /**
20275  * @class Roo.bootstrap.MonthField
20276  * @extends Roo.bootstrap.Input
20277  * Bootstrap MonthField class
20278  * 
20279  * @cfg {String} language default en
20280  * 
20281  * @constructor
20282  * Create a new MonthField
20283  * @param {Object} config The config object
20284  */
20285
20286 Roo.bootstrap.MonthField = function(config){
20287     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20288     
20289     this.addEvents({
20290         /**
20291          * @event show
20292          * Fires when this field show.
20293          * @param {Roo.bootstrap.MonthField} this
20294          * @param {Mixed} date The date value
20295          */
20296         show : true,
20297         /**
20298          * @event show
20299          * Fires when this field hide.
20300          * @param {Roo.bootstrap.MonthField} this
20301          * @param {Mixed} date The date value
20302          */
20303         hide : true,
20304         /**
20305          * @event select
20306          * Fires when select a date.
20307          * @param {Roo.bootstrap.MonthField} this
20308          * @param {String} oldvalue The old value
20309          * @param {String} newvalue The new value
20310          */
20311         select : true
20312     });
20313 };
20314
20315 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20316     
20317     onRender: function(ct, position)
20318     {
20319         
20320         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20321         
20322         this.language = this.language || 'en';
20323         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20324         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20325         
20326         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20327         this.isInline = false;
20328         this.isInput = true;
20329         this.component = this.el.select('.add-on', true).first() || false;
20330         this.component = (this.component && this.component.length === 0) ? false : this.component;
20331         this.hasInput = this.component && this.inputEL().length;
20332         
20333         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20334         
20335         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20336         
20337         this.picker().on('mousedown', this.onMousedown, this);
20338         this.picker().on('click', this.onClick, this);
20339         
20340         this.picker().addClass('datepicker-dropdown');
20341         
20342         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20343             v.setStyle('width', '189px');
20344         });
20345         
20346         this.fillMonths();
20347         
20348         this.update();
20349         
20350         if(this.isInline) {
20351             this.show();
20352         }
20353         
20354     },
20355     
20356     setValue: function(v, suppressEvent)
20357     {   
20358         var o = this.getValue();
20359         
20360         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20361         
20362         this.update();
20363
20364         if(suppressEvent !== true){
20365             this.fireEvent('select', this, o, v);
20366         }
20367         
20368     },
20369     
20370     getValue: function()
20371     {
20372         return this.value;
20373     },
20374     
20375     onClick: function(e) 
20376     {
20377         e.stopPropagation();
20378         e.preventDefault();
20379         
20380         var target = e.getTarget();
20381         
20382         if(target.nodeName.toLowerCase() === 'i'){
20383             target = Roo.get(target).dom.parentNode;
20384         }
20385         
20386         var nodeName = target.nodeName;
20387         var className = target.className;
20388         var html = target.innerHTML;
20389         
20390         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20391             return;
20392         }
20393         
20394         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20395         
20396         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20397         
20398         this.hide();
20399                         
20400     },
20401     
20402     picker : function()
20403     {
20404         return this.pickerEl;
20405     },
20406     
20407     fillMonths: function()
20408     {    
20409         var i = 0;
20410         var months = this.picker().select('>.datepicker-months td', true).first();
20411         
20412         months.dom.innerHTML = '';
20413         
20414         while (i < 12) {
20415             var month = {
20416                 tag: 'span',
20417                 cls: 'month',
20418                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20419             };
20420             
20421             months.createChild(month);
20422         }
20423         
20424     },
20425     
20426     update: function()
20427     {
20428         var _this = this;
20429         
20430         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20431             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20432         }
20433         
20434         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20435             e.removeClass('active');
20436             
20437             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20438                 e.addClass('active');
20439             }
20440         })
20441     },
20442     
20443     place: function()
20444     {
20445         if(this.isInline) {
20446             return;
20447         }
20448         
20449         this.picker().removeClass(['bottom', 'top']);
20450         
20451         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20452             /*
20453              * place to the top of element!
20454              *
20455              */
20456             
20457             this.picker().addClass('top');
20458             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20459             
20460             return;
20461         }
20462         
20463         this.picker().addClass('bottom');
20464         
20465         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20466     },
20467     
20468     onFocus : function()
20469     {
20470         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20471         this.show();
20472     },
20473     
20474     onBlur : function()
20475     {
20476         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20477         
20478         var d = this.inputEl().getValue();
20479         
20480         this.setValue(d);
20481                 
20482         this.hide();
20483     },
20484     
20485     show : function()
20486     {
20487         this.picker().show();
20488         this.picker().select('>.datepicker-months', true).first().show();
20489         this.update();
20490         this.place();
20491         
20492         this.fireEvent('show', this, this.date);
20493     },
20494     
20495     hide : function()
20496     {
20497         if(this.isInline) {
20498             return;
20499         }
20500         this.picker().hide();
20501         this.fireEvent('hide', this, this.date);
20502         
20503     },
20504     
20505     onMousedown: function(e)
20506     {
20507         e.stopPropagation();
20508         e.preventDefault();
20509     },
20510     
20511     keyup: function(e)
20512     {
20513         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20514         this.update();
20515     },
20516
20517     fireKey: function(e)
20518     {
20519         if (!this.picker().isVisible()){
20520             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20521                 this.show();
20522             }
20523             return;
20524         }
20525         
20526         var dir;
20527         
20528         switch(e.keyCode){
20529             case 27: // escape
20530                 this.hide();
20531                 e.preventDefault();
20532                 break;
20533             case 37: // left
20534             case 39: // right
20535                 dir = e.keyCode == 37 ? -1 : 1;
20536                 
20537                 this.vIndex = this.vIndex + dir;
20538                 
20539                 if(this.vIndex < 0){
20540                     this.vIndex = 0;
20541                 }
20542                 
20543                 if(this.vIndex > 11){
20544                     this.vIndex = 11;
20545                 }
20546                 
20547                 if(isNaN(this.vIndex)){
20548                     this.vIndex = 0;
20549                 }
20550                 
20551                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20552                 
20553                 break;
20554             case 38: // up
20555             case 40: // down
20556                 
20557                 dir = e.keyCode == 38 ? -1 : 1;
20558                 
20559                 this.vIndex = this.vIndex + dir * 4;
20560                 
20561                 if(this.vIndex < 0){
20562                     this.vIndex = 0;
20563                 }
20564                 
20565                 if(this.vIndex > 11){
20566                     this.vIndex = 11;
20567                 }
20568                 
20569                 if(isNaN(this.vIndex)){
20570                     this.vIndex = 0;
20571                 }
20572                 
20573                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20574                 break;
20575                 
20576             case 13: // enter
20577                 
20578                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20579                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20580                 }
20581                 
20582                 this.hide();
20583                 e.preventDefault();
20584                 break;
20585             case 9: // tab
20586                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20587                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20588                 }
20589                 this.hide();
20590                 break;
20591             case 16: // shift
20592             case 17: // ctrl
20593             case 18: // alt
20594                 break;
20595             default :
20596                 this.hide();
20597                 
20598         }
20599     },
20600     
20601     remove: function() 
20602     {
20603         this.picker().remove();
20604     }
20605    
20606 });
20607
20608 Roo.apply(Roo.bootstrap.MonthField,  {
20609     
20610     content : {
20611         tag: 'tbody',
20612         cn: [
20613         {
20614             tag: 'tr',
20615             cn: [
20616             {
20617                 tag: 'td',
20618                 colspan: '7'
20619             }
20620             ]
20621         }
20622         ]
20623     },
20624     
20625     dates:{
20626         en: {
20627             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20628             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20629         }
20630     }
20631 });
20632
20633 Roo.apply(Roo.bootstrap.MonthField,  {
20634   
20635     template : {
20636         tag: 'div',
20637         cls: 'datepicker dropdown-menu roo-dynamic',
20638         cn: [
20639             {
20640                 tag: 'div',
20641                 cls: 'datepicker-months',
20642                 cn: [
20643                 {
20644                     tag: 'table',
20645                     cls: 'table-condensed',
20646                     cn:[
20647                         Roo.bootstrap.DateField.content
20648                     ]
20649                 }
20650                 ]
20651             }
20652         ]
20653     }
20654 });
20655
20656  
20657
20658  
20659  /*
20660  * - LGPL
20661  *
20662  * CheckBox
20663  * 
20664  */
20665
20666 /**
20667  * @class Roo.bootstrap.CheckBox
20668  * @extends Roo.bootstrap.Input
20669  * Bootstrap CheckBox class
20670  * 
20671  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20672  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20673  * @cfg {String} boxLabel The text that appears beside the checkbox
20674  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20675  * @cfg {Boolean} checked initnal the element
20676  * @cfg {Boolean} inline inline the element (default false)
20677  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20678  * @cfg {String} tooltip label tooltip
20679  * 
20680  * @constructor
20681  * Create a new CheckBox
20682  * @param {Object} config The config object
20683  */
20684
20685 Roo.bootstrap.CheckBox = function(config){
20686     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20687    
20688     this.addEvents({
20689         /**
20690         * @event check
20691         * Fires when the element is checked or unchecked.
20692         * @param {Roo.bootstrap.CheckBox} this This input
20693         * @param {Boolean} checked The new checked value
20694         */
20695        check : true,
20696        /**
20697         * @event click
20698         * Fires when the element is click.
20699         * @param {Roo.bootstrap.CheckBox} this This input
20700         */
20701        click : true
20702     });
20703     
20704 };
20705
20706 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20707   
20708     inputType: 'checkbox',
20709     inputValue: 1,
20710     valueOff: 0,
20711     boxLabel: false,
20712     checked: false,
20713     weight : false,
20714     inline: false,
20715     tooltip : '',
20716     
20717     getAutoCreate : function()
20718     {
20719         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20720         
20721         var id = Roo.id();
20722         
20723         var cfg = {};
20724         
20725         cfg.cls = 'form-group ' + this.inputType; //input-group
20726         
20727         if(this.inline){
20728             cfg.cls += ' ' + this.inputType + '-inline';
20729         }
20730         
20731         var input =  {
20732             tag: 'input',
20733             id : id,
20734             type : this.inputType,
20735             value : this.inputValue,
20736             cls : 'roo-' + this.inputType, //'form-box',
20737             placeholder : this.placeholder || ''
20738             
20739         };
20740         
20741         if(this.inputType != 'radio'){
20742             var hidden =  {
20743                 tag: 'input',
20744                 type : 'hidden',
20745                 cls : 'roo-hidden-value',
20746                 value : this.checked ? this.inputValue : this.valueOff
20747             };
20748         }
20749         
20750             
20751         if (this.weight) { // Validity check?
20752             cfg.cls += " " + this.inputType + "-" + this.weight;
20753         }
20754         
20755         if (this.disabled) {
20756             input.disabled=true;
20757         }
20758         
20759         if(this.checked){
20760             input.checked = this.checked;
20761         }
20762         
20763         if (this.name) {
20764             
20765             input.name = this.name;
20766             
20767             if(this.inputType != 'radio'){
20768                 hidden.name = this.name;
20769                 input.name = '_hidden_' + this.name;
20770             }
20771         }
20772         
20773         if (this.size) {
20774             input.cls += ' input-' + this.size;
20775         }
20776         
20777         var settings=this;
20778         
20779         ['xs','sm','md','lg'].map(function(size){
20780             if (settings[size]) {
20781                 cfg.cls += ' col-' + size + '-' + settings[size];
20782             }
20783         });
20784         
20785         var inputblock = input;
20786          
20787         if (this.before || this.after) {
20788             
20789             inputblock = {
20790                 cls : 'input-group',
20791                 cn :  [] 
20792             };
20793             
20794             if (this.before) {
20795                 inputblock.cn.push({
20796                     tag :'span',
20797                     cls : 'input-group-addon',
20798                     html : this.before
20799                 });
20800             }
20801             
20802             inputblock.cn.push(input);
20803             
20804             if(this.inputType != 'radio'){
20805                 inputblock.cn.push(hidden);
20806             }
20807             
20808             if (this.after) {
20809                 inputblock.cn.push({
20810                     tag :'span',
20811                     cls : 'input-group-addon',
20812                     html : this.after
20813                 });
20814             }
20815             
20816         }
20817         
20818         if (align ==='left' && this.fieldLabel.length) {
20819 //                Roo.log("left and has label");
20820             cfg.cn = [
20821                 {
20822                     tag: 'label',
20823                     'for' :  id,
20824                     cls : 'control-label',
20825                     html : this.fieldLabel
20826                 },
20827                 {
20828                     cls : "", 
20829                     cn: [
20830                         inputblock
20831                     ]
20832                 }
20833             ];
20834             
20835             if(this.labelWidth > 12){
20836                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20837             }
20838             
20839             if(this.labelWidth < 13 && this.labelmd == 0){
20840                 this.labelmd = this.labelWidth;
20841             }
20842             
20843             if(this.labellg > 0){
20844                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20845                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20846             }
20847             
20848             if(this.labelmd > 0){
20849                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20850                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20851             }
20852             
20853             if(this.labelsm > 0){
20854                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20855                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20856             }
20857             
20858             if(this.labelxs > 0){
20859                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20860                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20861             }
20862             
20863         } else if ( this.fieldLabel.length) {
20864 //                Roo.log(" label");
20865                 cfg.cn = [
20866                    
20867                     {
20868                         tag: this.boxLabel ? 'span' : 'label',
20869                         'for': id,
20870                         cls: 'control-label box-input-label',
20871                         //cls : 'input-group-addon',
20872                         html : this.fieldLabel
20873                     },
20874                     
20875                     inputblock
20876                     
20877                 ];
20878
20879         } else {
20880             
20881 //                Roo.log(" no label && no align");
20882                 cfg.cn = [  inputblock ] ;
20883                 
20884                 
20885         }
20886         
20887         if(this.boxLabel){
20888              var boxLabelCfg = {
20889                 tag: 'label',
20890                 //'for': id, // box label is handled by onclick - so no for...
20891                 cls: 'box-label',
20892                 html: this.boxLabel
20893             };
20894             
20895             if(this.tooltip){
20896                 boxLabelCfg.tooltip = this.tooltip;
20897             }
20898              
20899             cfg.cn.push(boxLabelCfg);
20900         }
20901         
20902         if(this.inputType != 'radio'){
20903             cfg.cn.push(hidden);
20904         }
20905         
20906         return cfg;
20907         
20908     },
20909     
20910     /**
20911      * return the real input element.
20912      */
20913     inputEl: function ()
20914     {
20915         return this.el.select('input.roo-' + this.inputType,true).first();
20916     },
20917     hiddenEl: function ()
20918     {
20919         return this.el.select('input.roo-hidden-value',true).first();
20920     },
20921     
20922     labelEl: function()
20923     {
20924         return this.el.select('label.control-label',true).first();
20925     },
20926     /* depricated... */
20927     
20928     label: function()
20929     {
20930         return this.labelEl();
20931     },
20932     
20933     boxLabelEl: function()
20934     {
20935         return this.el.select('label.box-label',true).first();
20936     },
20937     
20938     initEvents : function()
20939     {
20940 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20941         
20942         this.inputEl().on('click', this.onClick,  this);
20943         
20944         if (this.boxLabel) { 
20945             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20946         }
20947         
20948         this.startValue = this.getValue();
20949         
20950         if(this.groupId){
20951             Roo.bootstrap.CheckBox.register(this);
20952         }
20953     },
20954     
20955     onClick : function(e)
20956     {   
20957         if(this.fireEvent('click', this, e) !== false){
20958             this.setChecked(!this.checked);
20959         }
20960         
20961     },
20962     
20963     setChecked : function(state,suppressEvent)
20964     {
20965         this.startValue = this.getValue();
20966
20967         if(this.inputType == 'radio'){
20968             
20969             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20970                 e.dom.checked = false;
20971             });
20972             
20973             this.inputEl().dom.checked = true;
20974             
20975             this.inputEl().dom.value = this.inputValue;
20976             
20977             if(suppressEvent !== true){
20978                 this.fireEvent('check', this, true);
20979             }
20980             
20981             this.validate();
20982             
20983             return;
20984         }
20985         
20986         this.checked = state;
20987         
20988         this.inputEl().dom.checked = state;
20989         
20990         
20991         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20992         
20993         if(suppressEvent !== true){
20994             this.fireEvent('check', this, state);
20995         }
20996         
20997         this.validate();
20998     },
20999     
21000     getValue : function()
21001     {
21002         if(this.inputType == 'radio'){
21003             return this.getGroupValue();
21004         }
21005         
21006         return this.hiddenEl().dom.value;
21007         
21008     },
21009     
21010     getGroupValue : function()
21011     {
21012         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21013             return '';
21014         }
21015         
21016         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21017     },
21018     
21019     setValue : function(v,suppressEvent)
21020     {
21021         if(this.inputType == 'radio'){
21022             this.setGroupValue(v, suppressEvent);
21023             return;
21024         }
21025         
21026         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21027         
21028         this.validate();
21029     },
21030     
21031     setGroupValue : function(v, suppressEvent)
21032     {
21033         this.startValue = this.getValue();
21034         
21035         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21036             e.dom.checked = false;
21037             
21038             if(e.dom.value == v){
21039                 e.dom.checked = true;
21040             }
21041         });
21042         
21043         if(suppressEvent !== true){
21044             this.fireEvent('check', this, true);
21045         }
21046
21047         this.validate();
21048         
21049         return;
21050     },
21051     
21052     validate : function()
21053     {
21054         if(this.getVisibilityEl().hasClass('hidden')){
21055             return true;
21056         }
21057         
21058         if(
21059                 this.disabled || 
21060                 (this.inputType == 'radio' && this.validateRadio()) ||
21061                 (this.inputType == 'checkbox' && this.validateCheckbox())
21062         ){
21063             this.markValid();
21064             return true;
21065         }
21066         
21067         this.markInvalid();
21068         return false;
21069     },
21070     
21071     validateRadio : function()
21072     {
21073         if(this.getVisibilityEl().hasClass('hidden')){
21074             return true;
21075         }
21076         
21077         if(this.allowBlank){
21078             return true;
21079         }
21080         
21081         var valid = false;
21082         
21083         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21084             if(!e.dom.checked){
21085                 return;
21086             }
21087             
21088             valid = true;
21089             
21090             return false;
21091         });
21092         
21093         return valid;
21094     },
21095     
21096     validateCheckbox : function()
21097     {
21098         if(!this.groupId){
21099             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21100             //return (this.getValue() == this.inputValue) ? true : false;
21101         }
21102         
21103         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21104         
21105         if(!group){
21106             return false;
21107         }
21108         
21109         var r = false;
21110         
21111         for(var i in group){
21112             if(group[i].el.isVisible(true)){
21113                 r = false;
21114                 break;
21115             }
21116             
21117             r = true;
21118         }
21119         
21120         for(var i in group){
21121             if(r){
21122                 break;
21123             }
21124             
21125             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21126         }
21127         
21128         return r;
21129     },
21130     
21131     /**
21132      * Mark this field as valid
21133      */
21134     markValid : function()
21135     {
21136         var _this = this;
21137         
21138         this.fireEvent('valid', this);
21139         
21140         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21141         
21142         if(this.groupId){
21143             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21144         }
21145         
21146         if(label){
21147             label.markValid();
21148         }
21149
21150         if(this.inputType == 'radio'){
21151             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21152                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21153                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21154             });
21155             
21156             return;
21157         }
21158
21159         if(!this.groupId){
21160             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21161             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21162             return;
21163         }
21164         
21165         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21166         
21167         if(!group){
21168             return;
21169         }
21170         
21171         for(var i in group){
21172             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21173             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21174         }
21175     },
21176     
21177      /**
21178      * Mark this field as invalid
21179      * @param {String} msg The validation message
21180      */
21181     markInvalid : function(msg)
21182     {
21183         if(this.allowBlank){
21184             return;
21185         }
21186         
21187         var _this = this;
21188         
21189         this.fireEvent('invalid', this, msg);
21190         
21191         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21192         
21193         if(this.groupId){
21194             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21195         }
21196         
21197         if(label){
21198             label.markInvalid();
21199         }
21200             
21201         if(this.inputType == 'radio'){
21202             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21203                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21204                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21205             });
21206             
21207             return;
21208         }
21209         
21210         if(!this.groupId){
21211             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21212             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21213             return;
21214         }
21215         
21216         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21217         
21218         if(!group){
21219             return;
21220         }
21221         
21222         for(var i in group){
21223             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21224             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21225         }
21226         
21227     },
21228     
21229     clearInvalid : function()
21230     {
21231         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21232         
21233         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21234         
21235         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21236         
21237         if (label && label.iconEl) {
21238             label.iconEl.removeClass(label.validClass);
21239             label.iconEl.removeClass(label.invalidClass);
21240         }
21241     },
21242     
21243     disable : function()
21244     {
21245         if(this.inputType != 'radio'){
21246             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21247             return;
21248         }
21249         
21250         var _this = this;
21251         
21252         if(this.rendered){
21253             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21254                 _this.getActionEl().addClass(this.disabledClass);
21255                 e.dom.disabled = true;
21256             });
21257         }
21258         
21259         this.disabled = true;
21260         this.fireEvent("disable", this);
21261         return this;
21262     },
21263
21264     enable : function()
21265     {
21266         if(this.inputType != 'radio'){
21267             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21268             return;
21269         }
21270         
21271         var _this = this;
21272         
21273         if(this.rendered){
21274             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21275                 _this.getActionEl().removeClass(this.disabledClass);
21276                 e.dom.disabled = false;
21277             });
21278         }
21279         
21280         this.disabled = false;
21281         this.fireEvent("enable", this);
21282         return this;
21283     },
21284     
21285     setBoxLabel : function(v)
21286     {
21287         this.boxLabel = v;
21288         
21289         if(this.rendered){
21290             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21291         }
21292     }
21293
21294 });
21295
21296 Roo.apply(Roo.bootstrap.CheckBox, {
21297     
21298     groups: {},
21299     
21300      /**
21301     * register a CheckBox Group
21302     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21303     */
21304     register : function(checkbox)
21305     {
21306         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21307             this.groups[checkbox.groupId] = {};
21308         }
21309         
21310         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21311             return;
21312         }
21313         
21314         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21315         
21316     },
21317     /**
21318     * fetch a CheckBox Group based on the group ID
21319     * @param {string} the group ID
21320     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21321     */
21322     get: function(groupId) {
21323         if (typeof(this.groups[groupId]) == 'undefined') {
21324             return false;
21325         }
21326         
21327         return this.groups[groupId] ;
21328     }
21329     
21330     
21331 });
21332 /*
21333  * - LGPL
21334  *
21335  * RadioItem
21336  * 
21337  */
21338
21339 /**
21340  * @class Roo.bootstrap.Radio
21341  * @extends Roo.bootstrap.Component
21342  * Bootstrap Radio class
21343  * @cfg {String} boxLabel - the label associated
21344  * @cfg {String} value - the value of radio
21345  * 
21346  * @constructor
21347  * Create a new Radio
21348  * @param {Object} config The config object
21349  */
21350 Roo.bootstrap.Radio = function(config){
21351     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21352     
21353 };
21354
21355 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21356     
21357     boxLabel : '',
21358     
21359     value : '',
21360     
21361     getAutoCreate : function()
21362     {
21363         var cfg = {
21364             tag : 'div',
21365             cls : 'form-group radio',
21366             cn : [
21367                 {
21368                     tag : 'label',
21369                     cls : 'box-label',
21370                     html : this.boxLabel
21371                 }
21372             ]
21373         };
21374         
21375         return cfg;
21376     },
21377     
21378     initEvents : function() 
21379     {
21380         this.parent().register(this);
21381         
21382         this.el.on('click', this.onClick, this);
21383         
21384     },
21385     
21386     onClick : function(e)
21387     {
21388         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21389             this.setChecked(true);
21390         }
21391     },
21392     
21393     setChecked : function(state, suppressEvent)
21394     {
21395         this.parent().setValue(this.value, suppressEvent);
21396         
21397     },
21398     
21399     setBoxLabel : function(v)
21400     {
21401         this.boxLabel = v;
21402         
21403         if(this.rendered){
21404             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21405         }
21406     }
21407     
21408 });
21409  
21410
21411  /*
21412  * - LGPL
21413  *
21414  * Input
21415  * 
21416  */
21417
21418 /**
21419  * @class Roo.bootstrap.SecurePass
21420  * @extends Roo.bootstrap.Input
21421  * Bootstrap SecurePass class
21422  *
21423  * 
21424  * @constructor
21425  * Create a new SecurePass
21426  * @param {Object} config The config object
21427  */
21428  
21429 Roo.bootstrap.SecurePass = function (config) {
21430     // these go here, so the translation tool can replace them..
21431     this.errors = {
21432         PwdEmpty: "Please type a password, and then retype it to confirm.",
21433         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21434         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21435         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21436         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21437         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21438         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21439         TooWeak: "Your password is Too Weak."
21440     },
21441     this.meterLabel = "Password strength:";
21442     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21443     this.meterClass = [
21444         "roo-password-meter-tooweak", 
21445         "roo-password-meter-weak", 
21446         "roo-password-meter-medium", 
21447         "roo-password-meter-strong", 
21448         "roo-password-meter-grey"
21449     ];
21450     
21451     this.errors = {};
21452     
21453     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21454 }
21455
21456 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21457     /**
21458      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21459      * {
21460      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21461      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21462      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21463      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21464      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21465      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21466      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21467      * })
21468      */
21469     // private
21470     
21471     meterWidth: 300,
21472     errorMsg :'',    
21473     errors: false,
21474     imageRoot: '/',
21475     /**
21476      * @cfg {String/Object} Label for the strength meter (defaults to
21477      * 'Password strength:')
21478      */
21479     // private
21480     meterLabel: '',
21481     /**
21482      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21483      * ['Weak', 'Medium', 'Strong'])
21484      */
21485     // private    
21486     pwdStrengths: false,    
21487     // private
21488     strength: 0,
21489     // private
21490     _lastPwd: null,
21491     // private
21492     kCapitalLetter: 0,
21493     kSmallLetter: 1,
21494     kDigit: 2,
21495     kPunctuation: 3,
21496     
21497     insecure: false,
21498     // private
21499     initEvents: function ()
21500     {
21501         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21502
21503         if (this.el.is('input[type=password]') && Roo.isSafari) {
21504             this.el.on('keydown', this.SafariOnKeyDown, this);
21505         }
21506
21507         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21508     },
21509     // private
21510     onRender: function (ct, position)
21511     {
21512         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21513         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21514         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21515
21516         this.trigger.createChild({
21517                    cn: [
21518                     {
21519                     //id: 'PwdMeter',
21520                     tag: 'div',
21521                     cls: 'roo-password-meter-grey col-xs-12',
21522                     style: {
21523                         //width: 0,
21524                         //width: this.meterWidth + 'px'                                                
21525                         }
21526                     },
21527                     {                            
21528                          cls: 'roo-password-meter-text'                          
21529                     }
21530                 ]            
21531         });
21532
21533          
21534         if (this.hideTrigger) {
21535             this.trigger.setDisplayed(false);
21536         }
21537         this.setSize(this.width || '', this.height || '');
21538     },
21539     // private
21540     onDestroy: function ()
21541     {
21542         if (this.trigger) {
21543             this.trigger.removeAllListeners();
21544             this.trigger.remove();
21545         }
21546         if (this.wrap) {
21547             this.wrap.remove();
21548         }
21549         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21550     },
21551     // private
21552     checkStrength: function ()
21553     {
21554         var pwd = this.inputEl().getValue();
21555         if (pwd == this._lastPwd) {
21556             return;
21557         }
21558
21559         var strength;
21560         if (this.ClientSideStrongPassword(pwd)) {
21561             strength = 3;
21562         } else if (this.ClientSideMediumPassword(pwd)) {
21563             strength = 2;
21564         } else if (this.ClientSideWeakPassword(pwd)) {
21565             strength = 1;
21566         } else {
21567             strength = 0;
21568         }
21569         
21570         Roo.log('strength1: ' + strength);
21571         
21572         //var pm = this.trigger.child('div/div/div').dom;
21573         var pm = this.trigger.child('div/div');
21574         pm.removeClass(this.meterClass);
21575         pm.addClass(this.meterClass[strength]);
21576                 
21577         
21578         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21579                 
21580         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21581         
21582         this._lastPwd = pwd;
21583     },
21584     reset: function ()
21585     {
21586         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21587         
21588         this._lastPwd = '';
21589         
21590         var pm = this.trigger.child('div/div');
21591         pm.removeClass(this.meterClass);
21592         pm.addClass('roo-password-meter-grey');        
21593         
21594         
21595         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21596         
21597         pt.innerHTML = '';
21598         this.inputEl().dom.type='password';
21599     },
21600     // private
21601     validateValue: function (value)
21602     {
21603         
21604         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21605             return false;
21606         }
21607         if (value.length == 0) {
21608             if (this.allowBlank) {
21609                 this.clearInvalid();
21610                 return true;
21611             }
21612
21613             this.markInvalid(this.errors.PwdEmpty);
21614             this.errorMsg = this.errors.PwdEmpty;
21615             return false;
21616         }
21617         
21618         if(this.insecure){
21619             return true;
21620         }
21621         
21622         if ('[\x21-\x7e]*'.match(value)) {
21623             this.markInvalid(this.errors.PwdBadChar);
21624             this.errorMsg = this.errors.PwdBadChar;
21625             return false;
21626         }
21627         if (value.length < 6) {
21628             this.markInvalid(this.errors.PwdShort);
21629             this.errorMsg = this.errors.PwdShort;
21630             return false;
21631         }
21632         if (value.length > 16) {
21633             this.markInvalid(this.errors.PwdLong);
21634             this.errorMsg = this.errors.PwdLong;
21635             return false;
21636         }
21637         var strength;
21638         if (this.ClientSideStrongPassword(value)) {
21639             strength = 3;
21640         } else if (this.ClientSideMediumPassword(value)) {
21641             strength = 2;
21642         } else if (this.ClientSideWeakPassword(value)) {
21643             strength = 1;
21644         } else {
21645             strength = 0;
21646         }
21647
21648         
21649         if (strength < 2) {
21650             //this.markInvalid(this.errors.TooWeak);
21651             this.errorMsg = this.errors.TooWeak;
21652             //return false;
21653         }
21654         
21655         
21656         console.log('strength2: ' + strength);
21657         
21658         //var pm = this.trigger.child('div/div/div').dom;
21659         
21660         var pm = this.trigger.child('div/div');
21661         pm.removeClass(this.meterClass);
21662         pm.addClass(this.meterClass[strength]);
21663                 
21664         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21665                 
21666         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21667         
21668         this.errorMsg = ''; 
21669         return true;
21670     },
21671     // private
21672     CharacterSetChecks: function (type)
21673     {
21674         this.type = type;
21675         this.fResult = false;
21676     },
21677     // private
21678     isctype: function (character, type)
21679     {
21680         switch (type) {  
21681             case this.kCapitalLetter:
21682                 if (character >= 'A' && character <= 'Z') {
21683                     return true;
21684                 }
21685                 break;
21686             
21687             case this.kSmallLetter:
21688                 if (character >= 'a' && character <= 'z') {
21689                     return true;
21690                 }
21691                 break;
21692             
21693             case this.kDigit:
21694                 if (character >= '0' && character <= '9') {
21695                     return true;
21696                 }
21697                 break;
21698             
21699             case this.kPunctuation:
21700                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21701                     return true;
21702                 }
21703                 break;
21704             
21705             default:
21706                 return false;
21707         }
21708
21709     },
21710     // private
21711     IsLongEnough: function (pwd, size)
21712     {
21713         return !(pwd == null || isNaN(size) || pwd.length < size);
21714     },
21715     // private
21716     SpansEnoughCharacterSets: function (word, nb)
21717     {
21718         if (!this.IsLongEnough(word, nb))
21719         {
21720             return false;
21721         }
21722
21723         var characterSetChecks = new Array(
21724             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21725             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21726         );
21727         
21728         for (var index = 0; index < word.length; ++index) {
21729             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21730                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21731                     characterSetChecks[nCharSet].fResult = true;
21732                     break;
21733                 }
21734             }
21735         }
21736
21737         var nCharSets = 0;
21738         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21739             if (characterSetChecks[nCharSet].fResult) {
21740                 ++nCharSets;
21741             }
21742         }
21743
21744         if (nCharSets < nb) {
21745             return false;
21746         }
21747         return true;
21748     },
21749     // private
21750     ClientSideStrongPassword: function (pwd)
21751     {
21752         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21753     },
21754     // private
21755     ClientSideMediumPassword: function (pwd)
21756     {
21757         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21758     },
21759     // private
21760     ClientSideWeakPassword: function (pwd)
21761     {
21762         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21763     }
21764           
21765 })//<script type="text/javascript">
21766
21767 /*
21768  * Based  Ext JS Library 1.1.1
21769  * Copyright(c) 2006-2007, Ext JS, LLC.
21770  * LGPL
21771  *
21772  */
21773  
21774 /**
21775  * @class Roo.HtmlEditorCore
21776  * @extends Roo.Component
21777  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21778  *
21779  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21780  */
21781
21782 Roo.HtmlEditorCore = function(config){
21783     
21784     
21785     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21786     
21787     
21788     this.addEvents({
21789         /**
21790          * @event initialize
21791          * Fires when the editor is fully initialized (including the iframe)
21792          * @param {Roo.HtmlEditorCore} this
21793          */
21794         initialize: true,
21795         /**
21796          * @event activate
21797          * Fires when the editor is first receives the focus. Any insertion must wait
21798          * until after this event.
21799          * @param {Roo.HtmlEditorCore} this
21800          */
21801         activate: true,
21802          /**
21803          * @event beforesync
21804          * Fires before the textarea is updated with content from the editor iframe. Return false
21805          * to cancel the sync.
21806          * @param {Roo.HtmlEditorCore} this
21807          * @param {String} html
21808          */
21809         beforesync: true,
21810          /**
21811          * @event beforepush
21812          * Fires before the iframe editor is updated with content from the textarea. Return false
21813          * to cancel the push.
21814          * @param {Roo.HtmlEditorCore} this
21815          * @param {String} html
21816          */
21817         beforepush: true,
21818          /**
21819          * @event sync
21820          * Fires when the textarea is updated with content from the editor iframe.
21821          * @param {Roo.HtmlEditorCore} this
21822          * @param {String} html
21823          */
21824         sync: true,
21825          /**
21826          * @event push
21827          * Fires when the iframe editor is updated with content from the textarea.
21828          * @param {Roo.HtmlEditorCore} this
21829          * @param {String} html
21830          */
21831         push: true,
21832         
21833         /**
21834          * @event editorevent
21835          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21836          * @param {Roo.HtmlEditorCore} this
21837          */
21838         editorevent: true
21839         
21840     });
21841     
21842     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21843     
21844     // defaults : white / black...
21845     this.applyBlacklists();
21846     
21847     
21848     
21849 };
21850
21851
21852 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21853
21854
21855      /**
21856      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21857      */
21858     
21859     owner : false,
21860     
21861      /**
21862      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21863      *                        Roo.resizable.
21864      */
21865     resizable : false,
21866      /**
21867      * @cfg {Number} height (in pixels)
21868      */   
21869     height: 300,
21870    /**
21871      * @cfg {Number} width (in pixels)
21872      */   
21873     width: 500,
21874     
21875     /**
21876      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21877      * 
21878      */
21879     stylesheets: false,
21880     
21881     // id of frame..
21882     frameId: false,
21883     
21884     // private properties
21885     validationEvent : false,
21886     deferHeight: true,
21887     initialized : false,
21888     activated : false,
21889     sourceEditMode : false,
21890     onFocus : Roo.emptyFn,
21891     iframePad:3,
21892     hideMode:'offsets',
21893     
21894     clearUp: true,
21895     
21896     // blacklist + whitelisted elements..
21897     black: false,
21898     white: false,
21899      
21900     bodyCls : '',
21901
21902     /**
21903      * Protected method that will not generally be called directly. It
21904      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21905      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21906      */
21907     getDocMarkup : function(){
21908         // body styles..
21909         var st = '';
21910         
21911         // inherit styels from page...?? 
21912         if (this.stylesheets === false) {
21913             
21914             Roo.get(document.head).select('style').each(function(node) {
21915                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21916             });
21917             
21918             Roo.get(document.head).select('link').each(function(node) { 
21919                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21920             });
21921             
21922         } else if (!this.stylesheets.length) {
21923                 // simple..
21924                 st = '<style type="text/css">' +
21925                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21926                    '</style>';
21927         } else { 
21928             st = '<style type="text/css">' +
21929                     this.stylesheets +
21930                 '</style>';
21931         }
21932         
21933         st +=  '<style type="text/css">' +
21934             'IMG { cursor: pointer } ' +
21935         '</style>';
21936
21937         var cls = 'roo-htmleditor-body';
21938         
21939         if(this.bodyCls.length){
21940             cls += ' ' + this.bodyCls;
21941         }
21942         
21943         return '<html><head>' + st  +
21944             //<style type="text/css">' +
21945             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21946             //'</style>' +
21947             ' </head><body class="' +  cls + '"></body></html>';
21948     },
21949
21950     // private
21951     onRender : function(ct, position)
21952     {
21953         var _t = this;
21954         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21955         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21956         
21957         
21958         this.el.dom.style.border = '0 none';
21959         this.el.dom.setAttribute('tabIndex', -1);
21960         this.el.addClass('x-hidden hide');
21961         
21962         
21963         
21964         if(Roo.isIE){ // fix IE 1px bogus margin
21965             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21966         }
21967        
21968         
21969         this.frameId = Roo.id();
21970         
21971          
21972         
21973         var iframe = this.owner.wrap.createChild({
21974             tag: 'iframe',
21975             cls: 'form-control', // bootstrap..
21976             id: this.frameId,
21977             name: this.frameId,
21978             frameBorder : 'no',
21979             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21980         }, this.el
21981         );
21982         
21983         
21984         this.iframe = iframe.dom;
21985
21986          this.assignDocWin();
21987         
21988         this.doc.designMode = 'on';
21989        
21990         this.doc.open();
21991         this.doc.write(this.getDocMarkup());
21992         this.doc.close();
21993
21994         
21995         var task = { // must defer to wait for browser to be ready
21996             run : function(){
21997                 //console.log("run task?" + this.doc.readyState);
21998                 this.assignDocWin();
21999                 if(this.doc.body || this.doc.readyState == 'complete'){
22000                     try {
22001                         this.doc.designMode="on";
22002                     } catch (e) {
22003                         return;
22004                     }
22005                     Roo.TaskMgr.stop(task);
22006                     this.initEditor.defer(10, this);
22007                 }
22008             },
22009             interval : 10,
22010             duration: 10000,
22011             scope: this
22012         };
22013         Roo.TaskMgr.start(task);
22014
22015     },
22016
22017     // private
22018     onResize : function(w, h)
22019     {
22020          Roo.log('resize: ' +w + ',' + h );
22021         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22022         if(!this.iframe){
22023             return;
22024         }
22025         if(typeof w == 'number'){
22026             
22027             this.iframe.style.width = w + 'px';
22028         }
22029         if(typeof h == 'number'){
22030             
22031             this.iframe.style.height = h + 'px';
22032             if(this.doc){
22033                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22034             }
22035         }
22036         
22037     },
22038
22039     /**
22040      * Toggles the editor between standard and source edit mode.
22041      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22042      */
22043     toggleSourceEdit : function(sourceEditMode){
22044         
22045         this.sourceEditMode = sourceEditMode === true;
22046         
22047         if(this.sourceEditMode){
22048  
22049             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22050             
22051         }else{
22052             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22053             //this.iframe.className = '';
22054             this.deferFocus();
22055         }
22056         //this.setSize(this.owner.wrap.getSize());
22057         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22058     },
22059
22060     
22061   
22062
22063     /**
22064      * Protected method that will not generally be called directly. If you need/want
22065      * custom HTML cleanup, this is the method you should override.
22066      * @param {String} html The HTML to be cleaned
22067      * return {String} The cleaned HTML
22068      */
22069     cleanHtml : function(html){
22070         html = String(html);
22071         if(html.length > 5){
22072             if(Roo.isSafari){ // strip safari nonsense
22073                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22074             }
22075         }
22076         if(html == '&nbsp;'){
22077             html = '';
22078         }
22079         return html;
22080     },
22081
22082     /**
22083      * HTML Editor -> Textarea
22084      * Protected method that will not generally be called directly. Syncs the contents
22085      * of the editor iframe with the textarea.
22086      */
22087     syncValue : function(){
22088         if(this.initialized){
22089             var bd = (this.doc.body || this.doc.documentElement);
22090             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22091             var html = bd.innerHTML;
22092             if(Roo.isSafari){
22093                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22094                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22095                 if(m && m[1]){
22096                     html = '<div style="'+m[0]+'">' + html + '</div>';
22097                 }
22098             }
22099             html = this.cleanHtml(html);
22100             // fix up the special chars.. normaly like back quotes in word...
22101             // however we do not want to do this with chinese..
22102             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22103                 var cc = b.charCodeAt();
22104                 if (
22105                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22106                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22107                     (cc >= 0xf900 && cc < 0xfb00 )
22108                 ) {
22109                         return b;
22110                 }
22111                 return "&#"+cc+";" 
22112             });
22113             if(this.owner.fireEvent('beforesync', this, html) !== false){
22114                 this.el.dom.value = html;
22115                 this.owner.fireEvent('sync', this, html);
22116             }
22117         }
22118     },
22119
22120     /**
22121      * Protected method that will not generally be called directly. Pushes the value of the textarea
22122      * into the iframe editor.
22123      */
22124     pushValue : function(){
22125         if(this.initialized){
22126             var v = this.el.dom.value.trim();
22127             
22128 //            if(v.length < 1){
22129 //                v = '&#160;';
22130 //            }
22131             
22132             if(this.owner.fireEvent('beforepush', this, v) !== false){
22133                 var d = (this.doc.body || this.doc.documentElement);
22134                 d.innerHTML = v;
22135                 this.cleanUpPaste();
22136                 this.el.dom.value = d.innerHTML;
22137                 this.owner.fireEvent('push', this, v);
22138             }
22139         }
22140     },
22141
22142     // private
22143     deferFocus : function(){
22144         this.focus.defer(10, this);
22145     },
22146
22147     // doc'ed in Field
22148     focus : function(){
22149         if(this.win && !this.sourceEditMode){
22150             this.win.focus();
22151         }else{
22152             this.el.focus();
22153         }
22154     },
22155     
22156     assignDocWin: function()
22157     {
22158         var iframe = this.iframe;
22159         
22160          if(Roo.isIE){
22161             this.doc = iframe.contentWindow.document;
22162             this.win = iframe.contentWindow;
22163         } else {
22164 //            if (!Roo.get(this.frameId)) {
22165 //                return;
22166 //            }
22167 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22168 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22169             
22170             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22171                 return;
22172             }
22173             
22174             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22175             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22176         }
22177     },
22178     
22179     // private
22180     initEditor : function(){
22181         //console.log("INIT EDITOR");
22182         this.assignDocWin();
22183         
22184         
22185         
22186         this.doc.designMode="on";
22187         this.doc.open();
22188         this.doc.write(this.getDocMarkup());
22189         this.doc.close();
22190         
22191         var dbody = (this.doc.body || this.doc.documentElement);
22192         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22193         // this copies styles from the containing element into thsi one..
22194         // not sure why we need all of this..
22195         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22196         
22197         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22198         //ss['background-attachment'] = 'fixed'; // w3c
22199         dbody.bgProperties = 'fixed'; // ie
22200         //Roo.DomHelper.applyStyles(dbody, ss);
22201         Roo.EventManager.on(this.doc, {
22202             //'mousedown': this.onEditorEvent,
22203             'mouseup': this.onEditorEvent,
22204             'dblclick': this.onEditorEvent,
22205             'click': this.onEditorEvent,
22206             'keyup': this.onEditorEvent,
22207             buffer:100,
22208             scope: this
22209         });
22210         if(Roo.isGecko){
22211             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22212         }
22213         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22214             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22215         }
22216         this.initialized = true;
22217
22218         this.owner.fireEvent('initialize', this);
22219         this.pushValue();
22220     },
22221
22222     // private
22223     onDestroy : function(){
22224         
22225         
22226         
22227         if(this.rendered){
22228             
22229             //for (var i =0; i < this.toolbars.length;i++) {
22230             //    // fixme - ask toolbars for heights?
22231             //    this.toolbars[i].onDestroy();
22232            // }
22233             
22234             //this.wrap.dom.innerHTML = '';
22235             //this.wrap.remove();
22236         }
22237     },
22238
22239     // private
22240     onFirstFocus : function(){
22241         
22242         this.assignDocWin();
22243         
22244         
22245         this.activated = true;
22246          
22247     
22248         if(Roo.isGecko){ // prevent silly gecko errors
22249             this.win.focus();
22250             var s = this.win.getSelection();
22251             if(!s.focusNode || s.focusNode.nodeType != 3){
22252                 var r = s.getRangeAt(0);
22253                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22254                 r.collapse(true);
22255                 this.deferFocus();
22256             }
22257             try{
22258                 this.execCmd('useCSS', true);
22259                 this.execCmd('styleWithCSS', false);
22260             }catch(e){}
22261         }
22262         this.owner.fireEvent('activate', this);
22263     },
22264
22265     // private
22266     adjustFont: function(btn){
22267         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22268         //if(Roo.isSafari){ // safari
22269         //    adjust *= 2;
22270        // }
22271         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22272         if(Roo.isSafari){ // safari
22273             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22274             v =  (v < 10) ? 10 : v;
22275             v =  (v > 48) ? 48 : v;
22276             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22277             
22278         }
22279         
22280         
22281         v = Math.max(1, v+adjust);
22282         
22283         this.execCmd('FontSize', v  );
22284     },
22285
22286     onEditorEvent : function(e)
22287     {
22288         this.owner.fireEvent('editorevent', this, e);
22289       //  this.updateToolbar();
22290         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22291     },
22292
22293     insertTag : function(tg)
22294     {
22295         // could be a bit smarter... -> wrap the current selected tRoo..
22296         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22297             
22298             range = this.createRange(this.getSelection());
22299             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22300             wrappingNode.appendChild(range.extractContents());
22301             range.insertNode(wrappingNode);
22302
22303             return;
22304             
22305             
22306             
22307         }
22308         this.execCmd("formatblock",   tg);
22309         
22310     },
22311     
22312     insertText : function(txt)
22313     {
22314         
22315         
22316         var range = this.createRange();
22317         range.deleteContents();
22318                //alert(Sender.getAttribute('label'));
22319                
22320         range.insertNode(this.doc.createTextNode(txt));
22321     } ,
22322     
22323      
22324
22325     /**
22326      * Executes a Midas editor command on the editor document and performs necessary focus and
22327      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22328      * @param {String} cmd The Midas command
22329      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22330      */
22331     relayCmd : function(cmd, value){
22332         this.win.focus();
22333         this.execCmd(cmd, value);
22334         this.owner.fireEvent('editorevent', this);
22335         //this.updateToolbar();
22336         this.owner.deferFocus();
22337     },
22338
22339     /**
22340      * Executes a Midas editor command directly on the editor document.
22341      * For visual commands, you should use {@link #relayCmd} instead.
22342      * <b>This should only be called after the editor is initialized.</b>
22343      * @param {String} cmd The Midas command
22344      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22345      */
22346     execCmd : function(cmd, value){
22347         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22348         this.syncValue();
22349     },
22350  
22351  
22352    
22353     /**
22354      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22355      * to insert tRoo.
22356      * @param {String} text | dom node.. 
22357      */
22358     insertAtCursor : function(text)
22359     {
22360         
22361         if(!this.activated){
22362             return;
22363         }
22364         /*
22365         if(Roo.isIE){
22366             this.win.focus();
22367             var r = this.doc.selection.createRange();
22368             if(r){
22369                 r.collapse(true);
22370                 r.pasteHTML(text);
22371                 this.syncValue();
22372                 this.deferFocus();
22373             
22374             }
22375             return;
22376         }
22377         */
22378         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22379             this.win.focus();
22380             
22381             
22382             // from jquery ui (MIT licenced)
22383             var range, node;
22384             var win = this.win;
22385             
22386             if (win.getSelection && win.getSelection().getRangeAt) {
22387                 range = win.getSelection().getRangeAt(0);
22388                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22389                 range.insertNode(node);
22390             } else if (win.document.selection && win.document.selection.createRange) {
22391                 // no firefox support
22392                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22393                 win.document.selection.createRange().pasteHTML(txt);
22394             } else {
22395                 // no firefox support
22396                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22397                 this.execCmd('InsertHTML', txt);
22398             } 
22399             
22400             this.syncValue();
22401             
22402             this.deferFocus();
22403         }
22404     },
22405  // private
22406     mozKeyPress : function(e){
22407         if(e.ctrlKey){
22408             var c = e.getCharCode(), cmd;
22409           
22410             if(c > 0){
22411                 c = String.fromCharCode(c).toLowerCase();
22412                 switch(c){
22413                     case 'b':
22414                         cmd = 'bold';
22415                         break;
22416                     case 'i':
22417                         cmd = 'italic';
22418                         break;
22419                     
22420                     case 'u':
22421                         cmd = 'underline';
22422                         break;
22423                     
22424                     case 'v':
22425                         this.cleanUpPaste.defer(100, this);
22426                         return;
22427                         
22428                 }
22429                 if(cmd){
22430                     this.win.focus();
22431                     this.execCmd(cmd);
22432                     this.deferFocus();
22433                     e.preventDefault();
22434                 }
22435                 
22436             }
22437         }
22438     },
22439
22440     // private
22441     fixKeys : function(){ // load time branching for fastest keydown performance
22442         if(Roo.isIE){
22443             return function(e){
22444                 var k = e.getKey(), r;
22445                 if(k == e.TAB){
22446                     e.stopEvent();
22447                     r = this.doc.selection.createRange();
22448                     if(r){
22449                         r.collapse(true);
22450                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22451                         this.deferFocus();
22452                     }
22453                     return;
22454                 }
22455                 
22456                 if(k == e.ENTER){
22457                     r = this.doc.selection.createRange();
22458                     if(r){
22459                         var target = r.parentElement();
22460                         if(!target || target.tagName.toLowerCase() != 'li'){
22461                             e.stopEvent();
22462                             r.pasteHTML('<br />');
22463                             r.collapse(false);
22464                             r.select();
22465                         }
22466                     }
22467                 }
22468                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22469                     this.cleanUpPaste.defer(100, this);
22470                     return;
22471                 }
22472                 
22473                 
22474             };
22475         }else if(Roo.isOpera){
22476             return function(e){
22477                 var k = e.getKey();
22478                 if(k == e.TAB){
22479                     e.stopEvent();
22480                     this.win.focus();
22481                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22482                     this.deferFocus();
22483                 }
22484                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22485                     this.cleanUpPaste.defer(100, this);
22486                     return;
22487                 }
22488                 
22489             };
22490         }else if(Roo.isSafari){
22491             return function(e){
22492                 var k = e.getKey();
22493                 
22494                 if(k == e.TAB){
22495                     e.stopEvent();
22496                     this.execCmd('InsertText','\t');
22497                     this.deferFocus();
22498                     return;
22499                 }
22500                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22501                     this.cleanUpPaste.defer(100, this);
22502                     return;
22503                 }
22504                 
22505              };
22506         }
22507     }(),
22508     
22509     getAllAncestors: function()
22510     {
22511         var p = this.getSelectedNode();
22512         var a = [];
22513         if (!p) {
22514             a.push(p); // push blank onto stack..
22515             p = this.getParentElement();
22516         }
22517         
22518         
22519         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22520             a.push(p);
22521             p = p.parentNode;
22522         }
22523         a.push(this.doc.body);
22524         return a;
22525     },
22526     lastSel : false,
22527     lastSelNode : false,
22528     
22529     
22530     getSelection : function() 
22531     {
22532         this.assignDocWin();
22533         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22534     },
22535     
22536     getSelectedNode: function() 
22537     {
22538         // this may only work on Gecko!!!
22539         
22540         // should we cache this!!!!
22541         
22542         
22543         
22544          
22545         var range = this.createRange(this.getSelection()).cloneRange();
22546         
22547         if (Roo.isIE) {
22548             var parent = range.parentElement();
22549             while (true) {
22550                 var testRange = range.duplicate();
22551                 testRange.moveToElementText(parent);
22552                 if (testRange.inRange(range)) {
22553                     break;
22554                 }
22555                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22556                     break;
22557                 }
22558                 parent = parent.parentElement;
22559             }
22560             return parent;
22561         }
22562         
22563         // is ancestor a text element.
22564         var ac =  range.commonAncestorContainer;
22565         if (ac.nodeType == 3) {
22566             ac = ac.parentNode;
22567         }
22568         
22569         var ar = ac.childNodes;
22570          
22571         var nodes = [];
22572         var other_nodes = [];
22573         var has_other_nodes = false;
22574         for (var i=0;i<ar.length;i++) {
22575             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22576                 continue;
22577             }
22578             // fullly contained node.
22579             
22580             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22581                 nodes.push(ar[i]);
22582                 continue;
22583             }
22584             
22585             // probably selected..
22586             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22587                 other_nodes.push(ar[i]);
22588                 continue;
22589             }
22590             // outer..
22591             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22592                 continue;
22593             }
22594             
22595             
22596             has_other_nodes = true;
22597         }
22598         if (!nodes.length && other_nodes.length) {
22599             nodes= other_nodes;
22600         }
22601         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22602             return false;
22603         }
22604         
22605         return nodes[0];
22606     },
22607     createRange: function(sel)
22608     {
22609         // this has strange effects when using with 
22610         // top toolbar - not sure if it's a great idea.
22611         //this.editor.contentWindow.focus();
22612         if (typeof sel != "undefined") {
22613             try {
22614                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22615             } catch(e) {
22616                 return this.doc.createRange();
22617             }
22618         } else {
22619             return this.doc.createRange();
22620         }
22621     },
22622     getParentElement: function()
22623     {
22624         
22625         this.assignDocWin();
22626         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22627         
22628         var range = this.createRange(sel);
22629          
22630         try {
22631             var p = range.commonAncestorContainer;
22632             while (p.nodeType == 3) { // text node
22633                 p = p.parentNode;
22634             }
22635             return p;
22636         } catch (e) {
22637             return null;
22638         }
22639     
22640     },
22641     /***
22642      *
22643      * Range intersection.. the hard stuff...
22644      *  '-1' = before
22645      *  '0' = hits..
22646      *  '1' = after.
22647      *         [ -- selected range --- ]
22648      *   [fail]                        [fail]
22649      *
22650      *    basically..
22651      *      if end is before start or  hits it. fail.
22652      *      if start is after end or hits it fail.
22653      *
22654      *   if either hits (but other is outside. - then it's not 
22655      *   
22656      *    
22657      **/
22658     
22659     
22660     // @see http://www.thismuchiknow.co.uk/?p=64.
22661     rangeIntersectsNode : function(range, node)
22662     {
22663         var nodeRange = node.ownerDocument.createRange();
22664         try {
22665             nodeRange.selectNode(node);
22666         } catch (e) {
22667             nodeRange.selectNodeContents(node);
22668         }
22669     
22670         var rangeStartRange = range.cloneRange();
22671         rangeStartRange.collapse(true);
22672     
22673         var rangeEndRange = range.cloneRange();
22674         rangeEndRange.collapse(false);
22675     
22676         var nodeStartRange = nodeRange.cloneRange();
22677         nodeStartRange.collapse(true);
22678     
22679         var nodeEndRange = nodeRange.cloneRange();
22680         nodeEndRange.collapse(false);
22681     
22682         return rangeStartRange.compareBoundaryPoints(
22683                  Range.START_TO_START, nodeEndRange) == -1 &&
22684                rangeEndRange.compareBoundaryPoints(
22685                  Range.START_TO_START, nodeStartRange) == 1;
22686         
22687          
22688     },
22689     rangeCompareNode : function(range, node)
22690     {
22691         var nodeRange = node.ownerDocument.createRange();
22692         try {
22693             nodeRange.selectNode(node);
22694         } catch (e) {
22695             nodeRange.selectNodeContents(node);
22696         }
22697         
22698         
22699         range.collapse(true);
22700     
22701         nodeRange.collapse(true);
22702      
22703         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22704         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22705          
22706         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22707         
22708         var nodeIsBefore   =  ss == 1;
22709         var nodeIsAfter    = ee == -1;
22710         
22711         if (nodeIsBefore && nodeIsAfter) {
22712             return 0; // outer
22713         }
22714         if (!nodeIsBefore && nodeIsAfter) {
22715             return 1; //right trailed.
22716         }
22717         
22718         if (nodeIsBefore && !nodeIsAfter) {
22719             return 2;  // left trailed.
22720         }
22721         // fully contined.
22722         return 3;
22723     },
22724
22725     // private? - in a new class?
22726     cleanUpPaste :  function()
22727     {
22728         // cleans up the whole document..
22729         Roo.log('cleanuppaste');
22730         
22731         this.cleanUpChildren(this.doc.body);
22732         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22733         if (clean != this.doc.body.innerHTML) {
22734             this.doc.body.innerHTML = clean;
22735         }
22736         
22737     },
22738     
22739     cleanWordChars : function(input) {// change the chars to hex code
22740         var he = Roo.HtmlEditorCore;
22741         
22742         var output = input;
22743         Roo.each(he.swapCodes, function(sw) { 
22744             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22745             
22746             output = output.replace(swapper, sw[1]);
22747         });
22748         
22749         return output;
22750     },
22751     
22752     
22753     cleanUpChildren : function (n)
22754     {
22755         if (!n.childNodes.length) {
22756             return;
22757         }
22758         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22759            this.cleanUpChild(n.childNodes[i]);
22760         }
22761     },
22762     
22763     
22764         
22765     
22766     cleanUpChild : function (node)
22767     {
22768         var ed = this;
22769         //console.log(node);
22770         if (node.nodeName == "#text") {
22771             // clean up silly Windows -- stuff?
22772             return; 
22773         }
22774         if (node.nodeName == "#comment") {
22775             node.parentNode.removeChild(node);
22776             // clean up silly Windows -- stuff?
22777             return; 
22778         }
22779         var lcname = node.tagName.toLowerCase();
22780         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22781         // whitelist of tags..
22782         
22783         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22784             // remove node.
22785             node.parentNode.removeChild(node);
22786             return;
22787             
22788         }
22789         
22790         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22791         
22792         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22793         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22794         
22795         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22796         //    remove_keep_children = true;
22797         //}
22798         
22799         if (remove_keep_children) {
22800             this.cleanUpChildren(node);
22801             // inserts everything just before this node...
22802             while (node.childNodes.length) {
22803                 var cn = node.childNodes[0];
22804                 node.removeChild(cn);
22805                 node.parentNode.insertBefore(cn, node);
22806             }
22807             node.parentNode.removeChild(node);
22808             return;
22809         }
22810         
22811         if (!node.attributes || !node.attributes.length) {
22812             this.cleanUpChildren(node);
22813             return;
22814         }
22815         
22816         function cleanAttr(n,v)
22817         {
22818             
22819             if (v.match(/^\./) || v.match(/^\//)) {
22820                 return;
22821             }
22822             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22823                 return;
22824             }
22825             if (v.match(/^#/)) {
22826                 return;
22827             }
22828 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22829             node.removeAttribute(n);
22830             
22831         }
22832         
22833         var cwhite = this.cwhite;
22834         var cblack = this.cblack;
22835             
22836         function cleanStyle(n,v)
22837         {
22838             if (v.match(/expression/)) { //XSS?? should we even bother..
22839                 node.removeAttribute(n);
22840                 return;
22841             }
22842             
22843             var parts = v.split(/;/);
22844             var clean = [];
22845             
22846             Roo.each(parts, function(p) {
22847                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22848                 if (!p.length) {
22849                     return true;
22850                 }
22851                 var l = p.split(':').shift().replace(/\s+/g,'');
22852                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22853                 
22854                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22855 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22856                     //node.removeAttribute(n);
22857                     return true;
22858                 }
22859                 //Roo.log()
22860                 // only allow 'c whitelisted system attributes'
22861                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22862 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22863                     //node.removeAttribute(n);
22864                     return true;
22865                 }
22866                 
22867                 
22868                  
22869                 
22870                 clean.push(p);
22871                 return true;
22872             });
22873             if (clean.length) { 
22874                 node.setAttribute(n, clean.join(';'));
22875             } else {
22876                 node.removeAttribute(n);
22877             }
22878             
22879         }
22880         
22881         
22882         for (var i = node.attributes.length-1; i > -1 ; i--) {
22883             var a = node.attributes[i];
22884             //console.log(a);
22885             
22886             if (a.name.toLowerCase().substr(0,2)=='on')  {
22887                 node.removeAttribute(a.name);
22888                 continue;
22889             }
22890             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22891                 node.removeAttribute(a.name);
22892                 continue;
22893             }
22894             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22895                 cleanAttr(a.name,a.value); // fixme..
22896                 continue;
22897             }
22898             if (a.name == 'style') {
22899                 cleanStyle(a.name,a.value);
22900                 continue;
22901             }
22902             /// clean up MS crap..
22903             // tecnically this should be a list of valid class'es..
22904             
22905             
22906             if (a.name == 'class') {
22907                 if (a.value.match(/^Mso/)) {
22908                     node.className = '';
22909                 }
22910                 
22911                 if (a.value.match(/^body$/)) {
22912                     node.className = '';
22913                 }
22914                 continue;
22915             }
22916             
22917             // style cleanup!?
22918             // class cleanup?
22919             
22920         }
22921         
22922         
22923         this.cleanUpChildren(node);
22924         
22925         
22926     },
22927     
22928     /**
22929      * Clean up MS wordisms...
22930      */
22931     cleanWord : function(node)
22932     {
22933         
22934         
22935         if (!node) {
22936             this.cleanWord(this.doc.body);
22937             return;
22938         }
22939         if (node.nodeName == "#text") {
22940             // clean up silly Windows -- stuff?
22941             return; 
22942         }
22943         if (node.nodeName == "#comment") {
22944             node.parentNode.removeChild(node);
22945             // clean up silly Windows -- stuff?
22946             return; 
22947         }
22948         
22949         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22950             node.parentNode.removeChild(node);
22951             return;
22952         }
22953         
22954         // remove - but keep children..
22955         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22956             while (node.childNodes.length) {
22957                 var cn = node.childNodes[0];
22958                 node.removeChild(cn);
22959                 node.parentNode.insertBefore(cn, node);
22960             }
22961             node.parentNode.removeChild(node);
22962             this.iterateChildren(node, this.cleanWord);
22963             return;
22964         }
22965         // clean styles
22966         if (node.className.length) {
22967             
22968             var cn = node.className.split(/\W+/);
22969             var cna = [];
22970             Roo.each(cn, function(cls) {
22971                 if (cls.match(/Mso[a-zA-Z]+/)) {
22972                     return;
22973                 }
22974                 cna.push(cls);
22975             });
22976             node.className = cna.length ? cna.join(' ') : '';
22977             if (!cna.length) {
22978                 node.removeAttribute("class");
22979             }
22980         }
22981         
22982         if (node.hasAttribute("lang")) {
22983             node.removeAttribute("lang");
22984         }
22985         
22986         if (node.hasAttribute("style")) {
22987             
22988             var styles = node.getAttribute("style").split(";");
22989             var nstyle = [];
22990             Roo.each(styles, function(s) {
22991                 if (!s.match(/:/)) {
22992                     return;
22993                 }
22994                 var kv = s.split(":");
22995                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22996                     return;
22997                 }
22998                 // what ever is left... we allow.
22999                 nstyle.push(s);
23000             });
23001             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23002             if (!nstyle.length) {
23003                 node.removeAttribute('style');
23004             }
23005         }
23006         this.iterateChildren(node, this.cleanWord);
23007         
23008         
23009         
23010     },
23011     /**
23012      * iterateChildren of a Node, calling fn each time, using this as the scole..
23013      * @param {DomNode} node node to iterate children of.
23014      * @param {Function} fn method of this class to call on each item.
23015      */
23016     iterateChildren : function(node, fn)
23017     {
23018         if (!node.childNodes.length) {
23019                 return;
23020         }
23021         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23022            fn.call(this, node.childNodes[i])
23023         }
23024     },
23025     
23026     
23027     /**
23028      * cleanTableWidths.
23029      *
23030      * Quite often pasting from word etc.. results in tables with column and widths.
23031      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23032      *
23033      */
23034     cleanTableWidths : function(node)
23035     {
23036          
23037          
23038         if (!node) {
23039             this.cleanTableWidths(this.doc.body);
23040             return;
23041         }
23042         
23043         // ignore list...
23044         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23045             return; 
23046         }
23047         Roo.log(node.tagName);
23048         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23049             this.iterateChildren(node, this.cleanTableWidths);
23050             return;
23051         }
23052         if (node.hasAttribute('width')) {
23053             node.removeAttribute('width');
23054         }
23055         
23056          
23057         if (node.hasAttribute("style")) {
23058             // pretty basic...
23059             
23060             var styles = node.getAttribute("style").split(";");
23061             var nstyle = [];
23062             Roo.each(styles, function(s) {
23063                 if (!s.match(/:/)) {
23064                     return;
23065                 }
23066                 var kv = s.split(":");
23067                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23068                     return;
23069                 }
23070                 // what ever is left... we allow.
23071                 nstyle.push(s);
23072             });
23073             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23074             if (!nstyle.length) {
23075                 node.removeAttribute('style');
23076             }
23077         }
23078         
23079         this.iterateChildren(node, this.cleanTableWidths);
23080         
23081         
23082     },
23083     
23084     
23085     
23086     
23087     domToHTML : function(currentElement, depth, nopadtext) {
23088         
23089         depth = depth || 0;
23090         nopadtext = nopadtext || false;
23091     
23092         if (!currentElement) {
23093             return this.domToHTML(this.doc.body);
23094         }
23095         
23096         //Roo.log(currentElement);
23097         var j;
23098         var allText = false;
23099         var nodeName = currentElement.nodeName;
23100         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23101         
23102         if  (nodeName == '#text') {
23103             
23104             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23105         }
23106         
23107         
23108         var ret = '';
23109         if (nodeName != 'BODY') {
23110              
23111             var i = 0;
23112             // Prints the node tagName, such as <A>, <IMG>, etc
23113             if (tagName) {
23114                 var attr = [];
23115                 for(i = 0; i < currentElement.attributes.length;i++) {
23116                     // quoting?
23117                     var aname = currentElement.attributes.item(i).name;
23118                     if (!currentElement.attributes.item(i).value.length) {
23119                         continue;
23120                     }
23121                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23122                 }
23123                 
23124                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23125             } 
23126             else {
23127                 
23128                 // eack
23129             }
23130         } else {
23131             tagName = false;
23132         }
23133         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23134             return ret;
23135         }
23136         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23137             nopadtext = true;
23138         }
23139         
23140         
23141         // Traverse the tree
23142         i = 0;
23143         var currentElementChild = currentElement.childNodes.item(i);
23144         var allText = true;
23145         var innerHTML  = '';
23146         lastnode = '';
23147         while (currentElementChild) {
23148             // Formatting code (indent the tree so it looks nice on the screen)
23149             var nopad = nopadtext;
23150             if (lastnode == 'SPAN') {
23151                 nopad  = true;
23152             }
23153             // text
23154             if  (currentElementChild.nodeName == '#text') {
23155                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23156                 toadd = nopadtext ? toadd : toadd.trim();
23157                 if (!nopad && toadd.length > 80) {
23158                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23159                 }
23160                 innerHTML  += toadd;
23161                 
23162                 i++;
23163                 currentElementChild = currentElement.childNodes.item(i);
23164                 lastNode = '';
23165                 continue;
23166             }
23167             allText = false;
23168             
23169             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23170                 
23171             // Recursively traverse the tree structure of the child node
23172             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23173             lastnode = currentElementChild.nodeName;
23174             i++;
23175             currentElementChild=currentElement.childNodes.item(i);
23176         }
23177         
23178         ret += innerHTML;
23179         
23180         if (!allText) {
23181                 // The remaining code is mostly for formatting the tree
23182             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23183         }
23184         
23185         
23186         if (tagName) {
23187             ret+= "</"+tagName+">";
23188         }
23189         return ret;
23190         
23191     },
23192         
23193     applyBlacklists : function()
23194     {
23195         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23196         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23197         
23198         this.white = [];
23199         this.black = [];
23200         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23201             if (b.indexOf(tag) > -1) {
23202                 return;
23203             }
23204             this.white.push(tag);
23205             
23206         }, this);
23207         
23208         Roo.each(w, function(tag) {
23209             if (b.indexOf(tag) > -1) {
23210                 return;
23211             }
23212             if (this.white.indexOf(tag) > -1) {
23213                 return;
23214             }
23215             this.white.push(tag);
23216             
23217         }, this);
23218         
23219         
23220         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23221             if (w.indexOf(tag) > -1) {
23222                 return;
23223             }
23224             this.black.push(tag);
23225             
23226         }, this);
23227         
23228         Roo.each(b, function(tag) {
23229             if (w.indexOf(tag) > -1) {
23230                 return;
23231             }
23232             if (this.black.indexOf(tag) > -1) {
23233                 return;
23234             }
23235             this.black.push(tag);
23236             
23237         }, this);
23238         
23239         
23240         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23241         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23242         
23243         this.cwhite = [];
23244         this.cblack = [];
23245         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23246             if (b.indexOf(tag) > -1) {
23247                 return;
23248             }
23249             this.cwhite.push(tag);
23250             
23251         }, this);
23252         
23253         Roo.each(w, function(tag) {
23254             if (b.indexOf(tag) > -1) {
23255                 return;
23256             }
23257             if (this.cwhite.indexOf(tag) > -1) {
23258                 return;
23259             }
23260             this.cwhite.push(tag);
23261             
23262         }, this);
23263         
23264         
23265         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23266             if (w.indexOf(tag) > -1) {
23267                 return;
23268             }
23269             this.cblack.push(tag);
23270             
23271         }, this);
23272         
23273         Roo.each(b, function(tag) {
23274             if (w.indexOf(tag) > -1) {
23275                 return;
23276             }
23277             if (this.cblack.indexOf(tag) > -1) {
23278                 return;
23279             }
23280             this.cblack.push(tag);
23281             
23282         }, this);
23283     },
23284     
23285     setStylesheets : function(stylesheets)
23286     {
23287         if(typeof(stylesheets) == 'string'){
23288             Roo.get(this.iframe.contentDocument.head).createChild({
23289                 tag : 'link',
23290                 rel : 'stylesheet',
23291                 type : 'text/css',
23292                 href : stylesheets
23293             });
23294             
23295             return;
23296         }
23297         var _this = this;
23298      
23299         Roo.each(stylesheets, function(s) {
23300             if(!s.length){
23301                 return;
23302             }
23303             
23304             Roo.get(_this.iframe.contentDocument.head).createChild({
23305                 tag : 'link',
23306                 rel : 'stylesheet',
23307                 type : 'text/css',
23308                 href : s
23309             });
23310         });
23311
23312         
23313     },
23314     
23315     removeStylesheets : function()
23316     {
23317         var _this = this;
23318         
23319         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23320             s.remove();
23321         });
23322     },
23323     
23324     setStyle : function(style)
23325     {
23326         Roo.get(this.iframe.contentDocument.head).createChild({
23327             tag : 'style',
23328             type : 'text/css',
23329             html : style
23330         });
23331
23332         return;
23333     }
23334     
23335     // hide stuff that is not compatible
23336     /**
23337      * @event blur
23338      * @hide
23339      */
23340     /**
23341      * @event change
23342      * @hide
23343      */
23344     /**
23345      * @event focus
23346      * @hide
23347      */
23348     /**
23349      * @event specialkey
23350      * @hide
23351      */
23352     /**
23353      * @cfg {String} fieldClass @hide
23354      */
23355     /**
23356      * @cfg {String} focusClass @hide
23357      */
23358     /**
23359      * @cfg {String} autoCreate @hide
23360      */
23361     /**
23362      * @cfg {String} inputType @hide
23363      */
23364     /**
23365      * @cfg {String} invalidClass @hide
23366      */
23367     /**
23368      * @cfg {String} invalidText @hide
23369      */
23370     /**
23371      * @cfg {String} msgFx @hide
23372      */
23373     /**
23374      * @cfg {String} validateOnBlur @hide
23375      */
23376 });
23377
23378 Roo.HtmlEditorCore.white = [
23379         'area', 'br', 'img', 'input', 'hr', 'wbr',
23380         
23381        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23382        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23383        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23384        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23385        'table',   'ul',         'xmp', 
23386        
23387        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23388       'thead',   'tr', 
23389      
23390       'dir', 'menu', 'ol', 'ul', 'dl',
23391        
23392       'embed',  'object'
23393 ];
23394
23395
23396 Roo.HtmlEditorCore.black = [
23397     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23398         'applet', // 
23399         'base',   'basefont', 'bgsound', 'blink',  'body', 
23400         'frame',  'frameset', 'head',    'html',   'ilayer', 
23401         'iframe', 'layer',  'link',     'meta',    'object',   
23402         'script', 'style' ,'title',  'xml' // clean later..
23403 ];
23404 Roo.HtmlEditorCore.clean = [
23405     'script', 'style', 'title', 'xml'
23406 ];
23407 Roo.HtmlEditorCore.remove = [
23408     'font'
23409 ];
23410 // attributes..
23411
23412 Roo.HtmlEditorCore.ablack = [
23413     'on'
23414 ];
23415     
23416 Roo.HtmlEditorCore.aclean = [ 
23417     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23418 ];
23419
23420 // protocols..
23421 Roo.HtmlEditorCore.pwhite= [
23422         'http',  'https',  'mailto'
23423 ];
23424
23425 // white listed style attributes.
23426 Roo.HtmlEditorCore.cwhite= [
23427       //  'text-align', /// default is to allow most things..
23428       
23429          
23430 //        'font-size'//??
23431 ];
23432
23433 // black listed style attributes.
23434 Roo.HtmlEditorCore.cblack= [
23435       //  'font-size' -- this can be set by the project 
23436 ];
23437
23438
23439 Roo.HtmlEditorCore.swapCodes   =[ 
23440     [    8211, "--" ], 
23441     [    8212, "--" ], 
23442     [    8216,  "'" ],  
23443     [    8217, "'" ],  
23444     [    8220, '"' ],  
23445     [    8221, '"' ],  
23446     [    8226, "*" ],  
23447     [    8230, "..." ]
23448 ]; 
23449
23450     /*
23451  * - LGPL
23452  *
23453  * HtmlEditor
23454  * 
23455  */
23456
23457 /**
23458  * @class Roo.bootstrap.HtmlEditor
23459  * @extends Roo.bootstrap.TextArea
23460  * Bootstrap HtmlEditor class
23461
23462  * @constructor
23463  * Create a new HtmlEditor
23464  * @param {Object} config The config object
23465  */
23466
23467 Roo.bootstrap.HtmlEditor = function(config){
23468     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23469     if (!this.toolbars) {
23470         this.toolbars = [];
23471     }
23472     
23473     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23474     this.addEvents({
23475             /**
23476              * @event initialize
23477              * Fires when the editor is fully initialized (including the iframe)
23478              * @param {HtmlEditor} this
23479              */
23480             initialize: true,
23481             /**
23482              * @event activate
23483              * Fires when the editor is first receives the focus. Any insertion must wait
23484              * until after this event.
23485              * @param {HtmlEditor} this
23486              */
23487             activate: true,
23488              /**
23489              * @event beforesync
23490              * Fires before the textarea is updated with content from the editor iframe. Return false
23491              * to cancel the sync.
23492              * @param {HtmlEditor} this
23493              * @param {String} html
23494              */
23495             beforesync: true,
23496              /**
23497              * @event beforepush
23498              * Fires before the iframe editor is updated with content from the textarea. Return false
23499              * to cancel the push.
23500              * @param {HtmlEditor} this
23501              * @param {String} html
23502              */
23503             beforepush: true,
23504              /**
23505              * @event sync
23506              * Fires when the textarea is updated with content from the editor iframe.
23507              * @param {HtmlEditor} this
23508              * @param {String} html
23509              */
23510             sync: true,
23511              /**
23512              * @event push
23513              * Fires when the iframe editor is updated with content from the textarea.
23514              * @param {HtmlEditor} this
23515              * @param {String} html
23516              */
23517             push: true,
23518              /**
23519              * @event editmodechange
23520              * Fires when the editor switches edit modes
23521              * @param {HtmlEditor} this
23522              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23523              */
23524             editmodechange: true,
23525             /**
23526              * @event editorevent
23527              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23528              * @param {HtmlEditor} this
23529              */
23530             editorevent: true,
23531             /**
23532              * @event firstfocus
23533              * Fires when on first focus - needed by toolbars..
23534              * @param {HtmlEditor} this
23535              */
23536             firstfocus: true,
23537             /**
23538              * @event autosave
23539              * Auto save the htmlEditor value as a file into Events
23540              * @param {HtmlEditor} this
23541              */
23542             autosave: true,
23543             /**
23544              * @event savedpreview
23545              * preview the saved version of htmlEditor
23546              * @param {HtmlEditor} this
23547              */
23548             savedpreview: true
23549         });
23550 };
23551
23552
23553 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23554     
23555     
23556       /**
23557      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23558      */
23559     toolbars : false,
23560     
23561      /**
23562     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23563     */
23564     btns : [],
23565    
23566      /**
23567      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23568      *                        Roo.resizable.
23569      */
23570     resizable : false,
23571      /**
23572      * @cfg {Number} height (in pixels)
23573      */   
23574     height: 300,
23575    /**
23576      * @cfg {Number} width (in pixels)
23577      */   
23578     width: false,
23579     
23580     /**
23581      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23582      * 
23583      */
23584     stylesheets: false,
23585     
23586     // id of frame..
23587     frameId: false,
23588     
23589     // private properties
23590     validationEvent : false,
23591     deferHeight: true,
23592     initialized : false,
23593     activated : false,
23594     
23595     onFocus : Roo.emptyFn,
23596     iframePad:3,
23597     hideMode:'offsets',
23598     
23599     tbContainer : false,
23600     
23601     bodyCls : '',
23602     
23603     toolbarContainer :function() {
23604         return this.wrap.select('.x-html-editor-tb',true).first();
23605     },
23606
23607     /**
23608      * Protected method that will not generally be called directly. It
23609      * is called when the editor creates its toolbar. Override this method if you need to
23610      * add custom toolbar buttons.
23611      * @param {HtmlEditor} editor
23612      */
23613     createToolbar : function(){
23614         Roo.log('renewing');
23615         Roo.log("create toolbars");
23616         
23617         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23618         this.toolbars[0].render(this.toolbarContainer());
23619         
23620         return;
23621         
23622 //        if (!editor.toolbars || !editor.toolbars.length) {
23623 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23624 //        }
23625 //        
23626 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23627 //            editor.toolbars[i] = Roo.factory(
23628 //                    typeof(editor.toolbars[i]) == 'string' ?
23629 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23630 //                Roo.bootstrap.HtmlEditor);
23631 //            editor.toolbars[i].init(editor);
23632 //        }
23633     },
23634
23635      
23636     // private
23637     onRender : function(ct, position)
23638     {
23639        // Roo.log("Call onRender: " + this.xtype);
23640         var _t = this;
23641         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23642       
23643         this.wrap = this.inputEl().wrap({
23644             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23645         });
23646         
23647         this.editorcore.onRender(ct, position);
23648          
23649         if (this.resizable) {
23650             this.resizeEl = new Roo.Resizable(this.wrap, {
23651                 pinned : true,
23652                 wrap: true,
23653                 dynamic : true,
23654                 minHeight : this.height,
23655                 height: this.height,
23656                 handles : this.resizable,
23657                 width: this.width,
23658                 listeners : {
23659                     resize : function(r, w, h) {
23660                         _t.onResize(w,h); // -something
23661                     }
23662                 }
23663             });
23664             
23665         }
23666         this.createToolbar(this);
23667        
23668         
23669         if(!this.width && this.resizable){
23670             this.setSize(this.wrap.getSize());
23671         }
23672         if (this.resizeEl) {
23673             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23674             // should trigger onReize..
23675         }
23676         
23677     },
23678
23679     // private
23680     onResize : function(w, h)
23681     {
23682         Roo.log('resize: ' +w + ',' + h );
23683         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23684         var ew = false;
23685         var eh = false;
23686         
23687         if(this.inputEl() ){
23688             if(typeof w == 'number'){
23689                 var aw = w - this.wrap.getFrameWidth('lr');
23690                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23691                 ew = aw;
23692             }
23693             if(typeof h == 'number'){
23694                  var tbh = -11;  // fixme it needs to tool bar size!
23695                 for (var i =0; i < this.toolbars.length;i++) {
23696                     // fixme - ask toolbars for heights?
23697                     tbh += this.toolbars[i].el.getHeight();
23698                     //if (this.toolbars[i].footer) {
23699                     //    tbh += this.toolbars[i].footer.el.getHeight();
23700                     //}
23701                 }
23702               
23703                 
23704                 
23705                 
23706                 
23707                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23708                 ah -= 5; // knock a few pixes off for look..
23709                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23710                 var eh = ah;
23711             }
23712         }
23713         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23714         this.editorcore.onResize(ew,eh);
23715         
23716     },
23717
23718     /**
23719      * Toggles the editor between standard and source edit mode.
23720      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23721      */
23722     toggleSourceEdit : function(sourceEditMode)
23723     {
23724         this.editorcore.toggleSourceEdit(sourceEditMode);
23725         
23726         if(this.editorcore.sourceEditMode){
23727             Roo.log('editor - showing textarea');
23728             
23729 //            Roo.log('in');
23730 //            Roo.log(this.syncValue());
23731             this.syncValue();
23732             this.inputEl().removeClass(['hide', 'x-hidden']);
23733             this.inputEl().dom.removeAttribute('tabIndex');
23734             this.inputEl().focus();
23735         }else{
23736             Roo.log('editor - hiding textarea');
23737 //            Roo.log('out')
23738 //            Roo.log(this.pushValue()); 
23739             this.pushValue();
23740             
23741             this.inputEl().addClass(['hide', 'x-hidden']);
23742             this.inputEl().dom.setAttribute('tabIndex', -1);
23743             //this.deferFocus();
23744         }
23745          
23746         if(this.resizable){
23747             this.setSize(this.wrap.getSize());
23748         }
23749         
23750         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23751     },
23752  
23753     // private (for BoxComponent)
23754     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23755
23756     // private (for BoxComponent)
23757     getResizeEl : function(){
23758         return this.wrap;
23759     },
23760
23761     // private (for BoxComponent)
23762     getPositionEl : function(){
23763         return this.wrap;
23764     },
23765
23766     // private
23767     initEvents : function(){
23768         this.originalValue = this.getValue();
23769     },
23770
23771 //    /**
23772 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23773 //     * @method
23774 //     */
23775 //    markInvalid : Roo.emptyFn,
23776 //    /**
23777 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23778 //     * @method
23779 //     */
23780 //    clearInvalid : Roo.emptyFn,
23781
23782     setValue : function(v){
23783         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23784         this.editorcore.pushValue();
23785     },
23786
23787      
23788     // private
23789     deferFocus : function(){
23790         this.focus.defer(10, this);
23791     },
23792
23793     // doc'ed in Field
23794     focus : function(){
23795         this.editorcore.focus();
23796         
23797     },
23798       
23799
23800     // private
23801     onDestroy : function(){
23802         
23803         
23804         
23805         if(this.rendered){
23806             
23807             for (var i =0; i < this.toolbars.length;i++) {
23808                 // fixme - ask toolbars for heights?
23809                 this.toolbars[i].onDestroy();
23810             }
23811             
23812             this.wrap.dom.innerHTML = '';
23813             this.wrap.remove();
23814         }
23815     },
23816
23817     // private
23818     onFirstFocus : function(){
23819         //Roo.log("onFirstFocus");
23820         this.editorcore.onFirstFocus();
23821          for (var i =0; i < this.toolbars.length;i++) {
23822             this.toolbars[i].onFirstFocus();
23823         }
23824         
23825     },
23826     
23827     // private
23828     syncValue : function()
23829     {   
23830         this.editorcore.syncValue();
23831     },
23832     
23833     pushValue : function()
23834     {   
23835         this.editorcore.pushValue();
23836     }
23837      
23838     
23839     // hide stuff that is not compatible
23840     /**
23841      * @event blur
23842      * @hide
23843      */
23844     /**
23845      * @event change
23846      * @hide
23847      */
23848     /**
23849      * @event focus
23850      * @hide
23851      */
23852     /**
23853      * @event specialkey
23854      * @hide
23855      */
23856     /**
23857      * @cfg {String} fieldClass @hide
23858      */
23859     /**
23860      * @cfg {String} focusClass @hide
23861      */
23862     /**
23863      * @cfg {String} autoCreate @hide
23864      */
23865     /**
23866      * @cfg {String} inputType @hide
23867      */
23868     /**
23869      * @cfg {String} invalidClass @hide
23870      */
23871     /**
23872      * @cfg {String} invalidText @hide
23873      */
23874     /**
23875      * @cfg {String} msgFx @hide
23876      */
23877     /**
23878      * @cfg {String} validateOnBlur @hide
23879      */
23880 });
23881  
23882     
23883    
23884    
23885    
23886       
23887 Roo.namespace('Roo.bootstrap.htmleditor');
23888 /**
23889  * @class Roo.bootstrap.HtmlEditorToolbar1
23890  * Basic Toolbar
23891  * 
23892  * Usage:
23893  *
23894  new Roo.bootstrap.HtmlEditor({
23895     ....
23896     toolbars : [
23897         new Roo.bootstrap.HtmlEditorToolbar1({
23898             disable : { fonts: 1 , format: 1, ..., ... , ...],
23899             btns : [ .... ]
23900         })
23901     }
23902      
23903  * 
23904  * @cfg {Object} disable List of elements to disable..
23905  * @cfg {Array} btns List of additional buttons.
23906  * 
23907  * 
23908  * NEEDS Extra CSS? 
23909  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23910  */
23911  
23912 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23913 {
23914     
23915     Roo.apply(this, config);
23916     
23917     // default disabled, based on 'good practice'..
23918     this.disable = this.disable || {};
23919     Roo.applyIf(this.disable, {
23920         fontSize : true,
23921         colors : true,
23922         specialElements : true
23923     });
23924     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23925     
23926     this.editor = config.editor;
23927     this.editorcore = config.editor.editorcore;
23928     
23929     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23930     
23931     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23932     // dont call parent... till later.
23933 }
23934 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23935      
23936     bar : true,
23937     
23938     editor : false,
23939     editorcore : false,
23940     
23941     
23942     formats : [
23943         "p" ,  
23944         "h1","h2","h3","h4","h5","h6", 
23945         "pre", "code", 
23946         "abbr", "acronym", "address", "cite", "samp", "var",
23947         'div','span'
23948     ],
23949     
23950     onRender : function(ct, position)
23951     {
23952        // Roo.log("Call onRender: " + this.xtype);
23953         
23954        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23955        Roo.log(this.el);
23956        this.el.dom.style.marginBottom = '0';
23957        var _this = this;
23958        var editorcore = this.editorcore;
23959        var editor= this.editor;
23960        
23961        var children = [];
23962        var btn = function(id,cmd , toggle, handler, html){
23963        
23964             var  event = toggle ? 'toggle' : 'click';
23965        
23966             var a = {
23967                 size : 'sm',
23968                 xtype: 'Button',
23969                 xns: Roo.bootstrap,
23970                 glyphicon : id,
23971                 cmd : id || cmd,
23972                 enableToggle:toggle !== false,
23973                 html : html || '',
23974                 pressed : toggle ? false : null,
23975                 listeners : {}
23976             };
23977             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23978                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23979             };
23980             children.push(a);
23981             return a;
23982        }
23983        
23984     //    var cb_box = function...
23985         
23986         var style = {
23987                 xtype: 'Button',
23988                 size : 'sm',
23989                 xns: Roo.bootstrap,
23990                 glyphicon : 'font',
23991                 //html : 'submit'
23992                 menu : {
23993                     xtype: 'Menu',
23994                     xns: Roo.bootstrap,
23995                     items:  []
23996                 }
23997         };
23998         Roo.each(this.formats, function(f) {
23999             style.menu.items.push({
24000                 xtype :'MenuItem',
24001                 xns: Roo.bootstrap,
24002                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24003                 tagname : f,
24004                 listeners : {
24005                     click : function()
24006                     {
24007                         editorcore.insertTag(this.tagname);
24008                         editor.focus();
24009                     }
24010                 }
24011                 
24012             });
24013         });
24014         children.push(style);   
24015         
24016         btn('bold',false,true);
24017         btn('italic',false,true);
24018         btn('align-left', 'justifyleft',true);
24019         btn('align-center', 'justifycenter',true);
24020         btn('align-right' , 'justifyright',true);
24021         btn('link', false, false, function(btn) {
24022             //Roo.log("create link?");
24023             var url = prompt(this.createLinkText, this.defaultLinkValue);
24024             if(url && url != 'http:/'+'/'){
24025                 this.editorcore.relayCmd('createlink', url);
24026             }
24027         }),
24028         btn('list','insertunorderedlist',true);
24029         btn('pencil', false,true, function(btn){
24030                 Roo.log(this);
24031                 this.toggleSourceEdit(btn.pressed);
24032         });
24033         
24034         if (this.editor.btns.length > 0) {
24035             for (var i = 0; i<this.editor.btns.length; i++) {
24036                 children.push(this.editor.btns[i]);
24037             }
24038         }
24039         
24040         /*
24041         var cog = {
24042                 xtype: 'Button',
24043                 size : 'sm',
24044                 xns: Roo.bootstrap,
24045                 glyphicon : 'cog',
24046                 //html : 'submit'
24047                 menu : {
24048                     xtype: 'Menu',
24049                     xns: Roo.bootstrap,
24050                     items:  []
24051                 }
24052         };
24053         
24054         cog.menu.items.push({
24055             xtype :'MenuItem',
24056             xns: Roo.bootstrap,
24057             html : Clean styles,
24058             tagname : f,
24059             listeners : {
24060                 click : function()
24061                 {
24062                     editorcore.insertTag(this.tagname);
24063                     editor.focus();
24064                 }
24065             }
24066             
24067         });
24068        */
24069         
24070          
24071        this.xtype = 'NavSimplebar';
24072         
24073         for(var i=0;i< children.length;i++) {
24074             
24075             this.buttons.add(this.addxtypeChild(children[i]));
24076             
24077         }
24078         
24079         editor.on('editorevent', this.updateToolbar, this);
24080     },
24081     onBtnClick : function(id)
24082     {
24083        this.editorcore.relayCmd(id);
24084        this.editorcore.focus();
24085     },
24086     
24087     /**
24088      * Protected method that will not generally be called directly. It triggers
24089      * a toolbar update by reading the markup state of the current selection in the editor.
24090      */
24091     updateToolbar: function(){
24092
24093         if(!this.editorcore.activated){
24094             this.editor.onFirstFocus(); // is this neeed?
24095             return;
24096         }
24097
24098         var btns = this.buttons; 
24099         var doc = this.editorcore.doc;
24100         btns.get('bold').setActive(doc.queryCommandState('bold'));
24101         btns.get('italic').setActive(doc.queryCommandState('italic'));
24102         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24103         
24104         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24105         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24106         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24107         
24108         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24109         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24110          /*
24111         
24112         var ans = this.editorcore.getAllAncestors();
24113         if (this.formatCombo) {
24114             
24115             
24116             var store = this.formatCombo.store;
24117             this.formatCombo.setValue("");
24118             for (var i =0; i < ans.length;i++) {
24119                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24120                     // select it..
24121                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24122                     break;
24123                 }
24124             }
24125         }
24126         
24127         
24128         
24129         // hides menus... - so this cant be on a menu...
24130         Roo.bootstrap.MenuMgr.hideAll();
24131         */
24132         Roo.bootstrap.MenuMgr.hideAll();
24133         //this.editorsyncValue();
24134     },
24135     onFirstFocus: function() {
24136         this.buttons.each(function(item){
24137            item.enable();
24138         });
24139     },
24140     toggleSourceEdit : function(sourceEditMode){
24141         
24142           
24143         if(sourceEditMode){
24144             Roo.log("disabling buttons");
24145            this.buttons.each( function(item){
24146                 if(item.cmd != 'pencil'){
24147                     item.disable();
24148                 }
24149             });
24150           
24151         }else{
24152             Roo.log("enabling buttons");
24153             if(this.editorcore.initialized){
24154                 this.buttons.each( function(item){
24155                     item.enable();
24156                 });
24157             }
24158             
24159         }
24160         Roo.log("calling toggole on editor");
24161         // tell the editor that it's been pressed..
24162         this.editor.toggleSourceEdit(sourceEditMode);
24163        
24164     }
24165 });
24166
24167
24168
24169
24170
24171 /**
24172  * @class Roo.bootstrap.Table.AbstractSelectionModel
24173  * @extends Roo.util.Observable
24174  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24175  * implemented by descendant classes.  This class should not be directly instantiated.
24176  * @constructor
24177  */
24178 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24179     this.locked = false;
24180     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24181 };
24182
24183
24184 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24185     /** @ignore Called by the grid automatically. Do not call directly. */
24186     init : function(grid){
24187         this.grid = grid;
24188         this.initEvents();
24189     },
24190
24191     /**
24192      * Locks the selections.
24193      */
24194     lock : function(){
24195         this.locked = true;
24196     },
24197
24198     /**
24199      * Unlocks the selections.
24200      */
24201     unlock : function(){
24202         this.locked = false;
24203     },
24204
24205     /**
24206      * Returns true if the selections are locked.
24207      * @return {Boolean}
24208      */
24209     isLocked : function(){
24210         return this.locked;
24211     }
24212 });
24213 /**
24214  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24215  * @class Roo.bootstrap.Table.RowSelectionModel
24216  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24217  * It supports multiple selections and keyboard selection/navigation. 
24218  * @constructor
24219  * @param {Object} config
24220  */
24221
24222 Roo.bootstrap.Table.RowSelectionModel = function(config){
24223     Roo.apply(this, config);
24224     this.selections = new Roo.util.MixedCollection(false, function(o){
24225         return o.id;
24226     });
24227
24228     this.last = false;
24229     this.lastActive = false;
24230
24231     this.addEvents({
24232         /**
24233              * @event selectionchange
24234              * Fires when the selection changes
24235              * @param {SelectionModel} this
24236              */
24237             "selectionchange" : true,
24238         /**
24239              * @event afterselectionchange
24240              * Fires after the selection changes (eg. by key press or clicking)
24241              * @param {SelectionModel} this
24242              */
24243             "afterselectionchange" : true,
24244         /**
24245              * @event beforerowselect
24246              * Fires when a row is selected being selected, return false to cancel.
24247              * @param {SelectionModel} this
24248              * @param {Number} rowIndex The selected index
24249              * @param {Boolean} keepExisting False if other selections will be cleared
24250              */
24251             "beforerowselect" : true,
24252         /**
24253              * @event rowselect
24254              * Fires when a row is selected.
24255              * @param {SelectionModel} this
24256              * @param {Number} rowIndex The selected index
24257              * @param {Roo.data.Record} r The record
24258              */
24259             "rowselect" : true,
24260         /**
24261              * @event rowdeselect
24262              * Fires when a row is deselected.
24263              * @param {SelectionModel} this
24264              * @param {Number} rowIndex The selected index
24265              */
24266         "rowdeselect" : true
24267     });
24268     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24269     this.locked = false;
24270  };
24271
24272 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24273     /**
24274      * @cfg {Boolean} singleSelect
24275      * True to allow selection of only one row at a time (defaults to false)
24276      */
24277     singleSelect : false,
24278
24279     // private
24280     initEvents : function()
24281     {
24282
24283         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24284         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24285         //}else{ // allow click to work like normal
24286          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24287         //}
24288         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24289         this.grid.on("rowclick", this.handleMouseDown, this);
24290         
24291         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24292             "up" : function(e){
24293                 if(!e.shiftKey){
24294                     this.selectPrevious(e.shiftKey);
24295                 }else if(this.last !== false && this.lastActive !== false){
24296                     var last = this.last;
24297                     this.selectRange(this.last,  this.lastActive-1);
24298                     this.grid.getView().focusRow(this.lastActive);
24299                     if(last !== false){
24300                         this.last = last;
24301                     }
24302                 }else{
24303                     this.selectFirstRow();
24304                 }
24305                 this.fireEvent("afterselectionchange", this);
24306             },
24307             "down" : function(e){
24308                 if(!e.shiftKey){
24309                     this.selectNext(e.shiftKey);
24310                 }else if(this.last !== false && this.lastActive !== false){
24311                     var last = this.last;
24312                     this.selectRange(this.last,  this.lastActive+1);
24313                     this.grid.getView().focusRow(this.lastActive);
24314                     if(last !== false){
24315                         this.last = last;
24316                     }
24317                 }else{
24318                     this.selectFirstRow();
24319                 }
24320                 this.fireEvent("afterselectionchange", this);
24321             },
24322             scope: this
24323         });
24324         this.grid.store.on('load', function(){
24325             this.selections.clear();
24326         },this);
24327         /*
24328         var view = this.grid.view;
24329         view.on("refresh", this.onRefresh, this);
24330         view.on("rowupdated", this.onRowUpdated, this);
24331         view.on("rowremoved", this.onRemove, this);
24332         */
24333     },
24334
24335     // private
24336     onRefresh : function()
24337     {
24338         var ds = this.grid.store, i, v = this.grid.view;
24339         var s = this.selections;
24340         s.each(function(r){
24341             if((i = ds.indexOfId(r.id)) != -1){
24342                 v.onRowSelect(i);
24343             }else{
24344                 s.remove(r);
24345             }
24346         });
24347     },
24348
24349     // private
24350     onRemove : function(v, index, r){
24351         this.selections.remove(r);
24352     },
24353
24354     // private
24355     onRowUpdated : function(v, index, r){
24356         if(this.isSelected(r)){
24357             v.onRowSelect(index);
24358         }
24359     },
24360
24361     /**
24362      * Select records.
24363      * @param {Array} records The records to select
24364      * @param {Boolean} keepExisting (optional) True to keep existing selections
24365      */
24366     selectRecords : function(records, keepExisting)
24367     {
24368         if(!keepExisting){
24369             this.clearSelections();
24370         }
24371             var ds = this.grid.store;
24372         for(var i = 0, len = records.length; i < len; i++){
24373             this.selectRow(ds.indexOf(records[i]), true);
24374         }
24375     },
24376
24377     /**
24378      * Gets the number of selected rows.
24379      * @return {Number}
24380      */
24381     getCount : function(){
24382         return this.selections.length;
24383     },
24384
24385     /**
24386      * Selects the first row in the grid.
24387      */
24388     selectFirstRow : function(){
24389         this.selectRow(0);
24390     },
24391
24392     /**
24393      * Select the last row.
24394      * @param {Boolean} keepExisting (optional) True to keep existing selections
24395      */
24396     selectLastRow : function(keepExisting){
24397         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24398         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24399     },
24400
24401     /**
24402      * Selects the row immediately following the last selected row.
24403      * @param {Boolean} keepExisting (optional) True to keep existing selections
24404      */
24405     selectNext : function(keepExisting)
24406     {
24407             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24408             this.selectRow(this.last+1, keepExisting);
24409             this.grid.getView().focusRow(this.last);
24410         }
24411     },
24412
24413     /**
24414      * Selects the row that precedes the last selected row.
24415      * @param {Boolean} keepExisting (optional) True to keep existing selections
24416      */
24417     selectPrevious : function(keepExisting){
24418         if(this.last){
24419             this.selectRow(this.last-1, keepExisting);
24420             this.grid.getView().focusRow(this.last);
24421         }
24422     },
24423
24424     /**
24425      * Returns the selected records
24426      * @return {Array} Array of selected records
24427      */
24428     getSelections : function(){
24429         return [].concat(this.selections.items);
24430     },
24431
24432     /**
24433      * Returns the first selected record.
24434      * @return {Record}
24435      */
24436     getSelected : function(){
24437         return this.selections.itemAt(0);
24438     },
24439
24440
24441     /**
24442      * Clears all selections.
24443      */
24444     clearSelections : function(fast)
24445     {
24446         if(this.locked) {
24447             return;
24448         }
24449         if(fast !== true){
24450                 var ds = this.grid.store;
24451             var s = this.selections;
24452             s.each(function(r){
24453                 this.deselectRow(ds.indexOfId(r.id));
24454             }, this);
24455             s.clear();
24456         }else{
24457             this.selections.clear();
24458         }
24459         this.last = false;
24460     },
24461
24462
24463     /**
24464      * Selects all rows.
24465      */
24466     selectAll : function(){
24467         if(this.locked) {
24468             return;
24469         }
24470         this.selections.clear();
24471         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24472             this.selectRow(i, true);
24473         }
24474     },
24475
24476     /**
24477      * Returns True if there is a selection.
24478      * @return {Boolean}
24479      */
24480     hasSelection : function(){
24481         return this.selections.length > 0;
24482     },
24483
24484     /**
24485      * Returns True if the specified row is selected.
24486      * @param {Number/Record} record The record or index of the record to check
24487      * @return {Boolean}
24488      */
24489     isSelected : function(index){
24490             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24491         return (r && this.selections.key(r.id) ? true : false);
24492     },
24493
24494     /**
24495      * Returns True if the specified record id is selected.
24496      * @param {String} id The id of record to check
24497      * @return {Boolean}
24498      */
24499     isIdSelected : function(id){
24500         return (this.selections.key(id) ? true : false);
24501     },
24502
24503
24504     // private
24505     handleMouseDBClick : function(e, t){
24506         
24507     },
24508     // private
24509     handleMouseDown : function(e, t)
24510     {
24511             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24512         if(this.isLocked() || rowIndex < 0 ){
24513             return;
24514         };
24515         if(e.shiftKey && this.last !== false){
24516             var last = this.last;
24517             this.selectRange(last, rowIndex, e.ctrlKey);
24518             this.last = last; // reset the last
24519             t.focus();
24520     
24521         }else{
24522             var isSelected = this.isSelected(rowIndex);
24523             //Roo.log("select row:" + rowIndex);
24524             if(isSelected){
24525                 this.deselectRow(rowIndex);
24526             } else {
24527                         this.selectRow(rowIndex, true);
24528             }
24529     
24530             /*
24531                 if(e.button !== 0 && isSelected){
24532                 alert('rowIndex 2: ' + rowIndex);
24533                     view.focusRow(rowIndex);
24534                 }else if(e.ctrlKey && isSelected){
24535                     this.deselectRow(rowIndex);
24536                 }else if(!isSelected){
24537                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24538                     view.focusRow(rowIndex);
24539                 }
24540             */
24541         }
24542         this.fireEvent("afterselectionchange", this);
24543     },
24544     // private
24545     handleDragableRowClick :  function(grid, rowIndex, e) 
24546     {
24547         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24548             this.selectRow(rowIndex, false);
24549             grid.view.focusRow(rowIndex);
24550              this.fireEvent("afterselectionchange", this);
24551         }
24552     },
24553     
24554     /**
24555      * Selects multiple rows.
24556      * @param {Array} rows Array of the indexes of the row to select
24557      * @param {Boolean} keepExisting (optional) True to keep existing selections
24558      */
24559     selectRows : function(rows, keepExisting){
24560         if(!keepExisting){
24561             this.clearSelections();
24562         }
24563         for(var i = 0, len = rows.length; i < len; i++){
24564             this.selectRow(rows[i], true);
24565         }
24566     },
24567
24568     /**
24569      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24570      * @param {Number} startRow The index of the first row in the range
24571      * @param {Number} endRow The index of the last row in the range
24572      * @param {Boolean} keepExisting (optional) True to retain existing selections
24573      */
24574     selectRange : function(startRow, endRow, keepExisting){
24575         if(this.locked) {
24576             return;
24577         }
24578         if(!keepExisting){
24579             this.clearSelections();
24580         }
24581         if(startRow <= endRow){
24582             for(var i = startRow; i <= endRow; i++){
24583                 this.selectRow(i, true);
24584             }
24585         }else{
24586             for(var i = startRow; i >= endRow; i--){
24587                 this.selectRow(i, true);
24588             }
24589         }
24590     },
24591
24592     /**
24593      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24594      * @param {Number} startRow The index of the first row in the range
24595      * @param {Number} endRow The index of the last row in the range
24596      */
24597     deselectRange : function(startRow, endRow, preventViewNotify){
24598         if(this.locked) {
24599             return;
24600         }
24601         for(var i = startRow; i <= endRow; i++){
24602             this.deselectRow(i, preventViewNotify);
24603         }
24604     },
24605
24606     /**
24607      * Selects a row.
24608      * @param {Number} row The index of the row to select
24609      * @param {Boolean} keepExisting (optional) True to keep existing selections
24610      */
24611     selectRow : function(index, keepExisting, preventViewNotify)
24612     {
24613             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24614             return;
24615         }
24616         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24617             if(!keepExisting || this.singleSelect){
24618                 this.clearSelections();
24619             }
24620             
24621             var r = this.grid.store.getAt(index);
24622             //console.log('selectRow - record id :' + r.id);
24623             
24624             this.selections.add(r);
24625             this.last = this.lastActive = index;
24626             if(!preventViewNotify){
24627                 var proxy = new Roo.Element(
24628                                 this.grid.getRowDom(index)
24629                 );
24630                 proxy.addClass('bg-info info');
24631             }
24632             this.fireEvent("rowselect", this, index, r);
24633             this.fireEvent("selectionchange", this);
24634         }
24635     },
24636
24637     /**
24638      * Deselects a row.
24639      * @param {Number} row The index of the row to deselect
24640      */
24641     deselectRow : function(index, preventViewNotify)
24642     {
24643         if(this.locked) {
24644             return;
24645         }
24646         if(this.last == index){
24647             this.last = false;
24648         }
24649         if(this.lastActive == index){
24650             this.lastActive = false;
24651         }
24652         
24653         var r = this.grid.store.getAt(index);
24654         if (!r) {
24655             return;
24656         }
24657         
24658         this.selections.remove(r);
24659         //.console.log('deselectRow - record id :' + r.id);
24660         if(!preventViewNotify){
24661         
24662             var proxy = new Roo.Element(
24663                 this.grid.getRowDom(index)
24664             );
24665             proxy.removeClass('bg-info info');
24666         }
24667         this.fireEvent("rowdeselect", this, index);
24668         this.fireEvent("selectionchange", this);
24669     },
24670
24671     // private
24672     restoreLast : function(){
24673         if(this._last){
24674             this.last = this._last;
24675         }
24676     },
24677
24678     // private
24679     acceptsNav : function(row, col, cm){
24680         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24681     },
24682
24683     // private
24684     onEditorKey : function(field, e){
24685         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24686         if(k == e.TAB){
24687             e.stopEvent();
24688             ed.completeEdit();
24689             if(e.shiftKey){
24690                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24691             }else{
24692                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24693             }
24694         }else if(k == e.ENTER && !e.ctrlKey){
24695             e.stopEvent();
24696             ed.completeEdit();
24697             if(e.shiftKey){
24698                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24699             }else{
24700                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24701             }
24702         }else if(k == e.ESC){
24703             ed.cancelEdit();
24704         }
24705         if(newCell){
24706             g.startEditing(newCell[0], newCell[1]);
24707         }
24708     }
24709 });
24710 /*
24711  * Based on:
24712  * Ext JS Library 1.1.1
24713  * Copyright(c) 2006-2007, Ext JS, LLC.
24714  *
24715  * Originally Released Under LGPL - original licence link has changed is not relivant.
24716  *
24717  * Fork - LGPL
24718  * <script type="text/javascript">
24719  */
24720  
24721 /**
24722  * @class Roo.bootstrap.PagingToolbar
24723  * @extends Roo.bootstrap.NavSimplebar
24724  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24725  * @constructor
24726  * Create a new PagingToolbar
24727  * @param {Object} config The config object
24728  * @param {Roo.data.Store} store
24729  */
24730 Roo.bootstrap.PagingToolbar = function(config)
24731 {
24732     // old args format still supported... - xtype is prefered..
24733         // created from xtype...
24734     
24735     this.ds = config.dataSource;
24736     
24737     if (config.store && !this.ds) {
24738         this.store= Roo.factory(config.store, Roo.data);
24739         this.ds = this.store;
24740         this.ds.xmodule = this.xmodule || false;
24741     }
24742     
24743     this.toolbarItems = [];
24744     if (config.items) {
24745         this.toolbarItems = config.items;
24746     }
24747     
24748     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24749     
24750     this.cursor = 0;
24751     
24752     if (this.ds) { 
24753         this.bind(this.ds);
24754     }
24755     
24756     if (Roo.bootstrap.version == 4) {
24757         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24758     } else {
24759         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24760     }
24761     
24762 };
24763
24764 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24765     /**
24766      * @cfg {Roo.data.Store} dataSource
24767      * The underlying data store providing the paged data
24768      */
24769     /**
24770      * @cfg {String/HTMLElement/Element} container
24771      * container The id or element that will contain the toolbar
24772      */
24773     /**
24774      * @cfg {Boolean} displayInfo
24775      * True to display the displayMsg (defaults to false)
24776      */
24777     /**
24778      * @cfg {Number} pageSize
24779      * The number of records to display per page (defaults to 20)
24780      */
24781     pageSize: 20,
24782     /**
24783      * @cfg {String} displayMsg
24784      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24785      */
24786     displayMsg : 'Displaying {0} - {1} of {2}',
24787     /**
24788      * @cfg {String} emptyMsg
24789      * The message to display when no records are found (defaults to "No data to display")
24790      */
24791     emptyMsg : 'No data to display',
24792     /**
24793      * Customizable piece of the default paging text (defaults to "Page")
24794      * @type String
24795      */
24796     beforePageText : "Page",
24797     /**
24798      * Customizable piece of the default paging text (defaults to "of %0")
24799      * @type String
24800      */
24801     afterPageText : "of {0}",
24802     /**
24803      * Customizable piece of the default paging text (defaults to "First Page")
24804      * @type String
24805      */
24806     firstText : "First Page",
24807     /**
24808      * Customizable piece of the default paging text (defaults to "Previous Page")
24809      * @type String
24810      */
24811     prevText : "Previous Page",
24812     /**
24813      * Customizable piece of the default paging text (defaults to "Next Page")
24814      * @type String
24815      */
24816     nextText : "Next Page",
24817     /**
24818      * Customizable piece of the default paging text (defaults to "Last Page")
24819      * @type String
24820      */
24821     lastText : "Last Page",
24822     /**
24823      * Customizable piece of the default paging text (defaults to "Refresh")
24824      * @type String
24825      */
24826     refreshText : "Refresh",
24827
24828     buttons : false,
24829     // private
24830     onRender : function(ct, position) 
24831     {
24832         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24833         this.navgroup.parentId = this.id;
24834         this.navgroup.onRender(this.el, null);
24835         // add the buttons to the navgroup
24836         
24837         if(this.displayInfo){
24838             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24839             this.displayEl = this.el.select('.x-paging-info', true).first();
24840 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24841 //            this.displayEl = navel.el.select('span',true).first();
24842         }
24843         
24844         var _this = this;
24845         
24846         if(this.buttons){
24847             Roo.each(_this.buttons, function(e){ // this might need to use render????
24848                Roo.factory(e).render(_this.el);
24849             });
24850         }
24851             
24852         Roo.each(_this.toolbarItems, function(e) {
24853             _this.navgroup.addItem(e);
24854         });
24855         
24856         
24857         this.first = this.navgroup.addItem({
24858             tooltip: this.firstText,
24859             cls: "prev btn-outline-secondary",
24860             html : ' <i class="fa fa-step-backward"></i>',
24861             disabled: true,
24862             preventDefault: true,
24863             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24864         });
24865         
24866         this.prev =  this.navgroup.addItem({
24867             tooltip: this.prevText,
24868             cls: "prev btn-outline-secondary",
24869             html : ' <i class="fa fa-backward"></i>',
24870             disabled: true,
24871             preventDefault: true,
24872             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24873         });
24874     //this.addSeparator();
24875         
24876         
24877         var field = this.navgroup.addItem( {
24878             tagtype : 'span',
24879             cls : 'x-paging-position  btn-outline-secondary',
24880              disabled: true,
24881             html : this.beforePageText  +
24882                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24883                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24884          } ); //?? escaped?
24885         
24886         this.field = field.el.select('input', true).first();
24887         this.field.on("keydown", this.onPagingKeydown, this);
24888         this.field.on("focus", function(){this.dom.select();});
24889     
24890     
24891         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24892         //this.field.setHeight(18);
24893         //this.addSeparator();
24894         this.next = this.navgroup.addItem({
24895             tooltip: this.nextText,
24896             cls: "next btn-outline-secondary",
24897             html : ' <i class="fa fa-forward"></i>',
24898             disabled: true,
24899             preventDefault: true,
24900             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24901         });
24902         this.last = this.navgroup.addItem({
24903             tooltip: this.lastText,
24904             html : ' <i class="fa fa-step-forward"></i>',
24905             cls: "next btn-outline-secondary",
24906             disabled: true,
24907             preventDefault: true,
24908             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24909         });
24910     //this.addSeparator();
24911         this.loading = this.navgroup.addItem({
24912             tooltip: this.refreshText,
24913             cls: "btn-outline-secondary",
24914             html : ' <i class="fa fa-refresh"></i>',
24915             preventDefault: true,
24916             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24917         });
24918         
24919     },
24920
24921     // private
24922     updateInfo : function(){
24923         if(this.displayEl){
24924             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24925             var msg = count == 0 ?
24926                 this.emptyMsg :
24927                 String.format(
24928                     this.displayMsg,
24929                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24930                 );
24931             this.displayEl.update(msg);
24932         }
24933     },
24934
24935     // private
24936     onLoad : function(ds, r, o)
24937     {
24938         this.cursor = o.params.start ? o.params.start : 0;
24939         
24940         var d = this.getPageData(),
24941             ap = d.activePage,
24942             ps = d.pages;
24943         
24944         
24945         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24946         this.field.dom.value = ap;
24947         this.first.setDisabled(ap == 1);
24948         this.prev.setDisabled(ap == 1);
24949         this.next.setDisabled(ap == ps);
24950         this.last.setDisabled(ap == ps);
24951         this.loading.enable();
24952         this.updateInfo();
24953     },
24954
24955     // private
24956     getPageData : function(){
24957         var total = this.ds.getTotalCount();
24958         return {
24959             total : total,
24960             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24961             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24962         };
24963     },
24964
24965     // private
24966     onLoadError : function(){
24967         this.loading.enable();
24968     },
24969
24970     // private
24971     onPagingKeydown : function(e){
24972         var k = e.getKey();
24973         var d = this.getPageData();
24974         if(k == e.RETURN){
24975             var v = this.field.dom.value, pageNum;
24976             if(!v || isNaN(pageNum = parseInt(v, 10))){
24977                 this.field.dom.value = d.activePage;
24978                 return;
24979             }
24980             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24981             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24982             e.stopEvent();
24983         }
24984         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))
24985         {
24986           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24987           this.field.dom.value = pageNum;
24988           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24989           e.stopEvent();
24990         }
24991         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24992         {
24993           var v = this.field.dom.value, pageNum; 
24994           var increment = (e.shiftKey) ? 10 : 1;
24995           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24996                 increment *= -1;
24997           }
24998           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24999             this.field.dom.value = d.activePage;
25000             return;
25001           }
25002           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25003           {
25004             this.field.dom.value = parseInt(v, 10) + increment;
25005             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25006             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25007           }
25008           e.stopEvent();
25009         }
25010     },
25011
25012     // private
25013     beforeLoad : function(){
25014         if(this.loading){
25015             this.loading.disable();
25016         }
25017     },
25018
25019     // private
25020     onClick : function(which){
25021         
25022         var ds = this.ds;
25023         if (!ds) {
25024             return;
25025         }
25026         
25027         switch(which){
25028             case "first":
25029                 ds.load({params:{start: 0, limit: this.pageSize}});
25030             break;
25031             case "prev":
25032                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25033             break;
25034             case "next":
25035                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25036             break;
25037             case "last":
25038                 var total = ds.getTotalCount();
25039                 var extra = total % this.pageSize;
25040                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25041                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25042             break;
25043             case "refresh":
25044                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25045             break;
25046         }
25047     },
25048
25049     /**
25050      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25051      * @param {Roo.data.Store} store The data store to unbind
25052      */
25053     unbind : function(ds){
25054         ds.un("beforeload", this.beforeLoad, this);
25055         ds.un("load", this.onLoad, this);
25056         ds.un("loadexception", this.onLoadError, this);
25057         ds.un("remove", this.updateInfo, this);
25058         ds.un("add", this.updateInfo, this);
25059         this.ds = undefined;
25060     },
25061
25062     /**
25063      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25064      * @param {Roo.data.Store} store The data store to bind
25065      */
25066     bind : function(ds){
25067         ds.on("beforeload", this.beforeLoad, this);
25068         ds.on("load", this.onLoad, this);
25069         ds.on("loadexception", this.onLoadError, this);
25070         ds.on("remove", this.updateInfo, this);
25071         ds.on("add", this.updateInfo, this);
25072         this.ds = ds;
25073     }
25074 });/*
25075  * - LGPL
25076  *
25077  * element
25078  * 
25079  */
25080
25081 /**
25082  * @class Roo.bootstrap.MessageBar
25083  * @extends Roo.bootstrap.Component
25084  * Bootstrap MessageBar class
25085  * @cfg {String} html contents of the MessageBar
25086  * @cfg {String} weight (info | success | warning | danger) default info
25087  * @cfg {String} beforeClass insert the bar before the given class
25088  * @cfg {Boolean} closable (true | false) default false
25089  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25090  * 
25091  * @constructor
25092  * Create a new Element
25093  * @param {Object} config The config object
25094  */
25095
25096 Roo.bootstrap.MessageBar = function(config){
25097     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25098 };
25099
25100 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25101     
25102     html: '',
25103     weight: 'info',
25104     closable: false,
25105     fixed: false,
25106     beforeClass: 'bootstrap-sticky-wrap',
25107     
25108     getAutoCreate : function(){
25109         
25110         var cfg = {
25111             tag: 'div',
25112             cls: 'alert alert-dismissable alert-' + this.weight,
25113             cn: [
25114                 {
25115                     tag: 'span',
25116                     cls: 'message',
25117                     html: this.html || ''
25118                 }
25119             ]
25120         };
25121         
25122         if(this.fixed){
25123             cfg.cls += ' alert-messages-fixed';
25124         }
25125         
25126         if(this.closable){
25127             cfg.cn.push({
25128                 tag: 'button',
25129                 cls: 'close',
25130                 html: 'x'
25131             });
25132         }
25133         
25134         return cfg;
25135     },
25136     
25137     onRender : function(ct, position)
25138     {
25139         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25140         
25141         if(!this.el){
25142             var cfg = Roo.apply({},  this.getAutoCreate());
25143             cfg.id = Roo.id();
25144             
25145             if (this.cls) {
25146                 cfg.cls += ' ' + this.cls;
25147             }
25148             if (this.style) {
25149                 cfg.style = this.style;
25150             }
25151             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25152             
25153             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25154         }
25155         
25156         this.el.select('>button.close').on('click', this.hide, this);
25157         
25158     },
25159     
25160     show : function()
25161     {
25162         if (!this.rendered) {
25163             this.render();
25164         }
25165         
25166         this.el.show();
25167         
25168         this.fireEvent('show', this);
25169         
25170     },
25171     
25172     hide : function()
25173     {
25174         if (!this.rendered) {
25175             this.render();
25176         }
25177         
25178         this.el.hide();
25179         
25180         this.fireEvent('hide', this);
25181     },
25182     
25183     update : function()
25184     {
25185 //        var e = this.el.dom.firstChild;
25186 //        
25187 //        if(this.closable){
25188 //            e = e.nextSibling;
25189 //        }
25190 //        
25191 //        e.data = this.html || '';
25192
25193         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25194     }
25195    
25196 });
25197
25198  
25199
25200      /*
25201  * - LGPL
25202  *
25203  * Graph
25204  * 
25205  */
25206
25207
25208 /**
25209  * @class Roo.bootstrap.Graph
25210  * @extends Roo.bootstrap.Component
25211  * Bootstrap Graph class
25212 > Prameters
25213  -sm {number} sm 4
25214  -md {number} md 5
25215  @cfg {String} graphtype  bar | vbar | pie
25216  @cfg {number} g_x coodinator | centre x (pie)
25217  @cfg {number} g_y coodinator | centre y (pie)
25218  @cfg {number} g_r radius (pie)
25219  @cfg {number} g_height height of the chart (respected by all elements in the set)
25220  @cfg {number} g_width width of the chart (respected by all elements in the set)
25221  @cfg {Object} title The title of the chart
25222     
25223  -{Array}  values
25224  -opts (object) options for the chart 
25225      o {
25226      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25227      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25228      o vgutter (number)
25229      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.
25230      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25231      o to
25232      o stretch (boolean)
25233      o }
25234  -opts (object) options for the pie
25235      o{
25236      o cut
25237      o startAngle (number)
25238      o endAngle (number)
25239      } 
25240  *
25241  * @constructor
25242  * Create a new Input
25243  * @param {Object} config The config object
25244  */
25245
25246 Roo.bootstrap.Graph = function(config){
25247     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25248     
25249     this.addEvents({
25250         // img events
25251         /**
25252          * @event click
25253          * The img click event for the img.
25254          * @param {Roo.EventObject} e
25255          */
25256         "click" : true
25257     });
25258 };
25259
25260 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25261     
25262     sm: 4,
25263     md: 5,
25264     graphtype: 'bar',
25265     g_height: 250,
25266     g_width: 400,
25267     g_x: 50,
25268     g_y: 50,
25269     g_r: 30,
25270     opts:{
25271         //g_colors: this.colors,
25272         g_type: 'soft',
25273         g_gutter: '20%'
25274
25275     },
25276     title : false,
25277
25278     getAutoCreate : function(){
25279         
25280         var cfg = {
25281             tag: 'div',
25282             html : null
25283         };
25284         
25285         
25286         return  cfg;
25287     },
25288
25289     onRender : function(ct,position){
25290         
25291         
25292         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25293         
25294         if (typeof(Raphael) == 'undefined') {
25295             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25296             return;
25297         }
25298         
25299         this.raphael = Raphael(this.el.dom);
25300         
25301                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25302                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25303                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25304                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25305                 /*
25306                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25307                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25308                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25309                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25310                 
25311                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25312                 r.barchart(330, 10, 300, 220, data1);
25313                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25314                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25315                 */
25316                 
25317                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25318                 // r.barchart(30, 30, 560, 250,  xdata, {
25319                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25320                 //     axis : "0 0 1 1",
25321                 //     axisxlabels :  xdata
25322                 //     //yvalues : cols,
25323                    
25324                 // });
25325 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25326 //        
25327 //        this.load(null,xdata,{
25328 //                axis : "0 0 1 1",
25329 //                axisxlabels :  xdata
25330 //                });
25331
25332     },
25333
25334     load : function(graphtype,xdata,opts)
25335     {
25336         this.raphael.clear();
25337         if(!graphtype) {
25338             graphtype = this.graphtype;
25339         }
25340         if(!opts){
25341             opts = this.opts;
25342         }
25343         var r = this.raphael,
25344             fin = function () {
25345                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25346             },
25347             fout = function () {
25348                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25349             },
25350             pfin = function() {
25351                 this.sector.stop();
25352                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25353
25354                 if (this.label) {
25355                     this.label[0].stop();
25356                     this.label[0].attr({ r: 7.5 });
25357                     this.label[1].attr({ "font-weight": 800 });
25358                 }
25359             },
25360             pfout = function() {
25361                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25362
25363                 if (this.label) {
25364                     this.label[0].animate({ r: 5 }, 500, "bounce");
25365                     this.label[1].attr({ "font-weight": 400 });
25366                 }
25367             };
25368
25369         switch(graphtype){
25370             case 'bar':
25371                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25372                 break;
25373             case 'hbar':
25374                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25375                 break;
25376             case 'pie':
25377 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25378 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25379 //            
25380                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25381                 
25382                 break;
25383
25384         }
25385         
25386         if(this.title){
25387             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25388         }
25389         
25390     },
25391     
25392     setTitle: function(o)
25393     {
25394         this.title = o;
25395     },
25396     
25397     initEvents: function() {
25398         
25399         if(!this.href){
25400             this.el.on('click', this.onClick, this);
25401         }
25402     },
25403     
25404     onClick : function(e)
25405     {
25406         Roo.log('img onclick');
25407         this.fireEvent('click', this, e);
25408     }
25409    
25410 });
25411
25412  
25413 /*
25414  * - LGPL
25415  *
25416  * numberBox
25417  * 
25418  */
25419 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25420
25421 /**
25422  * @class Roo.bootstrap.dash.NumberBox
25423  * @extends Roo.bootstrap.Component
25424  * Bootstrap NumberBox class
25425  * @cfg {String} headline Box headline
25426  * @cfg {String} content Box content
25427  * @cfg {String} icon Box icon
25428  * @cfg {String} footer Footer text
25429  * @cfg {String} fhref Footer href
25430  * 
25431  * @constructor
25432  * Create a new NumberBox
25433  * @param {Object} config The config object
25434  */
25435
25436
25437 Roo.bootstrap.dash.NumberBox = function(config){
25438     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25439     
25440 };
25441
25442 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25443     
25444     headline : '',
25445     content : '',
25446     icon : '',
25447     footer : '',
25448     fhref : '',
25449     ficon : '',
25450     
25451     getAutoCreate : function(){
25452         
25453         var cfg = {
25454             tag : 'div',
25455             cls : 'small-box ',
25456             cn : [
25457                 {
25458                     tag : 'div',
25459                     cls : 'inner',
25460                     cn :[
25461                         {
25462                             tag : 'h3',
25463                             cls : 'roo-headline',
25464                             html : this.headline
25465                         },
25466                         {
25467                             tag : 'p',
25468                             cls : 'roo-content',
25469                             html : this.content
25470                         }
25471                     ]
25472                 }
25473             ]
25474         };
25475         
25476         if(this.icon){
25477             cfg.cn.push({
25478                 tag : 'div',
25479                 cls : 'icon',
25480                 cn :[
25481                     {
25482                         tag : 'i',
25483                         cls : 'ion ' + this.icon
25484                     }
25485                 ]
25486             });
25487         }
25488         
25489         if(this.footer){
25490             var footer = {
25491                 tag : 'a',
25492                 cls : 'small-box-footer',
25493                 href : this.fhref || '#',
25494                 html : this.footer
25495             };
25496             
25497             cfg.cn.push(footer);
25498             
25499         }
25500         
25501         return  cfg;
25502     },
25503
25504     onRender : function(ct,position){
25505         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25506
25507
25508        
25509                 
25510     },
25511
25512     setHeadline: function (value)
25513     {
25514         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25515     },
25516     
25517     setFooter: function (value, href)
25518     {
25519         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25520         
25521         if(href){
25522             this.el.select('a.small-box-footer',true).first().attr('href', href);
25523         }
25524         
25525     },
25526
25527     setContent: function (value)
25528     {
25529         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25530     },
25531
25532     initEvents: function() 
25533     {   
25534         
25535     }
25536     
25537 });
25538
25539  
25540 /*
25541  * - LGPL
25542  *
25543  * TabBox
25544  * 
25545  */
25546 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25547
25548 /**
25549  * @class Roo.bootstrap.dash.TabBox
25550  * @extends Roo.bootstrap.Component
25551  * Bootstrap TabBox class
25552  * @cfg {String} title Title of the TabBox
25553  * @cfg {String} icon Icon of the TabBox
25554  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25555  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25556  * 
25557  * @constructor
25558  * Create a new TabBox
25559  * @param {Object} config The config object
25560  */
25561
25562
25563 Roo.bootstrap.dash.TabBox = function(config){
25564     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25565     this.addEvents({
25566         // raw events
25567         /**
25568          * @event addpane
25569          * When a pane is added
25570          * @param {Roo.bootstrap.dash.TabPane} pane
25571          */
25572         "addpane" : true,
25573         /**
25574          * @event activatepane
25575          * When a pane is activated
25576          * @param {Roo.bootstrap.dash.TabPane} pane
25577          */
25578         "activatepane" : true
25579         
25580          
25581     });
25582     
25583     this.panes = [];
25584 };
25585
25586 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25587
25588     title : '',
25589     icon : false,
25590     showtabs : true,
25591     tabScrollable : false,
25592     
25593     getChildContainer : function()
25594     {
25595         return this.el.select('.tab-content', true).first();
25596     },
25597     
25598     getAutoCreate : function(){
25599         
25600         var header = {
25601             tag: 'li',
25602             cls: 'pull-left header',
25603             html: this.title,
25604             cn : []
25605         };
25606         
25607         if(this.icon){
25608             header.cn.push({
25609                 tag: 'i',
25610                 cls: 'fa ' + this.icon
25611             });
25612         }
25613         
25614         var h = {
25615             tag: 'ul',
25616             cls: 'nav nav-tabs pull-right',
25617             cn: [
25618                 header
25619             ]
25620         };
25621         
25622         if(this.tabScrollable){
25623             h = {
25624                 tag: 'div',
25625                 cls: 'tab-header',
25626                 cn: [
25627                     {
25628                         tag: 'ul',
25629                         cls: 'nav nav-tabs pull-right',
25630                         cn: [
25631                             header
25632                         ]
25633                     }
25634                 ]
25635             };
25636         }
25637         
25638         var cfg = {
25639             tag: 'div',
25640             cls: 'nav-tabs-custom',
25641             cn: [
25642                 h,
25643                 {
25644                     tag: 'div',
25645                     cls: 'tab-content no-padding',
25646                     cn: []
25647                 }
25648             ]
25649         };
25650
25651         return  cfg;
25652     },
25653     initEvents : function()
25654     {
25655         //Roo.log('add add pane handler');
25656         this.on('addpane', this.onAddPane, this);
25657     },
25658      /**
25659      * Updates the box title
25660      * @param {String} html to set the title to.
25661      */
25662     setTitle : function(value)
25663     {
25664         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25665     },
25666     onAddPane : function(pane)
25667     {
25668         this.panes.push(pane);
25669         //Roo.log('addpane');
25670         //Roo.log(pane);
25671         // tabs are rendere left to right..
25672         if(!this.showtabs){
25673             return;
25674         }
25675         
25676         var ctr = this.el.select('.nav-tabs', true).first();
25677          
25678          
25679         var existing = ctr.select('.nav-tab',true);
25680         var qty = existing.getCount();;
25681         
25682         
25683         var tab = ctr.createChild({
25684             tag : 'li',
25685             cls : 'nav-tab' + (qty ? '' : ' active'),
25686             cn : [
25687                 {
25688                     tag : 'a',
25689                     href:'#',
25690                     html : pane.title
25691                 }
25692             ]
25693         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25694         pane.tab = tab;
25695         
25696         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25697         if (!qty) {
25698             pane.el.addClass('active');
25699         }
25700         
25701                 
25702     },
25703     onTabClick : function(ev,un,ob,pane)
25704     {
25705         //Roo.log('tab - prev default');
25706         ev.preventDefault();
25707         
25708         
25709         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25710         pane.tab.addClass('active');
25711         //Roo.log(pane.title);
25712         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25713         // technically we should have a deactivate event.. but maybe add later.
25714         // and it should not de-activate the selected tab...
25715         this.fireEvent('activatepane', pane);
25716         pane.el.addClass('active');
25717         pane.fireEvent('activate');
25718         
25719         
25720     },
25721     
25722     getActivePane : function()
25723     {
25724         var r = false;
25725         Roo.each(this.panes, function(p) {
25726             if(p.el.hasClass('active')){
25727                 r = p;
25728                 return false;
25729             }
25730             
25731             return;
25732         });
25733         
25734         return r;
25735     }
25736     
25737     
25738 });
25739
25740  
25741 /*
25742  * - LGPL
25743  *
25744  * Tab pane
25745  * 
25746  */
25747 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25748 /**
25749  * @class Roo.bootstrap.TabPane
25750  * @extends Roo.bootstrap.Component
25751  * Bootstrap TabPane class
25752  * @cfg {Boolean} active (false | true) Default false
25753  * @cfg {String} title title of panel
25754
25755  * 
25756  * @constructor
25757  * Create a new TabPane
25758  * @param {Object} config The config object
25759  */
25760
25761 Roo.bootstrap.dash.TabPane = function(config){
25762     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25763     
25764     this.addEvents({
25765         // raw events
25766         /**
25767          * @event activate
25768          * When a pane is activated
25769          * @param {Roo.bootstrap.dash.TabPane} pane
25770          */
25771         "activate" : true
25772          
25773     });
25774 };
25775
25776 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25777     
25778     active : false,
25779     title : '',
25780     
25781     // the tabBox that this is attached to.
25782     tab : false,
25783      
25784     getAutoCreate : function() 
25785     {
25786         var cfg = {
25787             tag: 'div',
25788             cls: 'tab-pane'
25789         };
25790         
25791         if(this.active){
25792             cfg.cls += ' active';
25793         }
25794         
25795         return cfg;
25796     },
25797     initEvents  : function()
25798     {
25799         //Roo.log('trigger add pane handler');
25800         this.parent().fireEvent('addpane', this)
25801     },
25802     
25803      /**
25804      * Updates the tab title 
25805      * @param {String} html to set the title to.
25806      */
25807     setTitle: function(str)
25808     {
25809         if (!this.tab) {
25810             return;
25811         }
25812         this.title = str;
25813         this.tab.select('a', true).first().dom.innerHTML = str;
25814         
25815     }
25816     
25817     
25818     
25819 });
25820
25821  
25822
25823
25824  /*
25825  * - LGPL
25826  *
25827  * menu
25828  * 
25829  */
25830 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25831
25832 /**
25833  * @class Roo.bootstrap.menu.Menu
25834  * @extends Roo.bootstrap.Component
25835  * Bootstrap Menu class - container for Menu
25836  * @cfg {String} html Text of the menu
25837  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25838  * @cfg {String} icon Font awesome icon
25839  * @cfg {String} pos Menu align to (top | bottom) default bottom
25840  * 
25841  * 
25842  * @constructor
25843  * Create a new Menu
25844  * @param {Object} config The config object
25845  */
25846
25847
25848 Roo.bootstrap.menu.Menu = function(config){
25849     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25850     
25851     this.addEvents({
25852         /**
25853          * @event beforeshow
25854          * Fires before this menu is displayed
25855          * @param {Roo.bootstrap.menu.Menu} this
25856          */
25857         beforeshow : true,
25858         /**
25859          * @event beforehide
25860          * Fires before this menu is hidden
25861          * @param {Roo.bootstrap.menu.Menu} this
25862          */
25863         beforehide : true,
25864         /**
25865          * @event show
25866          * Fires after this menu is displayed
25867          * @param {Roo.bootstrap.menu.Menu} this
25868          */
25869         show : true,
25870         /**
25871          * @event hide
25872          * Fires after this menu is hidden
25873          * @param {Roo.bootstrap.menu.Menu} this
25874          */
25875         hide : true,
25876         /**
25877          * @event click
25878          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25879          * @param {Roo.bootstrap.menu.Menu} this
25880          * @param {Roo.EventObject} e
25881          */
25882         click : true
25883     });
25884     
25885 };
25886
25887 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25888     
25889     submenu : false,
25890     html : '',
25891     weight : 'default',
25892     icon : false,
25893     pos : 'bottom',
25894     
25895     
25896     getChildContainer : function() {
25897         if(this.isSubMenu){
25898             return this.el;
25899         }
25900         
25901         return this.el.select('ul.dropdown-menu', true).first();  
25902     },
25903     
25904     getAutoCreate : function()
25905     {
25906         var text = [
25907             {
25908                 tag : 'span',
25909                 cls : 'roo-menu-text',
25910                 html : this.html
25911             }
25912         ];
25913         
25914         if(this.icon){
25915             text.unshift({
25916                 tag : 'i',
25917                 cls : 'fa ' + this.icon
25918             })
25919         }
25920         
25921         
25922         var cfg = {
25923             tag : 'div',
25924             cls : 'btn-group',
25925             cn : [
25926                 {
25927                     tag : 'button',
25928                     cls : 'dropdown-button btn btn-' + this.weight,
25929                     cn : text
25930                 },
25931                 {
25932                     tag : 'button',
25933                     cls : 'dropdown-toggle btn btn-' + this.weight,
25934                     cn : [
25935                         {
25936                             tag : 'span',
25937                             cls : 'caret'
25938                         }
25939                     ]
25940                 },
25941                 {
25942                     tag : 'ul',
25943                     cls : 'dropdown-menu'
25944                 }
25945             ]
25946             
25947         };
25948         
25949         if(this.pos == 'top'){
25950             cfg.cls += ' dropup';
25951         }
25952         
25953         if(this.isSubMenu){
25954             cfg = {
25955                 tag : 'ul',
25956                 cls : 'dropdown-menu'
25957             }
25958         }
25959         
25960         return cfg;
25961     },
25962     
25963     onRender : function(ct, position)
25964     {
25965         this.isSubMenu = ct.hasClass('dropdown-submenu');
25966         
25967         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25968     },
25969     
25970     initEvents : function() 
25971     {
25972         if(this.isSubMenu){
25973             return;
25974         }
25975         
25976         this.hidden = true;
25977         
25978         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25979         this.triggerEl.on('click', this.onTriggerPress, this);
25980         
25981         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25982         this.buttonEl.on('click', this.onClick, this);
25983         
25984     },
25985     
25986     list : function()
25987     {
25988         if(this.isSubMenu){
25989             return this.el;
25990         }
25991         
25992         return this.el.select('ul.dropdown-menu', true).first();
25993     },
25994     
25995     onClick : function(e)
25996     {
25997         this.fireEvent("click", this, e);
25998     },
25999     
26000     onTriggerPress  : function(e)
26001     {   
26002         if (this.isVisible()) {
26003             this.hide();
26004         } else {
26005             this.show();
26006         }
26007     },
26008     
26009     isVisible : function(){
26010         return !this.hidden;
26011     },
26012     
26013     show : function()
26014     {
26015         this.fireEvent("beforeshow", this);
26016         
26017         this.hidden = false;
26018         this.el.addClass('open');
26019         
26020         Roo.get(document).on("mouseup", this.onMouseUp, this);
26021         
26022         this.fireEvent("show", this);
26023         
26024         
26025     },
26026     
26027     hide : function()
26028     {
26029         this.fireEvent("beforehide", this);
26030         
26031         this.hidden = true;
26032         this.el.removeClass('open');
26033         
26034         Roo.get(document).un("mouseup", this.onMouseUp);
26035         
26036         this.fireEvent("hide", this);
26037     },
26038     
26039     onMouseUp : function()
26040     {
26041         this.hide();
26042     }
26043     
26044 });
26045
26046  
26047  /*
26048  * - LGPL
26049  *
26050  * menu item
26051  * 
26052  */
26053 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26054
26055 /**
26056  * @class Roo.bootstrap.menu.Item
26057  * @extends Roo.bootstrap.Component
26058  * Bootstrap MenuItem class
26059  * @cfg {Boolean} submenu (true | false) default false
26060  * @cfg {String} html text of the item
26061  * @cfg {String} href the link
26062  * @cfg {Boolean} disable (true | false) default false
26063  * @cfg {Boolean} preventDefault (true | false) default true
26064  * @cfg {String} icon Font awesome icon
26065  * @cfg {String} pos Submenu align to (left | right) default right 
26066  * 
26067  * 
26068  * @constructor
26069  * Create a new Item
26070  * @param {Object} config The config object
26071  */
26072
26073
26074 Roo.bootstrap.menu.Item = function(config){
26075     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26076     this.addEvents({
26077         /**
26078          * @event mouseover
26079          * Fires when the mouse is hovering over this menu
26080          * @param {Roo.bootstrap.menu.Item} this
26081          * @param {Roo.EventObject} e
26082          */
26083         mouseover : true,
26084         /**
26085          * @event mouseout
26086          * Fires when the mouse exits this menu
26087          * @param {Roo.bootstrap.menu.Item} this
26088          * @param {Roo.EventObject} e
26089          */
26090         mouseout : true,
26091         // raw events
26092         /**
26093          * @event click
26094          * The raw click event for the entire grid.
26095          * @param {Roo.EventObject} e
26096          */
26097         click : true
26098     });
26099 };
26100
26101 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26102     
26103     submenu : false,
26104     href : '',
26105     html : '',
26106     preventDefault: true,
26107     disable : false,
26108     icon : false,
26109     pos : 'right',
26110     
26111     getAutoCreate : function()
26112     {
26113         var text = [
26114             {
26115                 tag : 'span',
26116                 cls : 'roo-menu-item-text',
26117                 html : this.html
26118             }
26119         ];
26120         
26121         if(this.icon){
26122             text.unshift({
26123                 tag : 'i',
26124                 cls : 'fa ' + this.icon
26125             })
26126         }
26127         
26128         var cfg = {
26129             tag : 'li',
26130             cn : [
26131                 {
26132                     tag : 'a',
26133                     href : this.href || '#',
26134                     cn : text
26135                 }
26136             ]
26137         };
26138         
26139         if(this.disable){
26140             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26141         }
26142         
26143         if(this.submenu){
26144             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26145             
26146             if(this.pos == 'left'){
26147                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26148             }
26149         }
26150         
26151         return cfg;
26152     },
26153     
26154     initEvents : function() 
26155     {
26156         this.el.on('mouseover', this.onMouseOver, this);
26157         this.el.on('mouseout', this.onMouseOut, this);
26158         
26159         this.el.select('a', true).first().on('click', this.onClick, this);
26160         
26161     },
26162     
26163     onClick : function(e)
26164     {
26165         if(this.preventDefault){
26166             e.preventDefault();
26167         }
26168         
26169         this.fireEvent("click", this, e);
26170     },
26171     
26172     onMouseOver : function(e)
26173     {
26174         if(this.submenu && this.pos == 'left'){
26175             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26176         }
26177         
26178         this.fireEvent("mouseover", this, e);
26179     },
26180     
26181     onMouseOut : function(e)
26182     {
26183         this.fireEvent("mouseout", this, e);
26184     }
26185 });
26186
26187  
26188
26189  /*
26190  * - LGPL
26191  *
26192  * menu separator
26193  * 
26194  */
26195 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26196
26197 /**
26198  * @class Roo.bootstrap.menu.Separator
26199  * @extends Roo.bootstrap.Component
26200  * Bootstrap Separator class
26201  * 
26202  * @constructor
26203  * Create a new Separator
26204  * @param {Object} config The config object
26205  */
26206
26207
26208 Roo.bootstrap.menu.Separator = function(config){
26209     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26210 };
26211
26212 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26213     
26214     getAutoCreate : function(){
26215         var cfg = {
26216             tag : 'li',
26217             cls: 'divider'
26218         };
26219         
26220         return cfg;
26221     }
26222    
26223 });
26224
26225  
26226
26227  /*
26228  * - LGPL
26229  *
26230  * Tooltip
26231  * 
26232  */
26233
26234 /**
26235  * @class Roo.bootstrap.Tooltip
26236  * Bootstrap Tooltip class
26237  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26238  * to determine which dom element triggers the tooltip.
26239  * 
26240  * It needs to add support for additional attributes like tooltip-position
26241  * 
26242  * @constructor
26243  * Create a new Toolti
26244  * @param {Object} config The config object
26245  */
26246
26247 Roo.bootstrap.Tooltip = function(config){
26248     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26249     
26250     this.alignment = Roo.bootstrap.Tooltip.alignment;
26251     
26252     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26253         this.alignment = config.alignment;
26254     }
26255     
26256 };
26257
26258 Roo.apply(Roo.bootstrap.Tooltip, {
26259     /**
26260      * @function init initialize tooltip monitoring.
26261      * @static
26262      */
26263     currentEl : false,
26264     currentTip : false,
26265     currentRegion : false,
26266     
26267     //  init : delay?
26268     
26269     init : function()
26270     {
26271         Roo.get(document).on('mouseover', this.enter ,this);
26272         Roo.get(document).on('mouseout', this.leave, this);
26273          
26274         
26275         this.currentTip = new Roo.bootstrap.Tooltip();
26276     },
26277     
26278     enter : function(ev)
26279     {
26280         var dom = ev.getTarget();
26281         
26282         //Roo.log(['enter',dom]);
26283         var el = Roo.fly(dom);
26284         if (this.currentEl) {
26285             //Roo.log(dom);
26286             //Roo.log(this.currentEl);
26287             //Roo.log(this.currentEl.contains(dom));
26288             if (this.currentEl == el) {
26289                 return;
26290             }
26291             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26292                 return;
26293             }
26294
26295         }
26296         
26297         if (this.currentTip.el) {
26298             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26299         }    
26300         //Roo.log(ev);
26301         
26302         if(!el || el.dom == document){
26303             return;
26304         }
26305         
26306         var bindEl = el;
26307         
26308         // you can not look for children, as if el is the body.. then everythign is the child..
26309         if (!el.attr('tooltip')) { //
26310             if (!el.select("[tooltip]").elements.length) {
26311                 return;
26312             }
26313             // is the mouse over this child...?
26314             bindEl = el.select("[tooltip]").first();
26315             var xy = ev.getXY();
26316             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26317                 //Roo.log("not in region.");
26318                 return;
26319             }
26320             //Roo.log("child element over..");
26321             
26322         }
26323         this.currentEl = bindEl;
26324         this.currentTip.bind(bindEl);
26325         this.currentRegion = Roo.lib.Region.getRegion(dom);
26326         this.currentTip.enter();
26327         
26328     },
26329     leave : function(ev)
26330     {
26331         var dom = ev.getTarget();
26332         //Roo.log(['leave',dom]);
26333         if (!this.currentEl) {
26334             return;
26335         }
26336         
26337         
26338         if (dom != this.currentEl.dom) {
26339             return;
26340         }
26341         var xy = ev.getXY();
26342         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26343             return;
26344         }
26345         // only activate leave if mouse cursor is outside... bounding box..
26346         
26347         
26348         
26349         
26350         if (this.currentTip) {
26351             this.currentTip.leave();
26352         }
26353         //Roo.log('clear currentEl');
26354         this.currentEl = false;
26355         
26356         
26357     },
26358     alignment : {
26359         'left' : ['r-l', [-2,0], 'right'],
26360         'right' : ['l-r', [2,0], 'left'],
26361         'bottom' : ['t-b', [0,2], 'top'],
26362         'top' : [ 'b-t', [0,-2], 'bottom']
26363     }
26364     
26365 });
26366
26367
26368 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26369     
26370     
26371     bindEl : false,
26372     
26373     delay : null, // can be { show : 300 , hide: 500}
26374     
26375     timeout : null,
26376     
26377     hoverState : null, //???
26378     
26379     placement : 'bottom', 
26380     
26381     alignment : false,
26382     
26383     getAutoCreate : function(){
26384     
26385         var cfg = {
26386            cls : 'tooltip',
26387            role : 'tooltip',
26388            cn : [
26389                 {
26390                     cls : 'tooltip-arrow'
26391                 },
26392                 {
26393                     cls : 'tooltip-inner'
26394                 }
26395            ]
26396         };
26397         
26398         return cfg;
26399     },
26400     bind : function(el)
26401     {
26402         this.bindEl = el;
26403     },
26404       
26405     
26406     enter : function () {
26407        
26408         if (this.timeout != null) {
26409             clearTimeout(this.timeout);
26410         }
26411         
26412         this.hoverState = 'in';
26413          //Roo.log("enter - show");
26414         if (!this.delay || !this.delay.show) {
26415             this.show();
26416             return;
26417         }
26418         var _t = this;
26419         this.timeout = setTimeout(function () {
26420             if (_t.hoverState == 'in') {
26421                 _t.show();
26422             }
26423         }, this.delay.show);
26424     },
26425     leave : function()
26426     {
26427         clearTimeout(this.timeout);
26428     
26429         this.hoverState = 'out';
26430          if (!this.delay || !this.delay.hide) {
26431             this.hide();
26432             return;
26433         }
26434        
26435         var _t = this;
26436         this.timeout = setTimeout(function () {
26437             //Roo.log("leave - timeout");
26438             
26439             if (_t.hoverState == 'out') {
26440                 _t.hide();
26441                 Roo.bootstrap.Tooltip.currentEl = false;
26442             }
26443         }, delay);
26444     },
26445     
26446     show : function (msg)
26447     {
26448         if (!this.el) {
26449             this.render(document.body);
26450         }
26451         // set content.
26452         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26453         
26454         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26455         
26456         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26457         
26458         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26459         
26460         var placement = typeof this.placement == 'function' ?
26461             this.placement.call(this, this.el, on_el) :
26462             this.placement;
26463             
26464         var autoToken = /\s?auto?\s?/i;
26465         var autoPlace = autoToken.test(placement);
26466         if (autoPlace) {
26467             placement = placement.replace(autoToken, '') || 'top';
26468         }
26469         
26470         //this.el.detach()
26471         //this.el.setXY([0,0]);
26472         this.el.show();
26473         //this.el.dom.style.display='block';
26474         
26475         //this.el.appendTo(on_el);
26476         
26477         var p = this.getPosition();
26478         var box = this.el.getBox();
26479         
26480         if (autoPlace) {
26481             // fixme..
26482         }
26483         
26484         var align = this.alignment[placement];
26485         
26486         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26487         
26488         if(placement == 'top' || placement == 'bottom'){
26489             if(xy[0] < 0){
26490                 placement = 'right';
26491             }
26492             
26493             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26494                 placement = 'left';
26495             }
26496             
26497             var scroll = Roo.select('body', true).first().getScroll();
26498             
26499             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26500                 placement = 'top';
26501             }
26502             
26503             align = this.alignment[placement];
26504         }
26505         
26506         this.el.alignTo(this.bindEl, align[0],align[1]);
26507         //var arrow = this.el.select('.arrow',true).first();
26508         //arrow.set(align[2], 
26509         
26510         this.el.addClass(placement);
26511         
26512         this.el.addClass('in fade');
26513         
26514         this.hoverState = null;
26515         
26516         if (this.el.hasClass('fade')) {
26517             // fade it?
26518         }
26519         
26520     },
26521     hide : function()
26522     {
26523          
26524         if (!this.el) {
26525             return;
26526         }
26527         //this.el.setXY([0,0]);
26528         this.el.removeClass('in');
26529         //this.el.hide();
26530         
26531     }
26532     
26533 });
26534  
26535
26536  /*
26537  * - LGPL
26538  *
26539  * Location Picker
26540  * 
26541  */
26542
26543 /**
26544  * @class Roo.bootstrap.LocationPicker
26545  * @extends Roo.bootstrap.Component
26546  * Bootstrap LocationPicker class
26547  * @cfg {Number} latitude Position when init default 0
26548  * @cfg {Number} longitude Position when init default 0
26549  * @cfg {Number} zoom default 15
26550  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26551  * @cfg {Boolean} mapTypeControl default false
26552  * @cfg {Boolean} disableDoubleClickZoom default false
26553  * @cfg {Boolean} scrollwheel default true
26554  * @cfg {Boolean} streetViewControl default false
26555  * @cfg {Number} radius default 0
26556  * @cfg {String} locationName
26557  * @cfg {Boolean} draggable default true
26558  * @cfg {Boolean} enableAutocomplete default false
26559  * @cfg {Boolean} enableReverseGeocode default true
26560  * @cfg {String} markerTitle
26561  * 
26562  * @constructor
26563  * Create a new LocationPicker
26564  * @param {Object} config The config object
26565  */
26566
26567
26568 Roo.bootstrap.LocationPicker = function(config){
26569     
26570     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26571     
26572     this.addEvents({
26573         /**
26574          * @event initial
26575          * Fires when the picker initialized.
26576          * @param {Roo.bootstrap.LocationPicker} this
26577          * @param {Google Location} location
26578          */
26579         initial : true,
26580         /**
26581          * @event positionchanged
26582          * Fires when the picker position changed.
26583          * @param {Roo.bootstrap.LocationPicker} this
26584          * @param {Google Location} location
26585          */
26586         positionchanged : true,
26587         /**
26588          * @event resize
26589          * Fires when the map resize.
26590          * @param {Roo.bootstrap.LocationPicker} this
26591          */
26592         resize : true,
26593         /**
26594          * @event show
26595          * Fires when the map show.
26596          * @param {Roo.bootstrap.LocationPicker} this
26597          */
26598         show : true,
26599         /**
26600          * @event hide
26601          * Fires when the map hide.
26602          * @param {Roo.bootstrap.LocationPicker} this
26603          */
26604         hide : true,
26605         /**
26606          * @event mapClick
26607          * Fires when click the map.
26608          * @param {Roo.bootstrap.LocationPicker} this
26609          * @param {Map event} e
26610          */
26611         mapClick : true,
26612         /**
26613          * @event mapRightClick
26614          * Fires when right click the map.
26615          * @param {Roo.bootstrap.LocationPicker} this
26616          * @param {Map event} e
26617          */
26618         mapRightClick : true,
26619         /**
26620          * @event markerClick
26621          * Fires when click the marker.
26622          * @param {Roo.bootstrap.LocationPicker} this
26623          * @param {Map event} e
26624          */
26625         markerClick : true,
26626         /**
26627          * @event markerRightClick
26628          * Fires when right click the marker.
26629          * @param {Roo.bootstrap.LocationPicker} this
26630          * @param {Map event} e
26631          */
26632         markerRightClick : true,
26633         /**
26634          * @event OverlayViewDraw
26635          * Fires when OverlayView Draw
26636          * @param {Roo.bootstrap.LocationPicker} this
26637          */
26638         OverlayViewDraw : true,
26639         /**
26640          * @event OverlayViewOnAdd
26641          * Fires when OverlayView Draw
26642          * @param {Roo.bootstrap.LocationPicker} this
26643          */
26644         OverlayViewOnAdd : true,
26645         /**
26646          * @event OverlayViewOnRemove
26647          * Fires when OverlayView Draw
26648          * @param {Roo.bootstrap.LocationPicker} this
26649          */
26650         OverlayViewOnRemove : true,
26651         /**
26652          * @event OverlayViewShow
26653          * Fires when OverlayView Draw
26654          * @param {Roo.bootstrap.LocationPicker} this
26655          * @param {Pixel} cpx
26656          */
26657         OverlayViewShow : true,
26658         /**
26659          * @event OverlayViewHide
26660          * Fires when OverlayView Draw
26661          * @param {Roo.bootstrap.LocationPicker} this
26662          */
26663         OverlayViewHide : true,
26664         /**
26665          * @event loadexception
26666          * Fires when load google lib failed.
26667          * @param {Roo.bootstrap.LocationPicker} this
26668          */
26669         loadexception : true
26670     });
26671         
26672 };
26673
26674 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26675     
26676     gMapContext: false,
26677     
26678     latitude: 0,
26679     longitude: 0,
26680     zoom: 15,
26681     mapTypeId: false,
26682     mapTypeControl: false,
26683     disableDoubleClickZoom: false,
26684     scrollwheel: true,
26685     streetViewControl: false,
26686     radius: 0,
26687     locationName: '',
26688     draggable: true,
26689     enableAutocomplete: false,
26690     enableReverseGeocode: true,
26691     markerTitle: '',
26692     
26693     getAutoCreate: function()
26694     {
26695
26696         var cfg = {
26697             tag: 'div',
26698             cls: 'roo-location-picker'
26699         };
26700         
26701         return cfg
26702     },
26703     
26704     initEvents: function(ct, position)
26705     {       
26706         if(!this.el.getWidth() || this.isApplied()){
26707             return;
26708         }
26709         
26710         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26711         
26712         this.initial();
26713     },
26714     
26715     initial: function()
26716     {
26717         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26718             this.fireEvent('loadexception', this);
26719             return;
26720         }
26721         
26722         if(!this.mapTypeId){
26723             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26724         }
26725         
26726         this.gMapContext = this.GMapContext();
26727         
26728         this.initOverlayView();
26729         
26730         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26731         
26732         var _this = this;
26733                 
26734         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26735             _this.setPosition(_this.gMapContext.marker.position);
26736         });
26737         
26738         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26739             _this.fireEvent('mapClick', this, event);
26740             
26741         });
26742
26743         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26744             _this.fireEvent('mapRightClick', this, event);
26745             
26746         });
26747         
26748         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26749             _this.fireEvent('markerClick', this, event);
26750             
26751         });
26752
26753         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26754             _this.fireEvent('markerRightClick', this, event);
26755             
26756         });
26757         
26758         this.setPosition(this.gMapContext.location);
26759         
26760         this.fireEvent('initial', this, this.gMapContext.location);
26761     },
26762     
26763     initOverlayView: function()
26764     {
26765         var _this = this;
26766         
26767         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26768             
26769             draw: function()
26770             {
26771                 _this.fireEvent('OverlayViewDraw', _this);
26772             },
26773             
26774             onAdd: function()
26775             {
26776                 _this.fireEvent('OverlayViewOnAdd', _this);
26777             },
26778             
26779             onRemove: function()
26780             {
26781                 _this.fireEvent('OverlayViewOnRemove', _this);
26782             },
26783             
26784             show: function(cpx)
26785             {
26786                 _this.fireEvent('OverlayViewShow', _this, cpx);
26787             },
26788             
26789             hide: function()
26790             {
26791                 _this.fireEvent('OverlayViewHide', _this);
26792             }
26793             
26794         });
26795     },
26796     
26797     fromLatLngToContainerPixel: function(event)
26798     {
26799         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26800     },
26801     
26802     isApplied: function() 
26803     {
26804         return this.getGmapContext() == false ? false : true;
26805     },
26806     
26807     getGmapContext: function() 
26808     {
26809         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26810     },
26811     
26812     GMapContext: function() 
26813     {
26814         var position = new google.maps.LatLng(this.latitude, this.longitude);
26815         
26816         var _map = new google.maps.Map(this.el.dom, {
26817             center: position,
26818             zoom: this.zoom,
26819             mapTypeId: this.mapTypeId,
26820             mapTypeControl: this.mapTypeControl,
26821             disableDoubleClickZoom: this.disableDoubleClickZoom,
26822             scrollwheel: this.scrollwheel,
26823             streetViewControl: this.streetViewControl,
26824             locationName: this.locationName,
26825             draggable: this.draggable,
26826             enableAutocomplete: this.enableAutocomplete,
26827             enableReverseGeocode: this.enableReverseGeocode
26828         });
26829         
26830         var _marker = new google.maps.Marker({
26831             position: position,
26832             map: _map,
26833             title: this.markerTitle,
26834             draggable: this.draggable
26835         });
26836         
26837         return {
26838             map: _map,
26839             marker: _marker,
26840             circle: null,
26841             location: position,
26842             radius: this.radius,
26843             locationName: this.locationName,
26844             addressComponents: {
26845                 formatted_address: null,
26846                 addressLine1: null,
26847                 addressLine2: null,
26848                 streetName: null,
26849                 streetNumber: null,
26850                 city: null,
26851                 district: null,
26852                 state: null,
26853                 stateOrProvince: null
26854             },
26855             settings: this,
26856             domContainer: this.el.dom,
26857             geodecoder: new google.maps.Geocoder()
26858         };
26859     },
26860     
26861     drawCircle: function(center, radius, options) 
26862     {
26863         if (this.gMapContext.circle != null) {
26864             this.gMapContext.circle.setMap(null);
26865         }
26866         if (radius > 0) {
26867             radius *= 1;
26868             options = Roo.apply({}, options, {
26869                 strokeColor: "#0000FF",
26870                 strokeOpacity: .35,
26871                 strokeWeight: 2,
26872                 fillColor: "#0000FF",
26873                 fillOpacity: .2
26874             });
26875             
26876             options.map = this.gMapContext.map;
26877             options.radius = radius;
26878             options.center = center;
26879             this.gMapContext.circle = new google.maps.Circle(options);
26880             return this.gMapContext.circle;
26881         }
26882         
26883         return null;
26884     },
26885     
26886     setPosition: function(location) 
26887     {
26888         this.gMapContext.location = location;
26889         this.gMapContext.marker.setPosition(location);
26890         this.gMapContext.map.panTo(location);
26891         this.drawCircle(location, this.gMapContext.radius, {});
26892         
26893         var _this = this;
26894         
26895         if (this.gMapContext.settings.enableReverseGeocode) {
26896             this.gMapContext.geodecoder.geocode({
26897                 latLng: this.gMapContext.location
26898             }, function(results, status) {
26899                 
26900                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26901                     _this.gMapContext.locationName = results[0].formatted_address;
26902                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26903                     
26904                     _this.fireEvent('positionchanged', this, location);
26905                 }
26906             });
26907             
26908             return;
26909         }
26910         
26911         this.fireEvent('positionchanged', this, location);
26912     },
26913     
26914     resize: function()
26915     {
26916         google.maps.event.trigger(this.gMapContext.map, "resize");
26917         
26918         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26919         
26920         this.fireEvent('resize', this);
26921     },
26922     
26923     setPositionByLatLng: function(latitude, longitude)
26924     {
26925         this.setPosition(new google.maps.LatLng(latitude, longitude));
26926     },
26927     
26928     getCurrentPosition: function() 
26929     {
26930         return {
26931             latitude: this.gMapContext.location.lat(),
26932             longitude: this.gMapContext.location.lng()
26933         };
26934     },
26935     
26936     getAddressName: function() 
26937     {
26938         return this.gMapContext.locationName;
26939     },
26940     
26941     getAddressComponents: function() 
26942     {
26943         return this.gMapContext.addressComponents;
26944     },
26945     
26946     address_component_from_google_geocode: function(address_components) 
26947     {
26948         var result = {};
26949         
26950         for (var i = 0; i < address_components.length; i++) {
26951             var component = address_components[i];
26952             if (component.types.indexOf("postal_code") >= 0) {
26953                 result.postalCode = component.short_name;
26954             } else if (component.types.indexOf("street_number") >= 0) {
26955                 result.streetNumber = component.short_name;
26956             } else if (component.types.indexOf("route") >= 0) {
26957                 result.streetName = component.short_name;
26958             } else if (component.types.indexOf("neighborhood") >= 0) {
26959                 result.city = component.short_name;
26960             } else if (component.types.indexOf("locality") >= 0) {
26961                 result.city = component.short_name;
26962             } else if (component.types.indexOf("sublocality") >= 0) {
26963                 result.district = component.short_name;
26964             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26965                 result.stateOrProvince = component.short_name;
26966             } else if (component.types.indexOf("country") >= 0) {
26967                 result.country = component.short_name;
26968             }
26969         }
26970         
26971         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26972         result.addressLine2 = "";
26973         return result;
26974     },
26975     
26976     setZoomLevel: function(zoom)
26977     {
26978         this.gMapContext.map.setZoom(zoom);
26979     },
26980     
26981     show: function()
26982     {
26983         if(!this.el){
26984             return;
26985         }
26986         
26987         this.el.show();
26988         
26989         this.resize();
26990         
26991         this.fireEvent('show', this);
26992     },
26993     
26994     hide: function()
26995     {
26996         if(!this.el){
26997             return;
26998         }
26999         
27000         this.el.hide();
27001         
27002         this.fireEvent('hide', this);
27003     }
27004     
27005 });
27006
27007 Roo.apply(Roo.bootstrap.LocationPicker, {
27008     
27009     OverlayView : function(map, options)
27010     {
27011         options = options || {};
27012         
27013         this.setMap(map);
27014     }
27015     
27016     
27017 });/*
27018  * - LGPL
27019  *
27020  * Alert
27021  * 
27022  */
27023
27024 /**
27025  * @class Roo.bootstrap.Alert
27026  * @extends Roo.bootstrap.Component
27027  * Bootstrap Alert class
27028  * @cfg {String} title The title of alert
27029  * @cfg {String} html The content of alert
27030  * @cfg {String} weight (  success | info | warning | danger )
27031  * @cfg {String} faicon font-awesomeicon
27032  * 
27033  * @constructor
27034  * Create a new alert
27035  * @param {Object} config The config object
27036  */
27037
27038
27039 Roo.bootstrap.Alert = function(config){
27040     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27041     
27042 };
27043
27044 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27045     
27046     title: '',
27047     html: '',
27048     weight: false,
27049     faicon: false,
27050     
27051     getAutoCreate : function()
27052     {
27053         
27054         var cfg = {
27055             tag : 'div',
27056             cls : 'alert',
27057             cn : [
27058                 {
27059                     tag : 'i',
27060                     cls : 'roo-alert-icon'
27061                     
27062                 },
27063                 {
27064                     tag : 'b',
27065                     cls : 'roo-alert-title',
27066                     html : this.title
27067                 },
27068                 {
27069                     tag : 'span',
27070                     cls : 'roo-alert-text',
27071                     html : this.html
27072                 }
27073             ]
27074         };
27075         
27076         if(this.faicon){
27077             cfg.cn[0].cls += ' fa ' + this.faicon;
27078         }
27079         
27080         if(this.weight){
27081             cfg.cls += ' alert-' + this.weight;
27082         }
27083         
27084         return cfg;
27085     },
27086     
27087     initEvents: function() 
27088     {
27089         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27090     },
27091     
27092     setTitle : function(str)
27093     {
27094         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27095     },
27096     
27097     setText : function(str)
27098     {
27099         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27100     },
27101     
27102     setWeight : function(weight)
27103     {
27104         if(this.weight){
27105             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27106         }
27107         
27108         this.weight = weight;
27109         
27110         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27111     },
27112     
27113     setIcon : function(icon)
27114     {
27115         if(this.faicon){
27116             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27117         }
27118         
27119         this.faicon = icon;
27120         
27121         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27122     },
27123     
27124     hide: function() 
27125     {
27126         this.el.hide();   
27127     },
27128     
27129     show: function() 
27130     {  
27131         this.el.show();   
27132     }
27133     
27134 });
27135
27136  
27137 /*
27138 * Licence: LGPL
27139 */
27140
27141 /**
27142  * @class Roo.bootstrap.UploadCropbox
27143  * @extends Roo.bootstrap.Component
27144  * Bootstrap UploadCropbox class
27145  * @cfg {String} emptyText show when image has been loaded
27146  * @cfg {String} rotateNotify show when image too small to rotate
27147  * @cfg {Number} errorTimeout default 3000
27148  * @cfg {Number} minWidth default 300
27149  * @cfg {Number} minHeight default 300
27150  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27151  * @cfg {Boolean} isDocument (true|false) default false
27152  * @cfg {String} url action url
27153  * @cfg {String} paramName default 'imageUpload'
27154  * @cfg {String} method default POST
27155  * @cfg {Boolean} loadMask (true|false) default true
27156  * @cfg {Boolean} loadingText default 'Loading...'
27157  * 
27158  * @constructor
27159  * Create a new UploadCropbox
27160  * @param {Object} config The config object
27161  */
27162
27163 Roo.bootstrap.UploadCropbox = function(config){
27164     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27165     
27166     this.addEvents({
27167         /**
27168          * @event beforeselectfile
27169          * Fire before select file
27170          * @param {Roo.bootstrap.UploadCropbox} this
27171          */
27172         "beforeselectfile" : true,
27173         /**
27174          * @event initial
27175          * Fire after initEvent
27176          * @param {Roo.bootstrap.UploadCropbox} this
27177          */
27178         "initial" : true,
27179         /**
27180          * @event crop
27181          * Fire after initEvent
27182          * @param {Roo.bootstrap.UploadCropbox} this
27183          * @param {String} data
27184          */
27185         "crop" : true,
27186         /**
27187          * @event prepare
27188          * Fire when preparing the file data
27189          * @param {Roo.bootstrap.UploadCropbox} this
27190          * @param {Object} file
27191          */
27192         "prepare" : true,
27193         /**
27194          * @event exception
27195          * Fire when get exception
27196          * @param {Roo.bootstrap.UploadCropbox} this
27197          * @param {XMLHttpRequest} xhr
27198          */
27199         "exception" : true,
27200         /**
27201          * @event beforeloadcanvas
27202          * Fire before load the canvas
27203          * @param {Roo.bootstrap.UploadCropbox} this
27204          * @param {String} src
27205          */
27206         "beforeloadcanvas" : true,
27207         /**
27208          * @event trash
27209          * Fire when trash image
27210          * @param {Roo.bootstrap.UploadCropbox} this
27211          */
27212         "trash" : true,
27213         /**
27214          * @event download
27215          * Fire when download the image
27216          * @param {Roo.bootstrap.UploadCropbox} this
27217          */
27218         "download" : true,
27219         /**
27220          * @event footerbuttonclick
27221          * Fire when footerbuttonclick
27222          * @param {Roo.bootstrap.UploadCropbox} this
27223          * @param {String} type
27224          */
27225         "footerbuttonclick" : true,
27226         /**
27227          * @event resize
27228          * Fire when resize
27229          * @param {Roo.bootstrap.UploadCropbox} this
27230          */
27231         "resize" : true,
27232         /**
27233          * @event rotate
27234          * Fire when rotate the image
27235          * @param {Roo.bootstrap.UploadCropbox} this
27236          * @param {String} pos
27237          */
27238         "rotate" : true,
27239         /**
27240          * @event inspect
27241          * Fire when inspect the file
27242          * @param {Roo.bootstrap.UploadCropbox} this
27243          * @param {Object} file
27244          */
27245         "inspect" : true,
27246         /**
27247          * @event upload
27248          * Fire when xhr upload the file
27249          * @param {Roo.bootstrap.UploadCropbox} this
27250          * @param {Object} data
27251          */
27252         "upload" : true,
27253         /**
27254          * @event arrange
27255          * Fire when arrange the file data
27256          * @param {Roo.bootstrap.UploadCropbox} this
27257          * @param {Object} formData
27258          */
27259         "arrange" : true
27260     });
27261     
27262     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27263 };
27264
27265 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27266     
27267     emptyText : 'Click to upload image',
27268     rotateNotify : 'Image is too small to rotate',
27269     errorTimeout : 3000,
27270     scale : 0,
27271     baseScale : 1,
27272     rotate : 0,
27273     dragable : false,
27274     pinching : false,
27275     mouseX : 0,
27276     mouseY : 0,
27277     cropData : false,
27278     minWidth : 300,
27279     minHeight : 300,
27280     file : false,
27281     exif : {},
27282     baseRotate : 1,
27283     cropType : 'image/jpeg',
27284     buttons : false,
27285     canvasLoaded : false,
27286     isDocument : false,
27287     method : 'POST',
27288     paramName : 'imageUpload',
27289     loadMask : true,
27290     loadingText : 'Loading...',
27291     maskEl : false,
27292     
27293     getAutoCreate : function()
27294     {
27295         var cfg = {
27296             tag : 'div',
27297             cls : 'roo-upload-cropbox',
27298             cn : [
27299                 {
27300                     tag : 'input',
27301                     cls : 'roo-upload-cropbox-selector',
27302                     type : 'file'
27303                 },
27304                 {
27305                     tag : 'div',
27306                     cls : 'roo-upload-cropbox-body',
27307                     style : 'cursor:pointer',
27308                     cn : [
27309                         {
27310                             tag : 'div',
27311                             cls : 'roo-upload-cropbox-preview'
27312                         },
27313                         {
27314                             tag : 'div',
27315                             cls : 'roo-upload-cropbox-thumb'
27316                         },
27317                         {
27318                             tag : 'div',
27319                             cls : 'roo-upload-cropbox-empty-notify',
27320                             html : this.emptyText
27321                         },
27322                         {
27323                             tag : 'div',
27324                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27325                             html : this.rotateNotify
27326                         }
27327                     ]
27328                 },
27329                 {
27330                     tag : 'div',
27331                     cls : 'roo-upload-cropbox-footer',
27332                     cn : {
27333                         tag : 'div',
27334                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27335                         cn : []
27336                     }
27337                 }
27338             ]
27339         };
27340         
27341         return cfg;
27342     },
27343     
27344     onRender : function(ct, position)
27345     {
27346         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27347         
27348         if (this.buttons.length) {
27349             
27350             Roo.each(this.buttons, function(bb) {
27351                 
27352                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27353                 
27354                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27355                 
27356             }, this);
27357         }
27358         
27359         if(this.loadMask){
27360             this.maskEl = this.el;
27361         }
27362     },
27363     
27364     initEvents : function()
27365     {
27366         this.urlAPI = (window.createObjectURL && window) || 
27367                                 (window.URL && URL.revokeObjectURL && URL) || 
27368                                 (window.webkitURL && webkitURL);
27369                         
27370         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27371         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27372         
27373         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27374         this.selectorEl.hide();
27375         
27376         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27377         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27378         
27379         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27380         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27381         this.thumbEl.hide();
27382         
27383         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27384         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27385         
27386         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27387         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27388         this.errorEl.hide();
27389         
27390         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27391         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27392         this.footerEl.hide();
27393         
27394         this.setThumbBoxSize();
27395         
27396         this.bind();
27397         
27398         this.resize();
27399         
27400         this.fireEvent('initial', this);
27401     },
27402
27403     bind : function()
27404     {
27405         var _this = this;
27406         
27407         window.addEventListener("resize", function() { _this.resize(); } );
27408         
27409         this.bodyEl.on('click', this.beforeSelectFile, this);
27410         
27411         if(Roo.isTouch){
27412             this.bodyEl.on('touchstart', this.onTouchStart, this);
27413             this.bodyEl.on('touchmove', this.onTouchMove, this);
27414             this.bodyEl.on('touchend', this.onTouchEnd, this);
27415         }
27416         
27417         if(!Roo.isTouch){
27418             this.bodyEl.on('mousedown', this.onMouseDown, this);
27419             this.bodyEl.on('mousemove', this.onMouseMove, this);
27420             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27421             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27422             Roo.get(document).on('mouseup', this.onMouseUp, this);
27423         }
27424         
27425         this.selectorEl.on('change', this.onFileSelected, this);
27426     },
27427     
27428     reset : function()
27429     {    
27430         this.scale = 0;
27431         this.baseScale = 1;
27432         this.rotate = 0;
27433         this.baseRotate = 1;
27434         this.dragable = false;
27435         this.pinching = false;
27436         this.mouseX = 0;
27437         this.mouseY = 0;
27438         this.cropData = false;
27439         this.notifyEl.dom.innerHTML = this.emptyText;
27440         
27441         this.selectorEl.dom.value = '';
27442         
27443     },
27444     
27445     resize : function()
27446     {
27447         if(this.fireEvent('resize', this) != false){
27448             this.setThumbBoxPosition();
27449             this.setCanvasPosition();
27450         }
27451     },
27452     
27453     onFooterButtonClick : function(e, el, o, type)
27454     {
27455         switch (type) {
27456             case 'rotate-left' :
27457                 this.onRotateLeft(e);
27458                 break;
27459             case 'rotate-right' :
27460                 this.onRotateRight(e);
27461                 break;
27462             case 'picture' :
27463                 this.beforeSelectFile(e);
27464                 break;
27465             case 'trash' :
27466                 this.trash(e);
27467                 break;
27468             case 'crop' :
27469                 this.crop(e);
27470                 break;
27471             case 'download' :
27472                 this.download(e);
27473                 break;
27474             default :
27475                 break;
27476         }
27477         
27478         this.fireEvent('footerbuttonclick', this, type);
27479     },
27480     
27481     beforeSelectFile : function(e)
27482     {
27483         e.preventDefault();
27484         
27485         if(this.fireEvent('beforeselectfile', this) != false){
27486             this.selectorEl.dom.click();
27487         }
27488     },
27489     
27490     onFileSelected : function(e)
27491     {
27492         e.preventDefault();
27493         
27494         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27495             return;
27496         }
27497         
27498         var file = this.selectorEl.dom.files[0];
27499         
27500         if(this.fireEvent('inspect', this, file) != false){
27501             this.prepare(file);
27502         }
27503         
27504     },
27505     
27506     trash : function(e)
27507     {
27508         this.fireEvent('trash', this);
27509     },
27510     
27511     download : function(e)
27512     {
27513         this.fireEvent('download', this);
27514     },
27515     
27516     loadCanvas : function(src)
27517     {   
27518         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27519             
27520             this.reset();
27521             
27522             this.imageEl = document.createElement('img');
27523             
27524             var _this = this;
27525             
27526             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27527             
27528             this.imageEl.src = src;
27529         }
27530     },
27531     
27532     onLoadCanvas : function()
27533     {   
27534         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27535         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27536         
27537         this.bodyEl.un('click', this.beforeSelectFile, this);
27538         
27539         this.notifyEl.hide();
27540         this.thumbEl.show();
27541         this.footerEl.show();
27542         
27543         this.baseRotateLevel();
27544         
27545         if(this.isDocument){
27546             this.setThumbBoxSize();
27547         }
27548         
27549         this.setThumbBoxPosition();
27550         
27551         this.baseScaleLevel();
27552         
27553         this.draw();
27554         
27555         this.resize();
27556         
27557         this.canvasLoaded = true;
27558         
27559         if(this.loadMask){
27560             this.maskEl.unmask();
27561         }
27562         
27563     },
27564     
27565     setCanvasPosition : function()
27566     {   
27567         if(!this.canvasEl){
27568             return;
27569         }
27570         
27571         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27572         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27573         
27574         this.previewEl.setLeft(pw);
27575         this.previewEl.setTop(ph);
27576         
27577     },
27578     
27579     onMouseDown : function(e)
27580     {   
27581         e.stopEvent();
27582         
27583         this.dragable = true;
27584         this.pinching = false;
27585         
27586         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27587             this.dragable = false;
27588             return;
27589         }
27590         
27591         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27592         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27593         
27594     },
27595     
27596     onMouseMove : function(e)
27597     {   
27598         e.stopEvent();
27599         
27600         if(!this.canvasLoaded){
27601             return;
27602         }
27603         
27604         if (!this.dragable){
27605             return;
27606         }
27607         
27608         var minX = Math.ceil(this.thumbEl.getLeft(true));
27609         var minY = Math.ceil(this.thumbEl.getTop(true));
27610         
27611         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27612         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27613         
27614         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27615         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27616         
27617         x = x - this.mouseX;
27618         y = y - this.mouseY;
27619         
27620         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27621         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27622         
27623         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27624         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27625         
27626         this.previewEl.setLeft(bgX);
27627         this.previewEl.setTop(bgY);
27628         
27629         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27630         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27631     },
27632     
27633     onMouseUp : function(e)
27634     {   
27635         e.stopEvent();
27636         
27637         this.dragable = false;
27638     },
27639     
27640     onMouseWheel : function(e)
27641     {   
27642         e.stopEvent();
27643         
27644         this.startScale = this.scale;
27645         
27646         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27647         
27648         if(!this.zoomable()){
27649             this.scale = this.startScale;
27650             return;
27651         }
27652         
27653         this.draw();
27654         
27655         return;
27656     },
27657     
27658     zoomable : function()
27659     {
27660         var minScale = this.thumbEl.getWidth() / this.minWidth;
27661         
27662         if(this.minWidth < this.minHeight){
27663             minScale = this.thumbEl.getHeight() / this.minHeight;
27664         }
27665         
27666         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27667         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27668         
27669         if(
27670                 this.isDocument &&
27671                 (this.rotate == 0 || this.rotate == 180) && 
27672                 (
27673                     width > this.imageEl.OriginWidth || 
27674                     height > this.imageEl.OriginHeight ||
27675                     (width < this.minWidth && height < this.minHeight)
27676                 )
27677         ){
27678             return false;
27679         }
27680         
27681         if(
27682                 this.isDocument &&
27683                 (this.rotate == 90 || this.rotate == 270) && 
27684                 (
27685                     width > this.imageEl.OriginWidth || 
27686                     height > this.imageEl.OriginHeight ||
27687                     (width < this.minHeight && height < this.minWidth)
27688                 )
27689         ){
27690             return false;
27691         }
27692         
27693         if(
27694                 !this.isDocument &&
27695                 (this.rotate == 0 || this.rotate == 180) && 
27696                 (
27697                     width < this.minWidth || 
27698                     width > this.imageEl.OriginWidth || 
27699                     height < this.minHeight || 
27700                     height > this.imageEl.OriginHeight
27701                 )
27702         ){
27703             return false;
27704         }
27705         
27706         if(
27707                 !this.isDocument &&
27708                 (this.rotate == 90 || this.rotate == 270) && 
27709                 (
27710                     width < this.minHeight || 
27711                     width > this.imageEl.OriginWidth || 
27712                     height < this.minWidth || 
27713                     height > this.imageEl.OriginHeight
27714                 )
27715         ){
27716             return false;
27717         }
27718         
27719         return true;
27720         
27721     },
27722     
27723     onRotateLeft : function(e)
27724     {   
27725         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27726             
27727             var minScale = this.thumbEl.getWidth() / this.minWidth;
27728             
27729             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27730             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27731             
27732             this.startScale = this.scale;
27733             
27734             while (this.getScaleLevel() < minScale){
27735             
27736                 this.scale = this.scale + 1;
27737                 
27738                 if(!this.zoomable()){
27739                     break;
27740                 }
27741                 
27742                 if(
27743                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27744                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27745                 ){
27746                     continue;
27747                 }
27748                 
27749                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27750
27751                 this.draw();
27752                 
27753                 return;
27754             }
27755             
27756             this.scale = this.startScale;
27757             
27758             this.onRotateFail();
27759             
27760             return false;
27761         }
27762         
27763         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27764
27765         if(this.isDocument){
27766             this.setThumbBoxSize();
27767             this.setThumbBoxPosition();
27768             this.setCanvasPosition();
27769         }
27770         
27771         this.draw();
27772         
27773         this.fireEvent('rotate', this, 'left');
27774         
27775     },
27776     
27777     onRotateRight : function(e)
27778     {
27779         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27780             
27781             var minScale = this.thumbEl.getWidth() / this.minWidth;
27782         
27783             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27784             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27785             
27786             this.startScale = this.scale;
27787             
27788             while (this.getScaleLevel() < minScale){
27789             
27790                 this.scale = this.scale + 1;
27791                 
27792                 if(!this.zoomable()){
27793                     break;
27794                 }
27795                 
27796                 if(
27797                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27798                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27799                 ){
27800                     continue;
27801                 }
27802                 
27803                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27804
27805                 this.draw();
27806                 
27807                 return;
27808             }
27809             
27810             this.scale = this.startScale;
27811             
27812             this.onRotateFail();
27813             
27814             return false;
27815         }
27816         
27817         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27818
27819         if(this.isDocument){
27820             this.setThumbBoxSize();
27821             this.setThumbBoxPosition();
27822             this.setCanvasPosition();
27823         }
27824         
27825         this.draw();
27826         
27827         this.fireEvent('rotate', this, 'right');
27828     },
27829     
27830     onRotateFail : function()
27831     {
27832         this.errorEl.show(true);
27833         
27834         var _this = this;
27835         
27836         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27837     },
27838     
27839     draw : function()
27840     {
27841         this.previewEl.dom.innerHTML = '';
27842         
27843         var canvasEl = document.createElement("canvas");
27844         
27845         var contextEl = canvasEl.getContext("2d");
27846         
27847         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27848         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27849         var center = this.imageEl.OriginWidth / 2;
27850         
27851         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27852             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27853             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27854             center = this.imageEl.OriginHeight / 2;
27855         }
27856         
27857         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27858         
27859         contextEl.translate(center, center);
27860         contextEl.rotate(this.rotate * Math.PI / 180);
27861
27862         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27863         
27864         this.canvasEl = document.createElement("canvas");
27865         
27866         this.contextEl = this.canvasEl.getContext("2d");
27867         
27868         switch (this.rotate) {
27869             case 0 :
27870                 
27871                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27872                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27873                 
27874                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27875                 
27876                 break;
27877             case 90 : 
27878                 
27879                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27880                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27881                 
27882                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27883                     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);
27884                     break;
27885                 }
27886                 
27887                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27888                 
27889                 break;
27890             case 180 :
27891                 
27892                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27893                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27894                 
27895                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27896                     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);
27897                     break;
27898                 }
27899                 
27900                 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);
27901                 
27902                 break;
27903             case 270 :
27904                 
27905                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27906                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27907         
27908                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27909                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27910                     break;
27911                 }
27912                 
27913                 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);
27914                 
27915                 break;
27916             default : 
27917                 break;
27918         }
27919         
27920         this.previewEl.appendChild(this.canvasEl);
27921         
27922         this.setCanvasPosition();
27923     },
27924     
27925     crop : function()
27926     {
27927         if(!this.canvasLoaded){
27928             return;
27929         }
27930         
27931         var imageCanvas = document.createElement("canvas");
27932         
27933         var imageContext = imageCanvas.getContext("2d");
27934         
27935         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27936         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27937         
27938         var center = imageCanvas.width / 2;
27939         
27940         imageContext.translate(center, center);
27941         
27942         imageContext.rotate(this.rotate * Math.PI / 180);
27943         
27944         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27945         
27946         var canvas = document.createElement("canvas");
27947         
27948         var context = canvas.getContext("2d");
27949                 
27950         canvas.width = this.minWidth;
27951         canvas.height = this.minHeight;
27952
27953         switch (this.rotate) {
27954             case 0 :
27955                 
27956                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27957                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27958                 
27959                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27960                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27961                 
27962                 var targetWidth = this.minWidth - 2 * x;
27963                 var targetHeight = this.minHeight - 2 * y;
27964                 
27965                 var scale = 1;
27966                 
27967                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27968                     scale = targetWidth / width;
27969                 }
27970                 
27971                 if(x > 0 && y == 0){
27972                     scale = targetHeight / height;
27973                 }
27974                 
27975                 if(x > 0 && y > 0){
27976                     scale = targetWidth / width;
27977                     
27978                     if(width < height){
27979                         scale = targetHeight / height;
27980                     }
27981                 }
27982                 
27983                 context.scale(scale, scale);
27984                 
27985                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27986                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27987
27988                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27989                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27990
27991                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27992                 
27993                 break;
27994             case 90 : 
27995                 
27996                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27997                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27998                 
27999                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28000                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28001                 
28002                 var targetWidth = this.minWidth - 2 * x;
28003                 var targetHeight = this.minHeight - 2 * y;
28004                 
28005                 var scale = 1;
28006                 
28007                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28008                     scale = targetWidth / width;
28009                 }
28010                 
28011                 if(x > 0 && y == 0){
28012                     scale = targetHeight / height;
28013                 }
28014                 
28015                 if(x > 0 && y > 0){
28016                     scale = targetWidth / width;
28017                     
28018                     if(width < height){
28019                         scale = targetHeight / height;
28020                     }
28021                 }
28022                 
28023                 context.scale(scale, scale);
28024                 
28025                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28026                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28027
28028                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28029                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28030                 
28031                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28032                 
28033                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28034                 
28035                 break;
28036             case 180 :
28037                 
28038                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28039                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28040                 
28041                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28042                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28043                 
28044                 var targetWidth = this.minWidth - 2 * x;
28045                 var targetHeight = this.minHeight - 2 * y;
28046                 
28047                 var scale = 1;
28048                 
28049                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28050                     scale = targetWidth / width;
28051                 }
28052                 
28053                 if(x > 0 && y == 0){
28054                     scale = targetHeight / height;
28055                 }
28056                 
28057                 if(x > 0 && y > 0){
28058                     scale = targetWidth / width;
28059                     
28060                     if(width < height){
28061                         scale = targetHeight / height;
28062                     }
28063                 }
28064                 
28065                 context.scale(scale, scale);
28066                 
28067                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28068                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28069
28070                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28071                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28072
28073                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28074                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28075                 
28076                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28077                 
28078                 break;
28079             case 270 :
28080                 
28081                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28082                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28083                 
28084                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28085                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28086                 
28087                 var targetWidth = this.minWidth - 2 * x;
28088                 var targetHeight = this.minHeight - 2 * y;
28089                 
28090                 var scale = 1;
28091                 
28092                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28093                     scale = targetWidth / width;
28094                 }
28095                 
28096                 if(x > 0 && y == 0){
28097                     scale = targetHeight / height;
28098                 }
28099                 
28100                 if(x > 0 && y > 0){
28101                     scale = targetWidth / width;
28102                     
28103                     if(width < height){
28104                         scale = targetHeight / height;
28105                     }
28106                 }
28107                 
28108                 context.scale(scale, scale);
28109                 
28110                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28111                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28112
28113                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28114                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28115                 
28116                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28117                 
28118                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28119                 
28120                 break;
28121             default : 
28122                 break;
28123         }
28124         
28125         this.cropData = canvas.toDataURL(this.cropType);
28126         
28127         if(this.fireEvent('crop', this, this.cropData) !== false){
28128             this.process(this.file, this.cropData);
28129         }
28130         
28131         return;
28132         
28133     },
28134     
28135     setThumbBoxSize : function()
28136     {
28137         var width, height;
28138         
28139         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28140             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28141             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28142             
28143             this.minWidth = width;
28144             this.minHeight = height;
28145             
28146             if(this.rotate == 90 || this.rotate == 270){
28147                 this.minWidth = height;
28148                 this.minHeight = width;
28149             }
28150         }
28151         
28152         height = 300;
28153         width = Math.ceil(this.minWidth * height / this.minHeight);
28154         
28155         if(this.minWidth > this.minHeight){
28156             width = 300;
28157             height = Math.ceil(this.minHeight * width / this.minWidth);
28158         }
28159         
28160         this.thumbEl.setStyle({
28161             width : width + 'px',
28162             height : height + 'px'
28163         });
28164
28165         return;
28166             
28167     },
28168     
28169     setThumbBoxPosition : function()
28170     {
28171         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28172         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28173         
28174         this.thumbEl.setLeft(x);
28175         this.thumbEl.setTop(y);
28176         
28177     },
28178     
28179     baseRotateLevel : function()
28180     {
28181         this.baseRotate = 1;
28182         
28183         if(
28184                 typeof(this.exif) != 'undefined' &&
28185                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28186                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28187         ){
28188             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28189         }
28190         
28191         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28192         
28193     },
28194     
28195     baseScaleLevel : function()
28196     {
28197         var width, height;
28198         
28199         if(this.isDocument){
28200             
28201             if(this.baseRotate == 6 || this.baseRotate == 8){
28202             
28203                 height = this.thumbEl.getHeight();
28204                 this.baseScale = height / this.imageEl.OriginWidth;
28205
28206                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28207                     width = this.thumbEl.getWidth();
28208                     this.baseScale = width / this.imageEl.OriginHeight;
28209                 }
28210
28211                 return;
28212             }
28213
28214             height = this.thumbEl.getHeight();
28215             this.baseScale = height / this.imageEl.OriginHeight;
28216
28217             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28218                 width = this.thumbEl.getWidth();
28219                 this.baseScale = width / this.imageEl.OriginWidth;
28220             }
28221
28222             return;
28223         }
28224         
28225         if(this.baseRotate == 6 || this.baseRotate == 8){
28226             
28227             width = this.thumbEl.getHeight();
28228             this.baseScale = width / this.imageEl.OriginHeight;
28229             
28230             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28231                 height = this.thumbEl.getWidth();
28232                 this.baseScale = height / this.imageEl.OriginHeight;
28233             }
28234             
28235             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28236                 height = this.thumbEl.getWidth();
28237                 this.baseScale = height / this.imageEl.OriginHeight;
28238                 
28239                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28240                     width = this.thumbEl.getHeight();
28241                     this.baseScale = width / this.imageEl.OriginWidth;
28242                 }
28243             }
28244             
28245             return;
28246         }
28247         
28248         width = this.thumbEl.getWidth();
28249         this.baseScale = width / this.imageEl.OriginWidth;
28250         
28251         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28252             height = this.thumbEl.getHeight();
28253             this.baseScale = height / this.imageEl.OriginHeight;
28254         }
28255         
28256         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28257             
28258             height = this.thumbEl.getHeight();
28259             this.baseScale = height / this.imageEl.OriginHeight;
28260             
28261             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28262                 width = this.thumbEl.getWidth();
28263                 this.baseScale = width / this.imageEl.OriginWidth;
28264             }
28265             
28266         }
28267         
28268         return;
28269     },
28270     
28271     getScaleLevel : function()
28272     {
28273         return this.baseScale * Math.pow(1.1, this.scale);
28274     },
28275     
28276     onTouchStart : function(e)
28277     {
28278         if(!this.canvasLoaded){
28279             this.beforeSelectFile(e);
28280             return;
28281         }
28282         
28283         var touches = e.browserEvent.touches;
28284         
28285         if(!touches){
28286             return;
28287         }
28288         
28289         if(touches.length == 1){
28290             this.onMouseDown(e);
28291             return;
28292         }
28293         
28294         if(touches.length != 2){
28295             return;
28296         }
28297         
28298         var coords = [];
28299         
28300         for(var i = 0, finger; finger = touches[i]; i++){
28301             coords.push(finger.pageX, finger.pageY);
28302         }
28303         
28304         var x = Math.pow(coords[0] - coords[2], 2);
28305         var y = Math.pow(coords[1] - coords[3], 2);
28306         
28307         this.startDistance = Math.sqrt(x + y);
28308         
28309         this.startScale = this.scale;
28310         
28311         this.pinching = true;
28312         this.dragable = false;
28313         
28314     },
28315     
28316     onTouchMove : function(e)
28317     {
28318         if(!this.pinching && !this.dragable){
28319             return;
28320         }
28321         
28322         var touches = e.browserEvent.touches;
28323         
28324         if(!touches){
28325             return;
28326         }
28327         
28328         if(this.dragable){
28329             this.onMouseMove(e);
28330             return;
28331         }
28332         
28333         var coords = [];
28334         
28335         for(var i = 0, finger; finger = touches[i]; i++){
28336             coords.push(finger.pageX, finger.pageY);
28337         }
28338         
28339         var x = Math.pow(coords[0] - coords[2], 2);
28340         var y = Math.pow(coords[1] - coords[3], 2);
28341         
28342         this.endDistance = Math.sqrt(x + y);
28343         
28344         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28345         
28346         if(!this.zoomable()){
28347             this.scale = this.startScale;
28348             return;
28349         }
28350         
28351         this.draw();
28352         
28353     },
28354     
28355     onTouchEnd : function(e)
28356     {
28357         this.pinching = false;
28358         this.dragable = false;
28359         
28360     },
28361     
28362     process : function(file, crop)
28363     {
28364         if(this.loadMask){
28365             this.maskEl.mask(this.loadingText);
28366         }
28367         
28368         this.xhr = new XMLHttpRequest();
28369         
28370         file.xhr = this.xhr;
28371
28372         this.xhr.open(this.method, this.url, true);
28373         
28374         var headers = {
28375             "Accept": "application/json",
28376             "Cache-Control": "no-cache",
28377             "X-Requested-With": "XMLHttpRequest"
28378         };
28379         
28380         for (var headerName in headers) {
28381             var headerValue = headers[headerName];
28382             if (headerValue) {
28383                 this.xhr.setRequestHeader(headerName, headerValue);
28384             }
28385         }
28386         
28387         var _this = this;
28388         
28389         this.xhr.onload = function()
28390         {
28391             _this.xhrOnLoad(_this.xhr);
28392         }
28393         
28394         this.xhr.onerror = function()
28395         {
28396             _this.xhrOnError(_this.xhr);
28397         }
28398         
28399         var formData = new FormData();
28400
28401         formData.append('returnHTML', 'NO');
28402         
28403         if(crop){
28404             formData.append('crop', crop);
28405         }
28406         
28407         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28408             formData.append(this.paramName, file, file.name);
28409         }
28410         
28411         if(typeof(file.filename) != 'undefined'){
28412             formData.append('filename', file.filename);
28413         }
28414         
28415         if(typeof(file.mimetype) != 'undefined'){
28416             formData.append('mimetype', file.mimetype);
28417         }
28418         
28419         if(this.fireEvent('arrange', this, formData) != false){
28420             this.xhr.send(formData);
28421         };
28422     },
28423     
28424     xhrOnLoad : function(xhr)
28425     {
28426         if(this.loadMask){
28427             this.maskEl.unmask();
28428         }
28429         
28430         if (xhr.readyState !== 4) {
28431             this.fireEvent('exception', this, xhr);
28432             return;
28433         }
28434
28435         var response = Roo.decode(xhr.responseText);
28436         
28437         if(!response.success){
28438             this.fireEvent('exception', this, xhr);
28439             return;
28440         }
28441         
28442         var response = Roo.decode(xhr.responseText);
28443         
28444         this.fireEvent('upload', this, response);
28445         
28446     },
28447     
28448     xhrOnError : function()
28449     {
28450         if(this.loadMask){
28451             this.maskEl.unmask();
28452         }
28453         
28454         Roo.log('xhr on error');
28455         
28456         var response = Roo.decode(xhr.responseText);
28457           
28458         Roo.log(response);
28459         
28460     },
28461     
28462     prepare : function(file)
28463     {   
28464         if(this.loadMask){
28465             this.maskEl.mask(this.loadingText);
28466         }
28467         
28468         this.file = false;
28469         this.exif = {};
28470         
28471         if(typeof(file) === 'string'){
28472             this.loadCanvas(file);
28473             return;
28474         }
28475         
28476         if(!file || !this.urlAPI){
28477             return;
28478         }
28479         
28480         this.file = file;
28481         this.cropType = file.type;
28482         
28483         var _this = this;
28484         
28485         if(this.fireEvent('prepare', this, this.file) != false){
28486             
28487             var reader = new FileReader();
28488             
28489             reader.onload = function (e) {
28490                 if (e.target.error) {
28491                     Roo.log(e.target.error);
28492                     return;
28493                 }
28494                 
28495                 var buffer = e.target.result,
28496                     dataView = new DataView(buffer),
28497                     offset = 2,
28498                     maxOffset = dataView.byteLength - 4,
28499                     markerBytes,
28500                     markerLength;
28501                 
28502                 if (dataView.getUint16(0) === 0xffd8) {
28503                     while (offset < maxOffset) {
28504                         markerBytes = dataView.getUint16(offset);
28505                         
28506                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28507                             markerLength = dataView.getUint16(offset + 2) + 2;
28508                             if (offset + markerLength > dataView.byteLength) {
28509                                 Roo.log('Invalid meta data: Invalid segment size.');
28510                                 break;
28511                             }
28512                             
28513                             if(markerBytes == 0xffe1){
28514                                 _this.parseExifData(
28515                                     dataView,
28516                                     offset,
28517                                     markerLength
28518                                 );
28519                             }
28520                             
28521                             offset += markerLength;
28522                             
28523                             continue;
28524                         }
28525                         
28526                         break;
28527                     }
28528                     
28529                 }
28530                 
28531                 var url = _this.urlAPI.createObjectURL(_this.file);
28532                 
28533                 _this.loadCanvas(url);
28534                 
28535                 return;
28536             }
28537             
28538             reader.readAsArrayBuffer(this.file);
28539             
28540         }
28541         
28542     },
28543     
28544     parseExifData : function(dataView, offset, length)
28545     {
28546         var tiffOffset = offset + 10,
28547             littleEndian,
28548             dirOffset;
28549     
28550         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28551             // No Exif data, might be XMP data instead
28552             return;
28553         }
28554         
28555         // Check for the ASCII code for "Exif" (0x45786966):
28556         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28557             // No Exif data, might be XMP data instead
28558             return;
28559         }
28560         if (tiffOffset + 8 > dataView.byteLength) {
28561             Roo.log('Invalid Exif data: Invalid segment size.');
28562             return;
28563         }
28564         // Check for the two null bytes:
28565         if (dataView.getUint16(offset + 8) !== 0x0000) {
28566             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28567             return;
28568         }
28569         // Check the byte alignment:
28570         switch (dataView.getUint16(tiffOffset)) {
28571         case 0x4949:
28572             littleEndian = true;
28573             break;
28574         case 0x4D4D:
28575             littleEndian = false;
28576             break;
28577         default:
28578             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28579             return;
28580         }
28581         // Check for the TIFF tag marker (0x002A):
28582         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28583             Roo.log('Invalid Exif data: Missing TIFF marker.');
28584             return;
28585         }
28586         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28587         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28588         
28589         this.parseExifTags(
28590             dataView,
28591             tiffOffset,
28592             tiffOffset + dirOffset,
28593             littleEndian
28594         );
28595     },
28596     
28597     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28598     {
28599         var tagsNumber,
28600             dirEndOffset,
28601             i;
28602         if (dirOffset + 6 > dataView.byteLength) {
28603             Roo.log('Invalid Exif data: Invalid directory offset.');
28604             return;
28605         }
28606         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28607         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28608         if (dirEndOffset + 4 > dataView.byteLength) {
28609             Roo.log('Invalid Exif data: Invalid directory size.');
28610             return;
28611         }
28612         for (i = 0; i < tagsNumber; i += 1) {
28613             this.parseExifTag(
28614                 dataView,
28615                 tiffOffset,
28616                 dirOffset + 2 + 12 * i, // tag offset
28617                 littleEndian
28618             );
28619         }
28620         // Return the offset to the next directory:
28621         return dataView.getUint32(dirEndOffset, littleEndian);
28622     },
28623     
28624     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28625     {
28626         var tag = dataView.getUint16(offset, littleEndian);
28627         
28628         this.exif[tag] = this.getExifValue(
28629             dataView,
28630             tiffOffset,
28631             offset,
28632             dataView.getUint16(offset + 2, littleEndian), // tag type
28633             dataView.getUint32(offset + 4, littleEndian), // tag length
28634             littleEndian
28635         );
28636     },
28637     
28638     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28639     {
28640         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28641             tagSize,
28642             dataOffset,
28643             values,
28644             i,
28645             str,
28646             c;
28647     
28648         if (!tagType) {
28649             Roo.log('Invalid Exif data: Invalid tag type.');
28650             return;
28651         }
28652         
28653         tagSize = tagType.size * length;
28654         // Determine if the value is contained in the dataOffset bytes,
28655         // or if the value at the dataOffset is a pointer to the actual data:
28656         dataOffset = tagSize > 4 ?
28657                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28658         if (dataOffset + tagSize > dataView.byteLength) {
28659             Roo.log('Invalid Exif data: Invalid data offset.');
28660             return;
28661         }
28662         if (length === 1) {
28663             return tagType.getValue(dataView, dataOffset, littleEndian);
28664         }
28665         values = [];
28666         for (i = 0; i < length; i += 1) {
28667             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28668         }
28669         
28670         if (tagType.ascii) {
28671             str = '';
28672             // Concatenate the chars:
28673             for (i = 0; i < values.length; i += 1) {
28674                 c = values[i];
28675                 // Ignore the terminating NULL byte(s):
28676                 if (c === '\u0000') {
28677                     break;
28678                 }
28679                 str += c;
28680             }
28681             return str;
28682         }
28683         return values;
28684     }
28685     
28686 });
28687
28688 Roo.apply(Roo.bootstrap.UploadCropbox, {
28689     tags : {
28690         'Orientation': 0x0112
28691     },
28692     
28693     Orientation: {
28694             1: 0, //'top-left',
28695 //            2: 'top-right',
28696             3: 180, //'bottom-right',
28697 //            4: 'bottom-left',
28698 //            5: 'left-top',
28699             6: 90, //'right-top',
28700 //            7: 'right-bottom',
28701             8: 270 //'left-bottom'
28702     },
28703     
28704     exifTagTypes : {
28705         // byte, 8-bit unsigned int:
28706         1: {
28707             getValue: function (dataView, dataOffset) {
28708                 return dataView.getUint8(dataOffset);
28709             },
28710             size: 1
28711         },
28712         // ascii, 8-bit byte:
28713         2: {
28714             getValue: function (dataView, dataOffset) {
28715                 return String.fromCharCode(dataView.getUint8(dataOffset));
28716             },
28717             size: 1,
28718             ascii: true
28719         },
28720         // short, 16 bit int:
28721         3: {
28722             getValue: function (dataView, dataOffset, littleEndian) {
28723                 return dataView.getUint16(dataOffset, littleEndian);
28724             },
28725             size: 2
28726         },
28727         // long, 32 bit int:
28728         4: {
28729             getValue: function (dataView, dataOffset, littleEndian) {
28730                 return dataView.getUint32(dataOffset, littleEndian);
28731             },
28732             size: 4
28733         },
28734         // rational = two long values, first is numerator, second is denominator:
28735         5: {
28736             getValue: function (dataView, dataOffset, littleEndian) {
28737                 return dataView.getUint32(dataOffset, littleEndian) /
28738                     dataView.getUint32(dataOffset + 4, littleEndian);
28739             },
28740             size: 8
28741         },
28742         // slong, 32 bit signed int:
28743         9: {
28744             getValue: function (dataView, dataOffset, littleEndian) {
28745                 return dataView.getInt32(dataOffset, littleEndian);
28746             },
28747             size: 4
28748         },
28749         // srational, two slongs, first is numerator, second is denominator:
28750         10: {
28751             getValue: function (dataView, dataOffset, littleEndian) {
28752                 return dataView.getInt32(dataOffset, littleEndian) /
28753                     dataView.getInt32(dataOffset + 4, littleEndian);
28754             },
28755             size: 8
28756         }
28757     },
28758     
28759     footer : {
28760         STANDARD : [
28761             {
28762                 tag : 'div',
28763                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28764                 action : 'rotate-left',
28765                 cn : [
28766                     {
28767                         tag : 'button',
28768                         cls : 'btn btn-default',
28769                         html : '<i class="fa fa-undo"></i>'
28770                     }
28771                 ]
28772             },
28773             {
28774                 tag : 'div',
28775                 cls : 'btn-group roo-upload-cropbox-picture',
28776                 action : 'picture',
28777                 cn : [
28778                     {
28779                         tag : 'button',
28780                         cls : 'btn btn-default',
28781                         html : '<i class="fa fa-picture-o"></i>'
28782                     }
28783                 ]
28784             },
28785             {
28786                 tag : 'div',
28787                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28788                 action : 'rotate-right',
28789                 cn : [
28790                     {
28791                         tag : 'button',
28792                         cls : 'btn btn-default',
28793                         html : '<i class="fa fa-repeat"></i>'
28794                     }
28795                 ]
28796             }
28797         ],
28798         DOCUMENT : [
28799             {
28800                 tag : 'div',
28801                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28802                 action : 'rotate-left',
28803                 cn : [
28804                     {
28805                         tag : 'button',
28806                         cls : 'btn btn-default',
28807                         html : '<i class="fa fa-undo"></i>'
28808                     }
28809                 ]
28810             },
28811             {
28812                 tag : 'div',
28813                 cls : 'btn-group roo-upload-cropbox-download',
28814                 action : 'download',
28815                 cn : [
28816                     {
28817                         tag : 'button',
28818                         cls : 'btn btn-default',
28819                         html : '<i class="fa fa-download"></i>'
28820                     }
28821                 ]
28822             },
28823             {
28824                 tag : 'div',
28825                 cls : 'btn-group roo-upload-cropbox-crop',
28826                 action : 'crop',
28827                 cn : [
28828                     {
28829                         tag : 'button',
28830                         cls : 'btn btn-default',
28831                         html : '<i class="fa fa-crop"></i>'
28832                     }
28833                 ]
28834             },
28835             {
28836                 tag : 'div',
28837                 cls : 'btn-group roo-upload-cropbox-trash',
28838                 action : 'trash',
28839                 cn : [
28840                     {
28841                         tag : 'button',
28842                         cls : 'btn btn-default',
28843                         html : '<i class="fa fa-trash"></i>'
28844                     }
28845                 ]
28846             },
28847             {
28848                 tag : 'div',
28849                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28850                 action : 'rotate-right',
28851                 cn : [
28852                     {
28853                         tag : 'button',
28854                         cls : 'btn btn-default',
28855                         html : '<i class="fa fa-repeat"></i>'
28856                     }
28857                 ]
28858             }
28859         ],
28860         ROTATOR : [
28861             {
28862                 tag : 'div',
28863                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28864                 action : 'rotate-left',
28865                 cn : [
28866                     {
28867                         tag : 'button',
28868                         cls : 'btn btn-default',
28869                         html : '<i class="fa fa-undo"></i>'
28870                     }
28871                 ]
28872             },
28873             {
28874                 tag : 'div',
28875                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28876                 action : 'rotate-right',
28877                 cn : [
28878                     {
28879                         tag : 'button',
28880                         cls : 'btn btn-default',
28881                         html : '<i class="fa fa-repeat"></i>'
28882                     }
28883                 ]
28884             }
28885         ]
28886     }
28887 });
28888
28889 /*
28890 * Licence: LGPL
28891 */
28892
28893 /**
28894  * @class Roo.bootstrap.DocumentManager
28895  * @extends Roo.bootstrap.Component
28896  * Bootstrap DocumentManager class
28897  * @cfg {String} paramName default 'imageUpload'
28898  * @cfg {String} toolTipName default 'filename'
28899  * @cfg {String} method default POST
28900  * @cfg {String} url action url
28901  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28902  * @cfg {Boolean} multiple multiple upload default true
28903  * @cfg {Number} thumbSize default 300
28904  * @cfg {String} fieldLabel
28905  * @cfg {Number} labelWidth default 4
28906  * @cfg {String} labelAlign (left|top) default left
28907  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28908 * @cfg {Number} labellg set the width of label (1-12)
28909  * @cfg {Number} labelmd set the width of label (1-12)
28910  * @cfg {Number} labelsm set the width of label (1-12)
28911  * @cfg {Number} labelxs set the width of label (1-12)
28912  * 
28913  * @constructor
28914  * Create a new DocumentManager
28915  * @param {Object} config The config object
28916  */
28917
28918 Roo.bootstrap.DocumentManager = function(config){
28919     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28920     
28921     this.files = [];
28922     this.delegates = [];
28923     
28924     this.addEvents({
28925         /**
28926          * @event initial
28927          * Fire when initial the DocumentManager
28928          * @param {Roo.bootstrap.DocumentManager} this
28929          */
28930         "initial" : true,
28931         /**
28932          * @event inspect
28933          * inspect selected file
28934          * @param {Roo.bootstrap.DocumentManager} this
28935          * @param {File} file
28936          */
28937         "inspect" : true,
28938         /**
28939          * @event exception
28940          * Fire when xhr load exception
28941          * @param {Roo.bootstrap.DocumentManager} this
28942          * @param {XMLHttpRequest} xhr
28943          */
28944         "exception" : true,
28945         /**
28946          * @event afterupload
28947          * Fire when xhr load exception
28948          * @param {Roo.bootstrap.DocumentManager} this
28949          * @param {XMLHttpRequest} xhr
28950          */
28951         "afterupload" : true,
28952         /**
28953          * @event prepare
28954          * prepare the form data
28955          * @param {Roo.bootstrap.DocumentManager} this
28956          * @param {Object} formData
28957          */
28958         "prepare" : true,
28959         /**
28960          * @event remove
28961          * Fire when remove the file
28962          * @param {Roo.bootstrap.DocumentManager} this
28963          * @param {Object} file
28964          */
28965         "remove" : true,
28966         /**
28967          * @event refresh
28968          * Fire after refresh the file
28969          * @param {Roo.bootstrap.DocumentManager} this
28970          */
28971         "refresh" : true,
28972         /**
28973          * @event click
28974          * Fire after click the image
28975          * @param {Roo.bootstrap.DocumentManager} this
28976          * @param {Object} file
28977          */
28978         "click" : true,
28979         /**
28980          * @event edit
28981          * Fire when upload a image and editable set to true
28982          * @param {Roo.bootstrap.DocumentManager} this
28983          * @param {Object} file
28984          */
28985         "edit" : true,
28986         /**
28987          * @event beforeselectfile
28988          * Fire before select file
28989          * @param {Roo.bootstrap.DocumentManager} this
28990          */
28991         "beforeselectfile" : true,
28992         /**
28993          * @event process
28994          * Fire before process file
28995          * @param {Roo.bootstrap.DocumentManager} this
28996          * @param {Object} file
28997          */
28998         "process" : true,
28999         /**
29000          * @event previewrendered
29001          * Fire when preview rendered
29002          * @param {Roo.bootstrap.DocumentManager} this
29003          * @param {Object} file
29004          */
29005         "previewrendered" : true,
29006         /**
29007          */
29008         "previewResize" : true
29009         
29010     });
29011 };
29012
29013 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29014     
29015     boxes : 0,
29016     inputName : '',
29017     thumbSize : 300,
29018     multiple : true,
29019     files : false,
29020     method : 'POST',
29021     url : '',
29022     paramName : 'imageUpload',
29023     toolTipName : 'filename',
29024     fieldLabel : '',
29025     labelWidth : 4,
29026     labelAlign : 'left',
29027     editable : true,
29028     delegates : false,
29029     xhr : false, 
29030     
29031     labellg : 0,
29032     labelmd : 0,
29033     labelsm : 0,
29034     labelxs : 0,
29035     
29036     getAutoCreate : function()
29037     {   
29038         var managerWidget = {
29039             tag : 'div',
29040             cls : 'roo-document-manager',
29041             cn : [
29042                 {
29043                     tag : 'input',
29044                     cls : 'roo-document-manager-selector',
29045                     type : 'file'
29046                 },
29047                 {
29048                     tag : 'div',
29049                     cls : 'roo-document-manager-uploader',
29050                     cn : [
29051                         {
29052                             tag : 'div',
29053                             cls : 'roo-document-manager-upload-btn',
29054                             html : '<i class="fa fa-plus"></i>'
29055                         }
29056                     ]
29057                     
29058                 }
29059             ]
29060         };
29061         
29062         var content = [
29063             {
29064                 tag : 'div',
29065                 cls : 'column col-md-12',
29066                 cn : managerWidget
29067             }
29068         ];
29069         
29070         if(this.fieldLabel.length){
29071             
29072             content = [
29073                 {
29074                     tag : 'div',
29075                     cls : 'column col-md-12',
29076                     html : this.fieldLabel
29077                 },
29078                 {
29079                     tag : 'div',
29080                     cls : 'column col-md-12',
29081                     cn : managerWidget
29082                 }
29083             ];
29084
29085             if(this.labelAlign == 'left'){
29086                 content = [
29087                     {
29088                         tag : 'div',
29089                         cls : 'column',
29090                         html : this.fieldLabel
29091                     },
29092                     {
29093                         tag : 'div',
29094                         cls : 'column',
29095                         cn : managerWidget
29096                     }
29097                 ];
29098                 
29099                 if(this.labelWidth > 12){
29100                     content[0].style = "width: " + this.labelWidth + 'px';
29101                 }
29102
29103                 if(this.labelWidth < 13 && this.labelmd == 0){
29104                     this.labelmd = this.labelWidth;
29105                 }
29106
29107                 if(this.labellg > 0){
29108                     content[0].cls += ' col-lg-' + this.labellg;
29109                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29110                 }
29111
29112                 if(this.labelmd > 0){
29113                     content[0].cls += ' col-md-' + this.labelmd;
29114                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29115                 }
29116
29117                 if(this.labelsm > 0){
29118                     content[0].cls += ' col-sm-' + this.labelsm;
29119                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29120                 }
29121
29122                 if(this.labelxs > 0){
29123                     content[0].cls += ' col-xs-' + this.labelxs;
29124                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29125                 }
29126                 
29127             }
29128         }
29129         
29130         var cfg = {
29131             tag : 'div',
29132             cls : 'row clearfix',
29133             cn : content
29134         };
29135         
29136         return cfg;
29137         
29138     },
29139     
29140     initEvents : function()
29141     {
29142         this.managerEl = this.el.select('.roo-document-manager', true).first();
29143         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29144         
29145         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29146         this.selectorEl.hide();
29147         
29148         if(this.multiple){
29149             this.selectorEl.attr('multiple', 'multiple');
29150         }
29151         
29152         this.selectorEl.on('change', this.onFileSelected, this);
29153         
29154         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29155         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29156         
29157         this.uploader.on('click', this.onUploaderClick, this);
29158         
29159         this.renderProgressDialog();
29160         
29161         var _this = this;
29162         
29163         window.addEventListener("resize", function() { _this.refresh(); } );
29164         
29165         this.fireEvent('initial', this);
29166     },
29167     
29168     renderProgressDialog : function()
29169     {
29170         var _this = this;
29171         
29172         this.progressDialog = new Roo.bootstrap.Modal({
29173             cls : 'roo-document-manager-progress-dialog',
29174             allow_close : false,
29175             title : '',
29176             buttons : [
29177                 {
29178                     name  :'cancel',
29179                     weight : 'danger',
29180                     html : 'Cancel'
29181                 }
29182             ], 
29183             listeners : { 
29184                 btnclick : function() {
29185                     _this.uploadCancel();
29186                     this.hide();
29187                 }
29188             }
29189         });
29190          
29191         this.progressDialog.render(Roo.get(document.body));
29192          
29193         this.progress = new Roo.bootstrap.Progress({
29194             cls : 'roo-document-manager-progress',
29195             active : true,
29196             striped : true
29197         });
29198         
29199         this.progress.render(this.progressDialog.getChildContainer());
29200         
29201         this.progressBar = new Roo.bootstrap.ProgressBar({
29202             cls : 'roo-document-manager-progress-bar',
29203             aria_valuenow : 0,
29204             aria_valuemin : 0,
29205             aria_valuemax : 12,
29206             panel : 'success'
29207         });
29208         
29209         this.progressBar.render(this.progress.getChildContainer());
29210     },
29211     
29212     onUploaderClick : function(e)
29213     {
29214         e.preventDefault();
29215      
29216         if(this.fireEvent('beforeselectfile', this) != false){
29217             this.selectorEl.dom.click();
29218         }
29219         
29220     },
29221     
29222     onFileSelected : function(e)
29223     {
29224         e.preventDefault();
29225         
29226         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29227             return;
29228         }
29229         
29230         Roo.each(this.selectorEl.dom.files, function(file){
29231             if(this.fireEvent('inspect', this, file) != false){
29232                 this.files.push(file);
29233             }
29234         }, this);
29235         
29236         this.queue();
29237         
29238     },
29239     
29240     queue : function()
29241     {
29242         this.selectorEl.dom.value = '';
29243         
29244         if(!this.files || !this.files.length){
29245             return;
29246         }
29247         
29248         if(this.boxes > 0 && this.files.length > this.boxes){
29249             this.files = this.files.slice(0, this.boxes);
29250         }
29251         
29252         this.uploader.show();
29253         
29254         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29255             this.uploader.hide();
29256         }
29257         
29258         var _this = this;
29259         
29260         var files = [];
29261         
29262         var docs = [];
29263         
29264         Roo.each(this.files, function(file){
29265             
29266             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29267                 var f = this.renderPreview(file);
29268                 files.push(f);
29269                 return;
29270             }
29271             
29272             if(file.type.indexOf('image') != -1){
29273                 this.delegates.push(
29274                     (function(){
29275                         _this.process(file);
29276                     }).createDelegate(this)
29277                 );
29278         
29279                 return;
29280             }
29281             
29282             docs.push(
29283                 (function(){
29284                     _this.process(file);
29285                 }).createDelegate(this)
29286             );
29287             
29288         }, this);
29289         
29290         this.files = files;
29291         
29292         this.delegates = this.delegates.concat(docs);
29293         
29294         if(!this.delegates.length){
29295             this.refresh();
29296             return;
29297         }
29298         
29299         this.progressBar.aria_valuemax = this.delegates.length;
29300         
29301         this.arrange();
29302         
29303         return;
29304     },
29305     
29306     arrange : function()
29307     {
29308         if(!this.delegates.length){
29309             this.progressDialog.hide();
29310             this.refresh();
29311             return;
29312         }
29313         
29314         var delegate = this.delegates.shift();
29315         
29316         this.progressDialog.show();
29317         
29318         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29319         
29320         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29321         
29322         delegate();
29323     },
29324     
29325     refresh : function()
29326     {
29327         this.uploader.show();
29328         
29329         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29330             this.uploader.hide();
29331         }
29332         
29333         Roo.isTouch ? this.closable(false) : this.closable(true);
29334         
29335         this.fireEvent('refresh', this);
29336     },
29337     
29338     onRemove : function(e, el, o)
29339     {
29340         e.preventDefault();
29341         
29342         this.fireEvent('remove', this, o);
29343         
29344     },
29345     
29346     remove : function(o)
29347     {
29348         var files = [];
29349         
29350         Roo.each(this.files, function(file){
29351             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29352                 files.push(file);
29353                 return;
29354             }
29355
29356             o.target.remove();
29357
29358         }, this);
29359         
29360         this.files = files;
29361         
29362         this.refresh();
29363     },
29364     
29365     clear : function()
29366     {
29367         Roo.each(this.files, function(file){
29368             if(!file.target){
29369                 return;
29370             }
29371             
29372             file.target.remove();
29373
29374         }, this);
29375         
29376         this.files = [];
29377         
29378         this.refresh();
29379     },
29380     
29381     onClick : function(e, el, o)
29382     {
29383         e.preventDefault();
29384         
29385         this.fireEvent('click', this, o);
29386         
29387     },
29388     
29389     closable : function(closable)
29390     {
29391         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29392             
29393             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29394             
29395             if(closable){
29396                 el.show();
29397                 return;
29398             }
29399             
29400             el.hide();
29401             
29402         }, this);
29403     },
29404     
29405     xhrOnLoad : function(xhr)
29406     {
29407         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29408             el.remove();
29409         }, this);
29410         
29411         if (xhr.readyState !== 4) {
29412             this.arrange();
29413             this.fireEvent('exception', this, xhr);
29414             return;
29415         }
29416
29417         var response = Roo.decode(xhr.responseText);
29418         
29419         if(!response.success){
29420             this.arrange();
29421             this.fireEvent('exception', this, xhr);
29422             return;
29423         }
29424         
29425         var file = this.renderPreview(response.data);
29426         
29427         this.files.push(file);
29428         
29429         this.arrange();
29430         
29431         this.fireEvent('afterupload', this, xhr);
29432         
29433     },
29434     
29435     xhrOnError : function(xhr)
29436     {
29437         Roo.log('xhr on error');
29438         
29439         var response = Roo.decode(xhr.responseText);
29440           
29441         Roo.log(response);
29442         
29443         this.arrange();
29444     },
29445     
29446     process : function(file)
29447     {
29448         if(this.fireEvent('process', this, file) !== false){
29449             if(this.editable && file.type.indexOf('image') != -1){
29450                 this.fireEvent('edit', this, file);
29451                 return;
29452             }
29453
29454             this.uploadStart(file, false);
29455
29456             return;
29457         }
29458         
29459     },
29460     
29461     uploadStart : function(file, crop)
29462     {
29463         this.xhr = new XMLHttpRequest();
29464         
29465         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29466             this.arrange();
29467             return;
29468         }
29469         
29470         file.xhr = this.xhr;
29471             
29472         this.managerEl.createChild({
29473             tag : 'div',
29474             cls : 'roo-document-manager-loading',
29475             cn : [
29476                 {
29477                     tag : 'div',
29478                     tooltip : file.name,
29479                     cls : 'roo-document-manager-thumb',
29480                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29481                 }
29482             ]
29483
29484         });
29485
29486         this.xhr.open(this.method, this.url, true);
29487         
29488         var headers = {
29489             "Accept": "application/json",
29490             "Cache-Control": "no-cache",
29491             "X-Requested-With": "XMLHttpRequest"
29492         };
29493         
29494         for (var headerName in headers) {
29495             var headerValue = headers[headerName];
29496             if (headerValue) {
29497                 this.xhr.setRequestHeader(headerName, headerValue);
29498             }
29499         }
29500         
29501         var _this = this;
29502         
29503         this.xhr.onload = function()
29504         {
29505             _this.xhrOnLoad(_this.xhr);
29506         }
29507         
29508         this.xhr.onerror = function()
29509         {
29510             _this.xhrOnError(_this.xhr);
29511         }
29512         
29513         var formData = new FormData();
29514
29515         formData.append('returnHTML', 'NO');
29516         
29517         if(crop){
29518             formData.append('crop', crop);
29519         }
29520         
29521         formData.append(this.paramName, file, file.name);
29522         
29523         var options = {
29524             file : file, 
29525             manually : false
29526         };
29527         
29528         if(this.fireEvent('prepare', this, formData, options) != false){
29529             
29530             if(options.manually){
29531                 return;
29532             }
29533             
29534             this.xhr.send(formData);
29535             return;
29536         };
29537         
29538         this.uploadCancel();
29539     },
29540     
29541     uploadCancel : function()
29542     {
29543         if (this.xhr) {
29544             this.xhr.abort();
29545         }
29546         
29547         this.delegates = [];
29548         
29549         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29550             el.remove();
29551         }, this);
29552         
29553         this.arrange();
29554     },
29555     
29556     renderPreview : function(file)
29557     {
29558         if(typeof(file.target) != 'undefined' && file.target){
29559             return file;
29560         }
29561         
29562         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29563         
29564         var previewEl = this.managerEl.createChild({
29565             tag : 'div',
29566             cls : 'roo-document-manager-preview',
29567             cn : [
29568                 {
29569                     tag : 'div',
29570                     tooltip : file[this.toolTipName],
29571                     cls : 'roo-document-manager-thumb',
29572                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29573                 },
29574                 {
29575                     tag : 'button',
29576                     cls : 'close',
29577                     html : '<i class="fa fa-times-circle"></i>'
29578                 }
29579             ]
29580         });
29581
29582         var close = previewEl.select('button.close', true).first();
29583
29584         close.on('click', this.onRemove, this, file);
29585
29586         file.target = previewEl;
29587
29588         var image = previewEl.select('img', true).first();
29589         
29590         var _this = this;
29591         
29592         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29593         
29594         image.on('click', this.onClick, this, file);
29595         
29596         this.fireEvent('previewrendered', this, file);
29597         
29598         return file;
29599         
29600     },
29601     
29602     onPreviewLoad : function(file, image)
29603     {
29604         if(typeof(file.target) == 'undefined' || !file.target){
29605             return;
29606         }
29607         
29608         var width = image.dom.naturalWidth || image.dom.width;
29609         var height = image.dom.naturalHeight || image.dom.height;
29610         
29611         if(!this.previewResize) {
29612             return;
29613         }
29614         
29615         if(width > height){
29616             file.target.addClass('wide');
29617             return;
29618         }
29619         
29620         file.target.addClass('tall');
29621         return;
29622         
29623     },
29624     
29625     uploadFromSource : function(file, crop)
29626     {
29627         this.xhr = new XMLHttpRequest();
29628         
29629         this.managerEl.createChild({
29630             tag : 'div',
29631             cls : 'roo-document-manager-loading',
29632             cn : [
29633                 {
29634                     tag : 'div',
29635                     tooltip : file.name,
29636                     cls : 'roo-document-manager-thumb',
29637                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29638                 }
29639             ]
29640
29641         });
29642
29643         this.xhr.open(this.method, this.url, true);
29644         
29645         var headers = {
29646             "Accept": "application/json",
29647             "Cache-Control": "no-cache",
29648             "X-Requested-With": "XMLHttpRequest"
29649         };
29650         
29651         for (var headerName in headers) {
29652             var headerValue = headers[headerName];
29653             if (headerValue) {
29654                 this.xhr.setRequestHeader(headerName, headerValue);
29655             }
29656         }
29657         
29658         var _this = this;
29659         
29660         this.xhr.onload = function()
29661         {
29662             _this.xhrOnLoad(_this.xhr);
29663         }
29664         
29665         this.xhr.onerror = function()
29666         {
29667             _this.xhrOnError(_this.xhr);
29668         }
29669         
29670         var formData = new FormData();
29671
29672         formData.append('returnHTML', 'NO');
29673         
29674         formData.append('crop', crop);
29675         
29676         if(typeof(file.filename) != 'undefined'){
29677             formData.append('filename', file.filename);
29678         }
29679         
29680         if(typeof(file.mimetype) != 'undefined'){
29681             formData.append('mimetype', file.mimetype);
29682         }
29683         
29684         Roo.log(formData);
29685         
29686         if(this.fireEvent('prepare', this, formData) != false){
29687             this.xhr.send(formData);
29688         };
29689     }
29690 });
29691
29692 /*
29693 * Licence: LGPL
29694 */
29695
29696 /**
29697  * @class Roo.bootstrap.DocumentViewer
29698  * @extends Roo.bootstrap.Component
29699  * Bootstrap DocumentViewer class
29700  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29701  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29702  * 
29703  * @constructor
29704  * Create a new DocumentViewer
29705  * @param {Object} config The config object
29706  */
29707
29708 Roo.bootstrap.DocumentViewer = function(config){
29709     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29710     
29711     this.addEvents({
29712         /**
29713          * @event initial
29714          * Fire after initEvent
29715          * @param {Roo.bootstrap.DocumentViewer} this
29716          */
29717         "initial" : true,
29718         /**
29719          * @event click
29720          * Fire after click
29721          * @param {Roo.bootstrap.DocumentViewer} this
29722          */
29723         "click" : true,
29724         /**
29725          * @event download
29726          * Fire after download button
29727          * @param {Roo.bootstrap.DocumentViewer} this
29728          */
29729         "download" : true,
29730         /**
29731          * @event trash
29732          * Fire after trash button
29733          * @param {Roo.bootstrap.DocumentViewer} this
29734          */
29735         "trash" : true
29736         
29737     });
29738 };
29739
29740 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29741     
29742     showDownload : true,
29743     
29744     showTrash : true,
29745     
29746     getAutoCreate : function()
29747     {
29748         var cfg = {
29749             tag : 'div',
29750             cls : 'roo-document-viewer',
29751             cn : [
29752                 {
29753                     tag : 'div',
29754                     cls : 'roo-document-viewer-body',
29755                     cn : [
29756                         {
29757                             tag : 'div',
29758                             cls : 'roo-document-viewer-thumb',
29759                             cn : [
29760                                 {
29761                                     tag : 'img',
29762                                     cls : 'roo-document-viewer-image'
29763                                 }
29764                             ]
29765                         }
29766                     ]
29767                 },
29768                 {
29769                     tag : 'div',
29770                     cls : 'roo-document-viewer-footer',
29771                     cn : {
29772                         tag : 'div',
29773                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29774                         cn : [
29775                             {
29776                                 tag : 'div',
29777                                 cls : 'btn-group roo-document-viewer-download',
29778                                 cn : [
29779                                     {
29780                                         tag : 'button',
29781                                         cls : 'btn btn-default',
29782                                         html : '<i class="fa fa-download"></i>'
29783                                     }
29784                                 ]
29785                             },
29786                             {
29787                                 tag : 'div',
29788                                 cls : 'btn-group roo-document-viewer-trash',
29789                                 cn : [
29790                                     {
29791                                         tag : 'button',
29792                                         cls : 'btn btn-default',
29793                                         html : '<i class="fa fa-trash"></i>'
29794                                     }
29795                                 ]
29796                             }
29797                         ]
29798                     }
29799                 }
29800             ]
29801         };
29802         
29803         return cfg;
29804     },
29805     
29806     initEvents : function()
29807     {
29808         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29809         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29810         
29811         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29812         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29813         
29814         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29815         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29816         
29817         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29818         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29819         
29820         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29821         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29822         
29823         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29824         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29825         
29826         this.bodyEl.on('click', this.onClick, this);
29827         this.downloadBtn.on('click', this.onDownload, this);
29828         this.trashBtn.on('click', this.onTrash, this);
29829         
29830         this.downloadBtn.hide();
29831         this.trashBtn.hide();
29832         
29833         if(this.showDownload){
29834             this.downloadBtn.show();
29835         }
29836         
29837         if(this.showTrash){
29838             this.trashBtn.show();
29839         }
29840         
29841         if(!this.showDownload && !this.showTrash) {
29842             this.footerEl.hide();
29843         }
29844         
29845     },
29846     
29847     initial : function()
29848     {
29849         this.fireEvent('initial', this);
29850         
29851     },
29852     
29853     onClick : function(e)
29854     {
29855         e.preventDefault();
29856         
29857         this.fireEvent('click', this);
29858     },
29859     
29860     onDownload : function(e)
29861     {
29862         e.preventDefault();
29863         
29864         this.fireEvent('download', this);
29865     },
29866     
29867     onTrash : function(e)
29868     {
29869         e.preventDefault();
29870         
29871         this.fireEvent('trash', this);
29872     }
29873     
29874 });
29875 /*
29876  * - LGPL
29877  *
29878  * nav progress bar
29879  * 
29880  */
29881
29882 /**
29883  * @class Roo.bootstrap.NavProgressBar
29884  * @extends Roo.bootstrap.Component
29885  * Bootstrap NavProgressBar class
29886  * 
29887  * @constructor
29888  * Create a new nav progress bar
29889  * @param {Object} config The config object
29890  */
29891
29892 Roo.bootstrap.NavProgressBar = function(config){
29893     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29894
29895     this.bullets = this.bullets || [];
29896    
29897 //    Roo.bootstrap.NavProgressBar.register(this);
29898      this.addEvents({
29899         /**
29900              * @event changed
29901              * Fires when the active item changes
29902              * @param {Roo.bootstrap.NavProgressBar} this
29903              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29904              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29905          */
29906         'changed': true
29907      });
29908     
29909 };
29910
29911 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29912     
29913     bullets : [],
29914     barItems : [],
29915     
29916     getAutoCreate : function()
29917     {
29918         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29919         
29920         cfg = {
29921             tag : 'div',
29922             cls : 'roo-navigation-bar-group',
29923             cn : [
29924                 {
29925                     tag : 'div',
29926                     cls : 'roo-navigation-top-bar'
29927                 },
29928                 {
29929                     tag : 'div',
29930                     cls : 'roo-navigation-bullets-bar',
29931                     cn : [
29932                         {
29933                             tag : 'ul',
29934                             cls : 'roo-navigation-bar'
29935                         }
29936                     ]
29937                 },
29938                 
29939                 {
29940                     tag : 'div',
29941                     cls : 'roo-navigation-bottom-bar'
29942                 }
29943             ]
29944             
29945         };
29946         
29947         return cfg;
29948         
29949     },
29950     
29951     initEvents: function() 
29952     {
29953         
29954     },
29955     
29956     onRender : function(ct, position) 
29957     {
29958         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29959         
29960         if(this.bullets.length){
29961             Roo.each(this.bullets, function(b){
29962                this.addItem(b);
29963             }, this);
29964         }
29965         
29966         this.format();
29967         
29968     },
29969     
29970     addItem : function(cfg)
29971     {
29972         var item = new Roo.bootstrap.NavProgressItem(cfg);
29973         
29974         item.parentId = this.id;
29975         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29976         
29977         if(cfg.html){
29978             var top = new Roo.bootstrap.Element({
29979                 tag : 'div',
29980                 cls : 'roo-navigation-bar-text'
29981             });
29982             
29983             var bottom = new Roo.bootstrap.Element({
29984                 tag : 'div',
29985                 cls : 'roo-navigation-bar-text'
29986             });
29987             
29988             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29989             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29990             
29991             var topText = new Roo.bootstrap.Element({
29992                 tag : 'span',
29993                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29994             });
29995             
29996             var bottomText = new Roo.bootstrap.Element({
29997                 tag : 'span',
29998                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29999             });
30000             
30001             topText.onRender(top.el, null);
30002             bottomText.onRender(bottom.el, null);
30003             
30004             item.topEl = top;
30005             item.bottomEl = bottom;
30006         }
30007         
30008         this.barItems.push(item);
30009         
30010         return item;
30011     },
30012     
30013     getActive : function()
30014     {
30015         var active = false;
30016         
30017         Roo.each(this.barItems, function(v){
30018             
30019             if (!v.isActive()) {
30020                 return;
30021             }
30022             
30023             active = v;
30024             return false;
30025             
30026         });
30027         
30028         return active;
30029     },
30030     
30031     setActiveItem : function(item)
30032     {
30033         var prev = false;
30034         
30035         Roo.each(this.barItems, function(v){
30036             if (v.rid == item.rid) {
30037                 return ;
30038             }
30039             
30040             if (v.isActive()) {
30041                 v.setActive(false);
30042                 prev = v;
30043             }
30044         });
30045
30046         item.setActive(true);
30047         
30048         this.fireEvent('changed', this, item, prev);
30049     },
30050     
30051     getBarItem: function(rid)
30052     {
30053         var ret = false;
30054         
30055         Roo.each(this.barItems, function(e) {
30056             if (e.rid != rid) {
30057                 return;
30058             }
30059             
30060             ret =  e;
30061             return false;
30062         });
30063         
30064         return ret;
30065     },
30066     
30067     indexOfItem : function(item)
30068     {
30069         var index = false;
30070         
30071         Roo.each(this.barItems, function(v, i){
30072             
30073             if (v.rid != item.rid) {
30074                 return;
30075             }
30076             
30077             index = i;
30078             return false
30079         });
30080         
30081         return index;
30082     },
30083     
30084     setActiveNext : function()
30085     {
30086         var i = this.indexOfItem(this.getActive());
30087         
30088         if (i > this.barItems.length) {
30089             return;
30090         }
30091         
30092         this.setActiveItem(this.barItems[i+1]);
30093     },
30094     
30095     setActivePrev : function()
30096     {
30097         var i = this.indexOfItem(this.getActive());
30098         
30099         if (i  < 1) {
30100             return;
30101         }
30102         
30103         this.setActiveItem(this.barItems[i-1]);
30104     },
30105     
30106     format : function()
30107     {
30108         if(!this.barItems.length){
30109             return;
30110         }
30111      
30112         var width = 100 / this.barItems.length;
30113         
30114         Roo.each(this.barItems, function(i){
30115             i.el.setStyle('width', width + '%');
30116             i.topEl.el.setStyle('width', width + '%');
30117             i.bottomEl.el.setStyle('width', width + '%');
30118         }, this);
30119         
30120     }
30121     
30122 });
30123 /*
30124  * - LGPL
30125  *
30126  * Nav Progress Item
30127  * 
30128  */
30129
30130 /**
30131  * @class Roo.bootstrap.NavProgressItem
30132  * @extends Roo.bootstrap.Component
30133  * Bootstrap NavProgressItem class
30134  * @cfg {String} rid the reference id
30135  * @cfg {Boolean} active (true|false) Is item active default false
30136  * @cfg {Boolean} disabled (true|false) Is item active default false
30137  * @cfg {String} html
30138  * @cfg {String} position (top|bottom) text position default bottom
30139  * @cfg {String} icon show icon instead of number
30140  * 
30141  * @constructor
30142  * Create a new NavProgressItem
30143  * @param {Object} config The config object
30144  */
30145 Roo.bootstrap.NavProgressItem = function(config){
30146     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30147     this.addEvents({
30148         // raw events
30149         /**
30150          * @event click
30151          * The raw click event for the entire grid.
30152          * @param {Roo.bootstrap.NavProgressItem} this
30153          * @param {Roo.EventObject} e
30154          */
30155         "click" : true
30156     });
30157    
30158 };
30159
30160 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30161     
30162     rid : '',
30163     active : false,
30164     disabled : false,
30165     html : '',
30166     position : 'bottom',
30167     icon : false,
30168     
30169     getAutoCreate : function()
30170     {
30171         var iconCls = 'roo-navigation-bar-item-icon';
30172         
30173         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30174         
30175         var cfg = {
30176             tag: 'li',
30177             cls: 'roo-navigation-bar-item',
30178             cn : [
30179                 {
30180                     tag : 'i',
30181                     cls : iconCls
30182                 }
30183             ]
30184         };
30185         
30186         if(this.active){
30187             cfg.cls += ' active';
30188         }
30189         if(this.disabled){
30190             cfg.cls += ' disabled';
30191         }
30192         
30193         return cfg;
30194     },
30195     
30196     disable : function()
30197     {
30198         this.setDisabled(true);
30199     },
30200     
30201     enable : function()
30202     {
30203         this.setDisabled(false);
30204     },
30205     
30206     initEvents: function() 
30207     {
30208         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30209         
30210         this.iconEl.on('click', this.onClick, this);
30211     },
30212     
30213     onClick : function(e)
30214     {
30215         e.preventDefault();
30216         
30217         if(this.disabled){
30218             return;
30219         }
30220         
30221         if(this.fireEvent('click', this, e) === false){
30222             return;
30223         };
30224         
30225         this.parent().setActiveItem(this);
30226     },
30227     
30228     isActive: function () 
30229     {
30230         return this.active;
30231     },
30232     
30233     setActive : function(state)
30234     {
30235         if(this.active == state){
30236             return;
30237         }
30238         
30239         this.active = state;
30240         
30241         if (state) {
30242             this.el.addClass('active');
30243             return;
30244         }
30245         
30246         this.el.removeClass('active');
30247         
30248         return;
30249     },
30250     
30251     setDisabled : function(state)
30252     {
30253         if(this.disabled == state){
30254             return;
30255         }
30256         
30257         this.disabled = state;
30258         
30259         if (state) {
30260             this.el.addClass('disabled');
30261             return;
30262         }
30263         
30264         this.el.removeClass('disabled');
30265     },
30266     
30267     tooltipEl : function()
30268     {
30269         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30270     }
30271 });
30272  
30273
30274  /*
30275  * - LGPL
30276  *
30277  * FieldLabel
30278  * 
30279  */
30280
30281 /**
30282  * @class Roo.bootstrap.FieldLabel
30283  * @extends Roo.bootstrap.Component
30284  * Bootstrap FieldLabel class
30285  * @cfg {String} html contents of the element
30286  * @cfg {String} tag tag of the element default label
30287  * @cfg {String} cls class of the element
30288  * @cfg {String} target label target 
30289  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30290  * @cfg {String} invalidClass default "text-warning"
30291  * @cfg {String} validClass default "text-success"
30292  * @cfg {String} iconTooltip default "This field is required"
30293  * @cfg {String} indicatorpos (left|right) default left
30294  * 
30295  * @constructor
30296  * Create a new FieldLabel
30297  * @param {Object} config The config object
30298  */
30299
30300 Roo.bootstrap.FieldLabel = function(config){
30301     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30302     
30303     this.addEvents({
30304             /**
30305              * @event invalid
30306              * Fires after the field has been marked as invalid.
30307              * @param {Roo.form.FieldLabel} this
30308              * @param {String} msg The validation message
30309              */
30310             invalid : true,
30311             /**
30312              * @event valid
30313              * Fires after the field has been validated with no errors.
30314              * @param {Roo.form.FieldLabel} this
30315              */
30316             valid : true
30317         });
30318 };
30319
30320 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30321     
30322     tag: 'label',
30323     cls: '',
30324     html: '',
30325     target: '',
30326     allowBlank : true,
30327     invalidClass : 'has-warning',
30328     validClass : 'has-success',
30329     iconTooltip : 'This field is required',
30330     indicatorpos : 'left',
30331     
30332     getAutoCreate : function(){
30333         
30334         var cls = "";
30335         if (!this.allowBlank) {
30336             cls  = "visible";
30337         }
30338         
30339         var cfg = {
30340             tag : this.tag,
30341             cls : 'roo-bootstrap-field-label ' + this.cls,
30342             for : this.target,
30343             cn : [
30344                 {
30345                     tag : 'i',
30346                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30347                     tooltip : this.iconTooltip
30348                 },
30349                 {
30350                     tag : 'span',
30351                     html : this.html
30352                 }
30353             ] 
30354         };
30355         
30356         if(this.indicatorpos == 'right'){
30357             var cfg = {
30358                 tag : this.tag,
30359                 cls : 'roo-bootstrap-field-label ' + this.cls,
30360                 for : this.target,
30361                 cn : [
30362                     {
30363                         tag : 'span',
30364                         html : this.html
30365                     },
30366                     {
30367                         tag : 'i',
30368                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30369                         tooltip : this.iconTooltip
30370                     }
30371                 ] 
30372             };
30373         }
30374         
30375         return cfg;
30376     },
30377     
30378     initEvents: function() 
30379     {
30380         Roo.bootstrap.Element.superclass.initEvents.call(this);
30381         
30382         this.indicator = this.indicatorEl();
30383         
30384         if(this.indicator){
30385             this.indicator.removeClass('visible');
30386             this.indicator.addClass('invisible');
30387         }
30388         
30389         Roo.bootstrap.FieldLabel.register(this);
30390     },
30391     
30392     indicatorEl : function()
30393     {
30394         var indicator = this.el.select('i.roo-required-indicator',true).first();
30395         
30396         if(!indicator){
30397             return false;
30398         }
30399         
30400         return indicator;
30401         
30402     },
30403     
30404     /**
30405      * Mark this field as valid
30406      */
30407     markValid : function()
30408     {
30409         if(this.indicator){
30410             this.indicator.removeClass('visible');
30411             this.indicator.addClass('invisible');
30412         }
30413         
30414         this.el.removeClass(this.invalidClass);
30415         
30416         this.el.addClass(this.validClass);
30417         
30418         this.fireEvent('valid', this);
30419     },
30420     
30421     /**
30422      * Mark this field as invalid
30423      * @param {String} msg The validation message
30424      */
30425     markInvalid : function(msg)
30426     {
30427         if(this.indicator){
30428             this.indicator.removeClass('invisible');
30429             this.indicator.addClass('visible');
30430         }
30431         
30432         this.el.removeClass(this.validClass);
30433         
30434         this.el.addClass(this.invalidClass);
30435         
30436         this.fireEvent('invalid', this, msg);
30437     }
30438     
30439    
30440 });
30441
30442 Roo.apply(Roo.bootstrap.FieldLabel, {
30443     
30444     groups: {},
30445     
30446      /**
30447     * register a FieldLabel Group
30448     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30449     */
30450     register : function(label)
30451     {
30452         if(this.groups.hasOwnProperty(label.target)){
30453             return;
30454         }
30455      
30456         this.groups[label.target] = label;
30457         
30458     },
30459     /**
30460     * fetch a FieldLabel Group based on the target
30461     * @param {string} target
30462     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30463     */
30464     get: function(target) {
30465         if (typeof(this.groups[target]) == 'undefined') {
30466             return false;
30467         }
30468         
30469         return this.groups[target] ;
30470     }
30471 });
30472
30473  
30474
30475  /*
30476  * - LGPL
30477  *
30478  * page DateSplitField.
30479  * 
30480  */
30481
30482
30483 /**
30484  * @class Roo.bootstrap.DateSplitField
30485  * @extends Roo.bootstrap.Component
30486  * Bootstrap DateSplitField class
30487  * @cfg {string} fieldLabel - the label associated
30488  * @cfg {Number} labelWidth set the width of label (0-12)
30489  * @cfg {String} labelAlign (top|left)
30490  * @cfg {Boolean} dayAllowBlank (true|false) default false
30491  * @cfg {Boolean} monthAllowBlank (true|false) default false
30492  * @cfg {Boolean} yearAllowBlank (true|false) default false
30493  * @cfg {string} dayPlaceholder 
30494  * @cfg {string} monthPlaceholder
30495  * @cfg {string} yearPlaceholder
30496  * @cfg {string} dayFormat default 'd'
30497  * @cfg {string} monthFormat default 'm'
30498  * @cfg {string} yearFormat default 'Y'
30499  * @cfg {Number} labellg set the width of label (1-12)
30500  * @cfg {Number} labelmd set the width of label (1-12)
30501  * @cfg {Number} labelsm set the width of label (1-12)
30502  * @cfg {Number} labelxs set the width of label (1-12)
30503
30504  *     
30505  * @constructor
30506  * Create a new DateSplitField
30507  * @param {Object} config The config object
30508  */
30509
30510 Roo.bootstrap.DateSplitField = function(config){
30511     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30512     
30513     this.addEvents({
30514         // raw events
30515          /**
30516          * @event years
30517          * getting the data of years
30518          * @param {Roo.bootstrap.DateSplitField} this
30519          * @param {Object} years
30520          */
30521         "years" : true,
30522         /**
30523          * @event days
30524          * getting the data of days
30525          * @param {Roo.bootstrap.DateSplitField} this
30526          * @param {Object} days
30527          */
30528         "days" : true,
30529         /**
30530          * @event invalid
30531          * Fires after the field has been marked as invalid.
30532          * @param {Roo.form.Field} this
30533          * @param {String} msg The validation message
30534          */
30535         invalid : true,
30536        /**
30537          * @event valid
30538          * Fires after the field has been validated with no errors.
30539          * @param {Roo.form.Field} this
30540          */
30541         valid : true
30542     });
30543 };
30544
30545 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30546     
30547     fieldLabel : '',
30548     labelAlign : 'top',
30549     labelWidth : 3,
30550     dayAllowBlank : false,
30551     monthAllowBlank : false,
30552     yearAllowBlank : false,
30553     dayPlaceholder : '',
30554     monthPlaceholder : '',
30555     yearPlaceholder : '',
30556     dayFormat : 'd',
30557     monthFormat : 'm',
30558     yearFormat : 'Y',
30559     isFormField : true,
30560     labellg : 0,
30561     labelmd : 0,
30562     labelsm : 0,
30563     labelxs : 0,
30564     
30565     getAutoCreate : function()
30566     {
30567         var cfg = {
30568             tag : 'div',
30569             cls : 'row roo-date-split-field-group',
30570             cn : [
30571                 {
30572                     tag : 'input',
30573                     type : 'hidden',
30574                     cls : 'form-hidden-field roo-date-split-field-group-value',
30575                     name : this.name
30576                 }
30577             ]
30578         };
30579         
30580         var labelCls = 'col-md-12';
30581         var contentCls = 'col-md-4';
30582         
30583         if(this.fieldLabel){
30584             
30585             var label = {
30586                 tag : 'div',
30587                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30588                 cn : [
30589                     {
30590                         tag : 'label',
30591                         html : this.fieldLabel
30592                     }
30593                 ]
30594             };
30595             
30596             if(this.labelAlign == 'left'){
30597             
30598                 if(this.labelWidth > 12){
30599                     label.style = "width: " + this.labelWidth + 'px';
30600                 }
30601
30602                 if(this.labelWidth < 13 && this.labelmd == 0){
30603                     this.labelmd = this.labelWidth;
30604                 }
30605
30606                 if(this.labellg > 0){
30607                     labelCls = ' col-lg-' + this.labellg;
30608                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30609                 }
30610
30611                 if(this.labelmd > 0){
30612                     labelCls = ' col-md-' + this.labelmd;
30613                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30614                 }
30615
30616                 if(this.labelsm > 0){
30617                     labelCls = ' col-sm-' + this.labelsm;
30618                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30619                 }
30620
30621                 if(this.labelxs > 0){
30622                     labelCls = ' col-xs-' + this.labelxs;
30623                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30624                 }
30625             }
30626             
30627             label.cls += ' ' + labelCls;
30628             
30629             cfg.cn.push(label);
30630         }
30631         
30632         Roo.each(['day', 'month', 'year'], function(t){
30633             cfg.cn.push({
30634                 tag : 'div',
30635                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30636             });
30637         }, this);
30638         
30639         return cfg;
30640     },
30641     
30642     inputEl: function ()
30643     {
30644         return this.el.select('.roo-date-split-field-group-value', true).first();
30645     },
30646     
30647     onRender : function(ct, position) 
30648     {
30649         var _this = this;
30650         
30651         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30652         
30653         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30654         
30655         this.dayField = new Roo.bootstrap.ComboBox({
30656             allowBlank : this.dayAllowBlank,
30657             alwaysQuery : true,
30658             displayField : 'value',
30659             editable : false,
30660             fieldLabel : '',
30661             forceSelection : true,
30662             mode : 'local',
30663             placeholder : this.dayPlaceholder,
30664             selectOnFocus : true,
30665             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30666             triggerAction : 'all',
30667             typeAhead : true,
30668             valueField : 'value',
30669             store : new Roo.data.SimpleStore({
30670                 data : (function() {    
30671                     var days = [];
30672                     _this.fireEvent('days', _this, days);
30673                     return days;
30674                 })(),
30675                 fields : [ 'value' ]
30676             }),
30677             listeners : {
30678                 select : function (_self, record, index)
30679                 {
30680                     _this.setValue(_this.getValue());
30681                 }
30682             }
30683         });
30684
30685         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30686         
30687         this.monthField = new Roo.bootstrap.MonthField({
30688             after : '<i class=\"fa fa-calendar\"></i>',
30689             allowBlank : this.monthAllowBlank,
30690             placeholder : this.monthPlaceholder,
30691             readOnly : true,
30692             listeners : {
30693                 render : function (_self)
30694                 {
30695                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30696                         e.preventDefault();
30697                         _self.focus();
30698                     });
30699                 },
30700                 select : function (_self, oldvalue, newvalue)
30701                 {
30702                     _this.setValue(_this.getValue());
30703                 }
30704             }
30705         });
30706         
30707         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30708         
30709         this.yearField = new Roo.bootstrap.ComboBox({
30710             allowBlank : this.yearAllowBlank,
30711             alwaysQuery : true,
30712             displayField : 'value',
30713             editable : false,
30714             fieldLabel : '',
30715             forceSelection : true,
30716             mode : 'local',
30717             placeholder : this.yearPlaceholder,
30718             selectOnFocus : true,
30719             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30720             triggerAction : 'all',
30721             typeAhead : true,
30722             valueField : 'value',
30723             store : new Roo.data.SimpleStore({
30724                 data : (function() {
30725                     var years = [];
30726                     _this.fireEvent('years', _this, years);
30727                     return years;
30728                 })(),
30729                 fields : [ 'value' ]
30730             }),
30731             listeners : {
30732                 select : function (_self, record, index)
30733                 {
30734                     _this.setValue(_this.getValue());
30735                 }
30736             }
30737         });
30738
30739         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30740     },
30741     
30742     setValue : function(v, format)
30743     {
30744         this.inputEl.dom.value = v;
30745         
30746         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30747         
30748         var d = Date.parseDate(v, f);
30749         
30750         if(!d){
30751             this.validate();
30752             return;
30753         }
30754         
30755         this.setDay(d.format(this.dayFormat));
30756         this.setMonth(d.format(this.monthFormat));
30757         this.setYear(d.format(this.yearFormat));
30758         
30759         this.validate();
30760         
30761         return;
30762     },
30763     
30764     setDay : function(v)
30765     {
30766         this.dayField.setValue(v);
30767         this.inputEl.dom.value = this.getValue();
30768         this.validate();
30769         return;
30770     },
30771     
30772     setMonth : function(v)
30773     {
30774         this.monthField.setValue(v, true);
30775         this.inputEl.dom.value = this.getValue();
30776         this.validate();
30777         return;
30778     },
30779     
30780     setYear : function(v)
30781     {
30782         this.yearField.setValue(v);
30783         this.inputEl.dom.value = this.getValue();
30784         this.validate();
30785         return;
30786     },
30787     
30788     getDay : function()
30789     {
30790         return this.dayField.getValue();
30791     },
30792     
30793     getMonth : function()
30794     {
30795         return this.monthField.getValue();
30796     },
30797     
30798     getYear : function()
30799     {
30800         return this.yearField.getValue();
30801     },
30802     
30803     getValue : function()
30804     {
30805         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30806         
30807         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30808         
30809         return date;
30810     },
30811     
30812     reset : function()
30813     {
30814         this.setDay('');
30815         this.setMonth('');
30816         this.setYear('');
30817         this.inputEl.dom.value = '';
30818         this.validate();
30819         return;
30820     },
30821     
30822     validate : function()
30823     {
30824         var d = this.dayField.validate();
30825         var m = this.monthField.validate();
30826         var y = this.yearField.validate();
30827         
30828         var valid = true;
30829         
30830         if(
30831                 (!this.dayAllowBlank && !d) ||
30832                 (!this.monthAllowBlank && !m) ||
30833                 (!this.yearAllowBlank && !y)
30834         ){
30835             valid = false;
30836         }
30837         
30838         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30839             return valid;
30840         }
30841         
30842         if(valid){
30843             this.markValid();
30844             return valid;
30845         }
30846         
30847         this.markInvalid();
30848         
30849         return valid;
30850     },
30851     
30852     markValid : function()
30853     {
30854         
30855         var label = this.el.select('label', true).first();
30856         var icon = this.el.select('i.fa-star', true).first();
30857
30858         if(label && icon){
30859             icon.remove();
30860         }
30861         
30862         this.fireEvent('valid', this);
30863     },
30864     
30865      /**
30866      * Mark this field as invalid
30867      * @param {String} msg The validation message
30868      */
30869     markInvalid : function(msg)
30870     {
30871         
30872         var label = this.el.select('label', true).first();
30873         var icon = this.el.select('i.fa-star', true).first();
30874
30875         if(label && !icon){
30876             this.el.select('.roo-date-split-field-label', true).createChild({
30877                 tag : 'i',
30878                 cls : 'text-danger fa fa-lg fa-star',
30879                 tooltip : 'This field is required',
30880                 style : 'margin-right:5px;'
30881             }, label, true);
30882         }
30883         
30884         this.fireEvent('invalid', this, msg);
30885     },
30886     
30887     clearInvalid : function()
30888     {
30889         var label = this.el.select('label', true).first();
30890         var icon = this.el.select('i.fa-star', true).first();
30891
30892         if(label && icon){
30893             icon.remove();
30894         }
30895         
30896         this.fireEvent('valid', this);
30897     },
30898     
30899     getName: function()
30900     {
30901         return this.name;
30902     }
30903     
30904 });
30905
30906  /**
30907  *
30908  * This is based on 
30909  * http://masonry.desandro.com
30910  *
30911  * The idea is to render all the bricks based on vertical width...
30912  *
30913  * The original code extends 'outlayer' - we might need to use that....
30914  * 
30915  */
30916
30917
30918 /**
30919  * @class Roo.bootstrap.LayoutMasonry
30920  * @extends Roo.bootstrap.Component
30921  * Bootstrap Layout Masonry class
30922  * 
30923  * @constructor
30924  * Create a new Element
30925  * @param {Object} config The config object
30926  */
30927
30928 Roo.bootstrap.LayoutMasonry = function(config){
30929     
30930     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30931     
30932     this.bricks = [];
30933     
30934     Roo.bootstrap.LayoutMasonry.register(this);
30935     
30936     this.addEvents({
30937         // raw events
30938         /**
30939          * @event layout
30940          * Fire after layout the items
30941          * @param {Roo.bootstrap.LayoutMasonry} this
30942          * @param {Roo.EventObject} e
30943          */
30944         "layout" : true
30945     });
30946     
30947 };
30948
30949 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30950     
30951     /**
30952      * @cfg {Boolean} isLayoutInstant = no animation?
30953      */   
30954     isLayoutInstant : false, // needed?
30955    
30956     /**
30957      * @cfg {Number} boxWidth  width of the columns
30958      */   
30959     boxWidth : 450,
30960     
30961       /**
30962      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30963      */   
30964     boxHeight : 0,
30965     
30966     /**
30967      * @cfg {Number} padWidth padding below box..
30968      */   
30969     padWidth : 10, 
30970     
30971     /**
30972      * @cfg {Number} gutter gutter width..
30973      */   
30974     gutter : 10,
30975     
30976      /**
30977      * @cfg {Number} maxCols maximum number of columns
30978      */   
30979     
30980     maxCols: 0,
30981     
30982     /**
30983      * @cfg {Boolean} isAutoInitial defalut true
30984      */   
30985     isAutoInitial : true, 
30986     
30987     containerWidth: 0,
30988     
30989     /**
30990      * @cfg {Boolean} isHorizontal defalut false
30991      */   
30992     isHorizontal : false, 
30993
30994     currentSize : null,
30995     
30996     tag: 'div',
30997     
30998     cls: '',
30999     
31000     bricks: null, //CompositeElement
31001     
31002     cols : 1,
31003     
31004     _isLayoutInited : false,
31005     
31006 //    isAlternative : false, // only use for vertical layout...
31007     
31008     /**
31009      * @cfg {Number} alternativePadWidth padding below box..
31010      */   
31011     alternativePadWidth : 50,
31012     
31013     selectedBrick : [],
31014     
31015     getAutoCreate : function(){
31016         
31017         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31018         
31019         var cfg = {
31020             tag: this.tag,
31021             cls: 'blog-masonary-wrapper ' + this.cls,
31022             cn : {
31023                 cls : 'mas-boxes masonary'
31024             }
31025         };
31026         
31027         return cfg;
31028     },
31029     
31030     getChildContainer: function( )
31031     {
31032         if (this.boxesEl) {
31033             return this.boxesEl;
31034         }
31035         
31036         this.boxesEl = this.el.select('.mas-boxes').first();
31037         
31038         return this.boxesEl;
31039     },
31040     
31041     
31042     initEvents : function()
31043     {
31044         var _this = this;
31045         
31046         if(this.isAutoInitial){
31047             Roo.log('hook children rendered');
31048             this.on('childrenrendered', function() {
31049                 Roo.log('children rendered');
31050                 _this.initial();
31051             } ,this);
31052         }
31053     },
31054     
31055     initial : function()
31056     {
31057         this.selectedBrick = [];
31058         
31059         this.currentSize = this.el.getBox(true);
31060         
31061         Roo.EventManager.onWindowResize(this.resize, this); 
31062
31063         if(!this.isAutoInitial){
31064             this.layout();
31065             return;
31066         }
31067         
31068         this.layout();
31069         
31070         return;
31071         //this.layout.defer(500,this);
31072         
31073     },
31074     
31075     resize : function()
31076     {
31077         var cs = this.el.getBox(true);
31078         
31079         if (
31080                 this.currentSize.width == cs.width && 
31081                 this.currentSize.x == cs.x && 
31082                 this.currentSize.height == cs.height && 
31083                 this.currentSize.y == cs.y 
31084         ) {
31085             Roo.log("no change in with or X or Y");
31086             return;
31087         }
31088         
31089         this.currentSize = cs;
31090         
31091         this.layout();
31092         
31093     },
31094     
31095     layout : function()
31096     {   
31097         this._resetLayout();
31098         
31099         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31100         
31101         this.layoutItems( isInstant );
31102       
31103         this._isLayoutInited = true;
31104         
31105         this.fireEvent('layout', this);
31106         
31107     },
31108     
31109     _resetLayout : function()
31110     {
31111         if(this.isHorizontal){
31112             this.horizontalMeasureColumns();
31113             return;
31114         }
31115         
31116         this.verticalMeasureColumns();
31117         
31118     },
31119     
31120     verticalMeasureColumns : function()
31121     {
31122         this.getContainerWidth();
31123         
31124 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31125 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31126 //            return;
31127 //        }
31128         
31129         var boxWidth = this.boxWidth + this.padWidth;
31130         
31131         if(this.containerWidth < this.boxWidth){
31132             boxWidth = this.containerWidth
31133         }
31134         
31135         var containerWidth = this.containerWidth;
31136         
31137         var cols = Math.floor(containerWidth / boxWidth);
31138         
31139         this.cols = Math.max( cols, 1 );
31140         
31141         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31142         
31143         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31144         
31145         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31146         
31147         this.colWidth = boxWidth + avail - this.padWidth;
31148         
31149         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31150         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31151     },
31152     
31153     horizontalMeasureColumns : function()
31154     {
31155         this.getContainerWidth();
31156         
31157         var boxWidth = this.boxWidth;
31158         
31159         if(this.containerWidth < boxWidth){
31160             boxWidth = this.containerWidth;
31161         }
31162         
31163         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31164         
31165         this.el.setHeight(boxWidth);
31166         
31167     },
31168     
31169     getContainerWidth : function()
31170     {
31171         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31172     },
31173     
31174     layoutItems : function( isInstant )
31175     {
31176         Roo.log(this.bricks);
31177         
31178         var items = Roo.apply([], this.bricks);
31179         
31180         if(this.isHorizontal){
31181             this._horizontalLayoutItems( items , isInstant );
31182             return;
31183         }
31184         
31185 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31186 //            this._verticalAlternativeLayoutItems( items , isInstant );
31187 //            return;
31188 //        }
31189         
31190         this._verticalLayoutItems( items , isInstant );
31191         
31192     },
31193     
31194     _verticalLayoutItems : function ( items , isInstant)
31195     {
31196         if ( !items || !items.length ) {
31197             return;
31198         }
31199         
31200         var standard = [
31201             ['xs', 'xs', 'xs', 'tall'],
31202             ['xs', 'xs', 'tall'],
31203             ['xs', 'xs', 'sm'],
31204             ['xs', 'xs', 'xs'],
31205             ['xs', 'tall'],
31206             ['xs', 'sm'],
31207             ['xs', 'xs'],
31208             ['xs'],
31209             
31210             ['sm', 'xs', 'xs'],
31211             ['sm', 'xs'],
31212             ['sm'],
31213             
31214             ['tall', 'xs', 'xs', 'xs'],
31215             ['tall', 'xs', 'xs'],
31216             ['tall', 'xs'],
31217             ['tall']
31218             
31219         ];
31220         
31221         var queue = [];
31222         
31223         var boxes = [];
31224         
31225         var box = [];
31226         
31227         Roo.each(items, function(item, k){
31228             
31229             switch (item.size) {
31230                 // these layouts take up a full box,
31231                 case 'md' :
31232                 case 'md-left' :
31233                 case 'md-right' :
31234                 case 'wide' :
31235                     
31236                     if(box.length){
31237                         boxes.push(box);
31238                         box = [];
31239                     }
31240                     
31241                     boxes.push([item]);
31242                     
31243                     break;
31244                     
31245                 case 'xs' :
31246                 case 'sm' :
31247                 case 'tall' :
31248                     
31249                     box.push(item);
31250                     
31251                     break;
31252                 default :
31253                     break;
31254                     
31255             }
31256             
31257         }, this);
31258         
31259         if(box.length){
31260             boxes.push(box);
31261             box = [];
31262         }
31263         
31264         var filterPattern = function(box, length)
31265         {
31266             if(!box.length){
31267                 return;
31268             }
31269             
31270             var match = false;
31271             
31272             var pattern = box.slice(0, length);
31273             
31274             var format = [];
31275             
31276             Roo.each(pattern, function(i){
31277                 format.push(i.size);
31278             }, this);
31279             
31280             Roo.each(standard, function(s){
31281                 
31282                 if(String(s) != String(format)){
31283                     return;
31284                 }
31285                 
31286                 match = true;
31287                 return false;
31288                 
31289             }, this);
31290             
31291             if(!match && length == 1){
31292                 return;
31293             }
31294             
31295             if(!match){
31296                 filterPattern(box, length - 1);
31297                 return;
31298             }
31299                 
31300             queue.push(pattern);
31301
31302             box = box.slice(length, box.length);
31303
31304             filterPattern(box, 4);
31305
31306             return;
31307             
31308         }
31309         
31310         Roo.each(boxes, function(box, k){
31311             
31312             if(!box.length){
31313                 return;
31314             }
31315             
31316             if(box.length == 1){
31317                 queue.push(box);
31318                 return;
31319             }
31320             
31321             filterPattern(box, 4);
31322             
31323         }, this);
31324         
31325         this._processVerticalLayoutQueue( queue, isInstant );
31326         
31327     },
31328     
31329 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31330 //    {
31331 //        if ( !items || !items.length ) {
31332 //            return;
31333 //        }
31334 //
31335 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31336 //        
31337 //    },
31338     
31339     _horizontalLayoutItems : function ( items , isInstant)
31340     {
31341         if ( !items || !items.length || items.length < 3) {
31342             return;
31343         }
31344         
31345         items.reverse();
31346         
31347         var eItems = items.slice(0, 3);
31348         
31349         items = items.slice(3, items.length);
31350         
31351         var standard = [
31352             ['xs', 'xs', 'xs', 'wide'],
31353             ['xs', 'xs', 'wide'],
31354             ['xs', 'xs', 'sm'],
31355             ['xs', 'xs', 'xs'],
31356             ['xs', 'wide'],
31357             ['xs', 'sm'],
31358             ['xs', 'xs'],
31359             ['xs'],
31360             
31361             ['sm', 'xs', 'xs'],
31362             ['sm', 'xs'],
31363             ['sm'],
31364             
31365             ['wide', 'xs', 'xs', 'xs'],
31366             ['wide', 'xs', 'xs'],
31367             ['wide', 'xs'],
31368             ['wide'],
31369             
31370             ['wide-thin']
31371         ];
31372         
31373         var queue = [];
31374         
31375         var boxes = [];
31376         
31377         var box = [];
31378         
31379         Roo.each(items, function(item, k){
31380             
31381             switch (item.size) {
31382                 case 'md' :
31383                 case 'md-left' :
31384                 case 'md-right' :
31385                 case 'tall' :
31386                     
31387                     if(box.length){
31388                         boxes.push(box);
31389                         box = [];
31390                     }
31391                     
31392                     boxes.push([item]);
31393                     
31394                     break;
31395                     
31396                 case 'xs' :
31397                 case 'sm' :
31398                 case 'wide' :
31399                 case 'wide-thin' :
31400                     
31401                     box.push(item);
31402                     
31403                     break;
31404                 default :
31405                     break;
31406                     
31407             }
31408             
31409         }, this);
31410         
31411         if(box.length){
31412             boxes.push(box);
31413             box = [];
31414         }
31415         
31416         var filterPattern = function(box, length)
31417         {
31418             if(!box.length){
31419                 return;
31420             }
31421             
31422             var match = false;
31423             
31424             var pattern = box.slice(0, length);
31425             
31426             var format = [];
31427             
31428             Roo.each(pattern, function(i){
31429                 format.push(i.size);
31430             }, this);
31431             
31432             Roo.each(standard, function(s){
31433                 
31434                 if(String(s) != String(format)){
31435                     return;
31436                 }
31437                 
31438                 match = true;
31439                 return false;
31440                 
31441             }, this);
31442             
31443             if(!match && length == 1){
31444                 return;
31445             }
31446             
31447             if(!match){
31448                 filterPattern(box, length - 1);
31449                 return;
31450             }
31451                 
31452             queue.push(pattern);
31453
31454             box = box.slice(length, box.length);
31455
31456             filterPattern(box, 4);
31457
31458             return;
31459             
31460         }
31461         
31462         Roo.each(boxes, function(box, k){
31463             
31464             if(!box.length){
31465                 return;
31466             }
31467             
31468             if(box.length == 1){
31469                 queue.push(box);
31470                 return;
31471             }
31472             
31473             filterPattern(box, 4);
31474             
31475         }, this);
31476         
31477         
31478         var prune = [];
31479         
31480         var pos = this.el.getBox(true);
31481         
31482         var minX = pos.x;
31483         
31484         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31485         
31486         var hit_end = false;
31487         
31488         Roo.each(queue, function(box){
31489             
31490             if(hit_end){
31491                 
31492                 Roo.each(box, function(b){
31493                 
31494                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31495                     b.el.hide();
31496
31497                 }, this);
31498
31499                 return;
31500             }
31501             
31502             var mx = 0;
31503             
31504             Roo.each(box, function(b){
31505                 
31506                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31507                 b.el.show();
31508
31509                 mx = Math.max(mx, b.x);
31510                 
31511             }, this);
31512             
31513             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31514             
31515             if(maxX < minX){
31516                 
31517                 Roo.each(box, function(b){
31518                 
31519                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31520                     b.el.hide();
31521                     
31522                 }, this);
31523                 
31524                 hit_end = true;
31525                 
31526                 return;
31527             }
31528             
31529             prune.push(box);
31530             
31531         }, this);
31532         
31533         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31534     },
31535     
31536     /** Sets position of item in DOM
31537     * @param {Element} item
31538     * @param {Number} x - horizontal position
31539     * @param {Number} y - vertical position
31540     * @param {Boolean} isInstant - disables transitions
31541     */
31542     _processVerticalLayoutQueue : function( queue, isInstant )
31543     {
31544         var pos = this.el.getBox(true);
31545         var x = pos.x;
31546         var y = pos.y;
31547         var maxY = [];
31548         
31549         for (var i = 0; i < this.cols; i++){
31550             maxY[i] = pos.y;
31551         }
31552         
31553         Roo.each(queue, function(box, k){
31554             
31555             var col = k % this.cols;
31556             
31557             Roo.each(box, function(b,kk){
31558                 
31559                 b.el.position('absolute');
31560                 
31561                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31562                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31563                 
31564                 if(b.size == 'md-left' || b.size == 'md-right'){
31565                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31566                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31567                 }
31568                 
31569                 b.el.setWidth(width);
31570                 b.el.setHeight(height);
31571                 // iframe?
31572                 b.el.select('iframe',true).setSize(width,height);
31573                 
31574             }, this);
31575             
31576             for (var i = 0; i < this.cols; i++){
31577                 
31578                 if(maxY[i] < maxY[col]){
31579                     col = i;
31580                     continue;
31581                 }
31582                 
31583                 col = Math.min(col, i);
31584                 
31585             }
31586             
31587             x = pos.x + col * (this.colWidth + this.padWidth);
31588             
31589             y = maxY[col];
31590             
31591             var positions = [];
31592             
31593             switch (box.length){
31594                 case 1 :
31595                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31596                     break;
31597                 case 2 :
31598                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31599                     break;
31600                 case 3 :
31601                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31602                     break;
31603                 case 4 :
31604                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31605                     break;
31606                 default :
31607                     break;
31608             }
31609             
31610             Roo.each(box, function(b,kk){
31611                 
31612                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31613                 
31614                 var sz = b.el.getSize();
31615                 
31616                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31617                 
31618             }, this);
31619             
31620         }, this);
31621         
31622         var mY = 0;
31623         
31624         for (var i = 0; i < this.cols; i++){
31625             mY = Math.max(mY, maxY[i]);
31626         }
31627         
31628         this.el.setHeight(mY - pos.y);
31629         
31630     },
31631     
31632 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31633 //    {
31634 //        var pos = this.el.getBox(true);
31635 //        var x = pos.x;
31636 //        var y = pos.y;
31637 //        var maxX = pos.right;
31638 //        
31639 //        var maxHeight = 0;
31640 //        
31641 //        Roo.each(items, function(item, k){
31642 //            
31643 //            var c = k % 2;
31644 //            
31645 //            item.el.position('absolute');
31646 //                
31647 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31648 //
31649 //            item.el.setWidth(width);
31650 //
31651 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31652 //
31653 //            item.el.setHeight(height);
31654 //            
31655 //            if(c == 0){
31656 //                item.el.setXY([x, y], isInstant ? false : true);
31657 //            } else {
31658 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31659 //            }
31660 //            
31661 //            y = y + height + this.alternativePadWidth;
31662 //            
31663 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31664 //            
31665 //        }, this);
31666 //        
31667 //        this.el.setHeight(maxHeight);
31668 //        
31669 //    },
31670     
31671     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31672     {
31673         var pos = this.el.getBox(true);
31674         
31675         var minX = pos.x;
31676         var minY = pos.y;
31677         
31678         var maxX = pos.right;
31679         
31680         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31681         
31682         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31683         
31684         Roo.each(queue, function(box, k){
31685             
31686             Roo.each(box, function(b, kk){
31687                 
31688                 b.el.position('absolute');
31689                 
31690                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31691                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31692                 
31693                 if(b.size == 'md-left' || b.size == 'md-right'){
31694                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31695                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31696                 }
31697                 
31698                 b.el.setWidth(width);
31699                 b.el.setHeight(height);
31700                 
31701             }, this);
31702             
31703             if(!box.length){
31704                 return;
31705             }
31706             
31707             var positions = [];
31708             
31709             switch (box.length){
31710                 case 1 :
31711                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31712                     break;
31713                 case 2 :
31714                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31715                     break;
31716                 case 3 :
31717                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31718                     break;
31719                 case 4 :
31720                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31721                     break;
31722                 default :
31723                     break;
31724             }
31725             
31726             Roo.each(box, function(b,kk){
31727                 
31728                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31729                 
31730                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31731                 
31732             }, this);
31733             
31734         }, this);
31735         
31736     },
31737     
31738     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31739     {
31740         Roo.each(eItems, function(b,k){
31741             
31742             b.size = (k == 0) ? 'sm' : 'xs';
31743             b.x = (k == 0) ? 2 : 1;
31744             b.y = (k == 0) ? 2 : 1;
31745             
31746             b.el.position('absolute');
31747             
31748             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31749                 
31750             b.el.setWidth(width);
31751             
31752             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31753             
31754             b.el.setHeight(height);
31755             
31756         }, this);
31757
31758         var positions = [];
31759         
31760         positions.push({
31761             x : maxX - this.unitWidth * 2 - this.gutter,
31762             y : minY
31763         });
31764         
31765         positions.push({
31766             x : maxX - this.unitWidth,
31767             y : minY + (this.unitWidth + this.gutter) * 2
31768         });
31769         
31770         positions.push({
31771             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31772             y : minY
31773         });
31774         
31775         Roo.each(eItems, function(b,k){
31776             
31777             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31778
31779         }, this);
31780         
31781     },
31782     
31783     getVerticalOneBoxColPositions : function(x, y, box)
31784     {
31785         var pos = [];
31786         
31787         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31788         
31789         if(box[0].size == 'md-left'){
31790             rand = 0;
31791         }
31792         
31793         if(box[0].size == 'md-right'){
31794             rand = 1;
31795         }
31796         
31797         pos.push({
31798             x : x + (this.unitWidth + this.gutter) * rand,
31799             y : y
31800         });
31801         
31802         return pos;
31803     },
31804     
31805     getVerticalTwoBoxColPositions : function(x, y, box)
31806     {
31807         var pos = [];
31808         
31809         if(box[0].size == 'xs'){
31810             
31811             pos.push({
31812                 x : x,
31813                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31814             });
31815
31816             pos.push({
31817                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31818                 y : y
31819             });
31820             
31821             return pos;
31822             
31823         }
31824         
31825         pos.push({
31826             x : x,
31827             y : y
31828         });
31829
31830         pos.push({
31831             x : x + (this.unitWidth + this.gutter) * 2,
31832             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31833         });
31834         
31835         return pos;
31836         
31837     },
31838     
31839     getVerticalThreeBoxColPositions : function(x, y, box)
31840     {
31841         var pos = [];
31842         
31843         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31844             
31845             pos.push({
31846                 x : x,
31847                 y : y
31848             });
31849
31850             pos.push({
31851                 x : x + (this.unitWidth + this.gutter) * 1,
31852                 y : y
31853             });
31854             
31855             pos.push({
31856                 x : x + (this.unitWidth + this.gutter) * 2,
31857                 y : y
31858             });
31859             
31860             return pos;
31861             
31862         }
31863         
31864         if(box[0].size == 'xs' && box[1].size == 'xs'){
31865             
31866             pos.push({
31867                 x : x,
31868                 y : y
31869             });
31870
31871             pos.push({
31872                 x : x,
31873                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31874             });
31875             
31876             pos.push({
31877                 x : x + (this.unitWidth + this.gutter) * 1,
31878                 y : y
31879             });
31880             
31881             return pos;
31882             
31883         }
31884         
31885         pos.push({
31886             x : x,
31887             y : y
31888         });
31889
31890         pos.push({
31891             x : x + (this.unitWidth + this.gutter) * 2,
31892             y : y
31893         });
31894
31895         pos.push({
31896             x : x + (this.unitWidth + this.gutter) * 2,
31897             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31898         });
31899             
31900         return pos;
31901         
31902     },
31903     
31904     getVerticalFourBoxColPositions : function(x, y, box)
31905     {
31906         var pos = [];
31907         
31908         if(box[0].size == 'xs'){
31909             
31910             pos.push({
31911                 x : x,
31912                 y : y
31913             });
31914
31915             pos.push({
31916                 x : x,
31917                 y : y + (this.unitHeight + this.gutter) * 1
31918             });
31919             
31920             pos.push({
31921                 x : x,
31922                 y : y + (this.unitHeight + this.gutter) * 2
31923             });
31924             
31925             pos.push({
31926                 x : x + (this.unitWidth + this.gutter) * 1,
31927                 y : y
31928             });
31929             
31930             return pos;
31931             
31932         }
31933         
31934         pos.push({
31935             x : x,
31936             y : y
31937         });
31938
31939         pos.push({
31940             x : x + (this.unitWidth + this.gutter) * 2,
31941             y : y
31942         });
31943
31944         pos.push({
31945             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31946             y : y + (this.unitHeight + this.gutter) * 1
31947         });
31948
31949         pos.push({
31950             x : x + (this.unitWidth + this.gutter) * 2,
31951             y : y + (this.unitWidth + this.gutter) * 2
31952         });
31953
31954         return pos;
31955         
31956     },
31957     
31958     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31959     {
31960         var pos = [];
31961         
31962         if(box[0].size == 'md-left'){
31963             pos.push({
31964                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31965                 y : minY
31966             });
31967             
31968             return pos;
31969         }
31970         
31971         if(box[0].size == 'md-right'){
31972             pos.push({
31973                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31974                 y : minY + (this.unitWidth + this.gutter) * 1
31975             });
31976             
31977             return pos;
31978         }
31979         
31980         var rand = Math.floor(Math.random() * (4 - box[0].y));
31981         
31982         pos.push({
31983             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31984             y : minY + (this.unitWidth + this.gutter) * rand
31985         });
31986         
31987         return pos;
31988         
31989     },
31990     
31991     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31992     {
31993         var pos = [];
31994         
31995         if(box[0].size == 'xs'){
31996             
31997             pos.push({
31998                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31999                 y : minY
32000             });
32001
32002             pos.push({
32003                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32004                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32005             });
32006             
32007             return pos;
32008             
32009         }
32010         
32011         pos.push({
32012             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32013             y : minY
32014         });
32015
32016         pos.push({
32017             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32018             y : minY + (this.unitWidth + this.gutter) * 2
32019         });
32020         
32021         return pos;
32022         
32023     },
32024     
32025     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32026     {
32027         var pos = [];
32028         
32029         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32030             
32031             pos.push({
32032                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32033                 y : minY
32034             });
32035
32036             pos.push({
32037                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32038                 y : minY + (this.unitWidth + this.gutter) * 1
32039             });
32040             
32041             pos.push({
32042                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32043                 y : minY + (this.unitWidth + this.gutter) * 2
32044             });
32045             
32046             return pos;
32047             
32048         }
32049         
32050         if(box[0].size == 'xs' && box[1].size == 'xs'){
32051             
32052             pos.push({
32053                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32054                 y : minY
32055             });
32056
32057             pos.push({
32058                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32059                 y : minY
32060             });
32061             
32062             pos.push({
32063                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32064                 y : minY + (this.unitWidth + this.gutter) * 1
32065             });
32066             
32067             return pos;
32068             
32069         }
32070         
32071         pos.push({
32072             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32073             y : minY
32074         });
32075
32076         pos.push({
32077             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32078             y : minY + (this.unitWidth + this.gutter) * 2
32079         });
32080
32081         pos.push({
32082             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32083             y : minY + (this.unitWidth + this.gutter) * 2
32084         });
32085             
32086         return pos;
32087         
32088     },
32089     
32090     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32091     {
32092         var pos = [];
32093         
32094         if(box[0].size == 'xs'){
32095             
32096             pos.push({
32097                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32098                 y : minY
32099             });
32100
32101             pos.push({
32102                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32103                 y : minY
32104             });
32105             
32106             pos.push({
32107                 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),
32108                 y : minY
32109             });
32110             
32111             pos.push({
32112                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32113                 y : minY + (this.unitWidth + this.gutter) * 1
32114             });
32115             
32116             return pos;
32117             
32118         }
32119         
32120         pos.push({
32121             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32122             y : minY
32123         });
32124         
32125         pos.push({
32126             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32127             y : minY + (this.unitWidth + this.gutter) * 2
32128         });
32129         
32130         pos.push({
32131             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32132             y : minY + (this.unitWidth + this.gutter) * 2
32133         });
32134         
32135         pos.push({
32136             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),
32137             y : minY + (this.unitWidth + this.gutter) * 2
32138         });
32139
32140         return pos;
32141         
32142     },
32143     
32144     /**
32145     * remove a Masonry Brick
32146     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32147     */
32148     removeBrick : function(brick_id)
32149     {
32150         if (!brick_id) {
32151             return;
32152         }
32153         
32154         for (var i = 0; i<this.bricks.length; i++) {
32155             if (this.bricks[i].id == brick_id) {
32156                 this.bricks.splice(i,1);
32157                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32158                 this.initial();
32159             }
32160         }
32161     },
32162     
32163     /**
32164     * adds a Masonry Brick
32165     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32166     */
32167     addBrick : function(cfg)
32168     {
32169         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32170         //this.register(cn);
32171         cn.parentId = this.id;
32172         cn.render(this.el);
32173         return cn;
32174     },
32175     
32176     /**
32177     * register a Masonry Brick
32178     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32179     */
32180     
32181     register : function(brick)
32182     {
32183         this.bricks.push(brick);
32184         brick.masonryId = this.id;
32185     },
32186     
32187     /**
32188     * clear all the Masonry Brick
32189     */
32190     clearAll : function()
32191     {
32192         this.bricks = [];
32193         //this.getChildContainer().dom.innerHTML = "";
32194         this.el.dom.innerHTML = '';
32195     },
32196     
32197     getSelected : function()
32198     {
32199         if (!this.selectedBrick) {
32200             return false;
32201         }
32202         
32203         return this.selectedBrick;
32204     }
32205 });
32206
32207 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32208     
32209     groups: {},
32210      /**
32211     * register a Masonry Layout
32212     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32213     */
32214     
32215     register : function(layout)
32216     {
32217         this.groups[layout.id] = layout;
32218     },
32219     /**
32220     * fetch a  Masonry Layout based on the masonry layout ID
32221     * @param {string} the masonry layout to add
32222     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32223     */
32224     
32225     get: function(layout_id) {
32226         if (typeof(this.groups[layout_id]) == 'undefined') {
32227             return false;
32228         }
32229         return this.groups[layout_id] ;
32230     }
32231     
32232     
32233     
32234 });
32235
32236  
32237
32238  /**
32239  *
32240  * This is based on 
32241  * http://masonry.desandro.com
32242  *
32243  * The idea is to render all the bricks based on vertical width...
32244  *
32245  * The original code extends 'outlayer' - we might need to use that....
32246  * 
32247  */
32248
32249
32250 /**
32251  * @class Roo.bootstrap.LayoutMasonryAuto
32252  * @extends Roo.bootstrap.Component
32253  * Bootstrap Layout Masonry class
32254  * 
32255  * @constructor
32256  * Create a new Element
32257  * @param {Object} config The config object
32258  */
32259
32260 Roo.bootstrap.LayoutMasonryAuto = function(config){
32261     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32262 };
32263
32264 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32265     
32266       /**
32267      * @cfg {Boolean} isFitWidth  - resize the width..
32268      */   
32269     isFitWidth : false,  // options..
32270     /**
32271      * @cfg {Boolean} isOriginLeft = left align?
32272      */   
32273     isOriginLeft : true,
32274     /**
32275      * @cfg {Boolean} isOriginTop = top align?
32276      */   
32277     isOriginTop : false,
32278     /**
32279      * @cfg {Boolean} isLayoutInstant = no animation?
32280      */   
32281     isLayoutInstant : false, // needed?
32282     /**
32283      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32284      */   
32285     isResizingContainer : true,
32286     /**
32287      * @cfg {Number} columnWidth  width of the columns 
32288      */   
32289     
32290     columnWidth : 0,
32291     
32292     /**
32293      * @cfg {Number} maxCols maximum number of columns
32294      */   
32295     
32296     maxCols: 0,
32297     /**
32298      * @cfg {Number} padHeight padding below box..
32299      */   
32300     
32301     padHeight : 10, 
32302     
32303     /**
32304      * @cfg {Boolean} isAutoInitial defalut true
32305      */   
32306     
32307     isAutoInitial : true, 
32308     
32309     // private?
32310     gutter : 0,
32311     
32312     containerWidth: 0,
32313     initialColumnWidth : 0,
32314     currentSize : null,
32315     
32316     colYs : null, // array.
32317     maxY : 0,
32318     padWidth: 10,
32319     
32320     
32321     tag: 'div',
32322     cls: '',
32323     bricks: null, //CompositeElement
32324     cols : 0, // array?
32325     // element : null, // wrapped now this.el
32326     _isLayoutInited : null, 
32327     
32328     
32329     getAutoCreate : function(){
32330         
32331         var cfg = {
32332             tag: this.tag,
32333             cls: 'blog-masonary-wrapper ' + this.cls,
32334             cn : {
32335                 cls : 'mas-boxes masonary'
32336             }
32337         };
32338         
32339         return cfg;
32340     },
32341     
32342     getChildContainer: function( )
32343     {
32344         if (this.boxesEl) {
32345             return this.boxesEl;
32346         }
32347         
32348         this.boxesEl = this.el.select('.mas-boxes').first();
32349         
32350         return this.boxesEl;
32351     },
32352     
32353     
32354     initEvents : function()
32355     {
32356         var _this = this;
32357         
32358         if(this.isAutoInitial){
32359             Roo.log('hook children rendered');
32360             this.on('childrenrendered', function() {
32361                 Roo.log('children rendered');
32362                 _this.initial();
32363             } ,this);
32364         }
32365         
32366     },
32367     
32368     initial : function()
32369     {
32370         this.reloadItems();
32371
32372         this.currentSize = this.el.getBox(true);
32373
32374         /// was window resize... - let's see if this works..
32375         Roo.EventManager.onWindowResize(this.resize, this); 
32376
32377         if(!this.isAutoInitial){
32378             this.layout();
32379             return;
32380         }
32381         
32382         this.layout.defer(500,this);
32383     },
32384     
32385     reloadItems: function()
32386     {
32387         this.bricks = this.el.select('.masonry-brick', true);
32388         
32389         this.bricks.each(function(b) {
32390             //Roo.log(b.getSize());
32391             if (!b.attr('originalwidth')) {
32392                 b.attr('originalwidth',  b.getSize().width);
32393             }
32394             
32395         });
32396         
32397         Roo.log(this.bricks.elements.length);
32398     },
32399     
32400     resize : function()
32401     {
32402         Roo.log('resize');
32403         var cs = this.el.getBox(true);
32404         
32405         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32406             Roo.log("no change in with or X");
32407             return;
32408         }
32409         this.currentSize = cs;
32410         this.layout();
32411     },
32412     
32413     layout : function()
32414     {
32415          Roo.log('layout');
32416         this._resetLayout();
32417         //this._manageStamps();
32418       
32419         // don't animate first layout
32420         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32421         this.layoutItems( isInstant );
32422       
32423         // flag for initalized
32424         this._isLayoutInited = true;
32425     },
32426     
32427     layoutItems : function( isInstant )
32428     {
32429         //var items = this._getItemsForLayout( this.items );
32430         // original code supports filtering layout items.. we just ignore it..
32431         
32432         this._layoutItems( this.bricks , isInstant );
32433       
32434         this._postLayout();
32435     },
32436     _layoutItems : function ( items , isInstant)
32437     {
32438        //this.fireEvent( 'layout', this, items );
32439     
32440
32441         if ( !items || !items.elements.length ) {
32442           // no items, emit event with empty array
32443             return;
32444         }
32445
32446         var queue = [];
32447         items.each(function(item) {
32448             Roo.log("layout item");
32449             Roo.log(item);
32450             // get x/y object from method
32451             var position = this._getItemLayoutPosition( item );
32452             // enqueue
32453             position.item = item;
32454             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32455             queue.push( position );
32456         }, this);
32457       
32458         this._processLayoutQueue( queue );
32459     },
32460     /** Sets position of item in DOM
32461     * @param {Element} item
32462     * @param {Number} x - horizontal position
32463     * @param {Number} y - vertical position
32464     * @param {Boolean} isInstant - disables transitions
32465     */
32466     _processLayoutQueue : function( queue )
32467     {
32468         for ( var i=0, len = queue.length; i < len; i++ ) {
32469             var obj = queue[i];
32470             obj.item.position('absolute');
32471             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32472         }
32473     },
32474       
32475     
32476     /**
32477     * Any logic you want to do after each layout,
32478     * i.e. size the container
32479     */
32480     _postLayout : function()
32481     {
32482         this.resizeContainer();
32483     },
32484     
32485     resizeContainer : function()
32486     {
32487         if ( !this.isResizingContainer ) {
32488             return;
32489         }
32490         var size = this._getContainerSize();
32491         if ( size ) {
32492             this.el.setSize(size.width,size.height);
32493             this.boxesEl.setSize(size.width,size.height);
32494         }
32495     },
32496     
32497     
32498     
32499     _resetLayout : function()
32500     {
32501         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32502         this.colWidth = this.el.getWidth();
32503         //this.gutter = this.el.getWidth(); 
32504         
32505         this.measureColumns();
32506
32507         // reset column Y
32508         var i = this.cols;
32509         this.colYs = [];
32510         while (i--) {
32511             this.colYs.push( 0 );
32512         }
32513     
32514         this.maxY = 0;
32515     },
32516
32517     measureColumns : function()
32518     {
32519         this.getContainerWidth();
32520       // if columnWidth is 0, default to outerWidth of first item
32521         if ( !this.columnWidth ) {
32522             var firstItem = this.bricks.first();
32523             Roo.log(firstItem);
32524             this.columnWidth  = this.containerWidth;
32525             if (firstItem && firstItem.attr('originalwidth') ) {
32526                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32527             }
32528             // columnWidth fall back to item of first element
32529             Roo.log("set column width?");
32530                         this.initialColumnWidth = this.columnWidth  ;
32531
32532             // if first elem has no width, default to size of container
32533             
32534         }
32535         
32536         
32537         if (this.initialColumnWidth) {
32538             this.columnWidth = this.initialColumnWidth;
32539         }
32540         
32541         
32542             
32543         // column width is fixed at the top - however if container width get's smaller we should
32544         // reduce it...
32545         
32546         // this bit calcs how man columns..
32547             
32548         var columnWidth = this.columnWidth += this.gutter;
32549       
32550         // calculate columns
32551         var containerWidth = this.containerWidth + this.gutter;
32552         
32553         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32554         // fix rounding errors, typically with gutters
32555         var excess = columnWidth - containerWidth % columnWidth;
32556         
32557         
32558         // if overshoot is less than a pixel, round up, otherwise floor it
32559         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32560         cols = Math[ mathMethod ]( cols );
32561         this.cols = Math.max( cols, 1 );
32562         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32563         
32564          // padding positioning..
32565         var totalColWidth = this.cols * this.columnWidth;
32566         var padavail = this.containerWidth - totalColWidth;
32567         // so for 2 columns - we need 3 'pads'
32568         
32569         var padNeeded = (1+this.cols) * this.padWidth;
32570         
32571         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32572         
32573         this.columnWidth += padExtra
32574         //this.padWidth = Math.floor(padavail /  ( this.cols));
32575         
32576         // adjust colum width so that padding is fixed??
32577         
32578         // we have 3 columns ... total = width * 3
32579         // we have X left over... that should be used by 
32580         
32581         //if (this.expandC) {
32582             
32583         //}
32584         
32585         
32586         
32587     },
32588     
32589     getContainerWidth : function()
32590     {
32591        /* // container is parent if fit width
32592         var container = this.isFitWidth ? this.element.parentNode : this.element;
32593         // check that this.size and size are there
32594         // IE8 triggers resize on body size change, so they might not be
32595         
32596         var size = getSize( container );  //FIXME
32597         this.containerWidth = size && size.innerWidth; //FIXME
32598         */
32599          
32600         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32601         
32602     },
32603     
32604     _getItemLayoutPosition : function( item )  // what is item?
32605     {
32606         // we resize the item to our columnWidth..
32607       
32608         item.setWidth(this.columnWidth);
32609         item.autoBoxAdjust  = false;
32610         
32611         var sz = item.getSize();
32612  
32613         // how many columns does this brick span
32614         var remainder = this.containerWidth % this.columnWidth;
32615         
32616         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32617         // round if off by 1 pixel, otherwise use ceil
32618         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32619         colSpan = Math.min( colSpan, this.cols );
32620         
32621         // normally this should be '1' as we dont' currently allow multi width columns..
32622         
32623         var colGroup = this._getColGroup( colSpan );
32624         // get the minimum Y value from the columns
32625         var minimumY = Math.min.apply( Math, colGroup );
32626         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32627         
32628         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32629          
32630         // position the brick
32631         var position = {
32632             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32633             y: this.currentSize.y + minimumY + this.padHeight
32634         };
32635         
32636         Roo.log(position);
32637         // apply setHeight to necessary columns
32638         var setHeight = minimumY + sz.height + this.padHeight;
32639         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32640         
32641         var setSpan = this.cols + 1 - colGroup.length;
32642         for ( var i = 0; i < setSpan; i++ ) {
32643           this.colYs[ shortColIndex + i ] = setHeight ;
32644         }
32645       
32646         return position;
32647     },
32648     
32649     /**
32650      * @param {Number} colSpan - number of columns the element spans
32651      * @returns {Array} colGroup
32652      */
32653     _getColGroup : function( colSpan )
32654     {
32655         if ( colSpan < 2 ) {
32656           // if brick spans only one column, use all the column Ys
32657           return this.colYs;
32658         }
32659       
32660         var colGroup = [];
32661         // how many different places could this brick fit horizontally
32662         var groupCount = this.cols + 1 - colSpan;
32663         // for each group potential horizontal position
32664         for ( var i = 0; i < groupCount; i++ ) {
32665           // make an array of colY values for that one group
32666           var groupColYs = this.colYs.slice( i, i + colSpan );
32667           // and get the max value of the array
32668           colGroup[i] = Math.max.apply( Math, groupColYs );
32669         }
32670         return colGroup;
32671     },
32672     /*
32673     _manageStamp : function( stamp )
32674     {
32675         var stampSize =  stamp.getSize();
32676         var offset = stamp.getBox();
32677         // get the columns that this stamp affects
32678         var firstX = this.isOriginLeft ? offset.x : offset.right;
32679         var lastX = firstX + stampSize.width;
32680         var firstCol = Math.floor( firstX / this.columnWidth );
32681         firstCol = Math.max( 0, firstCol );
32682         
32683         var lastCol = Math.floor( lastX / this.columnWidth );
32684         // lastCol should not go over if multiple of columnWidth #425
32685         lastCol -= lastX % this.columnWidth ? 0 : 1;
32686         lastCol = Math.min( this.cols - 1, lastCol );
32687         
32688         // set colYs to bottom of the stamp
32689         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32690             stampSize.height;
32691             
32692         for ( var i = firstCol; i <= lastCol; i++ ) {
32693           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32694         }
32695     },
32696     */
32697     
32698     _getContainerSize : function()
32699     {
32700         this.maxY = Math.max.apply( Math, this.colYs );
32701         var size = {
32702             height: this.maxY
32703         };
32704       
32705         if ( this.isFitWidth ) {
32706             size.width = this._getContainerFitWidth();
32707         }
32708       
32709         return size;
32710     },
32711     
32712     _getContainerFitWidth : function()
32713     {
32714         var unusedCols = 0;
32715         // count unused columns
32716         var i = this.cols;
32717         while ( --i ) {
32718           if ( this.colYs[i] !== 0 ) {
32719             break;
32720           }
32721           unusedCols++;
32722         }
32723         // fit container to columns that have been used
32724         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32725     },
32726     
32727     needsResizeLayout : function()
32728     {
32729         var previousWidth = this.containerWidth;
32730         this.getContainerWidth();
32731         return previousWidth !== this.containerWidth;
32732     }
32733  
32734 });
32735
32736  
32737
32738  /*
32739  * - LGPL
32740  *
32741  * element
32742  * 
32743  */
32744
32745 /**
32746  * @class Roo.bootstrap.MasonryBrick
32747  * @extends Roo.bootstrap.Component
32748  * Bootstrap MasonryBrick class
32749  * 
32750  * @constructor
32751  * Create a new MasonryBrick
32752  * @param {Object} config The config object
32753  */
32754
32755 Roo.bootstrap.MasonryBrick = function(config){
32756     
32757     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32758     
32759     Roo.bootstrap.MasonryBrick.register(this);
32760     
32761     this.addEvents({
32762         // raw events
32763         /**
32764          * @event click
32765          * When a MasonryBrick is clcik
32766          * @param {Roo.bootstrap.MasonryBrick} this
32767          * @param {Roo.EventObject} e
32768          */
32769         "click" : true
32770     });
32771 };
32772
32773 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32774     
32775     /**
32776      * @cfg {String} title
32777      */   
32778     title : '',
32779     /**
32780      * @cfg {String} html
32781      */   
32782     html : '',
32783     /**
32784      * @cfg {String} bgimage
32785      */   
32786     bgimage : '',
32787     /**
32788      * @cfg {String} videourl
32789      */   
32790     videourl : '',
32791     /**
32792      * @cfg {String} cls
32793      */   
32794     cls : '',
32795     /**
32796      * @cfg {String} href
32797      */   
32798     href : '',
32799     /**
32800      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32801      */   
32802     size : 'xs',
32803     
32804     /**
32805      * @cfg {String} placetitle (center|bottom)
32806      */   
32807     placetitle : '',
32808     
32809     /**
32810      * @cfg {Boolean} isFitContainer defalut true
32811      */   
32812     isFitContainer : true, 
32813     
32814     /**
32815      * @cfg {Boolean} preventDefault defalut false
32816      */   
32817     preventDefault : false, 
32818     
32819     /**
32820      * @cfg {Boolean} inverse defalut false
32821      */   
32822     maskInverse : false, 
32823     
32824     getAutoCreate : function()
32825     {
32826         if(!this.isFitContainer){
32827             return this.getSplitAutoCreate();
32828         }
32829         
32830         var cls = 'masonry-brick masonry-brick-full';
32831         
32832         if(this.href.length){
32833             cls += ' masonry-brick-link';
32834         }
32835         
32836         if(this.bgimage.length){
32837             cls += ' masonry-brick-image';
32838         }
32839         
32840         if(this.maskInverse){
32841             cls += ' mask-inverse';
32842         }
32843         
32844         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32845             cls += ' enable-mask';
32846         }
32847         
32848         if(this.size){
32849             cls += ' masonry-' + this.size + '-brick';
32850         }
32851         
32852         if(this.placetitle.length){
32853             
32854             switch (this.placetitle) {
32855                 case 'center' :
32856                     cls += ' masonry-center-title';
32857                     break;
32858                 case 'bottom' :
32859                     cls += ' masonry-bottom-title';
32860                     break;
32861                 default:
32862                     break;
32863             }
32864             
32865         } else {
32866             if(!this.html.length && !this.bgimage.length){
32867                 cls += ' masonry-center-title';
32868             }
32869
32870             if(!this.html.length && this.bgimage.length){
32871                 cls += ' masonry-bottom-title';
32872             }
32873         }
32874         
32875         if(this.cls){
32876             cls += ' ' + this.cls;
32877         }
32878         
32879         var cfg = {
32880             tag: (this.href.length) ? 'a' : 'div',
32881             cls: cls,
32882             cn: [
32883                 {
32884                     tag: 'div',
32885                     cls: 'masonry-brick-mask'
32886                 },
32887                 {
32888                     tag: 'div',
32889                     cls: 'masonry-brick-paragraph',
32890                     cn: []
32891                 }
32892             ]
32893         };
32894         
32895         if(this.href.length){
32896             cfg.href = this.href;
32897         }
32898         
32899         var cn = cfg.cn[1].cn;
32900         
32901         if(this.title.length){
32902             cn.push({
32903                 tag: 'h4',
32904                 cls: 'masonry-brick-title',
32905                 html: this.title
32906             });
32907         }
32908         
32909         if(this.html.length){
32910             cn.push({
32911                 tag: 'p',
32912                 cls: 'masonry-brick-text',
32913                 html: this.html
32914             });
32915         }
32916         
32917         if (!this.title.length && !this.html.length) {
32918             cfg.cn[1].cls += ' hide';
32919         }
32920         
32921         if(this.bgimage.length){
32922             cfg.cn.push({
32923                 tag: 'img',
32924                 cls: 'masonry-brick-image-view',
32925                 src: this.bgimage
32926             });
32927         }
32928         
32929         if(this.videourl.length){
32930             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32931             // youtube support only?
32932             cfg.cn.push({
32933                 tag: 'iframe',
32934                 cls: 'masonry-brick-image-view',
32935                 src: vurl,
32936                 frameborder : 0,
32937                 allowfullscreen : true
32938             });
32939         }
32940         
32941         return cfg;
32942         
32943     },
32944     
32945     getSplitAutoCreate : function()
32946     {
32947         var cls = 'masonry-brick masonry-brick-split';
32948         
32949         if(this.href.length){
32950             cls += ' masonry-brick-link';
32951         }
32952         
32953         if(this.bgimage.length){
32954             cls += ' masonry-brick-image';
32955         }
32956         
32957         if(this.size){
32958             cls += ' masonry-' + this.size + '-brick';
32959         }
32960         
32961         switch (this.placetitle) {
32962             case 'center' :
32963                 cls += ' masonry-center-title';
32964                 break;
32965             case 'bottom' :
32966                 cls += ' masonry-bottom-title';
32967                 break;
32968             default:
32969                 if(!this.bgimage.length){
32970                     cls += ' masonry-center-title';
32971                 }
32972
32973                 if(this.bgimage.length){
32974                     cls += ' masonry-bottom-title';
32975                 }
32976                 break;
32977         }
32978         
32979         if(this.cls){
32980             cls += ' ' + this.cls;
32981         }
32982         
32983         var cfg = {
32984             tag: (this.href.length) ? 'a' : 'div',
32985             cls: cls,
32986             cn: [
32987                 {
32988                     tag: 'div',
32989                     cls: 'masonry-brick-split-head',
32990                     cn: [
32991                         {
32992                             tag: 'div',
32993                             cls: 'masonry-brick-paragraph',
32994                             cn: []
32995                         }
32996                     ]
32997                 },
32998                 {
32999                     tag: 'div',
33000                     cls: 'masonry-brick-split-body',
33001                     cn: []
33002                 }
33003             ]
33004         };
33005         
33006         if(this.href.length){
33007             cfg.href = this.href;
33008         }
33009         
33010         if(this.title.length){
33011             cfg.cn[0].cn[0].cn.push({
33012                 tag: 'h4',
33013                 cls: 'masonry-brick-title',
33014                 html: this.title
33015             });
33016         }
33017         
33018         if(this.html.length){
33019             cfg.cn[1].cn.push({
33020                 tag: 'p',
33021                 cls: 'masonry-brick-text',
33022                 html: this.html
33023             });
33024         }
33025
33026         if(this.bgimage.length){
33027             cfg.cn[0].cn.push({
33028                 tag: 'img',
33029                 cls: 'masonry-brick-image-view',
33030                 src: this.bgimage
33031             });
33032         }
33033         
33034         if(this.videourl.length){
33035             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33036             // youtube support only?
33037             cfg.cn[0].cn.cn.push({
33038                 tag: 'iframe',
33039                 cls: 'masonry-brick-image-view',
33040                 src: vurl,
33041                 frameborder : 0,
33042                 allowfullscreen : true
33043             });
33044         }
33045         
33046         return cfg;
33047     },
33048     
33049     initEvents: function() 
33050     {
33051         switch (this.size) {
33052             case 'xs' :
33053                 this.x = 1;
33054                 this.y = 1;
33055                 break;
33056             case 'sm' :
33057                 this.x = 2;
33058                 this.y = 2;
33059                 break;
33060             case 'md' :
33061             case 'md-left' :
33062             case 'md-right' :
33063                 this.x = 3;
33064                 this.y = 3;
33065                 break;
33066             case 'tall' :
33067                 this.x = 2;
33068                 this.y = 3;
33069                 break;
33070             case 'wide' :
33071                 this.x = 3;
33072                 this.y = 2;
33073                 break;
33074             case 'wide-thin' :
33075                 this.x = 3;
33076                 this.y = 1;
33077                 break;
33078                         
33079             default :
33080                 break;
33081         }
33082         
33083         if(Roo.isTouch){
33084             this.el.on('touchstart', this.onTouchStart, this);
33085             this.el.on('touchmove', this.onTouchMove, this);
33086             this.el.on('touchend', this.onTouchEnd, this);
33087             this.el.on('contextmenu', this.onContextMenu, this);
33088         } else {
33089             this.el.on('mouseenter'  ,this.enter, this);
33090             this.el.on('mouseleave', this.leave, this);
33091             this.el.on('click', this.onClick, this);
33092         }
33093         
33094         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33095             this.parent().bricks.push(this);   
33096         }
33097         
33098     },
33099     
33100     onClick: function(e, el)
33101     {
33102         var time = this.endTimer - this.startTimer;
33103         // Roo.log(e.preventDefault());
33104         if(Roo.isTouch){
33105             if(time > 1000){
33106                 e.preventDefault();
33107                 return;
33108             }
33109         }
33110         
33111         if(!this.preventDefault){
33112             return;
33113         }
33114         
33115         e.preventDefault();
33116         
33117         if (this.activeClass != '') {
33118             this.selectBrick();
33119         }
33120         
33121         this.fireEvent('click', this, e);
33122     },
33123     
33124     enter: function(e, el)
33125     {
33126         e.preventDefault();
33127         
33128         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33129             return;
33130         }
33131         
33132         if(this.bgimage.length && this.html.length){
33133             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33134         }
33135     },
33136     
33137     leave: function(e, el)
33138     {
33139         e.preventDefault();
33140         
33141         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33142             return;
33143         }
33144         
33145         if(this.bgimage.length && this.html.length){
33146             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33147         }
33148     },
33149     
33150     onTouchStart: function(e, el)
33151     {
33152 //        e.preventDefault();
33153         
33154         this.touchmoved = false;
33155         
33156         if(!this.isFitContainer){
33157             return;
33158         }
33159         
33160         if(!this.bgimage.length || !this.html.length){
33161             return;
33162         }
33163         
33164         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33165         
33166         this.timer = new Date().getTime();
33167         
33168     },
33169     
33170     onTouchMove: function(e, el)
33171     {
33172         this.touchmoved = true;
33173     },
33174     
33175     onContextMenu : function(e,el)
33176     {
33177         e.preventDefault();
33178         e.stopPropagation();
33179         return false;
33180     },
33181     
33182     onTouchEnd: function(e, el)
33183     {
33184 //        e.preventDefault();
33185         
33186         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33187         
33188             this.leave(e,el);
33189             
33190             return;
33191         }
33192         
33193         if(!this.bgimage.length || !this.html.length){
33194             
33195             if(this.href.length){
33196                 window.location.href = this.href;
33197             }
33198             
33199             return;
33200         }
33201         
33202         if(!this.isFitContainer){
33203             return;
33204         }
33205         
33206         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33207         
33208         window.location.href = this.href;
33209     },
33210     
33211     //selection on single brick only
33212     selectBrick : function() {
33213         
33214         if (!this.parentId) {
33215             return;
33216         }
33217         
33218         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33219         var index = m.selectedBrick.indexOf(this.id);
33220         
33221         if ( index > -1) {
33222             m.selectedBrick.splice(index,1);
33223             this.el.removeClass(this.activeClass);
33224             return;
33225         }
33226         
33227         for(var i = 0; i < m.selectedBrick.length; i++) {
33228             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33229             b.el.removeClass(b.activeClass);
33230         }
33231         
33232         m.selectedBrick = [];
33233         
33234         m.selectedBrick.push(this.id);
33235         this.el.addClass(this.activeClass);
33236         return;
33237     },
33238     
33239     isSelected : function(){
33240         return this.el.hasClass(this.activeClass);
33241         
33242     }
33243 });
33244
33245 Roo.apply(Roo.bootstrap.MasonryBrick, {
33246     
33247     //groups: {},
33248     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33249      /**
33250     * register a Masonry Brick
33251     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33252     */
33253     
33254     register : function(brick)
33255     {
33256         //this.groups[brick.id] = brick;
33257         this.groups.add(brick.id, brick);
33258     },
33259     /**
33260     * fetch a  masonry brick based on the masonry brick ID
33261     * @param {string} the masonry brick to add
33262     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33263     */
33264     
33265     get: function(brick_id) 
33266     {
33267         // if (typeof(this.groups[brick_id]) == 'undefined') {
33268         //     return false;
33269         // }
33270         // return this.groups[brick_id] ;
33271         
33272         if(this.groups.key(brick_id)) {
33273             return this.groups.key(brick_id);
33274         }
33275         
33276         return false;
33277     }
33278     
33279     
33280     
33281 });
33282
33283  /*
33284  * - LGPL
33285  *
33286  * element
33287  * 
33288  */
33289
33290 /**
33291  * @class Roo.bootstrap.Brick
33292  * @extends Roo.bootstrap.Component
33293  * Bootstrap Brick class
33294  * 
33295  * @constructor
33296  * Create a new Brick
33297  * @param {Object} config The config object
33298  */
33299
33300 Roo.bootstrap.Brick = function(config){
33301     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33302     
33303     this.addEvents({
33304         // raw events
33305         /**
33306          * @event click
33307          * When a Brick is click
33308          * @param {Roo.bootstrap.Brick} this
33309          * @param {Roo.EventObject} e
33310          */
33311         "click" : true
33312     });
33313 };
33314
33315 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33316     
33317     /**
33318      * @cfg {String} title
33319      */   
33320     title : '',
33321     /**
33322      * @cfg {String} html
33323      */   
33324     html : '',
33325     /**
33326      * @cfg {String} bgimage
33327      */   
33328     bgimage : '',
33329     /**
33330      * @cfg {String} cls
33331      */   
33332     cls : '',
33333     /**
33334      * @cfg {String} href
33335      */   
33336     href : '',
33337     /**
33338      * @cfg {String} video
33339      */   
33340     video : '',
33341     /**
33342      * @cfg {Boolean} square
33343      */   
33344     square : true,
33345     
33346     getAutoCreate : function()
33347     {
33348         var cls = 'roo-brick';
33349         
33350         if(this.href.length){
33351             cls += ' roo-brick-link';
33352         }
33353         
33354         if(this.bgimage.length){
33355             cls += ' roo-brick-image';
33356         }
33357         
33358         if(!this.html.length && !this.bgimage.length){
33359             cls += ' roo-brick-center-title';
33360         }
33361         
33362         if(!this.html.length && this.bgimage.length){
33363             cls += ' roo-brick-bottom-title';
33364         }
33365         
33366         if(this.cls){
33367             cls += ' ' + this.cls;
33368         }
33369         
33370         var cfg = {
33371             tag: (this.href.length) ? 'a' : 'div',
33372             cls: cls,
33373             cn: [
33374                 {
33375                     tag: 'div',
33376                     cls: 'roo-brick-paragraph',
33377                     cn: []
33378                 }
33379             ]
33380         };
33381         
33382         if(this.href.length){
33383             cfg.href = this.href;
33384         }
33385         
33386         var cn = cfg.cn[0].cn;
33387         
33388         if(this.title.length){
33389             cn.push({
33390                 tag: 'h4',
33391                 cls: 'roo-brick-title',
33392                 html: this.title
33393             });
33394         }
33395         
33396         if(this.html.length){
33397             cn.push({
33398                 tag: 'p',
33399                 cls: 'roo-brick-text',
33400                 html: this.html
33401             });
33402         } else {
33403             cn.cls += ' hide';
33404         }
33405         
33406         if(this.bgimage.length){
33407             cfg.cn.push({
33408                 tag: 'img',
33409                 cls: 'roo-brick-image-view',
33410                 src: this.bgimage
33411             });
33412         }
33413         
33414         return cfg;
33415     },
33416     
33417     initEvents: function() 
33418     {
33419         if(this.title.length || this.html.length){
33420             this.el.on('mouseenter'  ,this.enter, this);
33421             this.el.on('mouseleave', this.leave, this);
33422         }
33423         
33424         Roo.EventManager.onWindowResize(this.resize, this); 
33425         
33426         if(this.bgimage.length){
33427             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33428             this.imageEl.on('load', this.onImageLoad, this);
33429             return;
33430         }
33431         
33432         this.resize();
33433     },
33434     
33435     onImageLoad : function()
33436     {
33437         this.resize();
33438     },
33439     
33440     resize : function()
33441     {
33442         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33443         
33444         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33445         
33446         if(this.bgimage.length){
33447             var image = this.el.select('.roo-brick-image-view', true).first();
33448             
33449             image.setWidth(paragraph.getWidth());
33450             
33451             if(this.square){
33452                 image.setHeight(paragraph.getWidth());
33453             }
33454             
33455             this.el.setHeight(image.getHeight());
33456             paragraph.setHeight(image.getHeight());
33457             
33458         }
33459         
33460     },
33461     
33462     enter: function(e, el)
33463     {
33464         e.preventDefault();
33465         
33466         if(this.bgimage.length){
33467             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33468             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33469         }
33470     },
33471     
33472     leave: function(e, el)
33473     {
33474         e.preventDefault();
33475         
33476         if(this.bgimage.length){
33477             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33478             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33479         }
33480     }
33481     
33482 });
33483
33484  
33485
33486  /*
33487  * - LGPL
33488  *
33489  * Number field 
33490  */
33491
33492 /**
33493  * @class Roo.bootstrap.NumberField
33494  * @extends Roo.bootstrap.Input
33495  * Bootstrap NumberField class
33496  * 
33497  * 
33498  * 
33499  * 
33500  * @constructor
33501  * Create a new NumberField
33502  * @param {Object} config The config object
33503  */
33504
33505 Roo.bootstrap.NumberField = function(config){
33506     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33507 };
33508
33509 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33510     
33511     /**
33512      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33513      */
33514     allowDecimals : true,
33515     /**
33516      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33517      */
33518     decimalSeparator : ".",
33519     /**
33520      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33521      */
33522     decimalPrecision : 2,
33523     /**
33524      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33525      */
33526     allowNegative : true,
33527     
33528     /**
33529      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33530      */
33531     allowZero: true,
33532     /**
33533      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33534      */
33535     minValue : Number.NEGATIVE_INFINITY,
33536     /**
33537      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33538      */
33539     maxValue : Number.MAX_VALUE,
33540     /**
33541      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33542      */
33543     minText : "The minimum value for this field is {0}",
33544     /**
33545      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33546      */
33547     maxText : "The maximum value for this field is {0}",
33548     /**
33549      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33550      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33551      */
33552     nanText : "{0} is not a valid number",
33553     /**
33554      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33555      */
33556     thousandsDelimiter : false,
33557     /**
33558      * @cfg {String} valueAlign alignment of value
33559      */
33560     valueAlign : "left",
33561
33562     getAutoCreate : function()
33563     {
33564         var hiddenInput = {
33565             tag: 'input',
33566             type: 'hidden',
33567             id: Roo.id(),
33568             cls: 'hidden-number-input'
33569         };
33570         
33571         if (this.name) {
33572             hiddenInput.name = this.name;
33573         }
33574         
33575         this.name = '';
33576         
33577         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33578         
33579         this.name = hiddenInput.name;
33580         
33581         if(cfg.cn.length > 0) {
33582             cfg.cn.push(hiddenInput);
33583         }
33584         
33585         return cfg;
33586     },
33587
33588     // private
33589     initEvents : function()
33590     {   
33591         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33592         
33593         var allowed = "0123456789";
33594         
33595         if(this.allowDecimals){
33596             allowed += this.decimalSeparator;
33597         }
33598         
33599         if(this.allowNegative){
33600             allowed += "-";
33601         }
33602         
33603         if(this.thousandsDelimiter) {
33604             allowed += ",";
33605         }
33606         
33607         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33608         
33609         var keyPress = function(e){
33610             
33611             var k = e.getKey();
33612             
33613             var c = e.getCharCode();
33614             
33615             if(
33616                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33617                     allowed.indexOf(String.fromCharCode(c)) === -1
33618             ){
33619                 e.stopEvent();
33620                 return;
33621             }
33622             
33623             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33624                 return;
33625             }
33626             
33627             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33628                 e.stopEvent();
33629             }
33630         };
33631         
33632         this.el.on("keypress", keyPress, this);
33633     },
33634     
33635     validateValue : function(value)
33636     {
33637         
33638         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33639             return false;
33640         }
33641         
33642         var num = this.parseValue(value);
33643         
33644         if(isNaN(num)){
33645             this.markInvalid(String.format(this.nanText, value));
33646             return false;
33647         }
33648         
33649         if(num < this.minValue){
33650             this.markInvalid(String.format(this.minText, this.minValue));
33651             return false;
33652         }
33653         
33654         if(num > this.maxValue){
33655             this.markInvalid(String.format(this.maxText, this.maxValue));
33656             return false;
33657         }
33658         
33659         return true;
33660     },
33661
33662     getValue : function()
33663     {
33664         var v = this.hiddenEl().getValue();
33665         
33666         return this.fixPrecision(this.parseValue(v));
33667     },
33668
33669     parseValue : function(value)
33670     {
33671         if(this.thousandsDelimiter) {
33672             value += "";
33673             r = new RegExp(",", "g");
33674             value = value.replace(r, "");
33675         }
33676         
33677         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33678         return isNaN(value) ? '' : value;
33679     },
33680
33681     fixPrecision : function(value)
33682     {
33683         if(this.thousandsDelimiter) {
33684             value += "";
33685             r = new RegExp(",", "g");
33686             value = value.replace(r, "");
33687         }
33688         
33689         var nan = isNaN(value);
33690         
33691         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33692             return nan ? '' : value;
33693         }
33694         return parseFloat(value).toFixed(this.decimalPrecision);
33695     },
33696
33697     setValue : function(v)
33698     {
33699         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33700         
33701         this.value = v;
33702         
33703         if(this.rendered){
33704             
33705             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33706             
33707             this.inputEl().dom.value = (v == '') ? '' :
33708                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33709             
33710             if(!this.allowZero && v === '0') {
33711                 this.hiddenEl().dom.value = '';
33712                 this.inputEl().dom.value = '';
33713             }
33714             
33715             this.validate();
33716         }
33717     },
33718
33719     decimalPrecisionFcn : function(v)
33720     {
33721         return Math.floor(v);
33722     },
33723
33724     beforeBlur : function()
33725     {
33726         var v = this.parseValue(this.getRawValue());
33727         
33728         if(v || v === 0 || v === ''){
33729             this.setValue(v);
33730         }
33731     },
33732     
33733     hiddenEl : function()
33734     {
33735         return this.el.select('input.hidden-number-input',true).first();
33736     }
33737     
33738 });
33739
33740  
33741
33742 /*
33743 * Licence: LGPL
33744 */
33745
33746 /**
33747  * @class Roo.bootstrap.DocumentSlider
33748  * @extends Roo.bootstrap.Component
33749  * Bootstrap DocumentSlider class
33750  * 
33751  * @constructor
33752  * Create a new DocumentViewer
33753  * @param {Object} config The config object
33754  */
33755
33756 Roo.bootstrap.DocumentSlider = function(config){
33757     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33758     
33759     this.files = [];
33760     
33761     this.addEvents({
33762         /**
33763          * @event initial
33764          * Fire after initEvent
33765          * @param {Roo.bootstrap.DocumentSlider} this
33766          */
33767         "initial" : true,
33768         /**
33769          * @event update
33770          * Fire after update
33771          * @param {Roo.bootstrap.DocumentSlider} this
33772          */
33773         "update" : true,
33774         /**
33775          * @event click
33776          * Fire after click
33777          * @param {Roo.bootstrap.DocumentSlider} this
33778          */
33779         "click" : true
33780     });
33781 };
33782
33783 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33784     
33785     files : false,
33786     
33787     indicator : 0,
33788     
33789     getAutoCreate : function()
33790     {
33791         var cfg = {
33792             tag : 'div',
33793             cls : 'roo-document-slider',
33794             cn : [
33795                 {
33796                     tag : 'div',
33797                     cls : 'roo-document-slider-header',
33798                     cn : [
33799                         {
33800                             tag : 'div',
33801                             cls : 'roo-document-slider-header-title'
33802                         }
33803                     ]
33804                 },
33805                 {
33806                     tag : 'div',
33807                     cls : 'roo-document-slider-body',
33808                     cn : [
33809                         {
33810                             tag : 'div',
33811                             cls : 'roo-document-slider-prev',
33812                             cn : [
33813                                 {
33814                                     tag : 'i',
33815                                     cls : 'fa fa-chevron-left'
33816                                 }
33817                             ]
33818                         },
33819                         {
33820                             tag : 'div',
33821                             cls : 'roo-document-slider-thumb',
33822                             cn : [
33823                                 {
33824                                     tag : 'img',
33825                                     cls : 'roo-document-slider-image'
33826                                 }
33827                             ]
33828                         },
33829                         {
33830                             tag : 'div',
33831                             cls : 'roo-document-slider-next',
33832                             cn : [
33833                                 {
33834                                     tag : 'i',
33835                                     cls : 'fa fa-chevron-right'
33836                                 }
33837                             ]
33838                         }
33839                     ]
33840                 }
33841             ]
33842         };
33843         
33844         return cfg;
33845     },
33846     
33847     initEvents : function()
33848     {
33849         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33850         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33851         
33852         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33853         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33854         
33855         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33856         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33857         
33858         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33859         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33860         
33861         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33862         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33863         
33864         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33865         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33866         
33867         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33868         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33869         
33870         this.thumbEl.on('click', this.onClick, this);
33871         
33872         this.prevIndicator.on('click', this.prev, this);
33873         
33874         this.nextIndicator.on('click', this.next, this);
33875         
33876     },
33877     
33878     initial : function()
33879     {
33880         if(this.files.length){
33881             this.indicator = 1;
33882             this.update()
33883         }
33884         
33885         this.fireEvent('initial', this);
33886     },
33887     
33888     update : function()
33889     {
33890         this.imageEl.attr('src', this.files[this.indicator - 1]);
33891         
33892         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33893         
33894         this.prevIndicator.show();
33895         
33896         if(this.indicator == 1){
33897             this.prevIndicator.hide();
33898         }
33899         
33900         this.nextIndicator.show();
33901         
33902         if(this.indicator == this.files.length){
33903             this.nextIndicator.hide();
33904         }
33905         
33906         this.thumbEl.scrollTo('top');
33907         
33908         this.fireEvent('update', this);
33909     },
33910     
33911     onClick : function(e)
33912     {
33913         e.preventDefault();
33914         
33915         this.fireEvent('click', this);
33916     },
33917     
33918     prev : function(e)
33919     {
33920         e.preventDefault();
33921         
33922         this.indicator = Math.max(1, this.indicator - 1);
33923         
33924         this.update();
33925     },
33926     
33927     next : function(e)
33928     {
33929         e.preventDefault();
33930         
33931         this.indicator = Math.min(this.files.length, this.indicator + 1);
33932         
33933         this.update();
33934     }
33935 });
33936 /*
33937  * - LGPL
33938  *
33939  * RadioSet
33940  *
33941  *
33942  */
33943
33944 /**
33945  * @class Roo.bootstrap.RadioSet
33946  * @extends Roo.bootstrap.Input
33947  * Bootstrap RadioSet class
33948  * @cfg {String} indicatorpos (left|right) default left
33949  * @cfg {Boolean} inline (true|false) inline the element (default true)
33950  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33951  * @constructor
33952  * Create a new RadioSet
33953  * @param {Object} config The config object
33954  */
33955
33956 Roo.bootstrap.RadioSet = function(config){
33957     
33958     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33959     
33960     this.radioes = [];
33961     
33962     Roo.bootstrap.RadioSet.register(this);
33963     
33964     this.addEvents({
33965         /**
33966         * @event check
33967         * Fires when the element is checked or unchecked.
33968         * @param {Roo.bootstrap.RadioSet} this This radio
33969         * @param {Roo.bootstrap.Radio} item The checked item
33970         */
33971        check : true,
33972        /**
33973         * @event click
33974         * Fires when the element is click.
33975         * @param {Roo.bootstrap.RadioSet} this This radio set
33976         * @param {Roo.bootstrap.Radio} item The checked item
33977         * @param {Roo.EventObject} e The event object
33978         */
33979        click : true
33980     });
33981     
33982 };
33983
33984 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33985
33986     radioes : false,
33987     
33988     inline : true,
33989     
33990     weight : '',
33991     
33992     indicatorpos : 'left',
33993     
33994     getAutoCreate : function()
33995     {
33996         var label = {
33997             tag : 'label',
33998             cls : 'roo-radio-set-label',
33999             cn : [
34000                 {
34001                     tag : 'span',
34002                     html : this.fieldLabel
34003                 }
34004             ]
34005         };
34006         
34007         if(this.indicatorpos == 'left'){
34008             label.cn.unshift({
34009                 tag : 'i',
34010                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34011                 tooltip : 'This field is required'
34012             });
34013         } else {
34014             label.cn.push({
34015                 tag : 'i',
34016                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34017                 tooltip : 'This field is required'
34018             });
34019         }
34020         
34021         var items = {
34022             tag : 'div',
34023             cls : 'roo-radio-set-items'
34024         };
34025         
34026         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34027         
34028         if (align === 'left' && this.fieldLabel.length) {
34029             
34030             items = {
34031                 cls : "roo-radio-set-right", 
34032                 cn: [
34033                     items
34034                 ]
34035             };
34036             
34037             if(this.labelWidth > 12){
34038                 label.style = "width: " + this.labelWidth + 'px';
34039             }
34040             
34041             if(this.labelWidth < 13 && this.labelmd == 0){
34042                 this.labelmd = this.labelWidth;
34043             }
34044             
34045             if(this.labellg > 0){
34046                 label.cls += ' col-lg-' + this.labellg;
34047                 items.cls += ' col-lg-' + (12 - this.labellg);
34048             }
34049             
34050             if(this.labelmd > 0){
34051                 label.cls += ' col-md-' + this.labelmd;
34052                 items.cls += ' col-md-' + (12 - this.labelmd);
34053             }
34054             
34055             if(this.labelsm > 0){
34056                 label.cls += ' col-sm-' + this.labelsm;
34057                 items.cls += ' col-sm-' + (12 - this.labelsm);
34058             }
34059             
34060             if(this.labelxs > 0){
34061                 label.cls += ' col-xs-' + this.labelxs;
34062                 items.cls += ' col-xs-' + (12 - this.labelxs);
34063             }
34064         }
34065         
34066         var cfg = {
34067             tag : 'div',
34068             cls : 'roo-radio-set',
34069             cn : [
34070                 {
34071                     tag : 'input',
34072                     cls : 'roo-radio-set-input',
34073                     type : 'hidden',
34074                     name : this.name,
34075                     value : this.value ? this.value :  ''
34076                 },
34077                 label,
34078                 items
34079             ]
34080         };
34081         
34082         if(this.weight.length){
34083             cfg.cls += ' roo-radio-' + this.weight;
34084         }
34085         
34086         if(this.inline) {
34087             cfg.cls += ' roo-radio-set-inline';
34088         }
34089         
34090         var settings=this;
34091         ['xs','sm','md','lg'].map(function(size){
34092             if (settings[size]) {
34093                 cfg.cls += ' col-' + size + '-' + settings[size];
34094             }
34095         });
34096         
34097         return cfg;
34098         
34099     },
34100
34101     initEvents : function()
34102     {
34103         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34104         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34105         
34106         if(!this.fieldLabel.length){
34107             this.labelEl.hide();
34108         }
34109         
34110         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34111         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34112         
34113         this.indicator = this.indicatorEl();
34114         
34115         if(this.indicator){
34116             this.indicator.addClass('invisible');
34117         }
34118         
34119         this.originalValue = this.getValue();
34120         
34121     },
34122     
34123     inputEl: function ()
34124     {
34125         return this.el.select('.roo-radio-set-input', true).first();
34126     },
34127     
34128     getChildContainer : function()
34129     {
34130         return this.itemsEl;
34131     },
34132     
34133     register : function(item)
34134     {
34135         this.radioes.push(item);
34136         
34137     },
34138     
34139     validate : function()
34140     {   
34141         if(this.getVisibilityEl().hasClass('hidden')){
34142             return true;
34143         }
34144         
34145         var valid = false;
34146         
34147         Roo.each(this.radioes, function(i){
34148             if(!i.checked){
34149                 return;
34150             }
34151             
34152             valid = true;
34153             return false;
34154         });
34155         
34156         if(this.allowBlank) {
34157             return true;
34158         }
34159         
34160         if(this.disabled || valid){
34161             this.markValid();
34162             return true;
34163         }
34164         
34165         this.markInvalid();
34166         return false;
34167         
34168     },
34169     
34170     markValid : function()
34171     {
34172         if(this.labelEl.isVisible(true)){
34173             this.indicatorEl().removeClass('visible');
34174             this.indicatorEl().addClass('invisible');
34175         }
34176         
34177         this.el.removeClass([this.invalidClass, this.validClass]);
34178         this.el.addClass(this.validClass);
34179         
34180         this.fireEvent('valid', this);
34181     },
34182     
34183     markInvalid : function(msg)
34184     {
34185         if(this.allowBlank || this.disabled){
34186             return;
34187         }
34188         
34189         if(this.labelEl.isVisible(true)){
34190             this.indicatorEl().removeClass('invisible');
34191             this.indicatorEl().addClass('visible');
34192         }
34193         
34194         this.el.removeClass([this.invalidClass, this.validClass]);
34195         this.el.addClass(this.invalidClass);
34196         
34197         this.fireEvent('invalid', this, msg);
34198         
34199     },
34200     
34201     setValue : function(v, suppressEvent)
34202     {   
34203         if(this.value === v){
34204             return;
34205         }
34206         
34207         this.value = v;
34208         
34209         if(this.rendered){
34210             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34211         }
34212         
34213         Roo.each(this.radioes, function(i){
34214             i.checked = false;
34215             i.el.removeClass('checked');
34216         });
34217         
34218         Roo.each(this.radioes, function(i){
34219             
34220             if(i.value === v || i.value.toString() === v.toString()){
34221                 i.checked = true;
34222                 i.el.addClass('checked');
34223                 
34224                 if(suppressEvent !== true){
34225                     this.fireEvent('check', this, i);
34226                 }
34227                 
34228                 return false;
34229             }
34230             
34231         }, this);
34232         
34233         this.validate();
34234     },
34235     
34236     clearInvalid : function(){
34237         
34238         if(!this.el || this.preventMark){
34239             return;
34240         }
34241         
34242         this.el.removeClass([this.invalidClass]);
34243         
34244         this.fireEvent('valid', this);
34245     }
34246     
34247 });
34248
34249 Roo.apply(Roo.bootstrap.RadioSet, {
34250     
34251     groups: {},
34252     
34253     register : function(set)
34254     {
34255         this.groups[set.name] = set;
34256     },
34257     
34258     get: function(name) 
34259     {
34260         if (typeof(this.groups[name]) == 'undefined') {
34261             return false;
34262         }
34263         
34264         return this.groups[name] ;
34265     }
34266     
34267 });
34268 /*
34269  * Based on:
34270  * Ext JS Library 1.1.1
34271  * Copyright(c) 2006-2007, Ext JS, LLC.
34272  *
34273  * Originally Released Under LGPL - original licence link has changed is not relivant.
34274  *
34275  * Fork - LGPL
34276  * <script type="text/javascript">
34277  */
34278
34279
34280 /**
34281  * @class Roo.bootstrap.SplitBar
34282  * @extends Roo.util.Observable
34283  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34284  * <br><br>
34285  * Usage:
34286  * <pre><code>
34287 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34288                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34289 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34290 split.minSize = 100;
34291 split.maxSize = 600;
34292 split.animate = true;
34293 split.on('moved', splitterMoved);
34294 </code></pre>
34295  * @constructor
34296  * Create a new SplitBar
34297  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34298  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34299  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34300  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34301                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34302                         position of the SplitBar).
34303  */
34304 Roo.bootstrap.SplitBar = function(cfg){
34305     
34306     /** @private */
34307     
34308     //{
34309     //  dragElement : elm
34310     //  resizingElement: el,
34311         // optional..
34312     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34313     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34314         // existingProxy ???
34315     //}
34316     
34317     this.el = Roo.get(cfg.dragElement, true);
34318     this.el.dom.unselectable = "on";
34319     /** @private */
34320     this.resizingEl = Roo.get(cfg.resizingElement, true);
34321
34322     /**
34323      * @private
34324      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34325      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34326      * @type Number
34327      */
34328     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34329     
34330     /**
34331      * The minimum size of the resizing element. (Defaults to 0)
34332      * @type Number
34333      */
34334     this.minSize = 0;
34335     
34336     /**
34337      * The maximum size of the resizing element. (Defaults to 2000)
34338      * @type Number
34339      */
34340     this.maxSize = 2000;
34341     
34342     /**
34343      * Whether to animate the transition to the new size
34344      * @type Boolean
34345      */
34346     this.animate = false;
34347     
34348     /**
34349      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34350      * @type Boolean
34351      */
34352     this.useShim = false;
34353     
34354     /** @private */
34355     this.shim = null;
34356     
34357     if(!cfg.existingProxy){
34358         /** @private */
34359         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34360     }else{
34361         this.proxy = Roo.get(cfg.existingProxy).dom;
34362     }
34363     /** @private */
34364     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34365     
34366     /** @private */
34367     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34368     
34369     /** @private */
34370     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34371     
34372     /** @private */
34373     this.dragSpecs = {};
34374     
34375     /**
34376      * @private The adapter to use to positon and resize elements
34377      */
34378     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34379     this.adapter.init(this);
34380     
34381     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34382         /** @private */
34383         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34384         this.el.addClass("roo-splitbar-h");
34385     }else{
34386         /** @private */
34387         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34388         this.el.addClass("roo-splitbar-v");
34389     }
34390     
34391     this.addEvents({
34392         /**
34393          * @event resize
34394          * Fires when the splitter is moved (alias for {@link #event-moved})
34395          * @param {Roo.bootstrap.SplitBar} this
34396          * @param {Number} newSize the new width or height
34397          */
34398         "resize" : true,
34399         /**
34400          * @event moved
34401          * Fires when the splitter is moved
34402          * @param {Roo.bootstrap.SplitBar} this
34403          * @param {Number} newSize the new width or height
34404          */
34405         "moved" : true,
34406         /**
34407          * @event beforeresize
34408          * Fires before the splitter is dragged
34409          * @param {Roo.bootstrap.SplitBar} this
34410          */
34411         "beforeresize" : true,
34412
34413         "beforeapply" : true
34414     });
34415
34416     Roo.util.Observable.call(this);
34417 };
34418
34419 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34420     onStartProxyDrag : function(x, y){
34421         this.fireEvent("beforeresize", this);
34422         if(!this.overlay){
34423             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34424             o.unselectable();
34425             o.enableDisplayMode("block");
34426             // all splitbars share the same overlay
34427             Roo.bootstrap.SplitBar.prototype.overlay = o;
34428         }
34429         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34430         this.overlay.show();
34431         Roo.get(this.proxy).setDisplayed("block");
34432         var size = this.adapter.getElementSize(this);
34433         this.activeMinSize = this.getMinimumSize();;
34434         this.activeMaxSize = this.getMaximumSize();;
34435         var c1 = size - this.activeMinSize;
34436         var c2 = Math.max(this.activeMaxSize - size, 0);
34437         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34438             this.dd.resetConstraints();
34439             this.dd.setXConstraint(
34440                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34441                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34442             );
34443             this.dd.setYConstraint(0, 0);
34444         }else{
34445             this.dd.resetConstraints();
34446             this.dd.setXConstraint(0, 0);
34447             this.dd.setYConstraint(
34448                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34449                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34450             );
34451          }
34452         this.dragSpecs.startSize = size;
34453         this.dragSpecs.startPoint = [x, y];
34454         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34455     },
34456     
34457     /** 
34458      * @private Called after the drag operation by the DDProxy
34459      */
34460     onEndProxyDrag : function(e){
34461         Roo.get(this.proxy).setDisplayed(false);
34462         var endPoint = Roo.lib.Event.getXY(e);
34463         if(this.overlay){
34464             this.overlay.hide();
34465         }
34466         var newSize;
34467         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34468             newSize = this.dragSpecs.startSize + 
34469                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34470                     endPoint[0] - this.dragSpecs.startPoint[0] :
34471                     this.dragSpecs.startPoint[0] - endPoint[0]
34472                 );
34473         }else{
34474             newSize = this.dragSpecs.startSize + 
34475                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34476                     endPoint[1] - this.dragSpecs.startPoint[1] :
34477                     this.dragSpecs.startPoint[1] - endPoint[1]
34478                 );
34479         }
34480         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34481         if(newSize != this.dragSpecs.startSize){
34482             if(this.fireEvent('beforeapply', this, newSize) !== false){
34483                 this.adapter.setElementSize(this, newSize);
34484                 this.fireEvent("moved", this, newSize);
34485                 this.fireEvent("resize", this, newSize);
34486             }
34487         }
34488     },
34489     
34490     /**
34491      * Get the adapter this SplitBar uses
34492      * @return The adapter object
34493      */
34494     getAdapter : function(){
34495         return this.adapter;
34496     },
34497     
34498     /**
34499      * Set the adapter this SplitBar uses
34500      * @param {Object} adapter A SplitBar adapter object
34501      */
34502     setAdapter : function(adapter){
34503         this.adapter = adapter;
34504         this.adapter.init(this);
34505     },
34506     
34507     /**
34508      * Gets the minimum size for the resizing element
34509      * @return {Number} The minimum size
34510      */
34511     getMinimumSize : function(){
34512         return this.minSize;
34513     },
34514     
34515     /**
34516      * Sets the minimum size for the resizing element
34517      * @param {Number} minSize The minimum size
34518      */
34519     setMinimumSize : function(minSize){
34520         this.minSize = minSize;
34521     },
34522     
34523     /**
34524      * Gets the maximum size for the resizing element
34525      * @return {Number} The maximum size
34526      */
34527     getMaximumSize : function(){
34528         return this.maxSize;
34529     },
34530     
34531     /**
34532      * Sets the maximum size for the resizing element
34533      * @param {Number} maxSize The maximum size
34534      */
34535     setMaximumSize : function(maxSize){
34536         this.maxSize = maxSize;
34537     },
34538     
34539     /**
34540      * Sets the initialize size for the resizing element
34541      * @param {Number} size The initial size
34542      */
34543     setCurrentSize : function(size){
34544         var oldAnimate = this.animate;
34545         this.animate = false;
34546         this.adapter.setElementSize(this, size);
34547         this.animate = oldAnimate;
34548     },
34549     
34550     /**
34551      * Destroy this splitbar. 
34552      * @param {Boolean} removeEl True to remove the element
34553      */
34554     destroy : function(removeEl){
34555         if(this.shim){
34556             this.shim.remove();
34557         }
34558         this.dd.unreg();
34559         this.proxy.parentNode.removeChild(this.proxy);
34560         if(removeEl){
34561             this.el.remove();
34562         }
34563     }
34564 });
34565
34566 /**
34567  * @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.
34568  */
34569 Roo.bootstrap.SplitBar.createProxy = function(dir){
34570     var proxy = new Roo.Element(document.createElement("div"));
34571     proxy.unselectable();
34572     var cls = 'roo-splitbar-proxy';
34573     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34574     document.body.appendChild(proxy.dom);
34575     return proxy.dom;
34576 };
34577
34578 /** 
34579  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34580  * Default Adapter. It assumes the splitter and resizing element are not positioned
34581  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34582  */
34583 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34584 };
34585
34586 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34587     // do nothing for now
34588     init : function(s){
34589     
34590     },
34591     /**
34592      * Called before drag operations to get the current size of the resizing element. 
34593      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34594      */
34595      getElementSize : function(s){
34596         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34597             return s.resizingEl.getWidth();
34598         }else{
34599             return s.resizingEl.getHeight();
34600         }
34601     },
34602     
34603     /**
34604      * Called after drag operations to set the size of the resizing element.
34605      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34606      * @param {Number} newSize The new size to set
34607      * @param {Function} onComplete A function to be invoked when resizing is complete
34608      */
34609     setElementSize : function(s, newSize, onComplete){
34610         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34611             if(!s.animate){
34612                 s.resizingEl.setWidth(newSize);
34613                 if(onComplete){
34614                     onComplete(s, newSize);
34615                 }
34616             }else{
34617                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34618             }
34619         }else{
34620             
34621             if(!s.animate){
34622                 s.resizingEl.setHeight(newSize);
34623                 if(onComplete){
34624                     onComplete(s, newSize);
34625                 }
34626             }else{
34627                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34628             }
34629         }
34630     }
34631 };
34632
34633 /** 
34634  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34635  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34636  * Adapter that  moves the splitter element to align with the resized sizing element. 
34637  * Used with an absolute positioned SplitBar.
34638  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34639  * document.body, make sure you assign an id to the body element.
34640  */
34641 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34642     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34643     this.container = Roo.get(container);
34644 };
34645
34646 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34647     init : function(s){
34648         this.basic.init(s);
34649     },
34650     
34651     getElementSize : function(s){
34652         return this.basic.getElementSize(s);
34653     },
34654     
34655     setElementSize : function(s, newSize, onComplete){
34656         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34657     },
34658     
34659     moveSplitter : function(s){
34660         var yes = Roo.bootstrap.SplitBar;
34661         switch(s.placement){
34662             case yes.LEFT:
34663                 s.el.setX(s.resizingEl.getRight());
34664                 break;
34665             case yes.RIGHT:
34666                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34667                 break;
34668             case yes.TOP:
34669                 s.el.setY(s.resizingEl.getBottom());
34670                 break;
34671             case yes.BOTTOM:
34672                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34673                 break;
34674         }
34675     }
34676 };
34677
34678 /**
34679  * Orientation constant - Create a vertical SplitBar
34680  * @static
34681  * @type Number
34682  */
34683 Roo.bootstrap.SplitBar.VERTICAL = 1;
34684
34685 /**
34686  * Orientation constant - Create a horizontal SplitBar
34687  * @static
34688  * @type Number
34689  */
34690 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34691
34692 /**
34693  * Placement constant - The resizing element is to the left of the splitter element
34694  * @static
34695  * @type Number
34696  */
34697 Roo.bootstrap.SplitBar.LEFT = 1;
34698
34699 /**
34700  * Placement constant - The resizing element is to the right of the splitter element
34701  * @static
34702  * @type Number
34703  */
34704 Roo.bootstrap.SplitBar.RIGHT = 2;
34705
34706 /**
34707  * Placement constant - The resizing element is positioned above the splitter element
34708  * @static
34709  * @type Number
34710  */
34711 Roo.bootstrap.SplitBar.TOP = 3;
34712
34713 /**
34714  * Placement constant - The resizing element is positioned under splitter element
34715  * @static
34716  * @type Number
34717  */
34718 Roo.bootstrap.SplitBar.BOTTOM = 4;
34719 Roo.namespace("Roo.bootstrap.layout");/*
34720  * Based on:
34721  * Ext JS Library 1.1.1
34722  * Copyright(c) 2006-2007, Ext JS, LLC.
34723  *
34724  * Originally Released Under LGPL - original licence link has changed is not relivant.
34725  *
34726  * Fork - LGPL
34727  * <script type="text/javascript">
34728  */
34729
34730 /**
34731  * @class Roo.bootstrap.layout.Manager
34732  * @extends Roo.bootstrap.Component
34733  * Base class for layout managers.
34734  */
34735 Roo.bootstrap.layout.Manager = function(config)
34736 {
34737     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34738
34739
34740
34741
34742
34743     /** false to disable window resize monitoring @type Boolean */
34744     this.monitorWindowResize = true;
34745     this.regions = {};
34746     this.addEvents({
34747         /**
34748          * @event layout
34749          * Fires when a layout is performed.
34750          * @param {Roo.LayoutManager} this
34751          */
34752         "layout" : true,
34753         /**
34754          * @event regionresized
34755          * Fires when the user resizes a region.
34756          * @param {Roo.LayoutRegion} region The resized region
34757          * @param {Number} newSize The new size (width for east/west, height for north/south)
34758          */
34759         "regionresized" : true,
34760         /**
34761          * @event regioncollapsed
34762          * Fires when a region is collapsed.
34763          * @param {Roo.LayoutRegion} region The collapsed region
34764          */
34765         "regioncollapsed" : true,
34766         /**
34767          * @event regionexpanded
34768          * Fires when a region is expanded.
34769          * @param {Roo.LayoutRegion} region The expanded region
34770          */
34771         "regionexpanded" : true
34772     });
34773     this.updating = false;
34774
34775     if (config.el) {
34776         this.el = Roo.get(config.el);
34777         this.initEvents();
34778     }
34779
34780 };
34781
34782 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34783
34784
34785     regions : null,
34786
34787     monitorWindowResize : true,
34788
34789
34790     updating : false,
34791
34792
34793     onRender : function(ct, position)
34794     {
34795         if(!this.el){
34796             this.el = Roo.get(ct);
34797             this.initEvents();
34798         }
34799         //this.fireEvent('render',this);
34800     },
34801
34802
34803     initEvents: function()
34804     {
34805
34806
34807         // ie scrollbar fix
34808         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34809             document.body.scroll = "no";
34810         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34811             this.el.position('relative');
34812         }
34813         this.id = this.el.id;
34814         this.el.addClass("roo-layout-container");
34815         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34816         if(this.el.dom != document.body ) {
34817             this.el.on('resize', this.layout,this);
34818             this.el.on('show', this.layout,this);
34819         }
34820
34821     },
34822
34823     /**
34824      * Returns true if this layout is currently being updated
34825      * @return {Boolean}
34826      */
34827     isUpdating : function(){
34828         return this.updating;
34829     },
34830
34831     /**
34832      * Suspend the LayoutManager from doing auto-layouts while
34833      * making multiple add or remove calls
34834      */
34835     beginUpdate : function(){
34836         this.updating = true;
34837     },
34838
34839     /**
34840      * Restore auto-layouts and optionally disable the manager from performing a layout
34841      * @param {Boolean} noLayout true to disable a layout update
34842      */
34843     endUpdate : function(noLayout){
34844         this.updating = false;
34845         if(!noLayout){
34846             this.layout();
34847         }
34848     },
34849
34850     layout: function(){
34851         // abstract...
34852     },
34853
34854     onRegionResized : function(region, newSize){
34855         this.fireEvent("regionresized", region, newSize);
34856         this.layout();
34857     },
34858
34859     onRegionCollapsed : function(region){
34860         this.fireEvent("regioncollapsed", region);
34861     },
34862
34863     onRegionExpanded : function(region){
34864         this.fireEvent("regionexpanded", region);
34865     },
34866
34867     /**
34868      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34869      * performs box-model adjustments.
34870      * @return {Object} The size as an object {width: (the width), height: (the height)}
34871      */
34872     getViewSize : function()
34873     {
34874         var size;
34875         if(this.el.dom != document.body){
34876             size = this.el.getSize();
34877         }else{
34878             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34879         }
34880         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34881         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34882         return size;
34883     },
34884
34885     /**
34886      * Returns the Element this layout is bound to.
34887      * @return {Roo.Element}
34888      */
34889     getEl : function(){
34890         return this.el;
34891     },
34892
34893     /**
34894      * Returns the specified region.
34895      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34896      * @return {Roo.LayoutRegion}
34897      */
34898     getRegion : function(target){
34899         return this.regions[target.toLowerCase()];
34900     },
34901
34902     onWindowResize : function(){
34903         if(this.monitorWindowResize){
34904             this.layout();
34905         }
34906     }
34907 });
34908 /*
34909  * Based on:
34910  * Ext JS Library 1.1.1
34911  * Copyright(c) 2006-2007, Ext JS, LLC.
34912  *
34913  * Originally Released Under LGPL - original licence link has changed is not relivant.
34914  *
34915  * Fork - LGPL
34916  * <script type="text/javascript">
34917  */
34918 /**
34919  * @class Roo.bootstrap.layout.Border
34920  * @extends Roo.bootstrap.layout.Manager
34921  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34922  * please see: examples/bootstrap/nested.html<br><br>
34923  
34924 <b>The container the layout is rendered into can be either the body element or any other element.
34925 If it is not the body element, the container needs to either be an absolute positioned element,
34926 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34927 the container size if it is not the body element.</b>
34928
34929 * @constructor
34930 * Create a new Border
34931 * @param {Object} config Configuration options
34932  */
34933 Roo.bootstrap.layout.Border = function(config){
34934     config = config || {};
34935     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34936     
34937     
34938     
34939     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34940         if(config[region]){
34941             config[region].region = region;
34942             this.addRegion(config[region]);
34943         }
34944     },this);
34945     
34946 };
34947
34948 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34949
34950 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34951     /**
34952      * Creates and adds a new region if it doesn't already exist.
34953      * @param {String} target The target region key (north, south, east, west or center).
34954      * @param {Object} config The regions config object
34955      * @return {BorderLayoutRegion} The new region
34956      */
34957     addRegion : function(config)
34958     {
34959         if(!this.regions[config.region]){
34960             var r = this.factory(config);
34961             this.bindRegion(r);
34962         }
34963         return this.regions[config.region];
34964     },
34965
34966     // private (kinda)
34967     bindRegion : function(r){
34968         this.regions[r.config.region] = r;
34969         
34970         r.on("visibilitychange",    this.layout, this);
34971         r.on("paneladded",          this.layout, this);
34972         r.on("panelremoved",        this.layout, this);
34973         r.on("invalidated",         this.layout, this);
34974         r.on("resized",             this.onRegionResized, this);
34975         r.on("collapsed",           this.onRegionCollapsed, this);
34976         r.on("expanded",            this.onRegionExpanded, this);
34977     },
34978
34979     /**
34980      * Performs a layout update.
34981      */
34982     layout : function()
34983     {
34984         if(this.updating) {
34985             return;
34986         }
34987         
34988         // render all the rebions if they have not been done alreayd?
34989         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34990             if(this.regions[region] && !this.regions[region].bodyEl){
34991                 this.regions[region].onRender(this.el)
34992             }
34993         },this);
34994         
34995         var size = this.getViewSize();
34996         var w = size.width;
34997         var h = size.height;
34998         var centerW = w;
34999         var centerH = h;
35000         var centerY = 0;
35001         var centerX = 0;
35002         //var x = 0, y = 0;
35003
35004         var rs = this.regions;
35005         var north = rs["north"];
35006         var south = rs["south"]; 
35007         var west = rs["west"];
35008         var east = rs["east"];
35009         var center = rs["center"];
35010         //if(this.hideOnLayout){ // not supported anymore
35011             //c.el.setStyle("display", "none");
35012         //}
35013         if(north && north.isVisible()){
35014             var b = north.getBox();
35015             var m = north.getMargins();
35016             b.width = w - (m.left+m.right);
35017             b.x = m.left;
35018             b.y = m.top;
35019             centerY = b.height + b.y + m.bottom;
35020             centerH -= centerY;
35021             north.updateBox(this.safeBox(b));
35022         }
35023         if(south && south.isVisible()){
35024             var b = south.getBox();
35025             var m = south.getMargins();
35026             b.width = w - (m.left+m.right);
35027             b.x = m.left;
35028             var totalHeight = (b.height + m.top + m.bottom);
35029             b.y = h - totalHeight + m.top;
35030             centerH -= totalHeight;
35031             south.updateBox(this.safeBox(b));
35032         }
35033         if(west && west.isVisible()){
35034             var b = west.getBox();
35035             var m = west.getMargins();
35036             b.height = centerH - (m.top+m.bottom);
35037             b.x = m.left;
35038             b.y = centerY + m.top;
35039             var totalWidth = (b.width + m.left + m.right);
35040             centerX += totalWidth;
35041             centerW -= totalWidth;
35042             west.updateBox(this.safeBox(b));
35043         }
35044         if(east && east.isVisible()){
35045             var b = east.getBox();
35046             var m = east.getMargins();
35047             b.height = centerH - (m.top+m.bottom);
35048             var totalWidth = (b.width + m.left + m.right);
35049             b.x = w - totalWidth + m.left;
35050             b.y = centerY + m.top;
35051             centerW -= totalWidth;
35052             east.updateBox(this.safeBox(b));
35053         }
35054         if(center){
35055             var m = center.getMargins();
35056             var centerBox = {
35057                 x: centerX + m.left,
35058                 y: centerY + m.top,
35059                 width: centerW - (m.left+m.right),
35060                 height: centerH - (m.top+m.bottom)
35061             };
35062             //if(this.hideOnLayout){
35063                 //center.el.setStyle("display", "block");
35064             //}
35065             center.updateBox(this.safeBox(centerBox));
35066         }
35067         this.el.repaint();
35068         this.fireEvent("layout", this);
35069     },
35070
35071     // private
35072     safeBox : function(box){
35073         box.width = Math.max(0, box.width);
35074         box.height = Math.max(0, box.height);
35075         return box;
35076     },
35077
35078     /**
35079      * Adds a ContentPanel (or subclass) to this layout.
35080      * @param {String} target The target region key (north, south, east, west or center).
35081      * @param {Roo.ContentPanel} panel The panel to add
35082      * @return {Roo.ContentPanel} The added panel
35083      */
35084     add : function(target, panel){
35085          
35086         target = target.toLowerCase();
35087         return this.regions[target].add(panel);
35088     },
35089
35090     /**
35091      * Remove a ContentPanel (or subclass) to this layout.
35092      * @param {String} target The target region key (north, south, east, west or center).
35093      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35094      * @return {Roo.ContentPanel} The removed panel
35095      */
35096     remove : function(target, panel){
35097         target = target.toLowerCase();
35098         return this.regions[target].remove(panel);
35099     },
35100
35101     /**
35102      * Searches all regions for a panel with the specified id
35103      * @param {String} panelId
35104      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35105      */
35106     findPanel : function(panelId){
35107         var rs = this.regions;
35108         for(var target in rs){
35109             if(typeof rs[target] != "function"){
35110                 var p = rs[target].getPanel(panelId);
35111                 if(p){
35112                     return p;
35113                 }
35114             }
35115         }
35116         return null;
35117     },
35118
35119     /**
35120      * Searches all regions for a panel with the specified id and activates (shows) it.
35121      * @param {String/ContentPanel} panelId The panels id or the panel itself
35122      * @return {Roo.ContentPanel} The shown panel or null
35123      */
35124     showPanel : function(panelId) {
35125       var rs = this.regions;
35126       for(var target in rs){
35127          var r = rs[target];
35128          if(typeof r != "function"){
35129             if(r.hasPanel(panelId)){
35130                return r.showPanel(panelId);
35131             }
35132          }
35133       }
35134       return null;
35135    },
35136
35137    /**
35138      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35139      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35140      */
35141    /*
35142     restoreState : function(provider){
35143         if(!provider){
35144             provider = Roo.state.Manager;
35145         }
35146         var sm = new Roo.LayoutStateManager();
35147         sm.init(this, provider);
35148     },
35149 */
35150  
35151  
35152     /**
35153      * Adds a xtype elements to the layout.
35154      * <pre><code>
35155
35156 layout.addxtype({
35157        xtype : 'ContentPanel',
35158        region: 'west',
35159        items: [ .... ]
35160    }
35161 );
35162
35163 layout.addxtype({
35164         xtype : 'NestedLayoutPanel',
35165         region: 'west',
35166         layout: {
35167            center: { },
35168            west: { }   
35169         },
35170         items : [ ... list of content panels or nested layout panels.. ]
35171    }
35172 );
35173 </code></pre>
35174      * @param {Object} cfg Xtype definition of item to add.
35175      */
35176     addxtype : function(cfg)
35177     {
35178         // basically accepts a pannel...
35179         // can accept a layout region..!?!?
35180         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35181         
35182         
35183         // theory?  children can only be panels??
35184         
35185         //if (!cfg.xtype.match(/Panel$/)) {
35186         //    return false;
35187         //}
35188         var ret = false;
35189         
35190         if (typeof(cfg.region) == 'undefined') {
35191             Roo.log("Failed to add Panel, region was not set");
35192             Roo.log(cfg);
35193             return false;
35194         }
35195         var region = cfg.region;
35196         delete cfg.region;
35197         
35198           
35199         var xitems = [];
35200         if (cfg.items) {
35201             xitems = cfg.items;
35202             delete cfg.items;
35203         }
35204         var nb = false;
35205         
35206         switch(cfg.xtype) 
35207         {
35208             case 'Content':  // ContentPanel (el, cfg)
35209             case 'Scroll':  // ContentPanel (el, cfg)
35210             case 'View': 
35211                 cfg.autoCreate = true;
35212                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35213                 //} else {
35214                 //    var el = this.el.createChild();
35215                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35216                 //}
35217                 
35218                 this.add(region, ret);
35219                 break;
35220             
35221             /*
35222             case 'TreePanel': // our new panel!
35223                 cfg.el = this.el.createChild();
35224                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35225                 this.add(region, ret);
35226                 break;
35227             */
35228             
35229             case 'Nest': 
35230                 // create a new Layout (which is  a Border Layout...
35231                 
35232                 var clayout = cfg.layout;
35233                 clayout.el  = this.el.createChild();
35234                 clayout.items   = clayout.items  || [];
35235                 
35236                 delete cfg.layout;
35237                 
35238                 // replace this exitems with the clayout ones..
35239                 xitems = clayout.items;
35240                  
35241                 // force background off if it's in center...
35242                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35243                     cfg.background = false;
35244                 }
35245                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35246                 
35247                 
35248                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35249                 //console.log('adding nested layout panel '  + cfg.toSource());
35250                 this.add(region, ret);
35251                 nb = {}; /// find first...
35252                 break;
35253             
35254             case 'Grid':
35255                 
35256                 // needs grid and region
35257                 
35258                 //var el = this.getRegion(region).el.createChild();
35259                 /*
35260                  *var el = this.el.createChild();
35261                 // create the grid first...
35262                 cfg.grid.container = el;
35263                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35264                 */
35265                 
35266                 if (region == 'center' && this.active ) {
35267                     cfg.background = false;
35268                 }
35269                 
35270                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35271                 
35272                 this.add(region, ret);
35273                 /*
35274                 if (cfg.background) {
35275                     // render grid on panel activation (if panel background)
35276                     ret.on('activate', function(gp) {
35277                         if (!gp.grid.rendered) {
35278                     //        gp.grid.render(el);
35279                         }
35280                     });
35281                 } else {
35282                   //  cfg.grid.render(el);
35283                 }
35284                 */
35285                 break;
35286            
35287            
35288             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35289                 // it was the old xcomponent building that caused this before.
35290                 // espeically if border is the top element in the tree.
35291                 ret = this;
35292                 break; 
35293                 
35294                     
35295                 
35296                 
35297                 
35298             default:
35299                 /*
35300                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35301                     
35302                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35303                     this.add(region, ret);
35304                 } else {
35305                 */
35306                     Roo.log(cfg);
35307                     throw "Can not add '" + cfg.xtype + "' to Border";
35308                     return null;
35309              
35310                                 
35311              
35312         }
35313         this.beginUpdate();
35314         // add children..
35315         var region = '';
35316         var abn = {};
35317         Roo.each(xitems, function(i)  {
35318             region = nb && i.region ? i.region : false;
35319             
35320             var add = ret.addxtype(i);
35321            
35322             if (region) {
35323                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35324                 if (!i.background) {
35325                     abn[region] = nb[region] ;
35326                 }
35327             }
35328             
35329         });
35330         this.endUpdate();
35331
35332         // make the last non-background panel active..
35333         //if (nb) { Roo.log(abn); }
35334         if (nb) {
35335             
35336             for(var r in abn) {
35337                 region = this.getRegion(r);
35338                 if (region) {
35339                     // tried using nb[r], but it does not work..
35340                      
35341                     region.showPanel(abn[r]);
35342                    
35343                 }
35344             }
35345         }
35346         return ret;
35347         
35348     },
35349     
35350     
35351 // private
35352     factory : function(cfg)
35353     {
35354         
35355         var validRegions = Roo.bootstrap.layout.Border.regions;
35356
35357         var target = cfg.region;
35358         cfg.mgr = this;
35359         
35360         var r = Roo.bootstrap.layout;
35361         Roo.log(target);
35362         switch(target){
35363             case "north":
35364                 return new r.North(cfg);
35365             case "south":
35366                 return new r.South(cfg);
35367             case "east":
35368                 return new r.East(cfg);
35369             case "west":
35370                 return new r.West(cfg);
35371             case "center":
35372                 return new r.Center(cfg);
35373         }
35374         throw 'Layout region "'+target+'" not supported.';
35375     }
35376     
35377     
35378 });
35379  /*
35380  * Based on:
35381  * Ext JS Library 1.1.1
35382  * Copyright(c) 2006-2007, Ext JS, LLC.
35383  *
35384  * Originally Released Under LGPL - original licence link has changed is not relivant.
35385  *
35386  * Fork - LGPL
35387  * <script type="text/javascript">
35388  */
35389  
35390 /**
35391  * @class Roo.bootstrap.layout.Basic
35392  * @extends Roo.util.Observable
35393  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35394  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35395  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35396  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35397  * @cfg {string}   region  the region that it inhabits..
35398  * @cfg {bool}   skipConfig skip config?
35399  * 
35400
35401  */
35402 Roo.bootstrap.layout.Basic = function(config){
35403     
35404     this.mgr = config.mgr;
35405     
35406     this.position = config.region;
35407     
35408     var skipConfig = config.skipConfig;
35409     
35410     this.events = {
35411         /**
35412          * @scope Roo.BasicLayoutRegion
35413          */
35414         
35415         /**
35416          * @event beforeremove
35417          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35418          * @param {Roo.LayoutRegion} this
35419          * @param {Roo.ContentPanel} panel The panel
35420          * @param {Object} e The cancel event object
35421          */
35422         "beforeremove" : true,
35423         /**
35424          * @event invalidated
35425          * Fires when the layout for this region is changed.
35426          * @param {Roo.LayoutRegion} this
35427          */
35428         "invalidated" : true,
35429         /**
35430          * @event visibilitychange
35431          * Fires when this region is shown or hidden 
35432          * @param {Roo.LayoutRegion} this
35433          * @param {Boolean} visibility true or false
35434          */
35435         "visibilitychange" : true,
35436         /**
35437          * @event paneladded
35438          * Fires when a panel is added. 
35439          * @param {Roo.LayoutRegion} this
35440          * @param {Roo.ContentPanel} panel The panel
35441          */
35442         "paneladded" : true,
35443         /**
35444          * @event panelremoved
35445          * Fires when a panel is removed. 
35446          * @param {Roo.LayoutRegion} this
35447          * @param {Roo.ContentPanel} panel The panel
35448          */
35449         "panelremoved" : true,
35450         /**
35451          * @event beforecollapse
35452          * Fires when this region before collapse.
35453          * @param {Roo.LayoutRegion} this
35454          */
35455         "beforecollapse" : true,
35456         /**
35457          * @event collapsed
35458          * Fires when this region is collapsed.
35459          * @param {Roo.LayoutRegion} this
35460          */
35461         "collapsed" : true,
35462         /**
35463          * @event expanded
35464          * Fires when this region is expanded.
35465          * @param {Roo.LayoutRegion} this
35466          */
35467         "expanded" : true,
35468         /**
35469          * @event slideshow
35470          * Fires when this region is slid into view.
35471          * @param {Roo.LayoutRegion} this
35472          */
35473         "slideshow" : true,
35474         /**
35475          * @event slidehide
35476          * Fires when this region slides out of view. 
35477          * @param {Roo.LayoutRegion} this
35478          */
35479         "slidehide" : true,
35480         /**
35481          * @event panelactivated
35482          * Fires when a panel is activated. 
35483          * @param {Roo.LayoutRegion} this
35484          * @param {Roo.ContentPanel} panel The activated panel
35485          */
35486         "panelactivated" : true,
35487         /**
35488          * @event resized
35489          * Fires when the user resizes this region. 
35490          * @param {Roo.LayoutRegion} this
35491          * @param {Number} newSize The new size (width for east/west, height for north/south)
35492          */
35493         "resized" : true
35494     };
35495     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35496     this.panels = new Roo.util.MixedCollection();
35497     this.panels.getKey = this.getPanelId.createDelegate(this);
35498     this.box = null;
35499     this.activePanel = null;
35500     // ensure listeners are added...
35501     
35502     if (config.listeners || config.events) {
35503         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35504             listeners : config.listeners || {},
35505             events : config.events || {}
35506         });
35507     }
35508     
35509     if(skipConfig !== true){
35510         this.applyConfig(config);
35511     }
35512 };
35513
35514 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35515 {
35516     getPanelId : function(p){
35517         return p.getId();
35518     },
35519     
35520     applyConfig : function(config){
35521         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35522         this.config = config;
35523         
35524     },
35525     
35526     /**
35527      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35528      * the width, for horizontal (north, south) the height.
35529      * @param {Number} newSize The new width or height
35530      */
35531     resizeTo : function(newSize){
35532         var el = this.el ? this.el :
35533                  (this.activePanel ? this.activePanel.getEl() : null);
35534         if(el){
35535             switch(this.position){
35536                 case "east":
35537                 case "west":
35538                     el.setWidth(newSize);
35539                     this.fireEvent("resized", this, newSize);
35540                 break;
35541                 case "north":
35542                 case "south":
35543                     el.setHeight(newSize);
35544                     this.fireEvent("resized", this, newSize);
35545                 break;                
35546             }
35547         }
35548     },
35549     
35550     getBox : function(){
35551         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35552     },
35553     
35554     getMargins : function(){
35555         return this.margins;
35556     },
35557     
35558     updateBox : function(box){
35559         this.box = box;
35560         var el = this.activePanel.getEl();
35561         el.dom.style.left = box.x + "px";
35562         el.dom.style.top = box.y + "px";
35563         this.activePanel.setSize(box.width, box.height);
35564     },
35565     
35566     /**
35567      * Returns the container element for this region.
35568      * @return {Roo.Element}
35569      */
35570     getEl : function(){
35571         return this.activePanel;
35572     },
35573     
35574     /**
35575      * Returns true if this region is currently visible.
35576      * @return {Boolean}
35577      */
35578     isVisible : function(){
35579         return this.activePanel ? true : false;
35580     },
35581     
35582     setActivePanel : function(panel){
35583         panel = this.getPanel(panel);
35584         if(this.activePanel && this.activePanel != panel){
35585             this.activePanel.setActiveState(false);
35586             this.activePanel.getEl().setLeftTop(-10000,-10000);
35587         }
35588         this.activePanel = panel;
35589         panel.setActiveState(true);
35590         if(this.box){
35591             panel.setSize(this.box.width, this.box.height);
35592         }
35593         this.fireEvent("panelactivated", this, panel);
35594         this.fireEvent("invalidated");
35595     },
35596     
35597     /**
35598      * Show the specified panel.
35599      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35600      * @return {Roo.ContentPanel} The shown panel or null
35601      */
35602     showPanel : function(panel){
35603         panel = this.getPanel(panel);
35604         if(panel){
35605             this.setActivePanel(panel);
35606         }
35607         return panel;
35608     },
35609     
35610     /**
35611      * Get the active panel for this region.
35612      * @return {Roo.ContentPanel} The active panel or null
35613      */
35614     getActivePanel : function(){
35615         return this.activePanel;
35616     },
35617     
35618     /**
35619      * Add the passed ContentPanel(s)
35620      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35621      * @return {Roo.ContentPanel} The panel added (if only one was added)
35622      */
35623     add : function(panel){
35624         if(arguments.length > 1){
35625             for(var i = 0, len = arguments.length; i < len; i++) {
35626                 this.add(arguments[i]);
35627             }
35628             return null;
35629         }
35630         if(this.hasPanel(panel)){
35631             this.showPanel(panel);
35632             return panel;
35633         }
35634         var el = panel.getEl();
35635         if(el.dom.parentNode != this.mgr.el.dom){
35636             this.mgr.el.dom.appendChild(el.dom);
35637         }
35638         if(panel.setRegion){
35639             panel.setRegion(this);
35640         }
35641         this.panels.add(panel);
35642         el.setStyle("position", "absolute");
35643         if(!panel.background){
35644             this.setActivePanel(panel);
35645             if(this.config.initialSize && this.panels.getCount()==1){
35646                 this.resizeTo(this.config.initialSize);
35647             }
35648         }
35649         this.fireEvent("paneladded", this, panel);
35650         return panel;
35651     },
35652     
35653     /**
35654      * Returns true if the panel is in this region.
35655      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35656      * @return {Boolean}
35657      */
35658     hasPanel : function(panel){
35659         if(typeof panel == "object"){ // must be panel obj
35660             panel = panel.getId();
35661         }
35662         return this.getPanel(panel) ? true : false;
35663     },
35664     
35665     /**
35666      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35667      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35668      * @param {Boolean} preservePanel Overrides the config preservePanel option
35669      * @return {Roo.ContentPanel} The panel that was removed
35670      */
35671     remove : function(panel, preservePanel){
35672         panel = this.getPanel(panel);
35673         if(!panel){
35674             return null;
35675         }
35676         var e = {};
35677         this.fireEvent("beforeremove", this, panel, e);
35678         if(e.cancel === true){
35679             return null;
35680         }
35681         var panelId = panel.getId();
35682         this.panels.removeKey(panelId);
35683         return panel;
35684     },
35685     
35686     /**
35687      * Returns the panel specified or null if it's not in this region.
35688      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35689      * @return {Roo.ContentPanel}
35690      */
35691     getPanel : function(id){
35692         if(typeof id == "object"){ // must be panel obj
35693             return id;
35694         }
35695         return this.panels.get(id);
35696     },
35697     
35698     /**
35699      * Returns this regions position (north/south/east/west/center).
35700      * @return {String} 
35701      */
35702     getPosition: function(){
35703         return this.position;    
35704     }
35705 });/*
35706  * Based on:
35707  * Ext JS Library 1.1.1
35708  * Copyright(c) 2006-2007, Ext JS, LLC.
35709  *
35710  * Originally Released Under LGPL - original licence link has changed is not relivant.
35711  *
35712  * Fork - LGPL
35713  * <script type="text/javascript">
35714  */
35715  
35716 /**
35717  * @class Roo.bootstrap.layout.Region
35718  * @extends Roo.bootstrap.layout.Basic
35719  * This class represents a region in a layout manager.
35720  
35721  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35722  * @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})
35723  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35724  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35725  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35726  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35727  * @cfg {String}    title           The title for the region (overrides panel titles)
35728  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35729  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35730  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35731  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35732  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35733  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35734  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35735  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35736  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35737  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35738
35739  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35740  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35741  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35742  * @cfg {Number}    width           For East/West panels
35743  * @cfg {Number}    height          For North/South panels
35744  * @cfg {Boolean}   split           To show the splitter
35745  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35746  * 
35747  * @cfg {string}   cls             Extra CSS classes to add to region
35748  * 
35749  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35750  * @cfg {string}   region  the region that it inhabits..
35751  *
35752
35753  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35754  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35755
35756  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35757  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35758  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35759  */
35760 Roo.bootstrap.layout.Region = function(config)
35761 {
35762     this.applyConfig(config);
35763
35764     var mgr = config.mgr;
35765     var pos = config.region;
35766     config.skipConfig = true;
35767     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35768     
35769     if (mgr.el) {
35770         this.onRender(mgr.el);   
35771     }
35772      
35773     this.visible = true;
35774     this.collapsed = false;
35775     this.unrendered_panels = [];
35776 };
35777
35778 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35779
35780     position: '', // set by wrapper (eg. north/south etc..)
35781     unrendered_panels : null,  // unrendered panels.
35782     createBody : function(){
35783         /** This region's body element 
35784         * @type Roo.Element */
35785         this.bodyEl = this.el.createChild({
35786                 tag: "div",
35787                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35788         });
35789     },
35790
35791     onRender: function(ctr, pos)
35792     {
35793         var dh = Roo.DomHelper;
35794         /** This region's container element 
35795         * @type Roo.Element */
35796         this.el = dh.append(ctr.dom, {
35797                 tag: "div",
35798                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35799             }, true);
35800         /** This region's title element 
35801         * @type Roo.Element */
35802     
35803         this.titleEl = dh.append(this.el.dom,
35804             {
35805                     tag: "div",
35806                     unselectable: "on",
35807                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35808                     children:[
35809                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35810                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35811                     ]}, true);
35812         
35813         this.titleEl.enableDisplayMode();
35814         /** This region's title text element 
35815         * @type HTMLElement */
35816         this.titleTextEl = this.titleEl.dom.firstChild;
35817         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35818         /*
35819         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35820         this.closeBtn.enableDisplayMode();
35821         this.closeBtn.on("click", this.closeClicked, this);
35822         this.closeBtn.hide();
35823     */
35824         this.createBody(this.config);
35825         if(this.config.hideWhenEmpty){
35826             this.hide();
35827             this.on("paneladded", this.validateVisibility, this);
35828             this.on("panelremoved", this.validateVisibility, this);
35829         }
35830         if(this.autoScroll){
35831             this.bodyEl.setStyle("overflow", "auto");
35832         }else{
35833             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35834         }
35835         //if(c.titlebar !== false){
35836             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35837                 this.titleEl.hide();
35838             }else{
35839                 this.titleEl.show();
35840                 if(this.config.title){
35841                     this.titleTextEl.innerHTML = this.config.title;
35842                 }
35843             }
35844         //}
35845         if(this.config.collapsed){
35846             this.collapse(true);
35847         }
35848         if(this.config.hidden){
35849             this.hide();
35850         }
35851         
35852         if (this.unrendered_panels && this.unrendered_panels.length) {
35853             for (var i =0;i< this.unrendered_panels.length; i++) {
35854                 this.add(this.unrendered_panels[i]);
35855             }
35856             this.unrendered_panels = null;
35857             
35858         }
35859         
35860     },
35861     
35862     applyConfig : function(c)
35863     {
35864         /*
35865          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35866             var dh = Roo.DomHelper;
35867             if(c.titlebar !== false){
35868                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35869                 this.collapseBtn.on("click", this.collapse, this);
35870                 this.collapseBtn.enableDisplayMode();
35871                 /*
35872                 if(c.showPin === true || this.showPin){
35873                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35874                     this.stickBtn.enableDisplayMode();
35875                     this.stickBtn.on("click", this.expand, this);
35876                     this.stickBtn.hide();
35877                 }
35878                 
35879             }
35880             */
35881             /** This region's collapsed element
35882             * @type Roo.Element */
35883             /*
35884              *
35885             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35886                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35887             ]}, true);
35888             
35889             if(c.floatable !== false){
35890                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35891                this.collapsedEl.on("click", this.collapseClick, this);
35892             }
35893
35894             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35895                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35896                    id: "message", unselectable: "on", style:{"float":"left"}});
35897                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35898              }
35899             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35900             this.expandBtn.on("click", this.expand, this);
35901             
35902         }
35903         
35904         if(this.collapseBtn){
35905             this.collapseBtn.setVisible(c.collapsible == true);
35906         }
35907         
35908         this.cmargins = c.cmargins || this.cmargins ||
35909                          (this.position == "west" || this.position == "east" ?
35910                              {top: 0, left: 2, right:2, bottom: 0} :
35911                              {top: 2, left: 0, right:0, bottom: 2});
35912         */
35913         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35914         
35915         
35916         this.bottomTabs = c.tabPosition != "top";
35917         
35918         this.autoScroll = c.autoScroll || false;
35919         
35920         
35921        
35922         
35923         this.duration = c.duration || .30;
35924         this.slideDuration = c.slideDuration || .45;
35925         this.config = c;
35926        
35927     },
35928     /**
35929      * Returns true if this region is currently visible.
35930      * @return {Boolean}
35931      */
35932     isVisible : function(){
35933         return this.visible;
35934     },
35935
35936     /**
35937      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35938      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35939      */
35940     //setCollapsedTitle : function(title){
35941     //    title = title || "&#160;";
35942      //   if(this.collapsedTitleTextEl){
35943       //      this.collapsedTitleTextEl.innerHTML = title;
35944        // }
35945     //},
35946
35947     getBox : function(){
35948         var b;
35949       //  if(!this.collapsed){
35950             b = this.el.getBox(false, true);
35951        // }else{
35952           //  b = this.collapsedEl.getBox(false, true);
35953         //}
35954         return b;
35955     },
35956
35957     getMargins : function(){
35958         return this.margins;
35959         //return this.collapsed ? this.cmargins : this.margins;
35960     },
35961 /*
35962     highlight : function(){
35963         this.el.addClass("x-layout-panel-dragover");
35964     },
35965
35966     unhighlight : function(){
35967         this.el.removeClass("x-layout-panel-dragover");
35968     },
35969 */
35970     updateBox : function(box)
35971     {
35972         if (!this.bodyEl) {
35973             return; // not rendered yet..
35974         }
35975         
35976         this.box = box;
35977         if(!this.collapsed){
35978             this.el.dom.style.left = box.x + "px";
35979             this.el.dom.style.top = box.y + "px";
35980             this.updateBody(box.width, box.height);
35981         }else{
35982             this.collapsedEl.dom.style.left = box.x + "px";
35983             this.collapsedEl.dom.style.top = box.y + "px";
35984             this.collapsedEl.setSize(box.width, box.height);
35985         }
35986         if(this.tabs){
35987             this.tabs.autoSizeTabs();
35988         }
35989     },
35990
35991     updateBody : function(w, h)
35992     {
35993         if(w !== null){
35994             this.el.setWidth(w);
35995             w -= this.el.getBorderWidth("rl");
35996             if(this.config.adjustments){
35997                 w += this.config.adjustments[0];
35998             }
35999         }
36000         if(h !== null && h > 0){
36001             this.el.setHeight(h);
36002             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36003             h -= this.el.getBorderWidth("tb");
36004             if(this.config.adjustments){
36005                 h += this.config.adjustments[1];
36006             }
36007             this.bodyEl.setHeight(h);
36008             if(this.tabs){
36009                 h = this.tabs.syncHeight(h);
36010             }
36011         }
36012         if(this.panelSize){
36013             w = w !== null ? w : this.panelSize.width;
36014             h = h !== null ? h : this.panelSize.height;
36015         }
36016         if(this.activePanel){
36017             var el = this.activePanel.getEl();
36018             w = w !== null ? w : el.getWidth();
36019             h = h !== null ? h : el.getHeight();
36020             this.panelSize = {width: w, height: h};
36021             this.activePanel.setSize(w, h);
36022         }
36023         if(Roo.isIE && this.tabs){
36024             this.tabs.el.repaint();
36025         }
36026     },
36027
36028     /**
36029      * Returns the container element for this region.
36030      * @return {Roo.Element}
36031      */
36032     getEl : function(){
36033         return this.el;
36034     },
36035
36036     /**
36037      * Hides this region.
36038      */
36039     hide : function(){
36040         //if(!this.collapsed){
36041             this.el.dom.style.left = "-2000px";
36042             this.el.hide();
36043         //}else{
36044          //   this.collapsedEl.dom.style.left = "-2000px";
36045          //   this.collapsedEl.hide();
36046        // }
36047         this.visible = false;
36048         this.fireEvent("visibilitychange", this, false);
36049     },
36050
36051     /**
36052      * Shows this region if it was previously hidden.
36053      */
36054     show : function(){
36055         //if(!this.collapsed){
36056             this.el.show();
36057         //}else{
36058         //    this.collapsedEl.show();
36059        // }
36060         this.visible = true;
36061         this.fireEvent("visibilitychange", this, true);
36062     },
36063 /*
36064     closeClicked : function(){
36065         if(this.activePanel){
36066             this.remove(this.activePanel);
36067         }
36068     },
36069
36070     collapseClick : function(e){
36071         if(this.isSlid){
36072            e.stopPropagation();
36073            this.slideIn();
36074         }else{
36075            e.stopPropagation();
36076            this.slideOut();
36077         }
36078     },
36079 */
36080     /**
36081      * Collapses this region.
36082      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36083      */
36084     /*
36085     collapse : function(skipAnim, skipCheck = false){
36086         if(this.collapsed) {
36087             return;
36088         }
36089         
36090         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36091             
36092             this.collapsed = true;
36093             if(this.split){
36094                 this.split.el.hide();
36095             }
36096             if(this.config.animate && skipAnim !== true){
36097                 this.fireEvent("invalidated", this);
36098                 this.animateCollapse();
36099             }else{
36100                 this.el.setLocation(-20000,-20000);
36101                 this.el.hide();
36102                 this.collapsedEl.show();
36103                 this.fireEvent("collapsed", this);
36104                 this.fireEvent("invalidated", this);
36105             }
36106         }
36107         
36108     },
36109 */
36110     animateCollapse : function(){
36111         // overridden
36112     },
36113
36114     /**
36115      * Expands this region if it was previously collapsed.
36116      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36117      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36118      */
36119     /*
36120     expand : function(e, skipAnim){
36121         if(e) {
36122             e.stopPropagation();
36123         }
36124         if(!this.collapsed || this.el.hasActiveFx()) {
36125             return;
36126         }
36127         if(this.isSlid){
36128             this.afterSlideIn();
36129             skipAnim = true;
36130         }
36131         this.collapsed = false;
36132         if(this.config.animate && skipAnim !== true){
36133             this.animateExpand();
36134         }else{
36135             this.el.show();
36136             if(this.split){
36137                 this.split.el.show();
36138             }
36139             this.collapsedEl.setLocation(-2000,-2000);
36140             this.collapsedEl.hide();
36141             this.fireEvent("invalidated", this);
36142             this.fireEvent("expanded", this);
36143         }
36144     },
36145 */
36146     animateExpand : function(){
36147         // overridden
36148     },
36149
36150     initTabs : function()
36151     {
36152         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36153         
36154         var ts = new Roo.bootstrap.panel.Tabs({
36155                 el: this.bodyEl.dom,
36156                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36157                 disableTooltips: this.config.disableTabTips,
36158                 toolbar : this.config.toolbar
36159             });
36160         
36161         if(this.config.hideTabs){
36162             ts.stripWrap.setDisplayed(false);
36163         }
36164         this.tabs = ts;
36165         ts.resizeTabs = this.config.resizeTabs === true;
36166         ts.minTabWidth = this.config.minTabWidth || 40;
36167         ts.maxTabWidth = this.config.maxTabWidth || 250;
36168         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36169         ts.monitorResize = false;
36170         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36171         ts.bodyEl.addClass('roo-layout-tabs-body');
36172         this.panels.each(this.initPanelAsTab, this);
36173     },
36174
36175     initPanelAsTab : function(panel){
36176         var ti = this.tabs.addTab(
36177             panel.getEl().id,
36178             panel.getTitle(),
36179             null,
36180             this.config.closeOnTab && panel.isClosable(),
36181             panel.tpl
36182         );
36183         if(panel.tabTip !== undefined){
36184             ti.setTooltip(panel.tabTip);
36185         }
36186         ti.on("activate", function(){
36187               this.setActivePanel(panel);
36188         }, this);
36189         
36190         if(this.config.closeOnTab){
36191             ti.on("beforeclose", function(t, e){
36192                 e.cancel = true;
36193                 this.remove(panel);
36194             }, this);
36195         }
36196         
36197         panel.tabItem = ti;
36198         
36199         return ti;
36200     },
36201
36202     updatePanelTitle : function(panel, title)
36203     {
36204         if(this.activePanel == panel){
36205             this.updateTitle(title);
36206         }
36207         if(this.tabs){
36208             var ti = this.tabs.getTab(panel.getEl().id);
36209             ti.setText(title);
36210             if(panel.tabTip !== undefined){
36211                 ti.setTooltip(panel.tabTip);
36212             }
36213         }
36214     },
36215
36216     updateTitle : function(title){
36217         if(this.titleTextEl && !this.config.title){
36218             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36219         }
36220     },
36221
36222     setActivePanel : function(panel)
36223     {
36224         panel = this.getPanel(panel);
36225         if(this.activePanel && this.activePanel != panel){
36226             if(this.activePanel.setActiveState(false) === false){
36227                 return;
36228             }
36229         }
36230         this.activePanel = panel;
36231         panel.setActiveState(true);
36232         if(this.panelSize){
36233             panel.setSize(this.panelSize.width, this.panelSize.height);
36234         }
36235         if(this.closeBtn){
36236             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36237         }
36238         this.updateTitle(panel.getTitle());
36239         if(this.tabs){
36240             this.fireEvent("invalidated", this);
36241         }
36242         this.fireEvent("panelactivated", this, panel);
36243     },
36244
36245     /**
36246      * Shows the specified panel.
36247      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36248      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36249      */
36250     showPanel : function(panel)
36251     {
36252         panel = this.getPanel(panel);
36253         if(panel){
36254             if(this.tabs){
36255                 var tab = this.tabs.getTab(panel.getEl().id);
36256                 if(tab.isHidden()){
36257                     this.tabs.unhideTab(tab.id);
36258                 }
36259                 tab.activate();
36260             }else{
36261                 this.setActivePanel(panel);
36262             }
36263         }
36264         return panel;
36265     },
36266
36267     /**
36268      * Get the active panel for this region.
36269      * @return {Roo.ContentPanel} The active panel or null
36270      */
36271     getActivePanel : function(){
36272         return this.activePanel;
36273     },
36274
36275     validateVisibility : function(){
36276         if(this.panels.getCount() < 1){
36277             this.updateTitle("&#160;");
36278             this.closeBtn.hide();
36279             this.hide();
36280         }else{
36281             if(!this.isVisible()){
36282                 this.show();
36283             }
36284         }
36285     },
36286
36287     /**
36288      * Adds the passed ContentPanel(s) to this region.
36289      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36290      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36291      */
36292     add : function(panel)
36293     {
36294         if(arguments.length > 1){
36295             for(var i = 0, len = arguments.length; i < len; i++) {
36296                 this.add(arguments[i]);
36297             }
36298             return null;
36299         }
36300         
36301         // if we have not been rendered yet, then we can not really do much of this..
36302         if (!this.bodyEl) {
36303             this.unrendered_panels.push(panel);
36304             return panel;
36305         }
36306         
36307         
36308         
36309         
36310         if(this.hasPanel(panel)){
36311             this.showPanel(panel);
36312             return panel;
36313         }
36314         panel.setRegion(this);
36315         this.panels.add(panel);
36316        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36317             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36318             // and hide them... ???
36319             this.bodyEl.dom.appendChild(panel.getEl().dom);
36320             if(panel.background !== true){
36321                 this.setActivePanel(panel);
36322             }
36323             this.fireEvent("paneladded", this, panel);
36324             return panel;
36325         }
36326         */
36327         if(!this.tabs){
36328             this.initTabs();
36329         }else{
36330             this.initPanelAsTab(panel);
36331         }
36332         
36333         
36334         if(panel.background !== true){
36335             this.tabs.activate(panel.getEl().id);
36336         }
36337         this.fireEvent("paneladded", this, panel);
36338         return panel;
36339     },
36340
36341     /**
36342      * Hides the tab for the specified panel.
36343      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36344      */
36345     hidePanel : function(panel){
36346         if(this.tabs && (panel = this.getPanel(panel))){
36347             this.tabs.hideTab(panel.getEl().id);
36348         }
36349     },
36350
36351     /**
36352      * Unhides the tab for a previously hidden panel.
36353      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36354      */
36355     unhidePanel : function(panel){
36356         if(this.tabs && (panel = this.getPanel(panel))){
36357             this.tabs.unhideTab(panel.getEl().id);
36358         }
36359     },
36360
36361     clearPanels : function(){
36362         while(this.panels.getCount() > 0){
36363              this.remove(this.panels.first());
36364         }
36365     },
36366
36367     /**
36368      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36369      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36370      * @param {Boolean} preservePanel Overrides the config preservePanel option
36371      * @return {Roo.ContentPanel} The panel that was removed
36372      */
36373     remove : function(panel, preservePanel)
36374     {
36375         panel = this.getPanel(panel);
36376         if(!panel){
36377             return null;
36378         }
36379         var e = {};
36380         this.fireEvent("beforeremove", this, panel, e);
36381         if(e.cancel === true){
36382             return null;
36383         }
36384         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36385         var panelId = panel.getId();
36386         this.panels.removeKey(panelId);
36387         if(preservePanel){
36388             document.body.appendChild(panel.getEl().dom);
36389         }
36390         if(this.tabs){
36391             this.tabs.removeTab(panel.getEl().id);
36392         }else if (!preservePanel){
36393             this.bodyEl.dom.removeChild(panel.getEl().dom);
36394         }
36395         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36396             var p = this.panels.first();
36397             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36398             tempEl.appendChild(p.getEl().dom);
36399             this.bodyEl.update("");
36400             this.bodyEl.dom.appendChild(p.getEl().dom);
36401             tempEl = null;
36402             this.updateTitle(p.getTitle());
36403             this.tabs = null;
36404             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36405             this.setActivePanel(p);
36406         }
36407         panel.setRegion(null);
36408         if(this.activePanel == panel){
36409             this.activePanel = null;
36410         }
36411         if(this.config.autoDestroy !== false && preservePanel !== true){
36412             try{panel.destroy();}catch(e){}
36413         }
36414         this.fireEvent("panelremoved", this, panel);
36415         return panel;
36416     },
36417
36418     /**
36419      * Returns the TabPanel component used by this region
36420      * @return {Roo.TabPanel}
36421      */
36422     getTabs : function(){
36423         return this.tabs;
36424     },
36425
36426     createTool : function(parentEl, className){
36427         var btn = Roo.DomHelper.append(parentEl, {
36428             tag: "div",
36429             cls: "x-layout-tools-button",
36430             children: [ {
36431                 tag: "div",
36432                 cls: "roo-layout-tools-button-inner " + className,
36433                 html: "&#160;"
36434             }]
36435         }, true);
36436         btn.addClassOnOver("roo-layout-tools-button-over");
36437         return btn;
36438     }
36439 });/*
36440  * Based on:
36441  * Ext JS Library 1.1.1
36442  * Copyright(c) 2006-2007, Ext JS, LLC.
36443  *
36444  * Originally Released Under LGPL - original licence link has changed is not relivant.
36445  *
36446  * Fork - LGPL
36447  * <script type="text/javascript">
36448  */
36449  
36450
36451
36452 /**
36453  * @class Roo.SplitLayoutRegion
36454  * @extends Roo.LayoutRegion
36455  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36456  */
36457 Roo.bootstrap.layout.Split = function(config){
36458     this.cursor = config.cursor;
36459     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36460 };
36461
36462 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36463 {
36464     splitTip : "Drag to resize.",
36465     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36466     useSplitTips : false,
36467
36468     applyConfig : function(config){
36469         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36470     },
36471     
36472     onRender : function(ctr,pos) {
36473         
36474         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36475         if(!this.config.split){
36476             return;
36477         }
36478         if(!this.split){
36479             
36480             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36481                             tag: "div",
36482                             id: this.el.id + "-split",
36483                             cls: "roo-layout-split roo-layout-split-"+this.position,
36484                             html: "&#160;"
36485             });
36486             /** The SplitBar for this region 
36487             * @type Roo.SplitBar */
36488             // does not exist yet...
36489             Roo.log([this.position, this.orientation]);
36490             
36491             this.split = new Roo.bootstrap.SplitBar({
36492                 dragElement : splitEl,
36493                 resizingElement: this.el,
36494                 orientation : this.orientation
36495             });
36496             
36497             this.split.on("moved", this.onSplitMove, this);
36498             this.split.useShim = this.config.useShim === true;
36499             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36500             if(this.useSplitTips){
36501                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36502             }
36503             //if(config.collapsible){
36504             //    this.split.el.on("dblclick", this.collapse,  this);
36505             //}
36506         }
36507         if(typeof this.config.minSize != "undefined"){
36508             this.split.minSize = this.config.minSize;
36509         }
36510         if(typeof this.config.maxSize != "undefined"){
36511             this.split.maxSize = this.config.maxSize;
36512         }
36513         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36514             this.hideSplitter();
36515         }
36516         
36517     },
36518
36519     getHMaxSize : function(){
36520          var cmax = this.config.maxSize || 10000;
36521          var center = this.mgr.getRegion("center");
36522          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36523     },
36524
36525     getVMaxSize : function(){
36526          var cmax = this.config.maxSize || 10000;
36527          var center = this.mgr.getRegion("center");
36528          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36529     },
36530
36531     onSplitMove : function(split, newSize){
36532         this.fireEvent("resized", this, newSize);
36533     },
36534     
36535     /** 
36536      * Returns the {@link Roo.SplitBar} for this region.
36537      * @return {Roo.SplitBar}
36538      */
36539     getSplitBar : function(){
36540         return this.split;
36541     },
36542     
36543     hide : function(){
36544         this.hideSplitter();
36545         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36546     },
36547
36548     hideSplitter : function(){
36549         if(this.split){
36550             this.split.el.setLocation(-2000,-2000);
36551             this.split.el.hide();
36552         }
36553     },
36554
36555     show : function(){
36556         if(this.split){
36557             this.split.el.show();
36558         }
36559         Roo.bootstrap.layout.Split.superclass.show.call(this);
36560     },
36561     
36562     beforeSlide: function(){
36563         if(Roo.isGecko){// firefox overflow auto bug workaround
36564             this.bodyEl.clip();
36565             if(this.tabs) {
36566                 this.tabs.bodyEl.clip();
36567             }
36568             if(this.activePanel){
36569                 this.activePanel.getEl().clip();
36570                 
36571                 if(this.activePanel.beforeSlide){
36572                     this.activePanel.beforeSlide();
36573                 }
36574             }
36575         }
36576     },
36577     
36578     afterSlide : function(){
36579         if(Roo.isGecko){// firefox overflow auto bug workaround
36580             this.bodyEl.unclip();
36581             if(this.tabs) {
36582                 this.tabs.bodyEl.unclip();
36583             }
36584             if(this.activePanel){
36585                 this.activePanel.getEl().unclip();
36586                 if(this.activePanel.afterSlide){
36587                     this.activePanel.afterSlide();
36588                 }
36589             }
36590         }
36591     },
36592
36593     initAutoHide : function(){
36594         if(this.autoHide !== false){
36595             if(!this.autoHideHd){
36596                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36597                 this.autoHideHd = {
36598                     "mouseout": function(e){
36599                         if(!e.within(this.el, true)){
36600                             st.delay(500);
36601                         }
36602                     },
36603                     "mouseover" : function(e){
36604                         st.cancel();
36605                     },
36606                     scope : this
36607                 };
36608             }
36609             this.el.on(this.autoHideHd);
36610         }
36611     },
36612
36613     clearAutoHide : function(){
36614         if(this.autoHide !== false){
36615             this.el.un("mouseout", this.autoHideHd.mouseout);
36616             this.el.un("mouseover", this.autoHideHd.mouseover);
36617         }
36618     },
36619
36620     clearMonitor : function(){
36621         Roo.get(document).un("click", this.slideInIf, this);
36622     },
36623
36624     // these names are backwards but not changed for compat
36625     slideOut : function(){
36626         if(this.isSlid || this.el.hasActiveFx()){
36627             return;
36628         }
36629         this.isSlid = true;
36630         if(this.collapseBtn){
36631             this.collapseBtn.hide();
36632         }
36633         this.closeBtnState = this.closeBtn.getStyle('display');
36634         this.closeBtn.hide();
36635         if(this.stickBtn){
36636             this.stickBtn.show();
36637         }
36638         this.el.show();
36639         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36640         this.beforeSlide();
36641         this.el.setStyle("z-index", 10001);
36642         this.el.slideIn(this.getSlideAnchor(), {
36643             callback: function(){
36644                 this.afterSlide();
36645                 this.initAutoHide();
36646                 Roo.get(document).on("click", this.slideInIf, this);
36647                 this.fireEvent("slideshow", this);
36648             },
36649             scope: this,
36650             block: true
36651         });
36652     },
36653
36654     afterSlideIn : function(){
36655         this.clearAutoHide();
36656         this.isSlid = false;
36657         this.clearMonitor();
36658         this.el.setStyle("z-index", "");
36659         if(this.collapseBtn){
36660             this.collapseBtn.show();
36661         }
36662         this.closeBtn.setStyle('display', this.closeBtnState);
36663         if(this.stickBtn){
36664             this.stickBtn.hide();
36665         }
36666         this.fireEvent("slidehide", this);
36667     },
36668
36669     slideIn : function(cb){
36670         if(!this.isSlid || this.el.hasActiveFx()){
36671             Roo.callback(cb);
36672             return;
36673         }
36674         this.isSlid = false;
36675         this.beforeSlide();
36676         this.el.slideOut(this.getSlideAnchor(), {
36677             callback: function(){
36678                 this.el.setLeftTop(-10000, -10000);
36679                 this.afterSlide();
36680                 this.afterSlideIn();
36681                 Roo.callback(cb);
36682             },
36683             scope: this,
36684             block: true
36685         });
36686     },
36687     
36688     slideInIf : function(e){
36689         if(!e.within(this.el)){
36690             this.slideIn();
36691         }
36692     },
36693
36694     animateCollapse : function(){
36695         this.beforeSlide();
36696         this.el.setStyle("z-index", 20000);
36697         var anchor = this.getSlideAnchor();
36698         this.el.slideOut(anchor, {
36699             callback : function(){
36700                 this.el.setStyle("z-index", "");
36701                 this.collapsedEl.slideIn(anchor, {duration:.3});
36702                 this.afterSlide();
36703                 this.el.setLocation(-10000,-10000);
36704                 this.el.hide();
36705                 this.fireEvent("collapsed", this);
36706             },
36707             scope: this,
36708             block: true
36709         });
36710     },
36711
36712     animateExpand : function(){
36713         this.beforeSlide();
36714         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36715         this.el.setStyle("z-index", 20000);
36716         this.collapsedEl.hide({
36717             duration:.1
36718         });
36719         this.el.slideIn(this.getSlideAnchor(), {
36720             callback : function(){
36721                 this.el.setStyle("z-index", "");
36722                 this.afterSlide();
36723                 if(this.split){
36724                     this.split.el.show();
36725                 }
36726                 this.fireEvent("invalidated", this);
36727                 this.fireEvent("expanded", this);
36728             },
36729             scope: this,
36730             block: true
36731         });
36732     },
36733
36734     anchors : {
36735         "west" : "left",
36736         "east" : "right",
36737         "north" : "top",
36738         "south" : "bottom"
36739     },
36740
36741     sanchors : {
36742         "west" : "l",
36743         "east" : "r",
36744         "north" : "t",
36745         "south" : "b"
36746     },
36747
36748     canchors : {
36749         "west" : "tl-tr",
36750         "east" : "tr-tl",
36751         "north" : "tl-bl",
36752         "south" : "bl-tl"
36753     },
36754
36755     getAnchor : function(){
36756         return this.anchors[this.position];
36757     },
36758
36759     getCollapseAnchor : function(){
36760         return this.canchors[this.position];
36761     },
36762
36763     getSlideAnchor : function(){
36764         return this.sanchors[this.position];
36765     },
36766
36767     getAlignAdj : function(){
36768         var cm = this.cmargins;
36769         switch(this.position){
36770             case "west":
36771                 return [0, 0];
36772             break;
36773             case "east":
36774                 return [0, 0];
36775             break;
36776             case "north":
36777                 return [0, 0];
36778             break;
36779             case "south":
36780                 return [0, 0];
36781             break;
36782         }
36783     },
36784
36785     getExpandAdj : function(){
36786         var c = this.collapsedEl, cm = this.cmargins;
36787         switch(this.position){
36788             case "west":
36789                 return [-(cm.right+c.getWidth()+cm.left), 0];
36790             break;
36791             case "east":
36792                 return [cm.right+c.getWidth()+cm.left, 0];
36793             break;
36794             case "north":
36795                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36796             break;
36797             case "south":
36798                 return [0, cm.top+cm.bottom+c.getHeight()];
36799             break;
36800         }
36801     }
36802 });/*
36803  * Based on:
36804  * Ext JS Library 1.1.1
36805  * Copyright(c) 2006-2007, Ext JS, LLC.
36806  *
36807  * Originally Released Under LGPL - original licence link has changed is not relivant.
36808  *
36809  * Fork - LGPL
36810  * <script type="text/javascript">
36811  */
36812 /*
36813  * These classes are private internal classes
36814  */
36815 Roo.bootstrap.layout.Center = function(config){
36816     config.region = "center";
36817     Roo.bootstrap.layout.Region.call(this, config);
36818     this.visible = true;
36819     this.minWidth = config.minWidth || 20;
36820     this.minHeight = config.minHeight || 20;
36821 };
36822
36823 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36824     hide : function(){
36825         // center panel can't be hidden
36826     },
36827     
36828     show : function(){
36829         // center panel can't be hidden
36830     },
36831     
36832     getMinWidth: function(){
36833         return this.minWidth;
36834     },
36835     
36836     getMinHeight: function(){
36837         return this.minHeight;
36838     }
36839 });
36840
36841
36842
36843
36844  
36845
36846
36847
36848
36849
36850 Roo.bootstrap.layout.North = function(config)
36851 {
36852     config.region = 'north';
36853     config.cursor = 'n-resize';
36854     
36855     Roo.bootstrap.layout.Split.call(this, config);
36856     
36857     
36858     if(this.split){
36859         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36860         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36861         this.split.el.addClass("roo-layout-split-v");
36862     }
36863     var size = config.initialSize || config.height;
36864     if(typeof size != "undefined"){
36865         this.el.setHeight(size);
36866     }
36867 };
36868 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36869 {
36870     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36871     
36872     
36873     
36874     getBox : function(){
36875         if(this.collapsed){
36876             return this.collapsedEl.getBox();
36877         }
36878         var box = this.el.getBox();
36879         if(this.split){
36880             box.height += this.split.el.getHeight();
36881         }
36882         return box;
36883     },
36884     
36885     updateBox : function(box){
36886         if(this.split && !this.collapsed){
36887             box.height -= this.split.el.getHeight();
36888             this.split.el.setLeft(box.x);
36889             this.split.el.setTop(box.y+box.height);
36890             this.split.el.setWidth(box.width);
36891         }
36892         if(this.collapsed){
36893             this.updateBody(box.width, null);
36894         }
36895         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36896     }
36897 });
36898
36899
36900
36901
36902
36903 Roo.bootstrap.layout.South = function(config){
36904     config.region = 'south';
36905     config.cursor = 's-resize';
36906     Roo.bootstrap.layout.Split.call(this, config);
36907     if(this.split){
36908         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36909         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36910         this.split.el.addClass("roo-layout-split-v");
36911     }
36912     var size = config.initialSize || config.height;
36913     if(typeof size != "undefined"){
36914         this.el.setHeight(size);
36915     }
36916 };
36917
36918 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36919     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36920     getBox : function(){
36921         if(this.collapsed){
36922             return this.collapsedEl.getBox();
36923         }
36924         var box = this.el.getBox();
36925         if(this.split){
36926             var sh = this.split.el.getHeight();
36927             box.height += sh;
36928             box.y -= sh;
36929         }
36930         return box;
36931     },
36932     
36933     updateBox : function(box){
36934         if(this.split && !this.collapsed){
36935             var sh = this.split.el.getHeight();
36936             box.height -= sh;
36937             box.y += sh;
36938             this.split.el.setLeft(box.x);
36939             this.split.el.setTop(box.y-sh);
36940             this.split.el.setWidth(box.width);
36941         }
36942         if(this.collapsed){
36943             this.updateBody(box.width, null);
36944         }
36945         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36946     }
36947 });
36948
36949 Roo.bootstrap.layout.East = function(config){
36950     config.region = "east";
36951     config.cursor = "e-resize";
36952     Roo.bootstrap.layout.Split.call(this, config);
36953     if(this.split){
36954         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36955         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36956         this.split.el.addClass("roo-layout-split-h");
36957     }
36958     var size = config.initialSize || config.width;
36959     if(typeof size != "undefined"){
36960         this.el.setWidth(size);
36961     }
36962 };
36963 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36964     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36965     getBox : function(){
36966         if(this.collapsed){
36967             return this.collapsedEl.getBox();
36968         }
36969         var box = this.el.getBox();
36970         if(this.split){
36971             var sw = this.split.el.getWidth();
36972             box.width += sw;
36973             box.x -= sw;
36974         }
36975         return box;
36976     },
36977
36978     updateBox : function(box){
36979         if(this.split && !this.collapsed){
36980             var sw = this.split.el.getWidth();
36981             box.width -= sw;
36982             this.split.el.setLeft(box.x);
36983             this.split.el.setTop(box.y);
36984             this.split.el.setHeight(box.height);
36985             box.x += sw;
36986         }
36987         if(this.collapsed){
36988             this.updateBody(null, box.height);
36989         }
36990         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36991     }
36992 });
36993
36994 Roo.bootstrap.layout.West = function(config){
36995     config.region = "west";
36996     config.cursor = "w-resize";
36997     
36998     Roo.bootstrap.layout.Split.call(this, config);
36999     if(this.split){
37000         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37001         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37002         this.split.el.addClass("roo-layout-split-h");
37003     }
37004     
37005 };
37006 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37007     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37008     
37009     onRender: function(ctr, pos)
37010     {
37011         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37012         var size = this.config.initialSize || this.config.width;
37013         if(typeof size != "undefined"){
37014             this.el.setWidth(size);
37015         }
37016     },
37017     
37018     getBox : function(){
37019         if(this.collapsed){
37020             return this.collapsedEl.getBox();
37021         }
37022         var box = this.el.getBox();
37023         if(this.split){
37024             box.width += this.split.el.getWidth();
37025         }
37026         return box;
37027     },
37028     
37029     updateBox : function(box){
37030         if(this.split && !this.collapsed){
37031             var sw = this.split.el.getWidth();
37032             box.width -= sw;
37033             this.split.el.setLeft(box.x+box.width);
37034             this.split.el.setTop(box.y);
37035             this.split.el.setHeight(box.height);
37036         }
37037         if(this.collapsed){
37038             this.updateBody(null, box.height);
37039         }
37040         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37041     }
37042 });
37043 Roo.namespace("Roo.bootstrap.panel");/*
37044  * Based on:
37045  * Ext JS Library 1.1.1
37046  * Copyright(c) 2006-2007, Ext JS, LLC.
37047  *
37048  * Originally Released Under LGPL - original licence link has changed is not relivant.
37049  *
37050  * Fork - LGPL
37051  * <script type="text/javascript">
37052  */
37053 /**
37054  * @class Roo.ContentPanel
37055  * @extends Roo.util.Observable
37056  * A basic ContentPanel element.
37057  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37058  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37059  * @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
37060  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37061  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37062  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37063  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37064  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37065  * @cfg {String} title          The title for this panel
37066  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37067  * @cfg {String} url            Calls {@link #setUrl} with this value
37068  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37069  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37070  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37071  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37072  * @cfg {Boolean} badges render the badges
37073
37074  * @constructor
37075  * Create a new ContentPanel.
37076  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37077  * @param {String/Object} config A string to set only the title or a config object
37078  * @param {String} content (optional) Set the HTML content for this panel
37079  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37080  */
37081 Roo.bootstrap.panel.Content = function( config){
37082     
37083     this.tpl = config.tpl || false;
37084     
37085     var el = config.el;
37086     var content = config.content;
37087
37088     if(config.autoCreate){ // xtype is available if this is called from factory
37089         el = Roo.id();
37090     }
37091     this.el = Roo.get(el);
37092     if(!this.el && config && config.autoCreate){
37093         if(typeof config.autoCreate == "object"){
37094             if(!config.autoCreate.id){
37095                 config.autoCreate.id = config.id||el;
37096             }
37097             this.el = Roo.DomHelper.append(document.body,
37098                         config.autoCreate, true);
37099         }else{
37100             var elcfg =  {   tag: "div",
37101                             cls: "roo-layout-inactive-content",
37102                             id: config.id||el
37103                             };
37104             if (config.html) {
37105                 elcfg.html = config.html;
37106                 
37107             }
37108                         
37109             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37110         }
37111     } 
37112     this.closable = false;
37113     this.loaded = false;
37114     this.active = false;
37115    
37116       
37117     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37118         
37119         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37120         
37121         this.wrapEl = this.el; //this.el.wrap();
37122         var ti = [];
37123         if (config.toolbar.items) {
37124             ti = config.toolbar.items ;
37125             delete config.toolbar.items ;
37126         }
37127         
37128         var nitems = [];
37129         this.toolbar.render(this.wrapEl, 'before');
37130         for(var i =0;i < ti.length;i++) {
37131           //  Roo.log(['add child', items[i]]);
37132             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37133         }
37134         this.toolbar.items = nitems;
37135         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37136         delete config.toolbar;
37137         
37138     }
37139     /*
37140     // xtype created footer. - not sure if will work as we normally have to render first..
37141     if (this.footer && !this.footer.el && this.footer.xtype) {
37142         if (!this.wrapEl) {
37143             this.wrapEl = this.el.wrap();
37144         }
37145     
37146         this.footer.container = this.wrapEl.createChild();
37147          
37148         this.footer = Roo.factory(this.footer, Roo);
37149         
37150     }
37151     */
37152     
37153      if(typeof config == "string"){
37154         this.title = config;
37155     }else{
37156         Roo.apply(this, config);
37157     }
37158     
37159     if(this.resizeEl){
37160         this.resizeEl = Roo.get(this.resizeEl, true);
37161     }else{
37162         this.resizeEl = this.el;
37163     }
37164     // handle view.xtype
37165     
37166  
37167     
37168     
37169     this.addEvents({
37170         /**
37171          * @event activate
37172          * Fires when this panel is activated. 
37173          * @param {Roo.ContentPanel} this
37174          */
37175         "activate" : true,
37176         /**
37177          * @event deactivate
37178          * Fires when this panel is activated. 
37179          * @param {Roo.ContentPanel} this
37180          */
37181         "deactivate" : true,
37182
37183         /**
37184          * @event resize
37185          * Fires when this panel is resized if fitToFrame is true.
37186          * @param {Roo.ContentPanel} this
37187          * @param {Number} width The width after any component adjustments
37188          * @param {Number} height The height after any component adjustments
37189          */
37190         "resize" : true,
37191         
37192          /**
37193          * @event render
37194          * Fires when this tab is created
37195          * @param {Roo.ContentPanel} this
37196          */
37197         "render" : true
37198         
37199         
37200         
37201     });
37202     
37203
37204     
37205     
37206     if(this.autoScroll){
37207         this.resizeEl.setStyle("overflow", "auto");
37208     } else {
37209         // fix randome scrolling
37210         //this.el.on('scroll', function() {
37211         //    Roo.log('fix random scolling');
37212         //    this.scrollTo('top',0); 
37213         //});
37214     }
37215     content = content || this.content;
37216     if(content){
37217         this.setContent(content);
37218     }
37219     if(config && config.url){
37220         this.setUrl(this.url, this.params, this.loadOnce);
37221     }
37222     
37223     
37224     
37225     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37226     
37227     if (this.view && typeof(this.view.xtype) != 'undefined') {
37228         this.view.el = this.el.appendChild(document.createElement("div"));
37229         this.view = Roo.factory(this.view); 
37230         this.view.render  &&  this.view.render(false, '');  
37231     }
37232     
37233     
37234     this.fireEvent('render', this);
37235 };
37236
37237 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37238     
37239     tabTip : '',
37240     
37241     setRegion : function(region){
37242         this.region = region;
37243         this.setActiveClass(region && !this.background);
37244     },
37245     
37246     
37247     setActiveClass: function(state)
37248     {
37249         if(state){
37250            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37251            this.el.setStyle('position','relative');
37252         }else{
37253            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37254            this.el.setStyle('position', 'absolute');
37255         } 
37256     },
37257     
37258     /**
37259      * Returns the toolbar for this Panel if one was configured. 
37260      * @return {Roo.Toolbar} 
37261      */
37262     getToolbar : function(){
37263         return this.toolbar;
37264     },
37265     
37266     setActiveState : function(active)
37267     {
37268         this.active = active;
37269         this.setActiveClass(active);
37270         if(!active){
37271             if(this.fireEvent("deactivate", this) === false){
37272                 return false;
37273             }
37274             return true;
37275         }
37276         this.fireEvent("activate", this);
37277         return true;
37278     },
37279     /**
37280      * Updates this panel's element
37281      * @param {String} content The new content
37282      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37283     */
37284     setContent : function(content, loadScripts){
37285         this.el.update(content, loadScripts);
37286     },
37287
37288     ignoreResize : function(w, h){
37289         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37290             return true;
37291         }else{
37292             this.lastSize = {width: w, height: h};
37293             return false;
37294         }
37295     },
37296     /**
37297      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37298      * @return {Roo.UpdateManager} The UpdateManager
37299      */
37300     getUpdateManager : function(){
37301         return this.el.getUpdateManager();
37302     },
37303      /**
37304      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37305      * @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:
37306 <pre><code>
37307 panel.load({
37308     url: "your-url.php",
37309     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37310     callback: yourFunction,
37311     scope: yourObject, //(optional scope)
37312     discardUrl: false,
37313     nocache: false,
37314     text: "Loading...",
37315     timeout: 30,
37316     scripts: false
37317 });
37318 </code></pre>
37319      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37320      * 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.
37321      * @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}
37322      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37323      * @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.
37324      * @return {Roo.ContentPanel} this
37325      */
37326     load : function(){
37327         var um = this.el.getUpdateManager();
37328         um.update.apply(um, arguments);
37329         return this;
37330     },
37331
37332
37333     /**
37334      * 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.
37335      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37336      * @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)
37337      * @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)
37338      * @return {Roo.UpdateManager} The UpdateManager
37339      */
37340     setUrl : function(url, params, loadOnce){
37341         if(this.refreshDelegate){
37342             this.removeListener("activate", this.refreshDelegate);
37343         }
37344         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37345         this.on("activate", this.refreshDelegate);
37346         return this.el.getUpdateManager();
37347     },
37348     
37349     _handleRefresh : function(url, params, loadOnce){
37350         if(!loadOnce || !this.loaded){
37351             var updater = this.el.getUpdateManager();
37352             updater.update(url, params, this._setLoaded.createDelegate(this));
37353         }
37354     },
37355     
37356     _setLoaded : function(){
37357         this.loaded = true;
37358     }, 
37359     
37360     /**
37361      * Returns this panel's id
37362      * @return {String} 
37363      */
37364     getId : function(){
37365         return this.el.id;
37366     },
37367     
37368     /** 
37369      * Returns this panel's element - used by regiosn to add.
37370      * @return {Roo.Element} 
37371      */
37372     getEl : function(){
37373         return this.wrapEl || this.el;
37374     },
37375     
37376    
37377     
37378     adjustForComponents : function(width, height)
37379     {
37380         //Roo.log('adjustForComponents ');
37381         if(this.resizeEl != this.el){
37382             width -= this.el.getFrameWidth('lr');
37383             height -= this.el.getFrameWidth('tb');
37384         }
37385         if(this.toolbar){
37386             var te = this.toolbar.getEl();
37387             te.setWidth(width);
37388             height -= te.getHeight();
37389         }
37390         if(this.footer){
37391             var te = this.footer.getEl();
37392             te.setWidth(width);
37393             height -= te.getHeight();
37394         }
37395         
37396         
37397         if(this.adjustments){
37398             width += this.adjustments[0];
37399             height += this.adjustments[1];
37400         }
37401         return {"width": width, "height": height};
37402     },
37403     
37404     setSize : function(width, height){
37405         if(this.fitToFrame && !this.ignoreResize(width, height)){
37406             if(this.fitContainer && this.resizeEl != this.el){
37407                 this.el.setSize(width, height);
37408             }
37409             var size = this.adjustForComponents(width, height);
37410             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37411             this.fireEvent('resize', this, size.width, size.height);
37412         }
37413     },
37414     
37415     /**
37416      * Returns this panel's title
37417      * @return {String} 
37418      */
37419     getTitle : function(){
37420         
37421         if (typeof(this.title) != 'object') {
37422             return this.title;
37423         }
37424         
37425         var t = '';
37426         for (var k in this.title) {
37427             if (!this.title.hasOwnProperty(k)) {
37428                 continue;
37429             }
37430             
37431             if (k.indexOf('-') >= 0) {
37432                 var s = k.split('-');
37433                 for (var i = 0; i<s.length; i++) {
37434                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37435                 }
37436             } else {
37437                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37438             }
37439         }
37440         return t;
37441     },
37442     
37443     /**
37444      * Set this panel's title
37445      * @param {String} title
37446      */
37447     setTitle : function(title){
37448         this.title = title;
37449         if(this.region){
37450             this.region.updatePanelTitle(this, title);
37451         }
37452     },
37453     
37454     /**
37455      * Returns true is this panel was configured to be closable
37456      * @return {Boolean} 
37457      */
37458     isClosable : function(){
37459         return this.closable;
37460     },
37461     
37462     beforeSlide : function(){
37463         this.el.clip();
37464         this.resizeEl.clip();
37465     },
37466     
37467     afterSlide : function(){
37468         this.el.unclip();
37469         this.resizeEl.unclip();
37470     },
37471     
37472     /**
37473      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37474      *   Will fail silently if the {@link #setUrl} method has not been called.
37475      *   This does not activate the panel, just updates its content.
37476      */
37477     refresh : function(){
37478         if(this.refreshDelegate){
37479            this.loaded = false;
37480            this.refreshDelegate();
37481         }
37482     },
37483     
37484     /**
37485      * Destroys this panel
37486      */
37487     destroy : function(){
37488         this.el.removeAllListeners();
37489         var tempEl = document.createElement("span");
37490         tempEl.appendChild(this.el.dom);
37491         tempEl.innerHTML = "";
37492         this.el.remove();
37493         this.el = null;
37494     },
37495     
37496     /**
37497      * form - if the content panel contains a form - this is a reference to it.
37498      * @type {Roo.form.Form}
37499      */
37500     form : false,
37501     /**
37502      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37503      *    This contains a reference to it.
37504      * @type {Roo.View}
37505      */
37506     view : false,
37507     
37508       /**
37509      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37510      * <pre><code>
37511
37512 layout.addxtype({
37513        xtype : 'Form',
37514        items: [ .... ]
37515    }
37516 );
37517
37518 </code></pre>
37519      * @param {Object} cfg Xtype definition of item to add.
37520      */
37521     
37522     
37523     getChildContainer: function () {
37524         return this.getEl();
37525     }
37526     
37527     
37528     /*
37529         var  ret = new Roo.factory(cfg);
37530         return ret;
37531         
37532         
37533         // add form..
37534         if (cfg.xtype.match(/^Form$/)) {
37535             
37536             var el;
37537             //if (this.footer) {
37538             //    el = this.footer.container.insertSibling(false, 'before');
37539             //} else {
37540                 el = this.el.createChild();
37541             //}
37542
37543             this.form = new  Roo.form.Form(cfg);
37544             
37545             
37546             if ( this.form.allItems.length) {
37547                 this.form.render(el.dom);
37548             }
37549             return this.form;
37550         }
37551         // should only have one of theses..
37552         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37553             // views.. should not be just added - used named prop 'view''
37554             
37555             cfg.el = this.el.appendChild(document.createElement("div"));
37556             // factory?
37557             
37558             var ret = new Roo.factory(cfg);
37559              
37560              ret.render && ret.render(false, ''); // render blank..
37561             this.view = ret;
37562             return ret;
37563         }
37564         return false;
37565     }
37566     \*/
37567 });
37568  
37569 /**
37570  * @class Roo.bootstrap.panel.Grid
37571  * @extends Roo.bootstrap.panel.Content
37572  * @constructor
37573  * Create a new GridPanel.
37574  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37575  * @param {Object} config A the config object
37576   
37577  */
37578
37579
37580
37581 Roo.bootstrap.panel.Grid = function(config)
37582 {
37583     
37584       
37585     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37586         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37587
37588     config.el = this.wrapper;
37589     //this.el = this.wrapper;
37590     
37591       if (config.container) {
37592         // ctor'ed from a Border/panel.grid
37593         
37594         
37595         this.wrapper.setStyle("overflow", "hidden");
37596         this.wrapper.addClass('roo-grid-container');
37597
37598     }
37599     
37600     
37601     if(config.toolbar){
37602         var tool_el = this.wrapper.createChild();    
37603         this.toolbar = Roo.factory(config.toolbar);
37604         var ti = [];
37605         if (config.toolbar.items) {
37606             ti = config.toolbar.items ;
37607             delete config.toolbar.items ;
37608         }
37609         
37610         var nitems = [];
37611         this.toolbar.render(tool_el);
37612         for(var i =0;i < ti.length;i++) {
37613           //  Roo.log(['add child', items[i]]);
37614             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37615         }
37616         this.toolbar.items = nitems;
37617         
37618         delete config.toolbar;
37619     }
37620     
37621     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37622     config.grid.scrollBody = true;;
37623     config.grid.monitorWindowResize = false; // turn off autosizing
37624     config.grid.autoHeight = false;
37625     config.grid.autoWidth = false;
37626     
37627     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37628     
37629     if (config.background) {
37630         // render grid on panel activation (if panel background)
37631         this.on('activate', function(gp) {
37632             if (!gp.grid.rendered) {
37633                 gp.grid.render(this.wrapper);
37634                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37635             }
37636         });
37637             
37638     } else {
37639         this.grid.render(this.wrapper);
37640         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37641
37642     }
37643     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37644     // ??? needed ??? config.el = this.wrapper;
37645     
37646     
37647     
37648   
37649     // xtype created footer. - not sure if will work as we normally have to render first..
37650     if (this.footer && !this.footer.el && this.footer.xtype) {
37651         
37652         var ctr = this.grid.getView().getFooterPanel(true);
37653         this.footer.dataSource = this.grid.dataSource;
37654         this.footer = Roo.factory(this.footer, Roo);
37655         this.footer.render(ctr);
37656         
37657     }
37658     
37659     
37660     
37661     
37662      
37663 };
37664
37665 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37666     getId : function(){
37667         return this.grid.id;
37668     },
37669     
37670     /**
37671      * Returns the grid for this panel
37672      * @return {Roo.bootstrap.Table} 
37673      */
37674     getGrid : function(){
37675         return this.grid;    
37676     },
37677     
37678     setSize : function(width, height){
37679         if(!this.ignoreResize(width, height)){
37680             var grid = this.grid;
37681             var size = this.adjustForComponents(width, height);
37682             var gridel = grid.getGridEl();
37683             gridel.setSize(size.width, size.height);
37684             /*
37685             var thd = grid.getGridEl().select('thead',true).first();
37686             var tbd = grid.getGridEl().select('tbody', true).first();
37687             if (tbd) {
37688                 tbd.setSize(width, height - thd.getHeight());
37689             }
37690             */
37691             grid.autoSize();
37692         }
37693     },
37694      
37695     
37696     
37697     beforeSlide : function(){
37698         this.grid.getView().scroller.clip();
37699     },
37700     
37701     afterSlide : function(){
37702         this.grid.getView().scroller.unclip();
37703     },
37704     
37705     destroy : function(){
37706         this.grid.destroy();
37707         delete this.grid;
37708         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37709     }
37710 });
37711
37712 /**
37713  * @class Roo.bootstrap.panel.Nest
37714  * @extends Roo.bootstrap.panel.Content
37715  * @constructor
37716  * Create a new Panel, that can contain a layout.Border.
37717  * 
37718  * 
37719  * @param {Roo.BorderLayout} layout The layout for this panel
37720  * @param {String/Object} config A string to set only the title or a config object
37721  */
37722 Roo.bootstrap.panel.Nest = function(config)
37723 {
37724     // construct with only one argument..
37725     /* FIXME - implement nicer consturctors
37726     if (layout.layout) {
37727         config = layout;
37728         layout = config.layout;
37729         delete config.layout;
37730     }
37731     if (layout.xtype && !layout.getEl) {
37732         // then layout needs constructing..
37733         layout = Roo.factory(layout, Roo);
37734     }
37735     */
37736     
37737     config.el =  config.layout.getEl();
37738     
37739     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37740     
37741     config.layout.monitorWindowResize = false; // turn off autosizing
37742     this.layout = config.layout;
37743     this.layout.getEl().addClass("roo-layout-nested-layout");
37744     
37745     
37746     
37747     
37748 };
37749
37750 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37751
37752     setSize : function(width, height){
37753         if(!this.ignoreResize(width, height)){
37754             var size = this.adjustForComponents(width, height);
37755             var el = this.layout.getEl();
37756             if (size.height < 1) {
37757                 el.setWidth(size.width);   
37758             } else {
37759                 el.setSize(size.width, size.height);
37760             }
37761             var touch = el.dom.offsetWidth;
37762             this.layout.layout();
37763             // ie requires a double layout on the first pass
37764             if(Roo.isIE && !this.initialized){
37765                 this.initialized = true;
37766                 this.layout.layout();
37767             }
37768         }
37769     },
37770     
37771     // activate all subpanels if not currently active..
37772     
37773     setActiveState : function(active){
37774         this.active = active;
37775         this.setActiveClass(active);
37776         
37777         if(!active){
37778             this.fireEvent("deactivate", this);
37779             return;
37780         }
37781         
37782         this.fireEvent("activate", this);
37783         // not sure if this should happen before or after..
37784         if (!this.layout) {
37785             return; // should not happen..
37786         }
37787         var reg = false;
37788         for (var r in this.layout.regions) {
37789             reg = this.layout.getRegion(r);
37790             if (reg.getActivePanel()) {
37791                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37792                 reg.setActivePanel(reg.getActivePanel());
37793                 continue;
37794             }
37795             if (!reg.panels.length) {
37796                 continue;
37797             }
37798             reg.showPanel(reg.getPanel(0));
37799         }
37800         
37801         
37802         
37803         
37804     },
37805     
37806     /**
37807      * Returns the nested BorderLayout for this panel
37808      * @return {Roo.BorderLayout} 
37809      */
37810     getLayout : function(){
37811         return this.layout;
37812     },
37813     
37814      /**
37815      * Adds a xtype elements to the layout of the nested panel
37816      * <pre><code>
37817
37818 panel.addxtype({
37819        xtype : 'ContentPanel',
37820        region: 'west',
37821        items: [ .... ]
37822    }
37823 );
37824
37825 panel.addxtype({
37826         xtype : 'NestedLayoutPanel',
37827         region: 'west',
37828         layout: {
37829            center: { },
37830            west: { }   
37831         },
37832         items : [ ... list of content panels or nested layout panels.. ]
37833    }
37834 );
37835 </code></pre>
37836      * @param {Object} cfg Xtype definition of item to add.
37837      */
37838     addxtype : function(cfg) {
37839         return this.layout.addxtype(cfg);
37840     
37841     }
37842 });        /*
37843  * Based on:
37844  * Ext JS Library 1.1.1
37845  * Copyright(c) 2006-2007, Ext JS, LLC.
37846  *
37847  * Originally Released Under LGPL - original licence link has changed is not relivant.
37848  *
37849  * Fork - LGPL
37850  * <script type="text/javascript">
37851  */
37852 /**
37853  * @class Roo.TabPanel
37854  * @extends Roo.util.Observable
37855  * A lightweight tab container.
37856  * <br><br>
37857  * Usage:
37858  * <pre><code>
37859 // basic tabs 1, built from existing content
37860 var tabs = new Roo.TabPanel("tabs1");
37861 tabs.addTab("script", "View Script");
37862 tabs.addTab("markup", "View Markup");
37863 tabs.activate("script");
37864
37865 // more advanced tabs, built from javascript
37866 var jtabs = new Roo.TabPanel("jtabs");
37867 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37868
37869 // set up the UpdateManager
37870 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37871 var updater = tab2.getUpdateManager();
37872 updater.setDefaultUrl("ajax1.htm");
37873 tab2.on('activate', updater.refresh, updater, true);
37874
37875 // Use setUrl for Ajax loading
37876 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37877 tab3.setUrl("ajax2.htm", null, true);
37878
37879 // Disabled tab
37880 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37881 tab4.disable();
37882
37883 jtabs.activate("jtabs-1");
37884  * </code></pre>
37885  * @constructor
37886  * Create a new TabPanel.
37887  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37888  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37889  */
37890 Roo.bootstrap.panel.Tabs = function(config){
37891     /**
37892     * The container element for this TabPanel.
37893     * @type Roo.Element
37894     */
37895     this.el = Roo.get(config.el);
37896     delete config.el;
37897     if(config){
37898         if(typeof config == "boolean"){
37899             this.tabPosition = config ? "bottom" : "top";
37900         }else{
37901             Roo.apply(this, config);
37902         }
37903     }
37904     
37905     if(this.tabPosition == "bottom"){
37906         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37907         this.el.addClass("roo-tabs-bottom");
37908     }
37909     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37910     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37911     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37912     if(Roo.isIE){
37913         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37914     }
37915     if(this.tabPosition != "bottom"){
37916         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37917          * @type Roo.Element
37918          */
37919         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37920         this.el.addClass("roo-tabs-top");
37921     }
37922     this.items = [];
37923
37924     this.bodyEl.setStyle("position", "relative");
37925
37926     this.active = null;
37927     this.activateDelegate = this.activate.createDelegate(this);
37928
37929     this.addEvents({
37930         /**
37931          * @event tabchange
37932          * Fires when the active tab changes
37933          * @param {Roo.TabPanel} this
37934          * @param {Roo.TabPanelItem} activePanel The new active tab
37935          */
37936         "tabchange": true,
37937         /**
37938          * @event beforetabchange
37939          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37940          * @param {Roo.TabPanel} this
37941          * @param {Object} e Set cancel to true on this object to cancel the tab change
37942          * @param {Roo.TabPanelItem} tab The tab being changed to
37943          */
37944         "beforetabchange" : true
37945     });
37946
37947     Roo.EventManager.onWindowResize(this.onResize, this);
37948     this.cpad = this.el.getPadding("lr");
37949     this.hiddenCount = 0;
37950
37951
37952     // toolbar on the tabbar support...
37953     if (this.toolbar) {
37954         alert("no toolbar support yet");
37955         this.toolbar  = false;
37956         /*
37957         var tcfg = this.toolbar;
37958         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37959         this.toolbar = new Roo.Toolbar(tcfg);
37960         if (Roo.isSafari) {
37961             var tbl = tcfg.container.child('table', true);
37962             tbl.setAttribute('width', '100%');
37963         }
37964         */
37965         
37966     }
37967    
37968
37969
37970     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37971 };
37972
37973 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37974     /*
37975      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37976      */
37977     tabPosition : "top",
37978     /*
37979      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37980      */
37981     currentTabWidth : 0,
37982     /*
37983      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37984      */
37985     minTabWidth : 40,
37986     /*
37987      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37988      */
37989     maxTabWidth : 250,
37990     /*
37991      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37992      */
37993     preferredTabWidth : 175,
37994     /*
37995      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37996      */
37997     resizeTabs : false,
37998     /*
37999      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38000      */
38001     monitorResize : true,
38002     /*
38003      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38004      */
38005     toolbar : false,
38006
38007     /**
38008      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38009      * @param {String} id The id of the div to use <b>or create</b>
38010      * @param {String} text The text for the tab
38011      * @param {String} content (optional) Content to put in the TabPanelItem body
38012      * @param {Boolean} closable (optional) True to create a close icon on the tab
38013      * @return {Roo.TabPanelItem} The created TabPanelItem
38014      */
38015     addTab : function(id, text, content, closable, tpl)
38016     {
38017         var item = new Roo.bootstrap.panel.TabItem({
38018             panel: this,
38019             id : id,
38020             text : text,
38021             closable : closable,
38022             tpl : tpl
38023         });
38024         this.addTabItem(item);
38025         if(content){
38026             item.setContent(content);
38027         }
38028         return item;
38029     },
38030
38031     /**
38032      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38033      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38034      * @return {Roo.TabPanelItem}
38035      */
38036     getTab : function(id){
38037         return this.items[id];
38038     },
38039
38040     /**
38041      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38042      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38043      */
38044     hideTab : function(id){
38045         var t = this.items[id];
38046         if(!t.isHidden()){
38047            t.setHidden(true);
38048            this.hiddenCount++;
38049            this.autoSizeTabs();
38050         }
38051     },
38052
38053     /**
38054      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38055      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38056      */
38057     unhideTab : function(id){
38058         var t = this.items[id];
38059         if(t.isHidden()){
38060            t.setHidden(false);
38061            this.hiddenCount--;
38062            this.autoSizeTabs();
38063         }
38064     },
38065
38066     /**
38067      * Adds an existing {@link Roo.TabPanelItem}.
38068      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38069      */
38070     addTabItem : function(item){
38071         this.items[item.id] = item;
38072         this.items.push(item);
38073       //  if(this.resizeTabs){
38074     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38075   //         this.autoSizeTabs();
38076 //        }else{
38077 //            item.autoSize();
38078        // }
38079     },
38080
38081     /**
38082      * Removes a {@link Roo.TabPanelItem}.
38083      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38084      */
38085     removeTab : function(id){
38086         var items = this.items;
38087         var tab = items[id];
38088         if(!tab) { return; }
38089         var index = items.indexOf(tab);
38090         if(this.active == tab && items.length > 1){
38091             var newTab = this.getNextAvailable(index);
38092             if(newTab) {
38093                 newTab.activate();
38094             }
38095         }
38096         this.stripEl.dom.removeChild(tab.pnode.dom);
38097         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38098             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38099         }
38100         items.splice(index, 1);
38101         delete this.items[tab.id];
38102         tab.fireEvent("close", tab);
38103         tab.purgeListeners();
38104         this.autoSizeTabs();
38105     },
38106
38107     getNextAvailable : function(start){
38108         var items = this.items;
38109         var index = start;
38110         // look for a next tab that will slide over to
38111         // replace the one being removed
38112         while(index < items.length){
38113             var item = items[++index];
38114             if(item && !item.isHidden()){
38115                 return item;
38116             }
38117         }
38118         // if one isn't found select the previous tab (on the left)
38119         index = start;
38120         while(index >= 0){
38121             var item = items[--index];
38122             if(item && !item.isHidden()){
38123                 return item;
38124             }
38125         }
38126         return null;
38127     },
38128
38129     /**
38130      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38131      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38132      */
38133     disableTab : function(id){
38134         var tab = this.items[id];
38135         if(tab && this.active != tab){
38136             tab.disable();
38137         }
38138     },
38139
38140     /**
38141      * Enables a {@link Roo.TabPanelItem} that is disabled.
38142      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38143      */
38144     enableTab : function(id){
38145         var tab = this.items[id];
38146         tab.enable();
38147     },
38148
38149     /**
38150      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38151      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38152      * @return {Roo.TabPanelItem} The TabPanelItem.
38153      */
38154     activate : function(id){
38155         var tab = this.items[id];
38156         if(!tab){
38157             return null;
38158         }
38159         if(tab == this.active || tab.disabled){
38160             return tab;
38161         }
38162         var e = {};
38163         this.fireEvent("beforetabchange", this, e, tab);
38164         if(e.cancel !== true && !tab.disabled){
38165             if(this.active){
38166                 this.active.hide();
38167             }
38168             this.active = this.items[id];
38169             this.active.show();
38170             this.fireEvent("tabchange", this, this.active);
38171         }
38172         return tab;
38173     },
38174
38175     /**
38176      * Gets the active {@link Roo.TabPanelItem}.
38177      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38178      */
38179     getActiveTab : function(){
38180         return this.active;
38181     },
38182
38183     /**
38184      * Updates the tab body element to fit the height of the container element
38185      * for overflow scrolling
38186      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38187      */
38188     syncHeight : function(targetHeight){
38189         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38190         var bm = this.bodyEl.getMargins();
38191         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38192         this.bodyEl.setHeight(newHeight);
38193         return newHeight;
38194     },
38195
38196     onResize : function(){
38197         if(this.monitorResize){
38198             this.autoSizeTabs();
38199         }
38200     },
38201
38202     /**
38203      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38204      */
38205     beginUpdate : function(){
38206         this.updating = true;
38207     },
38208
38209     /**
38210      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38211      */
38212     endUpdate : function(){
38213         this.updating = false;
38214         this.autoSizeTabs();
38215     },
38216
38217     /**
38218      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38219      */
38220     autoSizeTabs : function(){
38221         var count = this.items.length;
38222         var vcount = count - this.hiddenCount;
38223         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38224             return;
38225         }
38226         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38227         var availWidth = Math.floor(w / vcount);
38228         var b = this.stripBody;
38229         if(b.getWidth() > w){
38230             var tabs = this.items;
38231             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38232             if(availWidth < this.minTabWidth){
38233                 /*if(!this.sleft){    // incomplete scrolling code
38234                     this.createScrollButtons();
38235                 }
38236                 this.showScroll();
38237                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38238             }
38239         }else{
38240             if(this.currentTabWidth < this.preferredTabWidth){
38241                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38242             }
38243         }
38244     },
38245
38246     /**
38247      * Returns the number of tabs in this TabPanel.
38248      * @return {Number}
38249      */
38250      getCount : function(){
38251          return this.items.length;
38252      },
38253
38254     /**
38255      * Resizes all the tabs to the passed width
38256      * @param {Number} The new width
38257      */
38258     setTabWidth : function(width){
38259         this.currentTabWidth = width;
38260         for(var i = 0, len = this.items.length; i < len; i++) {
38261                 if(!this.items[i].isHidden()) {
38262                 this.items[i].setWidth(width);
38263             }
38264         }
38265     },
38266
38267     /**
38268      * Destroys this TabPanel
38269      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38270      */
38271     destroy : function(removeEl){
38272         Roo.EventManager.removeResizeListener(this.onResize, this);
38273         for(var i = 0, len = this.items.length; i < len; i++){
38274             this.items[i].purgeListeners();
38275         }
38276         if(removeEl === true){
38277             this.el.update("");
38278             this.el.remove();
38279         }
38280     },
38281     
38282     createStrip : function(container)
38283     {
38284         var strip = document.createElement("nav");
38285         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38286         container.appendChild(strip);
38287         return strip;
38288     },
38289     
38290     createStripList : function(strip)
38291     {
38292         // div wrapper for retard IE
38293         // returns the "tr" element.
38294         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38295         //'<div class="x-tabs-strip-wrap">'+
38296           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38297           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38298         return strip.firstChild; //.firstChild.firstChild.firstChild;
38299     },
38300     createBody : function(container)
38301     {
38302         var body = document.createElement("div");
38303         Roo.id(body, "tab-body");
38304         //Roo.fly(body).addClass("x-tabs-body");
38305         Roo.fly(body).addClass("tab-content");
38306         container.appendChild(body);
38307         return body;
38308     },
38309     createItemBody :function(bodyEl, id){
38310         var body = Roo.getDom(id);
38311         if(!body){
38312             body = document.createElement("div");
38313             body.id = id;
38314         }
38315         //Roo.fly(body).addClass("x-tabs-item-body");
38316         Roo.fly(body).addClass("tab-pane");
38317          bodyEl.insertBefore(body, bodyEl.firstChild);
38318         return body;
38319     },
38320     /** @private */
38321     createStripElements :  function(stripEl, text, closable, tpl)
38322     {
38323         var td = document.createElement("li"); // was td..
38324         
38325         
38326         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38327         
38328         
38329         stripEl.appendChild(td);
38330         /*if(closable){
38331             td.className = "x-tabs-closable";
38332             if(!this.closeTpl){
38333                 this.closeTpl = new Roo.Template(
38334                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38335                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38336                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38337                 );
38338             }
38339             var el = this.closeTpl.overwrite(td, {"text": text});
38340             var close = el.getElementsByTagName("div")[0];
38341             var inner = el.getElementsByTagName("em")[0];
38342             return {"el": el, "close": close, "inner": inner};
38343         } else {
38344         */
38345         // not sure what this is..
38346 //            if(!this.tabTpl){
38347                 //this.tabTpl = new Roo.Template(
38348                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38349                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38350                 //);
38351 //                this.tabTpl = new Roo.Template(
38352 //                   '<a href="#">' +
38353 //                   '<span unselectable="on"' +
38354 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38355 //                            ' >{text}</span></a>'
38356 //                );
38357 //                
38358 //            }
38359
38360
38361             var template = tpl || this.tabTpl || false;
38362             
38363             if(!template){
38364                 
38365                 template = new Roo.Template(
38366                    '<a href="#">' +
38367                    '<span unselectable="on"' +
38368                             (this.disableTooltips ? '' : ' title="{text}"') +
38369                             ' >{text}</span></a>'
38370                 );
38371             }
38372             
38373             switch (typeof(template)) {
38374                 case 'object' :
38375                     break;
38376                 case 'string' :
38377                     template = new Roo.Template(template);
38378                     break;
38379                 default :
38380                     break;
38381             }
38382             
38383             var el = template.overwrite(td, {"text": text});
38384             
38385             var inner = el.getElementsByTagName("span")[0];
38386             
38387             return {"el": el, "inner": inner};
38388             
38389     }
38390         
38391     
38392 });
38393
38394 /**
38395  * @class Roo.TabPanelItem
38396  * @extends Roo.util.Observable
38397  * Represents an individual item (tab plus body) in a TabPanel.
38398  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38399  * @param {String} id The id of this TabPanelItem
38400  * @param {String} text The text for the tab of this TabPanelItem
38401  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38402  */
38403 Roo.bootstrap.panel.TabItem = function(config){
38404     /**
38405      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38406      * @type Roo.TabPanel
38407      */
38408     this.tabPanel = config.panel;
38409     /**
38410      * The id for this TabPanelItem
38411      * @type String
38412      */
38413     this.id = config.id;
38414     /** @private */
38415     this.disabled = false;
38416     /** @private */
38417     this.text = config.text;
38418     /** @private */
38419     this.loaded = false;
38420     this.closable = config.closable;
38421
38422     /**
38423      * The body element for this TabPanelItem.
38424      * @type Roo.Element
38425      */
38426     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38427     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38428     this.bodyEl.setStyle("display", "block");
38429     this.bodyEl.setStyle("zoom", "1");
38430     //this.hideAction();
38431
38432     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38433     /** @private */
38434     this.el = Roo.get(els.el);
38435     this.inner = Roo.get(els.inner, true);
38436     this.textEl = Roo.get(this.el.dom.firstChild, true);
38437     this.pnode = Roo.get(els.el.parentNode, true);
38438 //    this.el.on("mousedown", this.onTabMouseDown, this);
38439     this.el.on("click", this.onTabClick, this);
38440     /** @private */
38441     if(config.closable){
38442         var c = Roo.get(els.close, true);
38443         c.dom.title = this.closeText;
38444         c.addClassOnOver("close-over");
38445         c.on("click", this.closeClick, this);
38446      }
38447
38448     this.addEvents({
38449          /**
38450          * @event activate
38451          * Fires when this tab becomes the active tab.
38452          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38453          * @param {Roo.TabPanelItem} this
38454          */
38455         "activate": true,
38456         /**
38457          * @event beforeclose
38458          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38459          * @param {Roo.TabPanelItem} this
38460          * @param {Object} e Set cancel to true on this object to cancel the close.
38461          */
38462         "beforeclose": true,
38463         /**
38464          * @event close
38465          * Fires when this tab is closed.
38466          * @param {Roo.TabPanelItem} this
38467          */
38468          "close": true,
38469         /**
38470          * @event deactivate
38471          * Fires when this tab is no longer the active tab.
38472          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38473          * @param {Roo.TabPanelItem} this
38474          */
38475          "deactivate" : true
38476     });
38477     this.hidden = false;
38478
38479     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38480 };
38481
38482 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38483            {
38484     purgeListeners : function(){
38485        Roo.util.Observable.prototype.purgeListeners.call(this);
38486        this.el.removeAllListeners();
38487     },
38488     /**
38489      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38490      */
38491     show : function(){
38492         this.pnode.addClass("active");
38493         this.showAction();
38494         if(Roo.isOpera){
38495             this.tabPanel.stripWrap.repaint();
38496         }
38497         this.fireEvent("activate", this.tabPanel, this);
38498     },
38499
38500     /**
38501      * Returns true if this tab is the active tab.
38502      * @return {Boolean}
38503      */
38504     isActive : function(){
38505         return this.tabPanel.getActiveTab() == this;
38506     },
38507
38508     /**
38509      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38510      */
38511     hide : function(){
38512         this.pnode.removeClass("active");
38513         this.hideAction();
38514         this.fireEvent("deactivate", this.tabPanel, this);
38515     },
38516
38517     hideAction : function(){
38518         this.bodyEl.hide();
38519         this.bodyEl.setStyle("position", "absolute");
38520         this.bodyEl.setLeft("-20000px");
38521         this.bodyEl.setTop("-20000px");
38522     },
38523
38524     showAction : function(){
38525         this.bodyEl.setStyle("position", "relative");
38526         this.bodyEl.setTop("");
38527         this.bodyEl.setLeft("");
38528         this.bodyEl.show();
38529     },
38530
38531     /**
38532      * Set the tooltip for the tab.
38533      * @param {String} tooltip The tab's tooltip
38534      */
38535     setTooltip : function(text){
38536         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38537             this.textEl.dom.qtip = text;
38538             this.textEl.dom.removeAttribute('title');
38539         }else{
38540             this.textEl.dom.title = text;
38541         }
38542     },
38543
38544     onTabClick : function(e){
38545         e.preventDefault();
38546         this.tabPanel.activate(this.id);
38547     },
38548
38549     onTabMouseDown : function(e){
38550         e.preventDefault();
38551         this.tabPanel.activate(this.id);
38552     },
38553 /*
38554     getWidth : function(){
38555         return this.inner.getWidth();
38556     },
38557
38558     setWidth : function(width){
38559         var iwidth = width - this.pnode.getPadding("lr");
38560         this.inner.setWidth(iwidth);
38561         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38562         this.pnode.setWidth(width);
38563     },
38564 */
38565     /**
38566      * Show or hide the tab
38567      * @param {Boolean} hidden True to hide or false to show.
38568      */
38569     setHidden : function(hidden){
38570         this.hidden = hidden;
38571         this.pnode.setStyle("display", hidden ? "none" : "");
38572     },
38573
38574     /**
38575      * Returns true if this tab is "hidden"
38576      * @return {Boolean}
38577      */
38578     isHidden : function(){
38579         return this.hidden;
38580     },
38581
38582     /**
38583      * Returns the text for this tab
38584      * @return {String}
38585      */
38586     getText : function(){
38587         return this.text;
38588     },
38589     /*
38590     autoSize : function(){
38591         //this.el.beginMeasure();
38592         this.textEl.setWidth(1);
38593         /*
38594          *  #2804 [new] Tabs in Roojs
38595          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38596          */
38597         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38598         //this.el.endMeasure();
38599     //},
38600
38601     /**
38602      * Sets the text for the tab (Note: this also sets the tooltip text)
38603      * @param {String} text The tab's text and tooltip
38604      */
38605     setText : function(text){
38606         this.text = text;
38607         this.textEl.update(text);
38608         this.setTooltip(text);
38609         //if(!this.tabPanel.resizeTabs){
38610         //    this.autoSize();
38611         //}
38612     },
38613     /**
38614      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38615      */
38616     activate : function(){
38617         this.tabPanel.activate(this.id);
38618     },
38619
38620     /**
38621      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38622      */
38623     disable : function(){
38624         if(this.tabPanel.active != this){
38625             this.disabled = true;
38626             this.pnode.addClass("disabled");
38627         }
38628     },
38629
38630     /**
38631      * Enables this TabPanelItem if it was previously disabled.
38632      */
38633     enable : function(){
38634         this.disabled = false;
38635         this.pnode.removeClass("disabled");
38636     },
38637
38638     /**
38639      * Sets the content for this TabPanelItem.
38640      * @param {String} content The content
38641      * @param {Boolean} loadScripts true to look for and load scripts
38642      */
38643     setContent : function(content, loadScripts){
38644         this.bodyEl.update(content, loadScripts);
38645     },
38646
38647     /**
38648      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38649      * @return {Roo.UpdateManager} The UpdateManager
38650      */
38651     getUpdateManager : function(){
38652         return this.bodyEl.getUpdateManager();
38653     },
38654
38655     /**
38656      * Set a URL to be used to load the content for this TabPanelItem.
38657      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38658      * @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)
38659      * @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)
38660      * @return {Roo.UpdateManager} The UpdateManager
38661      */
38662     setUrl : function(url, params, loadOnce){
38663         if(this.refreshDelegate){
38664             this.un('activate', this.refreshDelegate);
38665         }
38666         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38667         this.on("activate", this.refreshDelegate);
38668         return this.bodyEl.getUpdateManager();
38669     },
38670
38671     /** @private */
38672     _handleRefresh : function(url, params, loadOnce){
38673         if(!loadOnce || !this.loaded){
38674             var updater = this.bodyEl.getUpdateManager();
38675             updater.update(url, params, this._setLoaded.createDelegate(this));
38676         }
38677     },
38678
38679     /**
38680      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38681      *   Will fail silently if the setUrl method has not been called.
38682      *   This does not activate the panel, just updates its content.
38683      */
38684     refresh : function(){
38685         if(this.refreshDelegate){
38686            this.loaded = false;
38687            this.refreshDelegate();
38688         }
38689     },
38690
38691     /** @private */
38692     _setLoaded : function(){
38693         this.loaded = true;
38694     },
38695
38696     /** @private */
38697     closeClick : function(e){
38698         var o = {};
38699         e.stopEvent();
38700         this.fireEvent("beforeclose", this, o);
38701         if(o.cancel !== true){
38702             this.tabPanel.removeTab(this.id);
38703         }
38704     },
38705     /**
38706      * The text displayed in the tooltip for the close icon.
38707      * @type String
38708      */
38709     closeText : "Close this tab"
38710 });
38711 /**
38712 *    This script refer to:
38713 *    Title: International Telephone Input
38714 *    Author: Jack O'Connor
38715 *    Code version:  v12.1.12
38716 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38717 **/
38718
38719 Roo.bootstrap.PhoneInputData = function() {
38720     var d = [
38721       [
38722         "Afghanistan (‫افغانستان‬‎)",
38723         "af",
38724         "93"
38725       ],
38726       [
38727         "Albania (Shqipëri)",
38728         "al",
38729         "355"
38730       ],
38731       [
38732         "Algeria (‫الجزائر‬‎)",
38733         "dz",
38734         "213"
38735       ],
38736       [
38737         "American Samoa",
38738         "as",
38739         "1684"
38740       ],
38741       [
38742         "Andorra",
38743         "ad",
38744         "376"
38745       ],
38746       [
38747         "Angola",
38748         "ao",
38749         "244"
38750       ],
38751       [
38752         "Anguilla",
38753         "ai",
38754         "1264"
38755       ],
38756       [
38757         "Antigua and Barbuda",
38758         "ag",
38759         "1268"
38760       ],
38761       [
38762         "Argentina",
38763         "ar",
38764         "54"
38765       ],
38766       [
38767         "Armenia (Հայաստան)",
38768         "am",
38769         "374"
38770       ],
38771       [
38772         "Aruba",
38773         "aw",
38774         "297"
38775       ],
38776       [
38777         "Australia",
38778         "au",
38779         "61",
38780         0
38781       ],
38782       [
38783         "Austria (Österreich)",
38784         "at",
38785         "43"
38786       ],
38787       [
38788         "Azerbaijan (Azərbaycan)",
38789         "az",
38790         "994"
38791       ],
38792       [
38793         "Bahamas",
38794         "bs",
38795         "1242"
38796       ],
38797       [
38798         "Bahrain (‫البحرين‬‎)",
38799         "bh",
38800         "973"
38801       ],
38802       [
38803         "Bangladesh (বাংলাদেশ)",
38804         "bd",
38805         "880"
38806       ],
38807       [
38808         "Barbados",
38809         "bb",
38810         "1246"
38811       ],
38812       [
38813         "Belarus (Беларусь)",
38814         "by",
38815         "375"
38816       ],
38817       [
38818         "Belgium (België)",
38819         "be",
38820         "32"
38821       ],
38822       [
38823         "Belize",
38824         "bz",
38825         "501"
38826       ],
38827       [
38828         "Benin (Bénin)",
38829         "bj",
38830         "229"
38831       ],
38832       [
38833         "Bermuda",
38834         "bm",
38835         "1441"
38836       ],
38837       [
38838         "Bhutan (འབྲུག)",
38839         "bt",
38840         "975"
38841       ],
38842       [
38843         "Bolivia",
38844         "bo",
38845         "591"
38846       ],
38847       [
38848         "Bosnia and Herzegovina (Босна и Херцеговина)",
38849         "ba",
38850         "387"
38851       ],
38852       [
38853         "Botswana",
38854         "bw",
38855         "267"
38856       ],
38857       [
38858         "Brazil (Brasil)",
38859         "br",
38860         "55"
38861       ],
38862       [
38863         "British Indian Ocean Territory",
38864         "io",
38865         "246"
38866       ],
38867       [
38868         "British Virgin Islands",
38869         "vg",
38870         "1284"
38871       ],
38872       [
38873         "Brunei",
38874         "bn",
38875         "673"
38876       ],
38877       [
38878         "Bulgaria (България)",
38879         "bg",
38880         "359"
38881       ],
38882       [
38883         "Burkina Faso",
38884         "bf",
38885         "226"
38886       ],
38887       [
38888         "Burundi (Uburundi)",
38889         "bi",
38890         "257"
38891       ],
38892       [
38893         "Cambodia (កម្ពុជា)",
38894         "kh",
38895         "855"
38896       ],
38897       [
38898         "Cameroon (Cameroun)",
38899         "cm",
38900         "237"
38901       ],
38902       [
38903         "Canada",
38904         "ca",
38905         "1",
38906         1,
38907         ["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"]
38908       ],
38909       [
38910         "Cape Verde (Kabu Verdi)",
38911         "cv",
38912         "238"
38913       ],
38914       [
38915         "Caribbean Netherlands",
38916         "bq",
38917         "599",
38918         1
38919       ],
38920       [
38921         "Cayman Islands",
38922         "ky",
38923         "1345"
38924       ],
38925       [
38926         "Central African Republic (République centrafricaine)",
38927         "cf",
38928         "236"
38929       ],
38930       [
38931         "Chad (Tchad)",
38932         "td",
38933         "235"
38934       ],
38935       [
38936         "Chile",
38937         "cl",
38938         "56"
38939       ],
38940       [
38941         "China (中国)",
38942         "cn",
38943         "86"
38944       ],
38945       [
38946         "Christmas Island",
38947         "cx",
38948         "61",
38949         2
38950       ],
38951       [
38952         "Cocos (Keeling) Islands",
38953         "cc",
38954         "61",
38955         1
38956       ],
38957       [
38958         "Colombia",
38959         "co",
38960         "57"
38961       ],
38962       [
38963         "Comoros (‫جزر القمر‬‎)",
38964         "km",
38965         "269"
38966       ],
38967       [
38968         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38969         "cd",
38970         "243"
38971       ],
38972       [
38973         "Congo (Republic) (Congo-Brazzaville)",
38974         "cg",
38975         "242"
38976       ],
38977       [
38978         "Cook Islands",
38979         "ck",
38980         "682"
38981       ],
38982       [
38983         "Costa Rica",
38984         "cr",
38985         "506"
38986       ],
38987       [
38988         "Côte d’Ivoire",
38989         "ci",
38990         "225"
38991       ],
38992       [
38993         "Croatia (Hrvatska)",
38994         "hr",
38995         "385"
38996       ],
38997       [
38998         "Cuba",
38999         "cu",
39000         "53"
39001       ],
39002       [
39003         "Curaçao",
39004         "cw",
39005         "599",
39006         0
39007       ],
39008       [
39009         "Cyprus (Κύπρος)",
39010         "cy",
39011         "357"
39012       ],
39013       [
39014         "Czech Republic (Česká republika)",
39015         "cz",
39016         "420"
39017       ],
39018       [
39019         "Denmark (Danmark)",
39020         "dk",
39021         "45"
39022       ],
39023       [
39024         "Djibouti",
39025         "dj",
39026         "253"
39027       ],
39028       [
39029         "Dominica",
39030         "dm",
39031         "1767"
39032       ],
39033       [
39034         "Dominican Republic (República Dominicana)",
39035         "do",
39036         "1",
39037         2,
39038         ["809", "829", "849"]
39039       ],
39040       [
39041         "Ecuador",
39042         "ec",
39043         "593"
39044       ],
39045       [
39046         "Egypt (‫مصر‬‎)",
39047         "eg",
39048         "20"
39049       ],
39050       [
39051         "El Salvador",
39052         "sv",
39053         "503"
39054       ],
39055       [
39056         "Equatorial Guinea (Guinea Ecuatorial)",
39057         "gq",
39058         "240"
39059       ],
39060       [
39061         "Eritrea",
39062         "er",
39063         "291"
39064       ],
39065       [
39066         "Estonia (Eesti)",
39067         "ee",
39068         "372"
39069       ],
39070       [
39071         "Ethiopia",
39072         "et",
39073         "251"
39074       ],
39075       [
39076         "Falkland Islands (Islas Malvinas)",
39077         "fk",
39078         "500"
39079       ],
39080       [
39081         "Faroe Islands (Føroyar)",
39082         "fo",
39083         "298"
39084       ],
39085       [
39086         "Fiji",
39087         "fj",
39088         "679"
39089       ],
39090       [
39091         "Finland (Suomi)",
39092         "fi",
39093         "358",
39094         0
39095       ],
39096       [
39097         "France",
39098         "fr",
39099         "33"
39100       ],
39101       [
39102         "French Guiana (Guyane française)",
39103         "gf",
39104         "594"
39105       ],
39106       [
39107         "French Polynesia (Polynésie française)",
39108         "pf",
39109         "689"
39110       ],
39111       [
39112         "Gabon",
39113         "ga",
39114         "241"
39115       ],
39116       [
39117         "Gambia",
39118         "gm",
39119         "220"
39120       ],
39121       [
39122         "Georgia (საქართველო)",
39123         "ge",
39124         "995"
39125       ],
39126       [
39127         "Germany (Deutschland)",
39128         "de",
39129         "49"
39130       ],
39131       [
39132         "Ghana (Gaana)",
39133         "gh",
39134         "233"
39135       ],
39136       [
39137         "Gibraltar",
39138         "gi",
39139         "350"
39140       ],
39141       [
39142         "Greece (Ελλάδα)",
39143         "gr",
39144         "30"
39145       ],
39146       [
39147         "Greenland (Kalaallit Nunaat)",
39148         "gl",
39149         "299"
39150       ],
39151       [
39152         "Grenada",
39153         "gd",
39154         "1473"
39155       ],
39156       [
39157         "Guadeloupe",
39158         "gp",
39159         "590",
39160         0
39161       ],
39162       [
39163         "Guam",
39164         "gu",
39165         "1671"
39166       ],
39167       [
39168         "Guatemala",
39169         "gt",
39170         "502"
39171       ],
39172       [
39173         "Guernsey",
39174         "gg",
39175         "44",
39176         1
39177       ],
39178       [
39179         "Guinea (Guinée)",
39180         "gn",
39181         "224"
39182       ],
39183       [
39184         "Guinea-Bissau (Guiné Bissau)",
39185         "gw",
39186         "245"
39187       ],
39188       [
39189         "Guyana",
39190         "gy",
39191         "592"
39192       ],
39193       [
39194         "Haiti",
39195         "ht",
39196         "509"
39197       ],
39198       [
39199         "Honduras",
39200         "hn",
39201         "504"
39202       ],
39203       [
39204         "Hong Kong (香港)",
39205         "hk",
39206         "852"
39207       ],
39208       [
39209         "Hungary (Magyarország)",
39210         "hu",
39211         "36"
39212       ],
39213       [
39214         "Iceland (Ísland)",
39215         "is",
39216         "354"
39217       ],
39218       [
39219         "India (भारत)",
39220         "in",
39221         "91"
39222       ],
39223       [
39224         "Indonesia",
39225         "id",
39226         "62"
39227       ],
39228       [
39229         "Iran (‫ایران‬‎)",
39230         "ir",
39231         "98"
39232       ],
39233       [
39234         "Iraq (‫العراق‬‎)",
39235         "iq",
39236         "964"
39237       ],
39238       [
39239         "Ireland",
39240         "ie",
39241         "353"
39242       ],
39243       [
39244         "Isle of Man",
39245         "im",
39246         "44",
39247         2
39248       ],
39249       [
39250         "Israel (‫ישראל‬‎)",
39251         "il",
39252         "972"
39253       ],
39254       [
39255         "Italy (Italia)",
39256         "it",
39257         "39",
39258         0
39259       ],
39260       [
39261         "Jamaica",
39262         "jm",
39263         "1876"
39264       ],
39265       [
39266         "Japan (日本)",
39267         "jp",
39268         "81"
39269       ],
39270       [
39271         "Jersey",
39272         "je",
39273         "44",
39274         3
39275       ],
39276       [
39277         "Jordan (‫الأردن‬‎)",
39278         "jo",
39279         "962"
39280       ],
39281       [
39282         "Kazakhstan (Казахстан)",
39283         "kz",
39284         "7",
39285         1
39286       ],
39287       [
39288         "Kenya",
39289         "ke",
39290         "254"
39291       ],
39292       [
39293         "Kiribati",
39294         "ki",
39295         "686"
39296       ],
39297       [
39298         "Kosovo",
39299         "xk",
39300         "383"
39301       ],
39302       [
39303         "Kuwait (‫الكويت‬‎)",
39304         "kw",
39305         "965"
39306       ],
39307       [
39308         "Kyrgyzstan (Кыргызстан)",
39309         "kg",
39310         "996"
39311       ],
39312       [
39313         "Laos (ລາວ)",
39314         "la",
39315         "856"
39316       ],
39317       [
39318         "Latvia (Latvija)",
39319         "lv",
39320         "371"
39321       ],
39322       [
39323         "Lebanon (‫لبنان‬‎)",
39324         "lb",
39325         "961"
39326       ],
39327       [
39328         "Lesotho",
39329         "ls",
39330         "266"
39331       ],
39332       [
39333         "Liberia",
39334         "lr",
39335         "231"
39336       ],
39337       [
39338         "Libya (‫ليبيا‬‎)",
39339         "ly",
39340         "218"
39341       ],
39342       [
39343         "Liechtenstein",
39344         "li",
39345         "423"
39346       ],
39347       [
39348         "Lithuania (Lietuva)",
39349         "lt",
39350         "370"
39351       ],
39352       [
39353         "Luxembourg",
39354         "lu",
39355         "352"
39356       ],
39357       [
39358         "Macau (澳門)",
39359         "mo",
39360         "853"
39361       ],
39362       [
39363         "Macedonia (FYROM) (Македонија)",
39364         "mk",
39365         "389"
39366       ],
39367       [
39368         "Madagascar (Madagasikara)",
39369         "mg",
39370         "261"
39371       ],
39372       [
39373         "Malawi",
39374         "mw",
39375         "265"
39376       ],
39377       [
39378         "Malaysia",
39379         "my",
39380         "60"
39381       ],
39382       [
39383         "Maldives",
39384         "mv",
39385         "960"
39386       ],
39387       [
39388         "Mali",
39389         "ml",
39390         "223"
39391       ],
39392       [
39393         "Malta",
39394         "mt",
39395         "356"
39396       ],
39397       [
39398         "Marshall Islands",
39399         "mh",
39400         "692"
39401       ],
39402       [
39403         "Martinique",
39404         "mq",
39405         "596"
39406       ],
39407       [
39408         "Mauritania (‫موريتانيا‬‎)",
39409         "mr",
39410         "222"
39411       ],
39412       [
39413         "Mauritius (Moris)",
39414         "mu",
39415         "230"
39416       ],
39417       [
39418         "Mayotte",
39419         "yt",
39420         "262",
39421         1
39422       ],
39423       [
39424         "Mexico (México)",
39425         "mx",
39426         "52"
39427       ],
39428       [
39429         "Micronesia",
39430         "fm",
39431         "691"
39432       ],
39433       [
39434         "Moldova (Republica Moldova)",
39435         "md",
39436         "373"
39437       ],
39438       [
39439         "Monaco",
39440         "mc",
39441         "377"
39442       ],
39443       [
39444         "Mongolia (Монгол)",
39445         "mn",
39446         "976"
39447       ],
39448       [
39449         "Montenegro (Crna Gora)",
39450         "me",
39451         "382"
39452       ],
39453       [
39454         "Montserrat",
39455         "ms",
39456         "1664"
39457       ],
39458       [
39459         "Morocco (‫المغرب‬‎)",
39460         "ma",
39461         "212",
39462         0
39463       ],
39464       [
39465         "Mozambique (Moçambique)",
39466         "mz",
39467         "258"
39468       ],
39469       [
39470         "Myanmar (Burma) (မြန်မာ)",
39471         "mm",
39472         "95"
39473       ],
39474       [
39475         "Namibia (Namibië)",
39476         "na",
39477         "264"
39478       ],
39479       [
39480         "Nauru",
39481         "nr",
39482         "674"
39483       ],
39484       [
39485         "Nepal (नेपाल)",
39486         "np",
39487         "977"
39488       ],
39489       [
39490         "Netherlands (Nederland)",
39491         "nl",
39492         "31"
39493       ],
39494       [
39495         "New Caledonia (Nouvelle-Calédonie)",
39496         "nc",
39497         "687"
39498       ],
39499       [
39500         "New Zealand",
39501         "nz",
39502         "64"
39503       ],
39504       [
39505         "Nicaragua",
39506         "ni",
39507         "505"
39508       ],
39509       [
39510         "Niger (Nijar)",
39511         "ne",
39512         "227"
39513       ],
39514       [
39515         "Nigeria",
39516         "ng",
39517         "234"
39518       ],
39519       [
39520         "Niue",
39521         "nu",
39522         "683"
39523       ],
39524       [
39525         "Norfolk Island",
39526         "nf",
39527         "672"
39528       ],
39529       [
39530         "North Korea (조선 민주주의 인민 공화국)",
39531         "kp",
39532         "850"
39533       ],
39534       [
39535         "Northern Mariana Islands",
39536         "mp",
39537         "1670"
39538       ],
39539       [
39540         "Norway (Norge)",
39541         "no",
39542         "47",
39543         0
39544       ],
39545       [
39546         "Oman (‫عُمان‬‎)",
39547         "om",
39548         "968"
39549       ],
39550       [
39551         "Pakistan (‫پاکستان‬‎)",
39552         "pk",
39553         "92"
39554       ],
39555       [
39556         "Palau",
39557         "pw",
39558         "680"
39559       ],
39560       [
39561         "Palestine (‫فلسطين‬‎)",
39562         "ps",
39563         "970"
39564       ],
39565       [
39566         "Panama (Panamá)",
39567         "pa",
39568         "507"
39569       ],
39570       [
39571         "Papua New Guinea",
39572         "pg",
39573         "675"
39574       ],
39575       [
39576         "Paraguay",
39577         "py",
39578         "595"
39579       ],
39580       [
39581         "Peru (Perú)",
39582         "pe",
39583         "51"
39584       ],
39585       [
39586         "Philippines",
39587         "ph",
39588         "63"
39589       ],
39590       [
39591         "Poland (Polska)",
39592         "pl",
39593         "48"
39594       ],
39595       [
39596         "Portugal",
39597         "pt",
39598         "351"
39599       ],
39600       [
39601         "Puerto Rico",
39602         "pr",
39603         "1",
39604         3,
39605         ["787", "939"]
39606       ],
39607       [
39608         "Qatar (‫قطر‬‎)",
39609         "qa",
39610         "974"
39611       ],
39612       [
39613         "Réunion (La Réunion)",
39614         "re",
39615         "262",
39616         0
39617       ],
39618       [
39619         "Romania (România)",
39620         "ro",
39621         "40"
39622       ],
39623       [
39624         "Russia (Россия)",
39625         "ru",
39626         "7",
39627         0
39628       ],
39629       [
39630         "Rwanda",
39631         "rw",
39632         "250"
39633       ],
39634       [
39635         "Saint Barthélemy",
39636         "bl",
39637         "590",
39638         1
39639       ],
39640       [
39641         "Saint Helena",
39642         "sh",
39643         "290"
39644       ],
39645       [
39646         "Saint Kitts and Nevis",
39647         "kn",
39648         "1869"
39649       ],
39650       [
39651         "Saint Lucia",
39652         "lc",
39653         "1758"
39654       ],
39655       [
39656         "Saint Martin (Saint-Martin (partie française))",
39657         "mf",
39658         "590",
39659         2
39660       ],
39661       [
39662         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39663         "pm",
39664         "508"
39665       ],
39666       [
39667         "Saint Vincent and the Grenadines",
39668         "vc",
39669         "1784"
39670       ],
39671       [
39672         "Samoa",
39673         "ws",
39674         "685"
39675       ],
39676       [
39677         "San Marino",
39678         "sm",
39679         "378"
39680       ],
39681       [
39682         "São Tomé and Príncipe (São Tomé e Príncipe)",
39683         "st",
39684         "239"
39685       ],
39686       [
39687         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39688         "sa",
39689         "966"
39690       ],
39691       [
39692         "Senegal (Sénégal)",
39693         "sn",
39694         "221"
39695       ],
39696       [
39697         "Serbia (Србија)",
39698         "rs",
39699         "381"
39700       ],
39701       [
39702         "Seychelles",
39703         "sc",
39704         "248"
39705       ],
39706       [
39707         "Sierra Leone",
39708         "sl",
39709         "232"
39710       ],
39711       [
39712         "Singapore",
39713         "sg",
39714         "65"
39715       ],
39716       [
39717         "Sint Maarten",
39718         "sx",
39719         "1721"
39720       ],
39721       [
39722         "Slovakia (Slovensko)",
39723         "sk",
39724         "421"
39725       ],
39726       [
39727         "Slovenia (Slovenija)",
39728         "si",
39729         "386"
39730       ],
39731       [
39732         "Solomon Islands",
39733         "sb",
39734         "677"
39735       ],
39736       [
39737         "Somalia (Soomaaliya)",
39738         "so",
39739         "252"
39740       ],
39741       [
39742         "South Africa",
39743         "za",
39744         "27"
39745       ],
39746       [
39747         "South Korea (대한민국)",
39748         "kr",
39749         "82"
39750       ],
39751       [
39752         "South Sudan (‫جنوب السودان‬‎)",
39753         "ss",
39754         "211"
39755       ],
39756       [
39757         "Spain (España)",
39758         "es",
39759         "34"
39760       ],
39761       [
39762         "Sri Lanka (ශ්‍රී ලංකාව)",
39763         "lk",
39764         "94"
39765       ],
39766       [
39767         "Sudan (‫السودان‬‎)",
39768         "sd",
39769         "249"
39770       ],
39771       [
39772         "Suriname",
39773         "sr",
39774         "597"
39775       ],
39776       [
39777         "Svalbard and Jan Mayen",
39778         "sj",
39779         "47",
39780         1
39781       ],
39782       [
39783         "Swaziland",
39784         "sz",
39785         "268"
39786       ],
39787       [
39788         "Sweden (Sverige)",
39789         "se",
39790         "46"
39791       ],
39792       [
39793         "Switzerland (Schweiz)",
39794         "ch",
39795         "41"
39796       ],
39797       [
39798         "Syria (‫سوريا‬‎)",
39799         "sy",
39800         "963"
39801       ],
39802       [
39803         "Taiwan (台灣)",
39804         "tw",
39805         "886"
39806       ],
39807       [
39808         "Tajikistan",
39809         "tj",
39810         "992"
39811       ],
39812       [
39813         "Tanzania",
39814         "tz",
39815         "255"
39816       ],
39817       [
39818         "Thailand (ไทย)",
39819         "th",
39820         "66"
39821       ],
39822       [
39823         "Timor-Leste",
39824         "tl",
39825         "670"
39826       ],
39827       [
39828         "Togo",
39829         "tg",
39830         "228"
39831       ],
39832       [
39833         "Tokelau",
39834         "tk",
39835         "690"
39836       ],
39837       [
39838         "Tonga",
39839         "to",
39840         "676"
39841       ],
39842       [
39843         "Trinidad and Tobago",
39844         "tt",
39845         "1868"
39846       ],
39847       [
39848         "Tunisia (‫تونس‬‎)",
39849         "tn",
39850         "216"
39851       ],
39852       [
39853         "Turkey (Türkiye)",
39854         "tr",
39855         "90"
39856       ],
39857       [
39858         "Turkmenistan",
39859         "tm",
39860         "993"
39861       ],
39862       [
39863         "Turks and Caicos Islands",
39864         "tc",
39865         "1649"
39866       ],
39867       [
39868         "Tuvalu",
39869         "tv",
39870         "688"
39871       ],
39872       [
39873         "U.S. Virgin Islands",
39874         "vi",
39875         "1340"
39876       ],
39877       [
39878         "Uganda",
39879         "ug",
39880         "256"
39881       ],
39882       [
39883         "Ukraine (Україна)",
39884         "ua",
39885         "380"
39886       ],
39887       [
39888         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39889         "ae",
39890         "971"
39891       ],
39892       [
39893         "United Kingdom",
39894         "gb",
39895         "44",
39896         0
39897       ],
39898       [
39899         "United States",
39900         "us",
39901         "1",
39902         0
39903       ],
39904       [
39905         "Uruguay",
39906         "uy",
39907         "598"
39908       ],
39909       [
39910         "Uzbekistan (Oʻzbekiston)",
39911         "uz",
39912         "998"
39913       ],
39914       [
39915         "Vanuatu",
39916         "vu",
39917         "678"
39918       ],
39919       [
39920         "Vatican City (Città del Vaticano)",
39921         "va",
39922         "39",
39923         1
39924       ],
39925       [
39926         "Venezuela",
39927         "ve",
39928         "58"
39929       ],
39930       [
39931         "Vietnam (Việt Nam)",
39932         "vn",
39933         "84"
39934       ],
39935       [
39936         "Wallis and Futuna (Wallis-et-Futuna)",
39937         "wf",
39938         "681"
39939       ],
39940       [
39941         "Western Sahara (‫الصحراء الغربية‬‎)",
39942         "eh",
39943         "212",
39944         1
39945       ],
39946       [
39947         "Yemen (‫اليمن‬‎)",
39948         "ye",
39949         "967"
39950       ],
39951       [
39952         "Zambia",
39953         "zm",
39954         "260"
39955       ],
39956       [
39957         "Zimbabwe",
39958         "zw",
39959         "263"
39960       ],
39961       [
39962         "Åland Islands",
39963         "ax",
39964         "358",
39965         1
39966       ]
39967   ];
39968   
39969   return d;
39970 }/**
39971 *    This script refer to:
39972 *    Title: International Telephone Input
39973 *    Author: Jack O'Connor
39974 *    Code version:  v12.1.12
39975 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39976 **/
39977
39978 /**
39979  * @class Roo.bootstrap.PhoneInput
39980  * @extends Roo.bootstrap.TriggerField
39981  * An input with International dial-code selection
39982  
39983  * @cfg {String} defaultDialCode default '+852'
39984  * @cfg {Array} preferedCountries default []
39985   
39986  * @constructor
39987  * Create a new PhoneInput.
39988  * @param {Object} config Configuration options
39989  */
39990
39991 Roo.bootstrap.PhoneInput = function(config) {
39992     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39993 };
39994
39995 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39996         
39997         listWidth: undefined,
39998         
39999         selectedClass: 'active',
40000         
40001         invalidClass : "has-warning",
40002         
40003         validClass: 'has-success',
40004         
40005         allowed: '0123456789',
40006         
40007         max_length: 15,
40008         
40009         /**
40010          * @cfg {String} defaultDialCode The default dial code when initializing the input
40011          */
40012         defaultDialCode: '+852',
40013         
40014         /**
40015          * @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
40016          */
40017         preferedCountries: false,
40018         
40019         getAutoCreate : function()
40020         {
40021             var data = Roo.bootstrap.PhoneInputData();
40022             var align = this.labelAlign || this.parentLabelAlign();
40023             var id = Roo.id();
40024             
40025             this.allCountries = [];
40026             this.dialCodeMapping = [];
40027             
40028             for (var i = 0; i < data.length; i++) {
40029               var c = data[i];
40030               this.allCountries[i] = {
40031                 name: c[0],
40032                 iso2: c[1],
40033                 dialCode: c[2],
40034                 priority: c[3] || 0,
40035                 areaCodes: c[4] || null
40036               };
40037               this.dialCodeMapping[c[2]] = {
40038                   name: c[0],
40039                   iso2: c[1],
40040                   priority: c[3] || 0,
40041                   areaCodes: c[4] || null
40042               };
40043             }
40044             
40045             var cfg = {
40046                 cls: 'form-group',
40047                 cn: []
40048             };
40049             
40050             var input =  {
40051                 tag: 'input',
40052                 id : id,
40053                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40054                 maxlength: this.max_length,
40055                 cls : 'form-control tel-input',
40056                 autocomplete: 'new-password'
40057             };
40058             
40059             var hiddenInput = {
40060                 tag: 'input',
40061                 type: 'hidden',
40062                 cls: 'hidden-tel-input'
40063             };
40064             
40065             if (this.name) {
40066                 hiddenInput.name = this.name;
40067             }
40068             
40069             if (this.disabled) {
40070                 input.disabled = true;
40071             }
40072             
40073             var flag_container = {
40074                 tag: 'div',
40075                 cls: 'flag-box',
40076                 cn: [
40077                     {
40078                         tag: 'div',
40079                         cls: 'flag'
40080                     },
40081                     {
40082                         tag: 'div',
40083                         cls: 'caret'
40084                     }
40085                 ]
40086             };
40087             
40088             var box = {
40089                 tag: 'div',
40090                 cls: this.hasFeedback ? 'has-feedback' : '',
40091                 cn: [
40092                     hiddenInput,
40093                     input,
40094                     {
40095                         tag: 'input',
40096                         cls: 'dial-code-holder',
40097                         disabled: true
40098                     }
40099                 ]
40100             };
40101             
40102             var container = {
40103                 cls: 'roo-select2-container input-group',
40104                 cn: [
40105                     flag_container,
40106                     box
40107                 ]
40108             };
40109             
40110             if (this.fieldLabel.length) {
40111                 var indicator = {
40112                     tag: 'i',
40113                     tooltip: 'This field is required'
40114                 };
40115                 
40116                 var label = {
40117                     tag: 'label',
40118                     'for':  id,
40119                     cls: 'control-label',
40120                     cn: []
40121                 };
40122                 
40123                 var label_text = {
40124                     tag: 'span',
40125                     html: this.fieldLabel
40126                 };
40127                 
40128                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40129                 label.cn = [
40130                     indicator,
40131                     label_text
40132                 ];
40133                 
40134                 if(this.indicatorpos == 'right') {
40135                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40136                     label.cn = [
40137                         label_text,
40138                         indicator
40139                     ];
40140                 }
40141                 
40142                 if(align == 'left') {
40143                     container = {
40144                         tag: 'div',
40145                         cn: [
40146                             container
40147                         ]
40148                     };
40149                     
40150                     if(this.labelWidth > 12){
40151                         label.style = "width: " + this.labelWidth + 'px';
40152                     }
40153                     if(this.labelWidth < 13 && this.labelmd == 0){
40154                         this.labelmd = this.labelWidth;
40155                     }
40156                     if(this.labellg > 0){
40157                         label.cls += ' col-lg-' + this.labellg;
40158                         input.cls += ' col-lg-' + (12 - this.labellg);
40159                     }
40160                     if(this.labelmd > 0){
40161                         label.cls += ' col-md-' + this.labelmd;
40162                         container.cls += ' col-md-' + (12 - this.labelmd);
40163                     }
40164                     if(this.labelsm > 0){
40165                         label.cls += ' col-sm-' + this.labelsm;
40166                         container.cls += ' col-sm-' + (12 - this.labelsm);
40167                     }
40168                     if(this.labelxs > 0){
40169                         label.cls += ' col-xs-' + this.labelxs;
40170                         container.cls += ' col-xs-' + (12 - this.labelxs);
40171                     }
40172                 }
40173             }
40174             
40175             cfg.cn = [
40176                 label,
40177                 container
40178             ];
40179             
40180             var settings = this;
40181             
40182             ['xs','sm','md','lg'].map(function(size){
40183                 if (settings[size]) {
40184                     cfg.cls += ' col-' + size + '-' + settings[size];
40185                 }
40186             });
40187             
40188             this.store = new Roo.data.Store({
40189                 proxy : new Roo.data.MemoryProxy({}),
40190                 reader : new Roo.data.JsonReader({
40191                     fields : [
40192                         {
40193                             'name' : 'name',
40194                             'type' : 'string'
40195                         },
40196                         {
40197                             'name' : 'iso2',
40198                             'type' : 'string'
40199                         },
40200                         {
40201                             'name' : 'dialCode',
40202                             'type' : 'string'
40203                         },
40204                         {
40205                             'name' : 'priority',
40206                             'type' : 'string'
40207                         },
40208                         {
40209                             'name' : 'areaCodes',
40210                             'type' : 'string'
40211                         }
40212                     ]
40213                 })
40214             });
40215             
40216             if(!this.preferedCountries) {
40217                 this.preferedCountries = [
40218                     'hk',
40219                     'gb',
40220                     'us'
40221                 ];
40222             }
40223             
40224             var p = this.preferedCountries.reverse();
40225             
40226             if(p) {
40227                 for (var i = 0; i < p.length; i++) {
40228                     for (var j = 0; j < this.allCountries.length; j++) {
40229                         if(this.allCountries[j].iso2 == p[i]) {
40230                             var t = this.allCountries[j];
40231                             this.allCountries.splice(j,1);
40232                             this.allCountries.unshift(t);
40233                         }
40234                     } 
40235                 }
40236             }
40237             
40238             this.store.proxy.data = {
40239                 success: true,
40240                 data: this.allCountries
40241             };
40242             
40243             return cfg;
40244         },
40245         
40246         initEvents : function()
40247         {
40248             this.createList();
40249             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40250             
40251             this.indicator = this.indicatorEl();
40252             this.flag = this.flagEl();
40253             this.dialCodeHolder = this.dialCodeHolderEl();
40254             
40255             this.trigger = this.el.select('div.flag-box',true).first();
40256             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40257             
40258             var _this = this;
40259             
40260             (function(){
40261                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40262                 _this.list.setWidth(lw);
40263             }).defer(100);
40264             
40265             this.list.on('mouseover', this.onViewOver, this);
40266             this.list.on('mousemove', this.onViewMove, this);
40267             this.inputEl().on("keyup", this.onKeyUp, this);
40268             this.inputEl().on("keypress", this.onKeyPress, this);
40269             
40270             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40271
40272             this.view = new Roo.View(this.list, this.tpl, {
40273                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40274             });
40275             
40276             this.view.on('click', this.onViewClick, this);
40277             this.setValue(this.defaultDialCode);
40278         },
40279         
40280         onTriggerClick : function(e)
40281         {
40282             Roo.log('trigger click');
40283             if(this.disabled){
40284                 return;
40285             }
40286             
40287             if(this.isExpanded()){
40288                 this.collapse();
40289                 this.hasFocus = false;
40290             }else {
40291                 this.store.load({});
40292                 this.hasFocus = true;
40293                 this.expand();
40294             }
40295         },
40296         
40297         isExpanded : function()
40298         {
40299             return this.list.isVisible();
40300         },
40301         
40302         collapse : function()
40303         {
40304             if(!this.isExpanded()){
40305                 return;
40306             }
40307             this.list.hide();
40308             Roo.get(document).un('mousedown', this.collapseIf, this);
40309             Roo.get(document).un('mousewheel', this.collapseIf, this);
40310             this.fireEvent('collapse', this);
40311             this.validate();
40312         },
40313         
40314         expand : function()
40315         {
40316             Roo.log('expand');
40317
40318             if(this.isExpanded() || !this.hasFocus){
40319                 return;
40320             }
40321             
40322             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40323             this.list.setWidth(lw);
40324             
40325             this.list.show();
40326             this.restrictHeight();
40327             
40328             Roo.get(document).on('mousedown', this.collapseIf, this);
40329             Roo.get(document).on('mousewheel', this.collapseIf, this);
40330             
40331             this.fireEvent('expand', this);
40332         },
40333         
40334         restrictHeight : function()
40335         {
40336             this.list.alignTo(this.inputEl(), this.listAlign);
40337             this.list.alignTo(this.inputEl(), this.listAlign);
40338         },
40339         
40340         onViewOver : function(e, t)
40341         {
40342             if(this.inKeyMode){
40343                 return;
40344             }
40345             var item = this.view.findItemFromChild(t);
40346             
40347             if(item){
40348                 var index = this.view.indexOf(item);
40349                 this.select(index, false);
40350             }
40351         },
40352
40353         // private
40354         onViewClick : function(view, doFocus, el, e)
40355         {
40356             var index = this.view.getSelectedIndexes()[0];
40357             
40358             var r = this.store.getAt(index);
40359             
40360             if(r){
40361                 this.onSelect(r, index);
40362             }
40363             if(doFocus !== false && !this.blockFocus){
40364                 this.inputEl().focus();
40365             }
40366         },
40367         
40368         onViewMove : function(e, t)
40369         {
40370             this.inKeyMode = false;
40371         },
40372         
40373         select : function(index, scrollIntoView)
40374         {
40375             this.selectedIndex = index;
40376             this.view.select(index);
40377             if(scrollIntoView !== false){
40378                 var el = this.view.getNode(index);
40379                 if(el){
40380                     this.list.scrollChildIntoView(el, false);
40381                 }
40382             }
40383         },
40384         
40385         createList : function()
40386         {
40387             this.list = Roo.get(document.body).createChild({
40388                 tag: 'ul',
40389                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40390                 style: 'display:none'
40391             });
40392             
40393             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40394         },
40395         
40396         collapseIf : function(e)
40397         {
40398             var in_combo  = e.within(this.el);
40399             var in_list =  e.within(this.list);
40400             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40401             
40402             if (in_combo || in_list || is_list) {
40403                 return;
40404             }
40405             this.collapse();
40406         },
40407         
40408         onSelect : function(record, index)
40409         {
40410             if(this.fireEvent('beforeselect', this, record, index) !== false){
40411                 
40412                 this.setFlagClass(record.data.iso2);
40413                 this.setDialCode(record.data.dialCode);
40414                 this.hasFocus = false;
40415                 this.collapse();
40416                 this.fireEvent('select', this, record, index);
40417             }
40418         },
40419         
40420         flagEl : function()
40421         {
40422             var flag = this.el.select('div.flag',true).first();
40423             if(!flag){
40424                 return false;
40425             }
40426             return flag;
40427         },
40428         
40429         dialCodeHolderEl : function()
40430         {
40431             var d = this.el.select('input.dial-code-holder',true).first();
40432             if(!d){
40433                 return false;
40434             }
40435             return d;
40436         },
40437         
40438         setDialCode : function(v)
40439         {
40440             this.dialCodeHolder.dom.value = '+'+v;
40441         },
40442         
40443         setFlagClass : function(n)
40444         {
40445             this.flag.dom.className = 'flag '+n;
40446         },
40447         
40448         getValue : function()
40449         {
40450             var v = this.inputEl().getValue();
40451             if(this.dialCodeHolder) {
40452                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40453             }
40454             return v;
40455         },
40456         
40457         setValue : function(v)
40458         {
40459             var d = this.getDialCode(v);
40460             
40461             //invalid dial code
40462             if(v.length == 0 || !d || d.length == 0) {
40463                 if(this.rendered){
40464                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40465                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40466                 }
40467                 return;
40468             }
40469             
40470             //valid dial code
40471             this.setFlagClass(this.dialCodeMapping[d].iso2);
40472             this.setDialCode(d);
40473             this.inputEl().dom.value = v.replace('+'+d,'');
40474             this.hiddenEl().dom.value = this.getValue();
40475             
40476             this.validate();
40477         },
40478         
40479         getDialCode : function(v)
40480         {
40481             v = v ||  '';
40482             
40483             if (v.length == 0) {
40484                 return this.dialCodeHolder.dom.value;
40485             }
40486             
40487             var dialCode = "";
40488             if (v.charAt(0) != "+") {
40489                 return false;
40490             }
40491             var numericChars = "";
40492             for (var i = 1; i < v.length; i++) {
40493               var c = v.charAt(i);
40494               if (!isNaN(c)) {
40495                 numericChars += c;
40496                 if (this.dialCodeMapping[numericChars]) {
40497                   dialCode = v.substr(1, i);
40498                 }
40499                 if (numericChars.length == 4) {
40500                   break;
40501                 }
40502               }
40503             }
40504             return dialCode;
40505         },
40506         
40507         reset : function()
40508         {
40509             this.setValue(this.defaultDialCode);
40510             this.validate();
40511         },
40512         
40513         hiddenEl : function()
40514         {
40515             return this.el.select('input.hidden-tel-input',true).first();
40516         },
40517         
40518         // after setting val
40519         onKeyUp : function(e){
40520             this.setValue(this.getValue());
40521         },
40522         
40523         onKeyPress : function(e){
40524             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40525                 e.stopEvent();
40526             }
40527         }
40528         
40529 });
40530 /**
40531  * @class Roo.bootstrap.MoneyField
40532  * @extends Roo.bootstrap.ComboBox
40533  * Bootstrap MoneyField class
40534  * 
40535  * @constructor
40536  * Create a new MoneyField.
40537  * @param {Object} config Configuration options
40538  */
40539
40540 Roo.bootstrap.MoneyField = function(config) {
40541     
40542     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40543     
40544 };
40545
40546 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40547     
40548     /**
40549      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40550      */
40551     allowDecimals : true,
40552     /**
40553      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40554      */
40555     decimalSeparator : ".",
40556     /**
40557      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40558      */
40559     decimalPrecision : 0,
40560     /**
40561      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40562      */
40563     allowNegative : true,
40564     /**
40565      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40566      */
40567     allowZero: true,
40568     /**
40569      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40570      */
40571     minValue : Number.NEGATIVE_INFINITY,
40572     /**
40573      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40574      */
40575     maxValue : Number.MAX_VALUE,
40576     /**
40577      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40578      */
40579     minText : "The minimum value for this field is {0}",
40580     /**
40581      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40582      */
40583     maxText : "The maximum value for this field is {0}",
40584     /**
40585      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40586      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40587      */
40588     nanText : "{0} is not a valid number",
40589     /**
40590      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40591      */
40592     castInt : true,
40593     /**
40594      * @cfg {String} defaults currency of the MoneyField
40595      * value should be in lkey
40596      */
40597     defaultCurrency : false,
40598     /**
40599      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40600      */
40601     thousandsDelimiter : false,
40602     /**
40603      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40604      */
40605     max_length: false,
40606     
40607     inputlg : 9,
40608     inputmd : 9,
40609     inputsm : 9,
40610     inputxs : 6,
40611     
40612     store : false,
40613     
40614     getAutoCreate : function()
40615     {
40616         var align = this.labelAlign || this.parentLabelAlign();
40617         
40618         var id = Roo.id();
40619
40620         var cfg = {
40621             cls: 'form-group',
40622             cn: []
40623         };
40624
40625         var input =  {
40626             tag: 'input',
40627             id : id,
40628             cls : 'form-control roo-money-amount-input',
40629             autocomplete: 'new-password'
40630         };
40631         
40632         var hiddenInput = {
40633             tag: 'input',
40634             type: 'hidden',
40635             id: Roo.id(),
40636             cls: 'hidden-number-input'
40637         };
40638         
40639         if(this.max_length) {
40640             input.maxlength = this.max_length; 
40641         }
40642         
40643         if (this.name) {
40644             hiddenInput.name = this.name;
40645         }
40646
40647         if (this.disabled) {
40648             input.disabled = true;
40649         }
40650
40651         var clg = 12 - this.inputlg;
40652         var cmd = 12 - this.inputmd;
40653         var csm = 12 - this.inputsm;
40654         var cxs = 12 - this.inputxs;
40655         
40656         var container = {
40657             tag : 'div',
40658             cls : 'row roo-money-field',
40659             cn : [
40660                 {
40661                     tag : 'div',
40662                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40663                     cn : [
40664                         {
40665                             tag : 'div',
40666                             cls: 'roo-select2-container input-group',
40667                             cn: [
40668                                 {
40669                                     tag : 'input',
40670                                     cls : 'form-control roo-money-currency-input',
40671                                     autocomplete: 'new-password',
40672                                     readOnly : 1,
40673                                     name : this.currencyName
40674                                 },
40675                                 {
40676                                     tag :'span',
40677                                     cls : 'input-group-addon',
40678                                     cn : [
40679                                         {
40680                                             tag: 'span',
40681                                             cls: 'caret'
40682                                         }
40683                                     ]
40684                                 }
40685                             ]
40686                         }
40687                     ]
40688                 },
40689                 {
40690                     tag : 'div',
40691                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40692                     cn : [
40693                         {
40694                             tag: 'div',
40695                             cls: this.hasFeedback ? 'has-feedback' : '',
40696                             cn: [
40697                                 input
40698                             ]
40699                         }
40700                     ]
40701                 }
40702             ]
40703             
40704         };
40705         
40706         if (this.fieldLabel.length) {
40707             var indicator = {
40708                 tag: 'i',
40709                 tooltip: 'This field is required'
40710             };
40711
40712             var label = {
40713                 tag: 'label',
40714                 'for':  id,
40715                 cls: 'control-label',
40716                 cn: []
40717             };
40718
40719             var label_text = {
40720                 tag: 'span',
40721                 html: this.fieldLabel
40722             };
40723
40724             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40725             label.cn = [
40726                 indicator,
40727                 label_text
40728             ];
40729
40730             if(this.indicatorpos == 'right') {
40731                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40732                 label.cn = [
40733                     label_text,
40734                     indicator
40735                 ];
40736             }
40737
40738             if(align == 'left') {
40739                 container = {
40740                     tag: 'div',
40741                     cn: [
40742                         container
40743                     ]
40744                 };
40745
40746                 if(this.labelWidth > 12){
40747                     label.style = "width: " + this.labelWidth + 'px';
40748                 }
40749                 if(this.labelWidth < 13 && this.labelmd == 0){
40750                     this.labelmd = this.labelWidth;
40751                 }
40752                 if(this.labellg > 0){
40753                     label.cls += ' col-lg-' + this.labellg;
40754                     input.cls += ' col-lg-' + (12 - this.labellg);
40755                 }
40756                 if(this.labelmd > 0){
40757                     label.cls += ' col-md-' + this.labelmd;
40758                     container.cls += ' col-md-' + (12 - this.labelmd);
40759                 }
40760                 if(this.labelsm > 0){
40761                     label.cls += ' col-sm-' + this.labelsm;
40762                     container.cls += ' col-sm-' + (12 - this.labelsm);
40763                 }
40764                 if(this.labelxs > 0){
40765                     label.cls += ' col-xs-' + this.labelxs;
40766                     container.cls += ' col-xs-' + (12 - this.labelxs);
40767                 }
40768             }
40769         }
40770
40771         cfg.cn = [
40772             label,
40773             container,
40774             hiddenInput
40775         ];
40776         
40777         var settings = this;
40778
40779         ['xs','sm','md','lg'].map(function(size){
40780             if (settings[size]) {
40781                 cfg.cls += ' col-' + size + '-' + settings[size];
40782             }
40783         });
40784         
40785         return cfg;
40786     },
40787     
40788     initEvents : function()
40789     {
40790         this.indicator = this.indicatorEl();
40791         
40792         this.initCurrencyEvent();
40793         
40794         this.initNumberEvent();
40795     },
40796     
40797     initCurrencyEvent : function()
40798     {
40799         if (!this.store) {
40800             throw "can not find store for combo";
40801         }
40802         
40803         this.store = Roo.factory(this.store, Roo.data);
40804         this.store.parent = this;
40805         
40806         this.createList();
40807         
40808         this.triggerEl = this.el.select('.input-group-addon', true).first();
40809         
40810         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40811         
40812         var _this = this;
40813         
40814         (function(){
40815             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40816             _this.list.setWidth(lw);
40817         }).defer(100);
40818         
40819         this.list.on('mouseover', this.onViewOver, this);
40820         this.list.on('mousemove', this.onViewMove, this);
40821         this.list.on('scroll', this.onViewScroll, this);
40822         
40823         if(!this.tpl){
40824             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40825         }
40826         
40827         this.view = new Roo.View(this.list, this.tpl, {
40828             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40829         });
40830         
40831         this.view.on('click', this.onViewClick, this);
40832         
40833         this.store.on('beforeload', this.onBeforeLoad, this);
40834         this.store.on('load', this.onLoad, this);
40835         this.store.on('loadexception', this.onLoadException, this);
40836         
40837         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40838             "up" : function(e){
40839                 this.inKeyMode = true;
40840                 this.selectPrev();
40841             },
40842
40843             "down" : function(e){
40844                 if(!this.isExpanded()){
40845                     this.onTriggerClick();
40846                 }else{
40847                     this.inKeyMode = true;
40848                     this.selectNext();
40849                 }
40850             },
40851
40852             "enter" : function(e){
40853                 this.collapse();
40854                 
40855                 if(this.fireEvent("specialkey", this, e)){
40856                     this.onViewClick(false);
40857                 }
40858                 
40859                 return true;
40860             },
40861
40862             "esc" : function(e){
40863                 this.collapse();
40864             },
40865
40866             "tab" : function(e){
40867                 this.collapse();
40868                 
40869                 if(this.fireEvent("specialkey", this, e)){
40870                     this.onViewClick(false);
40871                 }
40872                 
40873                 return true;
40874             },
40875
40876             scope : this,
40877
40878             doRelay : function(foo, bar, hname){
40879                 if(hname == 'down' || this.scope.isExpanded()){
40880                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40881                 }
40882                 return true;
40883             },
40884
40885             forceKeyDown: true
40886         });
40887         
40888         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40889         
40890     },
40891     
40892     initNumberEvent : function(e)
40893     {
40894         this.inputEl().on("keydown" , this.fireKey,  this);
40895         this.inputEl().on("focus", this.onFocus,  this);
40896         this.inputEl().on("blur", this.onBlur,  this);
40897         
40898         this.inputEl().relayEvent('keyup', this);
40899         
40900         if(this.indicator){
40901             this.indicator.addClass('invisible');
40902         }
40903  
40904         this.originalValue = this.getValue();
40905         
40906         if(this.validationEvent == 'keyup'){
40907             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40908             this.inputEl().on('keyup', this.filterValidation, this);
40909         }
40910         else if(this.validationEvent !== false){
40911             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40912         }
40913         
40914         if(this.selectOnFocus){
40915             this.on("focus", this.preFocus, this);
40916             
40917         }
40918         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40919             this.inputEl().on("keypress", this.filterKeys, this);
40920         } else {
40921             this.inputEl().relayEvent('keypress', this);
40922         }
40923         
40924         var allowed = "0123456789";
40925         
40926         if(this.allowDecimals){
40927             allowed += this.decimalSeparator;
40928         }
40929         
40930         if(this.allowNegative){
40931             allowed += "-";
40932         }
40933         
40934         if(this.thousandsDelimiter) {
40935             allowed += ",";
40936         }
40937         
40938         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40939         
40940         var keyPress = function(e){
40941             
40942             var k = e.getKey();
40943             
40944             var c = e.getCharCode();
40945             
40946             if(
40947                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40948                     allowed.indexOf(String.fromCharCode(c)) === -1
40949             ){
40950                 e.stopEvent();
40951                 return;
40952             }
40953             
40954             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40955                 return;
40956             }
40957             
40958             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40959                 e.stopEvent();
40960             }
40961         };
40962         
40963         this.inputEl().on("keypress", keyPress, this);
40964         
40965     },
40966     
40967     onTriggerClick : function(e)
40968     {   
40969         if(this.disabled){
40970             return;
40971         }
40972         
40973         this.page = 0;
40974         this.loadNext = false;
40975         
40976         if(this.isExpanded()){
40977             this.collapse();
40978             return;
40979         }
40980         
40981         this.hasFocus = true;
40982         
40983         if(this.triggerAction == 'all') {
40984             this.doQuery(this.allQuery, true);
40985             return;
40986         }
40987         
40988         this.doQuery(this.getRawValue());
40989     },
40990     
40991     getCurrency : function()
40992     {   
40993         var v = this.currencyEl().getValue();
40994         
40995         return v;
40996     },
40997     
40998     restrictHeight : function()
40999     {
41000         this.list.alignTo(this.currencyEl(), this.listAlign);
41001         this.list.alignTo(this.currencyEl(), this.listAlign);
41002     },
41003     
41004     onViewClick : function(view, doFocus, el, e)
41005     {
41006         var index = this.view.getSelectedIndexes()[0];
41007         
41008         var r = this.store.getAt(index);
41009         
41010         if(r){
41011             this.onSelect(r, index);
41012         }
41013     },
41014     
41015     onSelect : function(record, index){
41016         
41017         if(this.fireEvent('beforeselect', this, record, index) !== false){
41018         
41019             this.setFromCurrencyData(index > -1 ? record.data : false);
41020             
41021             this.collapse();
41022             
41023             this.fireEvent('select', this, record, index);
41024         }
41025     },
41026     
41027     setFromCurrencyData : function(o)
41028     {
41029         var currency = '';
41030         
41031         this.lastCurrency = o;
41032         
41033         if (this.currencyField) {
41034             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41035         } else {
41036             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41037         }
41038         
41039         this.lastSelectionText = currency;
41040         
41041         //setting default currency
41042         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41043             this.setCurrency(this.defaultCurrency);
41044             return;
41045         }
41046         
41047         this.setCurrency(currency);
41048     },
41049     
41050     setFromData : function(o)
41051     {
41052         var c = {};
41053         
41054         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41055         
41056         this.setFromCurrencyData(c);
41057         
41058         var value = '';
41059         
41060         if (this.name) {
41061             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41062         } else {
41063             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41064         }
41065         
41066         this.setValue(value);
41067         
41068     },
41069     
41070     setCurrency : function(v)
41071     {   
41072         this.currencyValue = v;
41073         
41074         if(this.rendered){
41075             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41076             this.validate();
41077         }
41078     },
41079     
41080     setValue : function(v)
41081     {
41082         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41083         
41084         this.value = v;
41085         
41086         if(this.rendered){
41087             
41088             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41089             
41090             this.inputEl().dom.value = (v == '') ? '' :
41091                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41092             
41093             if(!this.allowZero && v === '0') {
41094                 this.hiddenEl().dom.value = '';
41095                 this.inputEl().dom.value = '';
41096             }
41097             
41098             this.validate();
41099         }
41100     },
41101     
41102     getRawValue : function()
41103     {
41104         var v = this.inputEl().getValue();
41105         
41106         return v;
41107     },
41108     
41109     getValue : function()
41110     {
41111         return this.fixPrecision(this.parseValue(this.getRawValue()));
41112     },
41113     
41114     parseValue : function(value)
41115     {
41116         if(this.thousandsDelimiter) {
41117             value += "";
41118             r = new RegExp(",", "g");
41119             value = value.replace(r, "");
41120         }
41121         
41122         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41123         return isNaN(value) ? '' : value;
41124         
41125     },
41126     
41127     fixPrecision : function(value)
41128     {
41129         if(this.thousandsDelimiter) {
41130             value += "";
41131             r = new RegExp(",", "g");
41132             value = value.replace(r, "");
41133         }
41134         
41135         var nan = isNaN(value);
41136         
41137         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41138             return nan ? '' : value;
41139         }
41140         return parseFloat(value).toFixed(this.decimalPrecision);
41141     },
41142     
41143     decimalPrecisionFcn : function(v)
41144     {
41145         return Math.floor(v);
41146     },
41147     
41148     validateValue : function(value)
41149     {
41150         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41151             return false;
41152         }
41153         
41154         var num = this.parseValue(value);
41155         
41156         if(isNaN(num)){
41157             this.markInvalid(String.format(this.nanText, value));
41158             return false;
41159         }
41160         
41161         if(num < this.minValue){
41162             this.markInvalid(String.format(this.minText, this.minValue));
41163             return false;
41164         }
41165         
41166         if(num > this.maxValue){
41167             this.markInvalid(String.format(this.maxText, this.maxValue));
41168             return false;
41169         }
41170         
41171         return true;
41172     },
41173     
41174     validate : function()
41175     {
41176         if(this.disabled || this.allowBlank){
41177             this.markValid();
41178             return true;
41179         }
41180         
41181         var currency = this.getCurrency();
41182         
41183         if(this.validateValue(this.getRawValue()) && currency.length){
41184             this.markValid();
41185             return true;
41186         }
41187         
41188         this.markInvalid();
41189         return false;
41190     },
41191     
41192     getName: function()
41193     {
41194         return this.name;
41195     },
41196     
41197     beforeBlur : function()
41198     {
41199         if(!this.castInt){
41200             return;
41201         }
41202         
41203         var v = this.parseValue(this.getRawValue());
41204         
41205         if(v || v == 0){
41206             this.setValue(v);
41207         }
41208     },
41209     
41210     onBlur : function()
41211     {
41212         this.beforeBlur();
41213         
41214         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41215             //this.el.removeClass(this.focusClass);
41216         }
41217         
41218         this.hasFocus = false;
41219         
41220         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41221             this.validate();
41222         }
41223         
41224         var v = this.getValue();
41225         
41226         if(String(v) !== String(this.startValue)){
41227             this.fireEvent('change', this, v, this.startValue);
41228         }
41229         
41230         this.fireEvent("blur", this);
41231     },
41232     
41233     inputEl : function()
41234     {
41235         return this.el.select('.roo-money-amount-input', true).first();
41236     },
41237     
41238     currencyEl : function()
41239     {
41240         return this.el.select('.roo-money-currency-input', true).first();
41241     },
41242     
41243     hiddenEl : function()
41244     {
41245         return this.el.select('input.hidden-number-input',true).first();
41246     }
41247     
41248 });