roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass('hidden');
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass('hidden');
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fs
593  * @cfg {String} badge text for badge
594  * @cfg {String} theme (default|glow)  
595  * @cfg {Boolean} inverse dark themed version
596  * @cfg {Boolean} toggle is it a slidy toggle button
597  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
598  * @cfg {String} ontext text for on slidy toggle state
599  * @cfg {String} offtext text for off slidy toggle state
600  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
601  * @cfg {Boolean} removeClass remove the standard class..
602  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
603  * 
604  * @constructor
605  * Create a new button
606  * @param {Object} config The config object
607  */
608
609
610 Roo.bootstrap.Button = function(config){
611     Roo.bootstrap.Button.superclass.constructor.call(this, config);
612     this.weightClass = ["btn-default btn-outline-secondary", 
613                        "btn-primary", 
614                        "btn-success", 
615                        "btn-info", 
616                        "btn-warning",
617                        "btn-danger",
618                        "btn-link"
619                       ],  
620     this.addEvents({
621         // raw events
622         /**
623          * @event click
624          * When a butotn is pressed
625          * @param {Roo.bootstrap.Button} btn
626          * @param {Roo.EventObject} e
627          */
628         "click" : true,
629          /**
630          * @event toggle
631          * After the button has been toggles
632          * @param {Roo.bootstrap.Button} btn
633          * @param {Roo.EventObject} e
634          * @param {boolean} pressed (also available as button.pressed)
635          */
636         "toggle" : true
637     });
638 };
639
640 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
641     html: false,
642     active: false,
643     weight: '',
644     badge_weight: '',
645     outline : false,
646     size: '',
647     tag: 'button',
648     href: '',
649     disabled: false,
650     isClose: false,
651     glyphicon: '',
652     fa: '',
653     badge: '',
654     theme: 'default',
655     inverse: false,
656     
657     toggle: false,
658     ontext: 'ON',
659     offtext: 'OFF',
660     defaulton: true,
661     preventDefault: true,
662     removeClass: false,
663     name: false,
664     target: false,
665      
666     pressed : null,
667      
668     
669     getAutoCreate : function(){
670         
671         var cfg = {
672             tag : 'button',
673             cls : 'roo-button',
674             html: ''
675         };
676         
677         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
678             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
679             this.tag = 'button';
680         } else {
681             cfg.tag = this.tag;
682         }
683         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
684         
685         if (this.toggle == true) {
686             cfg={
687                 tag: 'div',
688                 cls: 'slider-frame roo-button',
689                 cn: [
690                     {
691                         tag: 'span',
692                         'data-on-text':'ON',
693                         'data-off-text':'OFF',
694                         cls: 'slider-button',
695                         html: this.offtext
696                     }
697                 ]
698             };
699             
700             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
701                 cfg.cls += ' '+this.weight;
702             }
703             
704             return cfg;
705         }
706         
707         if (this.isClose) {
708             cfg.cls += ' close';
709             
710             cfg["aria-hidden"] = true;
711             
712             cfg.html = "&times;";
713             
714             return cfg;
715         }
716         
717          
718         if (this.theme==='default') {
719             cfg.cls = 'btn roo-button';
720             
721             //if (this.parentType != 'Navbar') {
722             this.weight = this.weight.length ?  this.weight : 'default';
723             //}
724             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
725                 
726                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
727                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
728                 cfg.cls += ' btn-' + outline + weight;
729                 if (this.weight == 'default') {
730                     // BC
731                     cfg.cls += ' btn-' + this.weight;
732                 }
733             }
734         } else if (this.theme==='glow') {
735             
736             cfg.tag = 'a';
737             cfg.cls = 'btn-glow roo-button';
738             
739             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
740                 
741                 cfg.cls += ' ' + this.weight;
742             }
743         }
744    
745         
746         if (this.inverse) {
747             this.cls += ' inverse';
748         }
749         
750         
751         if (this.active || this.pressed === true) {
752             cfg.cls += ' active';
753         }
754         
755         if (this.disabled) {
756             cfg.disabled = 'disabled';
757         }
758         
759         if (this.items) {
760             Roo.log('changing to ul' );
761             cfg.tag = 'ul';
762             this.glyphicon = 'caret';
763             if (Roo.bootstrap.version == 4) {
764                 this.fa = 'caret-down';
765             }
766             
767         }
768         
769         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
770          
771         //gsRoo.log(this.parentType);
772         if (this.parentType === 'Navbar' && !this.parent().bar) {
773             Roo.log('changing to li?');
774             
775             cfg.tag = 'li';
776             
777             cfg.cls = '';
778             cfg.cn =  [{
779                 tag : 'a',
780                 cls : 'roo-button',
781                 html : this.html,
782                 href : this.href || '#'
783             }];
784             if (this.menu) {
785                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
786                 cfg.cls += ' dropdown';
787             }   
788             
789             delete cfg.html;
790             
791         }
792         
793        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
794         
795         if (this.glyphicon) {
796             cfg.html = ' ' + cfg.html;
797             
798             cfg.cn = [
799                 {
800                     tag: 'span',
801                     cls: 'glyphicon glyphicon-' + this.glyphicon
802                 }
803             ];
804         }
805         if (this.fa) {
806             cfg.html = ' ' + cfg.html;
807             
808             cfg.cn = [
809                 {
810                     tag: 'i',
811                     cls: 'fa fas fa-' + this.fa
812                 }
813             ];
814         }
815         
816         if (this.badge) {
817             cfg.html += ' ';
818             
819             cfg.tag = 'a';
820             
821 //            cfg.cls='btn roo-button';
822             
823             cfg.href=this.href;
824             
825             var value = cfg.html;
826             
827             if(this.glyphicon){
828                 value = {
829                     tag: 'span',
830                     cls: 'glyphicon glyphicon-' + this.glyphicon,
831                     html: this.html
832                 };
833             }
834             if(this.fa){
835                 value = {
836                     tag: 'i',
837                     cls: 'fa fas fa-' + this.fa,
838                     html: this.html
839                 };
840             }
841             
842             var bw = this.badge_weight.length ? this.badge_weight :
843                 (this.weight.length ? this.weight : 'secondary');
844             bw = bw == 'default' ? 'secondary' : bw;
845             
846             cfg.cn = [
847                 value,
848                 {
849                     tag: 'span',
850                     cls: 'badge badge-' + bw,
851                     html: this.badge
852                 }
853             ];
854             
855             cfg.html='';
856         }
857         
858         if (this.menu) {
859             cfg.cls += ' dropdown';
860             cfg.html = typeof(cfg.html) != 'undefined' ?
861                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
862         }
863         
864         if (cfg.tag !== 'a' && this.href !== '') {
865             throw "Tag must be a to set href.";
866         } else if (this.href.length > 0) {
867             cfg.href = this.href;
868         }
869         
870         if(this.removeClass){
871             cfg.cls = '';
872         }
873         
874         if(this.target){
875             cfg.target = this.target;
876         }
877         
878         return cfg;
879     },
880     initEvents: function() {
881        // Roo.log('init events?');
882 //        Roo.log(this.el.dom);
883         // add the menu...
884         
885         if (typeof (this.menu) != 'undefined') {
886             this.menu.parentType = this.xtype;
887             this.menu.triggerEl = this.el;
888             this.addxtype(Roo.apply({}, this.menu));
889         }
890
891
892        if (this.el.hasClass('roo-button')) {
893             this.el.on('click', this.onClick, this);
894        } else {
895             this.el.select('.roo-button').on('click', this.onClick, this);
896        }
897        
898        if(this.removeClass){
899            this.el.on('click', this.onClick, this);
900        }
901        
902        this.el.enableDisplayMode();
903         
904     },
905     onClick : function(e)
906     {
907         if (this.disabled) {
908             return;
909         }
910         
911         Roo.log('button on click ');
912         if(this.preventDefault){
913             e.preventDefault();
914         }
915         
916         if (this.pressed === true || this.pressed === false) {
917             this.toggleActive(e);
918         }
919         
920         
921         this.fireEvent('click', this, e);
922     },
923     
924     /**
925      * Enables this button
926      */
927     enable : function()
928     {
929         this.disabled = false;
930         this.el.removeClass('disabled');
931     },
932     
933     /**
934      * Disable this button
935      */
936     disable : function()
937     {
938         this.disabled = true;
939         this.el.addClass('disabled');
940     },
941      /**
942      * sets the active state on/off, 
943      * @param {Boolean} state (optional) Force a particular state
944      */
945     setActive : function(v) {
946         
947         this.el[v ? 'addClass' : 'removeClass']('active');
948         this.pressed = v;
949     },
950      /**
951      * toggles the current active state 
952      */
953     toggleActive : function(e)
954     {
955         this.setActive(!this.pressed);
956         this.fireEvent('toggle', this, e, !this.pressed);
957     },
958      /**
959      * get the current active state
960      * @return {boolean} true if it's active
961      */
962     isActive : function()
963     {
964         return this.el.hasClass('active');
965     },
966     /**
967      * set the text of the first selected button
968      */
969     setText : function(str)
970     {
971         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
972     },
973     /**
974      * get the text of the first selected button
975      */
976     getText : function()
977     {
978         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
979     },
980     
981     setWeight : function(str)
982     {
983         this.el.removeClass(this.weightClass);
984         this.weight = str;
985         var outline = this.outline ? 'outline-' : '';
986         if (str == 'default') {
987             this.el.addClass('btn-default btn-outline-secondary');        
988             return;
989         }
990         this.el.addClass('btn-' + outline + str);        
991     }
992     
993     
994 });
995
996  /*
997  * - LGPL
998  *
999  * column
1000  * 
1001  */
1002
1003 /**
1004  * @class Roo.bootstrap.Column
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Column class
1007  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1008  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1009  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1010  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1011  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1012  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1013  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1014  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1015  *
1016  * 
1017  * @cfg {Boolean} hidden (true|false) hide the element
1018  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1019  * @cfg {String} fa (ban|check|...) font awesome icon
1020  * @cfg {Number} fasize (1|2|....) font awsome size
1021
1022  * @cfg {String} icon (info-sign|check|...) glyphicon name
1023
1024  * @cfg {String} html content of column.
1025  * 
1026  * @constructor
1027  * Create a new Column
1028  * @param {Object} config The config object
1029  */
1030
1031 Roo.bootstrap.Column = function(config){
1032     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1033 };
1034
1035 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1036     
1037     xs: false,
1038     sm: false,
1039     md: false,
1040     lg: false,
1041     xsoff: false,
1042     smoff: false,
1043     mdoff: false,
1044     lgoff: false,
1045     html: '',
1046     offset: 0,
1047     alert: false,
1048     fa: false,
1049     icon : false,
1050     hidden : false,
1051     fasize : 1,
1052     
1053     getAutoCreate : function(){
1054         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1055         
1056         cfg = {
1057             tag: 'div',
1058             cls: 'column'
1059         };
1060         
1061         var settings=this;
1062         ['xs','sm','md','lg'].map(function(size){
1063             //Roo.log( size + ':' + settings[size]);
1064             
1065             if (settings[size+'off'] !== false) {
1066                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1067             }
1068             
1069             if (settings[size] === false) {
1070                 return;
1071             }
1072             
1073             if (!settings[size]) { // 0 = hidden
1074                 cfg.cls += ' hidden-' + size;
1075                 return;
1076             }
1077             cfg.cls += ' col-' + size + '-' + settings[size];
1078             
1079         });
1080         
1081         if (this.hidden) {
1082             cfg.cls += ' hidden';
1083         }
1084         
1085         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1086             cfg.cls +=' alert alert-' + this.alert;
1087         }
1088         
1089         
1090         if (this.html.length) {
1091             cfg.html = this.html;
1092         }
1093         if (this.fa) {
1094             var fasize = '';
1095             if (this.fasize > 1) {
1096                 fasize = ' fa-' + this.fasize + 'x';
1097             }
1098             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1099             
1100             
1101         }
1102         if (this.icon) {
1103             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1104         }
1105         
1106         return cfg;
1107     }
1108    
1109 });
1110
1111  
1112
1113  /*
1114  * - LGPL
1115  *
1116  * page container.
1117  * 
1118  */
1119
1120
1121 /**
1122  * @class Roo.bootstrap.Container
1123  * @extends Roo.bootstrap.Component
1124  * Bootstrap Container class
1125  * @cfg {Boolean} jumbotron is it a jumbotron element
1126  * @cfg {String} html content of element
1127  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1128  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1129  * @cfg {String} header content of header (for panel)
1130  * @cfg {String} footer content of footer (for panel)
1131  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1132  * @cfg {String} tag (header|aside|section) type of HTML tag.
1133  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1134  * @cfg {String} fa font awesome icon
1135  * @cfg {String} icon (info-sign|check|...) glyphicon name
1136  * @cfg {Boolean} hidden (true|false) hide the element
1137  * @cfg {Boolean} expandable (true|false) default false
1138  * @cfg {Boolean} expanded (true|false) default true
1139  * @cfg {String} rheader contet on the right of header
1140  * @cfg {Boolean} clickable (true|false) default false
1141
1142  *     
1143  * @constructor
1144  * Create a new Container
1145  * @param {Object} config The config object
1146  */
1147
1148 Roo.bootstrap.Container = function(config){
1149     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1150     
1151     this.addEvents({
1152         // raw events
1153          /**
1154          * @event expand
1155          * After the panel has been expand
1156          * 
1157          * @param {Roo.bootstrap.Container} this
1158          */
1159         "expand" : true,
1160         /**
1161          * @event collapse
1162          * After the panel has been collapsed
1163          * 
1164          * @param {Roo.bootstrap.Container} this
1165          */
1166         "collapse" : true,
1167         /**
1168          * @event click
1169          * When a element is chick
1170          * @param {Roo.bootstrap.Container} this
1171          * @param {Roo.EventObject} e
1172          */
1173         "click" : true
1174     });
1175 };
1176
1177 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1178     
1179     jumbotron : false,
1180     well: '',
1181     panel : '',
1182     header: '',
1183     footer : '',
1184     sticky: '',
1185     tag : false,
1186     alert : false,
1187     fa: false,
1188     icon : false,
1189     expandable : false,
1190     rheader : '',
1191     expanded : true,
1192     clickable: false,
1193   
1194      
1195     getChildContainer : function() {
1196         
1197         if(!this.el){
1198             return false;
1199         }
1200         
1201         if (this.panel.length) {
1202             return this.el.select('.panel-body',true).first();
1203         }
1204         
1205         return this.el;
1206     },
1207     
1208     
1209     getAutoCreate : function(){
1210         
1211         var cfg = {
1212             tag : this.tag || 'div',
1213             html : '',
1214             cls : ''
1215         };
1216         if (this.jumbotron) {
1217             cfg.cls = 'jumbotron';
1218         }
1219         
1220         
1221         
1222         // - this is applied by the parent..
1223         //if (this.cls) {
1224         //    cfg.cls = this.cls + '';
1225         //}
1226         
1227         if (this.sticky.length) {
1228             
1229             var bd = Roo.get(document.body);
1230             if (!bd.hasClass('bootstrap-sticky')) {
1231                 bd.addClass('bootstrap-sticky');
1232                 Roo.select('html',true).setStyle('height', '100%');
1233             }
1234              
1235             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1236         }
1237         
1238         
1239         if (this.well.length) {
1240             switch (this.well) {
1241                 case 'lg':
1242                 case 'sm':
1243                     cfg.cls +=' well well-' +this.well;
1244                     break;
1245                 default:
1246                     cfg.cls +=' well';
1247                     break;
1248             }
1249         }
1250         
1251         if (this.hidden) {
1252             cfg.cls += ' hidden';
1253         }
1254         
1255         
1256         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1257             cfg.cls +=' alert alert-' + this.alert;
1258         }
1259         
1260         var body = cfg;
1261         
1262         if (this.panel.length) {
1263             cfg.cls += ' panel panel-' + this.panel;
1264             cfg.cn = [];
1265             if (this.header.length) {
1266                 
1267                 var h = [];
1268                 
1269                 if(this.expandable){
1270                     
1271                     cfg.cls = cfg.cls + ' expandable';
1272                     
1273                     h.push({
1274                         tag: 'i',
1275                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1276                     });
1277                     
1278                 }
1279                 
1280                 h.push(
1281                     {
1282                         tag: 'span',
1283                         cls : 'panel-title',
1284                         html : (this.expandable ? '&nbsp;' : '') + this.header
1285                     },
1286                     {
1287                         tag: 'span',
1288                         cls: 'panel-header-right',
1289                         html: this.rheader
1290                     }
1291                 );
1292                 
1293                 cfg.cn.push({
1294                     cls : 'panel-heading',
1295                     style : this.expandable ? 'cursor: pointer' : '',
1296                     cn : h
1297                 });
1298                 
1299             }
1300             
1301             body = false;
1302             cfg.cn.push({
1303                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1304                 html : this.html
1305             });
1306             
1307             
1308             if (this.footer.length) {
1309                 cfg.cn.push({
1310                     cls : 'panel-footer',
1311                     html : this.footer
1312                     
1313                 });
1314             }
1315             
1316         }
1317         
1318         if (body) {
1319             body.html = this.html || cfg.html;
1320             // prefix with the icons..
1321             if (this.fa) {
1322                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1323             }
1324             if (this.icon) {
1325                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1326             }
1327             
1328             
1329         }
1330         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1331             cfg.cls =  'container';
1332         }
1333         
1334         return cfg;
1335     },
1336     
1337     initEvents: function() 
1338     {
1339         if(this.expandable){
1340             var headerEl = this.headerEl();
1341         
1342             if(headerEl){
1343                 headerEl.on('click', this.onToggleClick, this);
1344             }
1345         }
1346         
1347         if(this.clickable){
1348             this.el.on('click', this.onClick, this);
1349         }
1350         
1351     },
1352     
1353     onToggleClick : function()
1354     {
1355         var headerEl = this.headerEl();
1356         
1357         if(!headerEl){
1358             return;
1359         }
1360         
1361         if(this.expanded){
1362             this.collapse();
1363             return;
1364         }
1365         
1366         this.expand();
1367     },
1368     
1369     expand : function()
1370     {
1371         if(this.fireEvent('expand', this)) {
1372             
1373             this.expanded = true;
1374             
1375             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1376             
1377             this.el.select('.panel-body',true).first().removeClass('hide');
1378             
1379             var toggleEl = this.toggleEl();
1380
1381             if(!toggleEl){
1382                 return;
1383             }
1384
1385             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1386         }
1387         
1388     },
1389     
1390     collapse : function()
1391     {
1392         if(this.fireEvent('collapse', this)) {
1393             
1394             this.expanded = false;
1395             
1396             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1397             this.el.select('.panel-body',true).first().addClass('hide');
1398         
1399             var toggleEl = this.toggleEl();
1400
1401             if(!toggleEl){
1402                 return;
1403             }
1404
1405             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1406         }
1407     },
1408     
1409     toggleEl : function()
1410     {
1411         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1412             return;
1413         }
1414         
1415         return this.el.select('.panel-heading .fa',true).first();
1416     },
1417     
1418     headerEl : function()
1419     {
1420         if(!this.el || !this.panel.length || !this.header.length){
1421             return;
1422         }
1423         
1424         return this.el.select('.panel-heading',true).first()
1425     },
1426     
1427     bodyEl : function()
1428     {
1429         if(!this.el || !this.panel.length){
1430             return;
1431         }
1432         
1433         return this.el.select('.panel-body',true).first()
1434     },
1435     
1436     titleEl : function()
1437     {
1438         if(!this.el || !this.panel.length || !this.header.length){
1439             return;
1440         }
1441         
1442         return this.el.select('.panel-title',true).first();
1443     },
1444     
1445     setTitle : function(v)
1446     {
1447         var titleEl = this.titleEl();
1448         
1449         if(!titleEl){
1450             return;
1451         }
1452         
1453         titleEl.dom.innerHTML = v;
1454     },
1455     
1456     getTitle : function()
1457     {
1458         
1459         var titleEl = this.titleEl();
1460         
1461         if(!titleEl){
1462             return '';
1463         }
1464         
1465         return titleEl.dom.innerHTML;
1466     },
1467     
1468     setRightTitle : function(v)
1469     {
1470         var t = this.el.select('.panel-header-right',true).first();
1471         
1472         if(!t){
1473             return;
1474         }
1475         
1476         t.dom.innerHTML = v;
1477     },
1478     
1479     onClick : function(e)
1480     {
1481         e.preventDefault();
1482         
1483         this.fireEvent('click', this, e);
1484     }
1485 });
1486
1487  /*
1488  * - LGPL
1489  *
1490  * image
1491  * 
1492  */
1493
1494
1495 /**
1496  * @class Roo.bootstrap.Img
1497  * @extends Roo.bootstrap.Component
1498  * Bootstrap Img class
1499  * @cfg {Boolean} imgResponsive false | true
1500  * @cfg {String} border rounded | circle | thumbnail
1501  * @cfg {String} src image source
1502  * @cfg {String} alt image alternative text
1503  * @cfg {String} href a tag href
1504  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1505  * @cfg {String} xsUrl xs image source
1506  * @cfg {String} smUrl sm image source
1507  * @cfg {String} mdUrl md image source
1508  * @cfg {String} lgUrl lg image source
1509  * 
1510  * @constructor
1511  * Create a new Input
1512  * @param {Object} config The config object
1513  */
1514
1515 Roo.bootstrap.Img = function(config){
1516     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1517     
1518     this.addEvents({
1519         // img events
1520         /**
1521          * @event click
1522          * The img click event for the img.
1523          * @param {Roo.EventObject} e
1524          */
1525         "click" : true
1526     });
1527 };
1528
1529 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1530     
1531     imgResponsive: true,
1532     border: '',
1533     src: 'about:blank',
1534     href: false,
1535     target: false,
1536     xsUrl: '',
1537     smUrl: '',
1538     mdUrl: '',
1539     lgUrl: '',
1540
1541     getAutoCreate : function()
1542     {   
1543         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1544             return this.createSingleImg();
1545         }
1546         
1547         var cfg = {
1548             tag: 'div',
1549             cls: 'roo-image-responsive-group',
1550             cn: []
1551         };
1552         var _this = this;
1553         
1554         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1555             
1556             if(!_this[size + 'Url']){
1557                 return;
1558             }
1559             
1560             var img = {
1561                 tag: 'img',
1562                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1563                 html: _this.html || cfg.html,
1564                 src: _this[size + 'Url']
1565             };
1566             
1567             img.cls += ' roo-image-responsive-' + size;
1568             
1569             var s = ['xs', 'sm', 'md', 'lg'];
1570             
1571             s.splice(s.indexOf(size), 1);
1572             
1573             Roo.each(s, function(ss){
1574                 img.cls += ' hidden-' + ss;
1575             });
1576             
1577             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1578                 cfg.cls += ' img-' + _this.border;
1579             }
1580             
1581             if(_this.alt){
1582                 cfg.alt = _this.alt;
1583             }
1584             
1585             if(_this.href){
1586                 var a = {
1587                     tag: 'a',
1588                     href: _this.href,
1589                     cn: [
1590                         img
1591                     ]
1592                 };
1593
1594                 if(this.target){
1595                     a.target = _this.target;
1596                 }
1597             }
1598             
1599             cfg.cn.push((_this.href) ? a : img);
1600             
1601         });
1602         
1603         return cfg;
1604     },
1605     
1606     createSingleImg : function()
1607     {
1608         var cfg = {
1609             tag: 'img',
1610             cls: (this.imgResponsive) ? 'img-responsive' : '',
1611             html : null,
1612             src : 'about:blank'  // just incase src get's set to undefined?!?
1613         };
1614         
1615         cfg.html = this.html || cfg.html;
1616         
1617         cfg.src = this.src || cfg.src;
1618         
1619         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1620             cfg.cls += ' img-' + this.border;
1621         }
1622         
1623         if(this.alt){
1624             cfg.alt = this.alt;
1625         }
1626         
1627         if(this.href){
1628             var a = {
1629                 tag: 'a',
1630                 href: this.href,
1631                 cn: [
1632                     cfg
1633                 ]
1634             };
1635             
1636             if(this.target){
1637                 a.target = this.target;
1638             }
1639             
1640         }
1641         
1642         return (this.href) ? a : cfg;
1643     },
1644     
1645     initEvents: function() 
1646     {
1647         if(!this.href){
1648             this.el.on('click', this.onClick, this);
1649         }
1650         
1651     },
1652     
1653     onClick : function(e)
1654     {
1655         Roo.log('img onclick');
1656         this.fireEvent('click', this, e);
1657     },
1658     /**
1659      * Sets the url of the image - used to update it
1660      * @param {String} url the url of the image
1661      */
1662     
1663     setSrc : function(url)
1664     {
1665         this.src =  url;
1666         
1667         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1668             this.el.dom.src =  url;
1669             return;
1670         }
1671         
1672         this.el.select('img', true).first().dom.src =  url;
1673     }
1674     
1675     
1676    
1677 });
1678
1679  /*
1680  * - LGPL
1681  *
1682  * image
1683  * 
1684  */
1685
1686
1687 /**
1688  * @class Roo.bootstrap.Link
1689  * @extends Roo.bootstrap.Component
1690  * Bootstrap Link Class
1691  * @cfg {String} alt image alternative text
1692  * @cfg {String} href a tag href
1693  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1694  * @cfg {String} html the content of the link.
1695  * @cfg {String} anchor name for the anchor link
1696  * @cfg {String} fa - favicon
1697
1698  * @cfg {Boolean} preventDefault (true | false) default false
1699
1700  * 
1701  * @constructor
1702  * Create a new Input
1703  * @param {Object} config The config object
1704  */
1705
1706 Roo.bootstrap.Link = function(config){
1707     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1708     
1709     this.addEvents({
1710         // img events
1711         /**
1712          * @event click
1713          * The img click event for the img.
1714          * @param {Roo.EventObject} e
1715          */
1716         "click" : true
1717     });
1718 };
1719
1720 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1721     
1722     href: false,
1723     target: false,
1724     preventDefault: false,
1725     anchor : false,
1726     alt : false,
1727     fa: false,
1728
1729
1730     getAutoCreate : function()
1731     {
1732         var html = this.html || '';
1733         
1734         if (this.fa !== false) {
1735             html = '<i class="fa fa-' + this.fa + '"></i>';
1736         }
1737         var cfg = {
1738             tag: 'a'
1739         };
1740         // anchor's do not require html/href...
1741         if (this.anchor === false) {
1742             cfg.html = html;
1743             cfg.href = this.href || '#';
1744         } else {
1745             cfg.name = this.anchor;
1746             if (this.html !== false || this.fa !== false) {
1747                 cfg.html = html;
1748             }
1749             if (this.href !== false) {
1750                 cfg.href = this.href;
1751             }
1752         }
1753         
1754         if(this.alt !== false){
1755             cfg.alt = this.alt;
1756         }
1757         
1758         
1759         if(this.target !== false) {
1760             cfg.target = this.target;
1761         }
1762         
1763         return cfg;
1764     },
1765     
1766     initEvents: function() {
1767         
1768         if(!this.href || this.preventDefault){
1769             this.el.on('click', this.onClick, this);
1770         }
1771     },
1772     
1773     onClick : function(e)
1774     {
1775         if(this.preventDefault){
1776             e.preventDefault();
1777         }
1778         //Roo.log('img onclick');
1779         this.fireEvent('click', this, e);
1780     }
1781    
1782 });
1783
1784  /*
1785  * - LGPL
1786  *
1787  * header
1788  * 
1789  */
1790
1791 /**
1792  * @class Roo.bootstrap.Header
1793  * @extends Roo.bootstrap.Component
1794  * Bootstrap Header class
1795  * @cfg {String} html content of header
1796  * @cfg {Number} level (1|2|3|4|5|6) default 1
1797  * 
1798  * @constructor
1799  * Create a new Header
1800  * @param {Object} config The config object
1801  */
1802
1803
1804 Roo.bootstrap.Header  = function(config){
1805     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1806 };
1807
1808 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1809     
1810     //href : false,
1811     html : false,
1812     level : 1,
1813     
1814     
1815     
1816     getAutoCreate : function(){
1817         
1818         
1819         
1820         var cfg = {
1821             tag: 'h' + (1 *this.level),
1822             html: this.html || ''
1823         } ;
1824         
1825         return cfg;
1826     }
1827    
1828 });
1829
1830  
1831
1832  /*
1833  * Based on:
1834  * Ext JS Library 1.1.1
1835  * Copyright(c) 2006-2007, Ext JS, LLC.
1836  *
1837  * Originally Released Under LGPL - original licence link has changed is not relivant.
1838  *
1839  * Fork - LGPL
1840  * <script type="text/javascript">
1841  */
1842  
1843 /**
1844  * @class Roo.bootstrap.MenuMgr
1845  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1846  * @singleton
1847  */
1848 Roo.bootstrap.MenuMgr = function(){
1849    var menus, active, groups = {}, attached = false, lastShow = new Date();
1850
1851    // private - called when first menu is created
1852    function init(){
1853        menus = {};
1854        active = new Roo.util.MixedCollection();
1855        Roo.get(document).addKeyListener(27, function(){
1856            if(active.length > 0){
1857                hideAll();
1858            }
1859        });
1860    }
1861
1862    // private
1863    function hideAll(){
1864        if(active && active.length > 0){
1865            var c = active.clone();
1866            c.each(function(m){
1867                m.hide();
1868            });
1869        }
1870    }
1871
1872    // private
1873    function onHide(m){
1874        active.remove(m);
1875        if(active.length < 1){
1876            Roo.get(document).un("mouseup", onMouseDown);
1877             
1878            attached = false;
1879        }
1880    }
1881
1882    // private
1883    function onShow(m){
1884        var last = active.last();
1885        lastShow = new Date();
1886        active.add(m);
1887        if(!attached){
1888           Roo.get(document).on("mouseup", onMouseDown);
1889            
1890            attached = true;
1891        }
1892        if(m.parentMenu){
1893           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1894           m.parentMenu.activeChild = m;
1895        }else if(last && last.isVisible()){
1896           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1897        }
1898    }
1899
1900    // private
1901    function onBeforeHide(m){
1902        if(m.activeChild){
1903            m.activeChild.hide();
1904        }
1905        if(m.autoHideTimer){
1906            clearTimeout(m.autoHideTimer);
1907            delete m.autoHideTimer;
1908        }
1909    }
1910
1911    // private
1912    function onBeforeShow(m){
1913        var pm = m.parentMenu;
1914        if(!pm && !m.allowOtherMenus){
1915            hideAll();
1916        }else if(pm && pm.activeChild && active != m){
1917            pm.activeChild.hide();
1918        }
1919    }
1920
1921    // private this should really trigger on mouseup..
1922    function onMouseDown(e){
1923         Roo.log("on Mouse Up");
1924         
1925         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1926             Roo.log("MenuManager hideAll");
1927             hideAll();
1928             e.stopEvent();
1929         }
1930         
1931         
1932    }
1933
1934    // private
1935    function onBeforeCheck(mi, state){
1936        if(state){
1937            var g = groups[mi.group];
1938            for(var i = 0, l = g.length; i < l; i++){
1939                if(g[i] != mi){
1940                    g[i].setChecked(false);
1941                }
1942            }
1943        }
1944    }
1945
1946    return {
1947
1948        /**
1949         * Hides all menus that are currently visible
1950         */
1951        hideAll : function(){
1952             hideAll();  
1953        },
1954
1955        // private
1956        register : function(menu){
1957            if(!menus){
1958                init();
1959            }
1960            menus[menu.id] = menu;
1961            menu.on("beforehide", onBeforeHide);
1962            menu.on("hide", onHide);
1963            menu.on("beforeshow", onBeforeShow);
1964            menu.on("show", onShow);
1965            var g = menu.group;
1966            if(g && menu.events["checkchange"]){
1967                if(!groups[g]){
1968                    groups[g] = [];
1969                }
1970                groups[g].push(menu);
1971                menu.on("checkchange", onCheck);
1972            }
1973        },
1974
1975         /**
1976          * Returns a {@link Roo.menu.Menu} object
1977          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1978          * be used to generate and return a new Menu instance.
1979          */
1980        get : function(menu){
1981            if(typeof menu == "string"){ // menu id
1982                return menus[menu];
1983            }else if(menu.events){  // menu instance
1984                return menu;
1985            }
1986            /*else if(typeof menu.length == 'number'){ // array of menu items?
1987                return new Roo.bootstrap.Menu({items:menu});
1988            }else{ // otherwise, must be a config
1989                return new Roo.bootstrap.Menu(menu);
1990            }
1991            */
1992            return false;
1993        },
1994
1995        // private
1996        unregister : function(menu){
1997            delete menus[menu.id];
1998            menu.un("beforehide", onBeforeHide);
1999            menu.un("hide", onHide);
2000            menu.un("beforeshow", onBeforeShow);
2001            menu.un("show", onShow);
2002            var g = menu.group;
2003            if(g && menu.events["checkchange"]){
2004                groups[g].remove(menu);
2005                menu.un("checkchange", onCheck);
2006            }
2007        },
2008
2009        // private
2010        registerCheckable : function(menuItem){
2011            var g = menuItem.group;
2012            if(g){
2013                if(!groups[g]){
2014                    groups[g] = [];
2015                }
2016                groups[g].push(menuItem);
2017                menuItem.on("beforecheckchange", onBeforeCheck);
2018            }
2019        },
2020
2021        // private
2022        unregisterCheckable : function(menuItem){
2023            var g = menuItem.group;
2024            if(g){
2025                groups[g].remove(menuItem);
2026                menuItem.un("beforecheckchange", onBeforeCheck);
2027            }
2028        }
2029    };
2030 }();/*
2031  * - LGPL
2032  *
2033  * menu
2034  * 
2035  */
2036
2037 /**
2038  * @class Roo.bootstrap.Menu
2039  * @extends Roo.bootstrap.Component
2040  * Bootstrap Menu class - container for MenuItems
2041  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2042  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2043  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2044  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2045  * 
2046  * @constructor
2047  * Create a new Menu
2048  * @param {Object} config The config object
2049  */
2050
2051
2052 Roo.bootstrap.Menu = function(config){
2053     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2054     if (this.registerMenu && this.type != 'treeview')  {
2055         Roo.bootstrap.MenuMgr.register(this);
2056     }
2057     
2058     
2059     this.addEvents({
2060         /**
2061          * @event beforeshow
2062          * Fires before this menu is displayed
2063          * @param {Roo.menu.Menu} this
2064          */
2065         beforeshow : true,
2066         /**
2067          * @event beforehide
2068          * Fires before this menu is hidden
2069          * @param {Roo.menu.Menu} this
2070          */
2071         beforehide : true,
2072         /**
2073          * @event show
2074          * Fires after this menu is displayed
2075          * @param {Roo.menu.Menu} this
2076          */
2077         show : true,
2078         /**
2079          * @event hide
2080          * Fires after this menu is hidden
2081          * @param {Roo.menu.Menu} this
2082          */
2083         hide : true,
2084         /**
2085          * @event click
2086          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2087          * @param {Roo.menu.Menu} this
2088          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2089          * @param {Roo.EventObject} e
2090          */
2091         click : true,
2092         /**
2093          * @event mouseover
2094          * Fires when the mouse is hovering over this menu
2095          * @param {Roo.menu.Menu} this
2096          * @param {Roo.EventObject} e
2097          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2098          */
2099         mouseover : true,
2100         /**
2101          * @event mouseout
2102          * Fires when the mouse exits this menu
2103          * @param {Roo.menu.Menu} this
2104          * @param {Roo.EventObject} e
2105          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2106          */
2107         mouseout : true,
2108         /**
2109          * @event itemclick
2110          * Fires when a menu item contained in this menu is clicked
2111          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2112          * @param {Roo.EventObject} e
2113          */
2114         itemclick: true
2115     });
2116     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2117 };
2118
2119 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2120     
2121    /// html : false,
2122     //align : '',
2123     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2124     type: false,
2125     /**
2126      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2127      */
2128     registerMenu : true,
2129     
2130     menuItems :false, // stores the menu items..
2131     
2132     hidden:true,
2133         
2134     parentMenu : false,
2135     
2136     stopEvent : true,
2137     
2138     isLink : false,
2139     
2140     getChildContainer : function() {
2141         return this.el;  
2142     },
2143     
2144     getAutoCreate : function(){
2145          
2146         //if (['right'].indexOf(this.align)!==-1) {
2147         //    cfg.cn[1].cls += ' pull-right'
2148         //}
2149         
2150         
2151         var cfg = {
2152             tag : 'ul',
2153             cls : 'dropdown-menu' ,
2154             style : 'z-index:1000'
2155             
2156         };
2157         
2158         if (this.type === 'submenu') {
2159             cfg.cls = 'submenu active';
2160         }
2161         if (this.type === 'treeview') {
2162             cfg.cls = 'treeview-menu';
2163         }
2164         
2165         return cfg;
2166     },
2167     initEvents : function() {
2168         
2169        // Roo.log("ADD event");
2170        // Roo.log(this.triggerEl.dom);
2171         
2172         this.triggerEl.on('click', this.onTriggerClick, this);
2173         
2174         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2175         
2176         
2177         if (this.triggerEl.hasClass('nav-item')) {
2178             // dropdown toggle on the 'a' in BS4?
2179             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2180         } else {
2181             this.triggerEl.addClass('dropdown-toggle');
2182         }
2183         if (Roo.isTouch) {
2184             this.el.on('touchstart'  , this.onTouch, this);
2185         }
2186         this.el.on('click' , this.onClick, this);
2187
2188         this.el.on("mouseover", this.onMouseOver, this);
2189         this.el.on("mouseout", this.onMouseOut, this);
2190         
2191     },
2192     
2193     findTargetItem : function(e)
2194     {
2195         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2196         if(!t){
2197             return false;
2198         }
2199         //Roo.log(t);         Roo.log(t.id);
2200         if(t && t.id){
2201             //Roo.log(this.menuitems);
2202             return this.menuitems.get(t.id);
2203             
2204             //return this.items.get(t.menuItemId);
2205         }
2206         
2207         return false;
2208     },
2209     
2210     onTouch : function(e) 
2211     {
2212         Roo.log("menu.onTouch");
2213         //e.stopEvent(); this make the user popdown broken
2214         this.onClick(e);
2215     },
2216     
2217     onClick : function(e)
2218     {
2219         Roo.log("menu.onClick");
2220         
2221         var t = this.findTargetItem(e);
2222         if(!t || t.isContainer){
2223             return;
2224         }
2225         Roo.log(e);
2226         /*
2227         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2228             if(t == this.activeItem && t.shouldDeactivate(e)){
2229                 this.activeItem.deactivate();
2230                 delete this.activeItem;
2231                 return;
2232             }
2233             if(t.canActivate){
2234                 this.setActiveItem(t, true);
2235             }
2236             return;
2237             
2238             
2239         }
2240         */
2241        
2242         Roo.log('pass click event');
2243         
2244         t.onClick(e);
2245         
2246         this.fireEvent("click", this, t, e);
2247         
2248         var _this = this;
2249         
2250         if(!t.href.length || t.href == '#'){
2251             (function() { _this.hide(); }).defer(100);
2252         }
2253         
2254     },
2255     
2256     onMouseOver : function(e){
2257         var t  = this.findTargetItem(e);
2258         //Roo.log(t);
2259         //if(t){
2260         //    if(t.canActivate && !t.disabled){
2261         //        this.setActiveItem(t, true);
2262         //    }
2263         //}
2264         
2265         this.fireEvent("mouseover", this, e, t);
2266     },
2267     isVisible : function(){
2268         return !this.hidden;
2269     },
2270      onMouseOut : function(e){
2271         var t  = this.findTargetItem(e);
2272         
2273         //if(t ){
2274         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2275         //        this.activeItem.deactivate();
2276         //        delete this.activeItem;
2277         //    }
2278         //}
2279         this.fireEvent("mouseout", this, e, t);
2280     },
2281     
2282     
2283     /**
2284      * Displays this menu relative to another element
2285      * @param {String/HTMLElement/Roo.Element} element The element to align to
2286      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2287      * the element (defaults to this.defaultAlign)
2288      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2289      */
2290     show : function(el, pos, parentMenu){
2291         this.parentMenu = parentMenu;
2292         if(!this.el){
2293             this.render();
2294         }
2295         this.fireEvent("beforeshow", this);
2296         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2297     },
2298      /**
2299      * Displays this menu at a specific xy position
2300      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2301      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2302      */
2303     showAt : function(xy, parentMenu, /* private: */_e){
2304         this.parentMenu = parentMenu;
2305         if(!this.el){
2306             this.render();
2307         }
2308         if(_e !== false){
2309             this.fireEvent("beforeshow", this);
2310             //xy = this.el.adjustForConstraints(xy);
2311         }
2312         
2313         //this.el.show();
2314         this.hideMenuItems();
2315         this.hidden = false;
2316         this.triggerEl.addClass('open');
2317         this.el.addClass('show');
2318         
2319         // reassign x when hitting right
2320         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2321             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2322         }
2323         
2324         // reassign y when hitting bottom
2325         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2326             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2327         }
2328         
2329         // but the list may align on trigger left or trigger top... should it be a properity?
2330         
2331         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2332             this.el.setXY(xy);
2333         }
2334         
2335         this.focus();
2336         this.fireEvent("show", this);
2337     },
2338     
2339     focus : function(){
2340         return;
2341         if(!this.hidden){
2342             this.doFocus.defer(50, this);
2343         }
2344     },
2345
2346     doFocus : function(){
2347         if(!this.hidden){
2348             this.focusEl.focus();
2349         }
2350     },
2351
2352     /**
2353      * Hides this menu and optionally all parent menus
2354      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2355      */
2356     hide : function(deep)
2357     {
2358         
2359         this.hideMenuItems();
2360         if(this.el && this.isVisible()){
2361             this.fireEvent("beforehide", this);
2362             if(this.activeItem){
2363                 this.activeItem.deactivate();
2364                 this.activeItem = null;
2365             }
2366             this.triggerEl.removeClass('open');;
2367             this.el.removeClass('show');
2368             this.hidden = true;
2369             this.fireEvent("hide", this);
2370         }
2371         if(deep === true && this.parentMenu){
2372             this.parentMenu.hide(true);
2373         }
2374     },
2375     
2376     onTriggerClick : function(e)
2377     {
2378         Roo.log('trigger click');
2379         
2380         var target = e.getTarget();
2381         
2382         Roo.log(target.nodeName.toLowerCase());
2383         
2384         if(target.nodeName.toLowerCase() === 'i'){
2385             e.preventDefault();
2386         }
2387         
2388     },
2389     
2390     onTriggerPress  : function(e)
2391     {
2392         Roo.log('trigger press');
2393         //Roo.log(e.getTarget());
2394        // Roo.log(this.triggerEl.dom);
2395        
2396         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2397         var pel = Roo.get(e.getTarget());
2398         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2399             Roo.log('is treeview or dropdown?');
2400             return;
2401         }
2402         
2403         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2404             return;
2405         }
2406         
2407         if (this.isVisible()) {
2408             Roo.log('hide');
2409             this.hide();
2410         } else {
2411             Roo.log('show');
2412             this.show(this.triggerEl, false, false);
2413         }
2414         
2415         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2416             e.stopEvent();
2417         }
2418         
2419     },
2420        
2421     
2422     hideMenuItems : function()
2423     {
2424         Roo.log("hide Menu Items");
2425         if (!this.el) { 
2426             return;
2427         }
2428         //$(backdrop).remove()
2429         this.el.select('.open',true).each(function(aa) {
2430             
2431             aa.removeClass('open');
2432           //var parent = getParent($(this))
2433           //var relatedTarget = { relatedTarget: this }
2434           
2435            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2436           //if (e.isDefaultPrevented()) return
2437            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2438         });
2439     },
2440     addxtypeChild : function (tree, cntr) {
2441         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2442           
2443         this.menuitems.add(comp);
2444         return comp;
2445
2446     },
2447     getEl : function()
2448     {
2449         Roo.log(this.el);
2450         return this.el;
2451     },
2452     
2453     clear : function()
2454     {
2455         this.getEl().dom.innerHTML = '';
2456         this.menuitems.clear();
2457     }
2458 });
2459
2460  
2461  /*
2462  * - LGPL
2463  *
2464  * menu item
2465  * 
2466  */
2467
2468
2469 /**
2470  * @class Roo.bootstrap.MenuItem
2471  * @extends Roo.bootstrap.Component
2472  * Bootstrap MenuItem class
2473  * @cfg {String} html the menu label
2474  * @cfg {String} href the link
2475  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2476  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2477  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2478  * @cfg {String} fa favicon to show on left of menu item.
2479  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2480  * 
2481  * 
2482  * @constructor
2483  * Create a new MenuItem
2484  * @param {Object} config The config object
2485  */
2486
2487
2488 Roo.bootstrap.MenuItem = function(config){
2489     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2490     this.addEvents({
2491         // raw events
2492         /**
2493          * @event click
2494          * The raw click event for the entire grid.
2495          * @param {Roo.bootstrap.MenuItem} this
2496          * @param {Roo.EventObject} e
2497          */
2498         "click" : true
2499     });
2500 };
2501
2502 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2503     
2504     href : false,
2505     html : false,
2506     preventDefault: false,
2507     isContainer : false,
2508     active : false,
2509     fa: false,
2510     
2511     getAutoCreate : function(){
2512         
2513         if(this.isContainer){
2514             return {
2515                 tag: 'li',
2516                 cls: 'dropdown-menu-item 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('collapsing');
3870                     var h = ce.getHeight();
3871                     Roo.log(h);
3872                     ce.setHeight(0); // resize it ...
3873                     ce.on('transitionend', function() {
3874                     
3875                         ce.removeClass('collapsing');
3876                         ce.addClass('show');
3877                         ce.removeClass('collapse');
3878
3879                         ce.dom.style.height = '';
3880                     }, this, { single: true} );
3881                     ce.setHeight(h);
3882                     
3883                 } else {
3884                     ce.addClass('collapsing');
3885                     ce.removeClass('show');
3886                     (function() {
3887                         ce.removeClass('collapsing');
3888                         ce.addClass('collapse');
3889                         
3890                     }).defer(200);
3891                     
3892                 }
3893             }
3894             
3895         }, this);
3896         
3897         var mark = {
3898             tag: "div",
3899             cls:"x-dlg-mask"
3900         };
3901         
3902         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3903         
3904         var size = this.el.getSize();
3905         this.maskEl.setSize(size.width, size.height);
3906         this.maskEl.enableDisplayMode("block");
3907         this.maskEl.hide();
3908         
3909         if(this.loadMask){
3910             this.maskEl.show();
3911         }
3912     },
3913     
3914     
3915     getChildContainer : function()
3916     {
3917         if (this.el.select('.collapse').getCount()) {
3918             return this.el.select('.collapse',true).first();
3919         }
3920         
3921         return this.el;
3922     },
3923     
3924     mask : function()
3925     {
3926         this.maskEl.show();
3927     },
3928     
3929     unmask : function()
3930     {
3931         this.maskEl.hide();
3932     } 
3933     
3934     
3935     
3936     
3937 });
3938
3939
3940
3941  
3942
3943  /*
3944  * - LGPL
3945  *
3946  * navbar
3947  * 
3948  */
3949
3950 /**
3951  * @class Roo.bootstrap.NavSimplebar
3952  * @extends Roo.bootstrap.Navbar
3953  * Bootstrap Sidebar class
3954  *
3955  * @cfg {Boolean} inverse is inverted color
3956  * 
3957  * @cfg {String} type (nav | pills | tabs)
3958  * @cfg {Boolean} arrangement stacked | justified
3959  * @cfg {String} align (left | right) alignment
3960  * 
3961  * @cfg {Boolean} main (true|false) main nav bar? default false
3962  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3963  * 
3964  * @cfg {String} tag (header|footer|nav|div) default is nav 
3965
3966  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3967  * 
3968  * 
3969  * @constructor
3970  * Create a new Sidebar
3971  * @param {Object} config The config object
3972  */
3973
3974
3975 Roo.bootstrap.NavSimplebar = function(config){
3976     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3977 };
3978
3979 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3980     
3981     inverse: false,
3982     
3983     type: false,
3984     arrangement: '',
3985     align : false,
3986     
3987     weight : 'light',
3988     
3989     main : false,
3990     
3991     
3992     tag : false,
3993     
3994     
3995     getAutoCreate : function(){
3996         
3997         
3998         var cfg = {
3999             tag : this.tag || 'div',
4000             cls : 'navbar navbar-expand-lg'
4001         };
4002         if (['light','white'].indexOf(this.weight) > -1) {
4003             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4004         }
4005         cfg.cls += ' bg-' + this.weight;
4006         
4007           
4008         
4009         cfg.cn = [
4010             {
4011                 cls: 'nav',
4012                 tag : 'ul'
4013             }
4014         ];
4015         
4016          
4017         this.type = this.type || 'nav';
4018         if (['tabs','pills'].indexOf(this.type)!==-1) {
4019             cfg.cn[0].cls += ' nav-' + this.type
4020         
4021         
4022         } else {
4023             if (this.type!=='nav') {
4024                 Roo.log('nav type must be nav/tabs/pills')
4025             }
4026             cfg.cn[0].cls += ' navbar-nav'
4027         }
4028         
4029         
4030         
4031         
4032         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4033             cfg.cn[0].cls += ' nav-' + this.arrangement;
4034         }
4035         
4036         
4037         if (this.align === 'right') {
4038             cfg.cn[0].cls += ' navbar-right';
4039         }
4040         
4041         if (this.inverse) {
4042             cfg.cls += ' navbar-inverse';
4043             
4044         }
4045         
4046         
4047         return cfg;
4048     
4049         
4050     }
4051     
4052     
4053     
4054 });
4055
4056
4057
4058  
4059
4060  
4061        /*
4062  * - LGPL
4063  *
4064  * navbar
4065  * navbar-fixed-top
4066  * navbar-expand-md  fixed-top 
4067  */
4068
4069 /**
4070  * @class Roo.bootstrap.NavHeaderbar
4071  * @extends Roo.bootstrap.NavSimplebar
4072  * Bootstrap Sidebar class
4073  *
4074  * @cfg {String} brand what is brand
4075  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4076  * @cfg {String} brand_href href of the brand
4077  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4078  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4079  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4080  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4081  * 
4082  * @constructor
4083  * Create a new Sidebar
4084  * @param {Object} config The config object
4085  */
4086
4087
4088 Roo.bootstrap.NavHeaderbar = function(config){
4089     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4090       
4091 };
4092
4093 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4094     
4095     position: '',
4096     brand: '',
4097     brand_href: false,
4098     srButton : true,
4099     autohide : false,
4100     desktopCenter : false,
4101    
4102     
4103     getAutoCreate : function(){
4104         
4105         var   cfg = {
4106             tag: this.nav || 'nav',
4107             cls: 'navbar navbar-expand-md',
4108             role: 'navigation',
4109             cn: []
4110         };
4111         
4112         var cn = cfg.cn;
4113         if (this.desktopCenter) {
4114             cn.push({cls : 'container', cn : []});
4115             cn = cn[0].cn;
4116         }
4117         
4118         if(this.srButton){
4119             var btn = {
4120                 tag: 'button',
4121                 type: 'button',
4122                 cls: 'navbar-toggle navbar-toggler',
4123                 'data-toggle': 'collapse',
4124                 cn: [
4125                     {
4126                         tag: 'span',
4127                         cls: 'sr-only',
4128                         html: 'Toggle navigation'
4129                     },
4130                     {
4131                         tag: 'span',
4132                         cls: 'icon-bar navbar-toggler-icon'
4133                     },
4134                     {
4135                         tag: 'span',
4136                         cls: 'icon-bar'
4137                     },
4138                     {
4139                         tag: 'span',
4140                         cls: 'icon-bar'
4141                     }
4142                 ]
4143             };
4144             
4145             cn.push( Roo.bootstrap.version == 4 ? btn : {
4146                 tag: 'div',
4147                 cls: 'navbar-header',
4148                 cn: [
4149                     btn
4150                 ]
4151             });
4152         }
4153         
4154         cn.push({
4155             tag: 'div',
4156             cls: 'collapse navbar-collapse',
4157             cn : []
4158         });
4159         
4160         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4161         
4162         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4163             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4164             
4165             // tag can override this..
4166             
4167             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4168         }
4169         
4170         if (this.brand !== '') {
4171             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4172             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4173                 tag: 'a',
4174                 href: this.brand_href ? this.brand_href : '#',
4175                 cls: 'navbar-brand',
4176                 cn: [
4177                 this.brand
4178                 ]
4179             });
4180         }
4181         
4182         if(this.main){
4183             cfg.cls += ' main-nav';
4184         }
4185         
4186         
4187         return cfg;
4188
4189         
4190     },
4191     getHeaderChildContainer : function()
4192     {
4193         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4194             return this.el.select('.navbar-header',true).first();
4195         }
4196         
4197         return this.getChildContainer();
4198     },
4199     
4200     
4201     initEvents : function()
4202     {
4203         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4204         
4205         if (this.autohide) {
4206             
4207             var prevScroll = 0;
4208             var ft = this.el;
4209             
4210             Roo.get(document).on('scroll',function(e) {
4211                 var ns = Roo.get(document).getScroll().top;
4212                 var os = prevScroll;
4213                 prevScroll = ns;
4214                 
4215                 if(ns > os){
4216                     ft.removeClass('slideDown');
4217                     ft.addClass('slideUp');
4218                     return;
4219                 }
4220                 ft.removeClass('slideUp');
4221                 ft.addClass('slideDown');
4222                  
4223               
4224           },this);
4225         }
4226     }    
4227     
4228 });
4229
4230
4231
4232  
4233
4234  /*
4235  * - LGPL
4236  *
4237  * navbar
4238  * 
4239  */
4240
4241 /**
4242  * @class Roo.bootstrap.NavSidebar
4243  * @extends Roo.bootstrap.Navbar
4244  * Bootstrap Sidebar class
4245  * 
4246  * @constructor
4247  * Create a new Sidebar
4248  * @param {Object} config The config object
4249  */
4250
4251
4252 Roo.bootstrap.NavSidebar = function(config){
4253     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4254 };
4255
4256 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4257     
4258     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4259     
4260     getAutoCreate : function(){
4261         
4262         
4263         return  {
4264             tag: 'div',
4265             cls: 'sidebar sidebar-nav'
4266         };
4267     
4268         
4269     }
4270     
4271     
4272     
4273 });
4274
4275
4276
4277  
4278
4279  /*
4280  * - LGPL
4281  *
4282  * nav group
4283  * 
4284  */
4285
4286 /**
4287  * @class Roo.bootstrap.NavGroup
4288  * @extends Roo.bootstrap.Component
4289  * Bootstrap NavGroup class
4290  * @cfg {String} align (left|right)
4291  * @cfg {Boolean} inverse
4292  * @cfg {String} type (nav|pills|tab) default nav
4293  * @cfg {String} navId - reference Id for navbar.
4294
4295  * 
4296  * @constructor
4297  * Create a new nav group
4298  * @param {Object} config The config object
4299  */
4300
4301 Roo.bootstrap.NavGroup = function(config){
4302     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4303     this.navItems = [];
4304    
4305     Roo.bootstrap.NavGroup.register(this);
4306      this.addEvents({
4307         /**
4308              * @event changed
4309              * Fires when the active item changes
4310              * @param {Roo.bootstrap.NavGroup} this
4311              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4312              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4313          */
4314         'changed': true
4315      });
4316     
4317 };
4318
4319 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4320     
4321     align: '',
4322     inverse: false,
4323     form: false,
4324     type: 'nav',
4325     navId : '',
4326     // private
4327     
4328     navItems : false, 
4329     
4330     getAutoCreate : function()
4331     {
4332         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4333         
4334         cfg = {
4335             tag : 'ul',
4336             cls: 'nav' 
4337         };
4338         
4339         if (['tabs','pills'].indexOf(this.type)!==-1) {
4340             cfg.cls += ' nav-' + this.type
4341         } else {
4342             if (this.type!=='nav') {
4343                 Roo.log('nav type must be nav/tabs/pills')
4344             }
4345             cfg.cls += ' navbar-nav'
4346         }
4347         
4348         if (this.parent() && this.parent().sidebar) {
4349             cfg = {
4350                 tag: 'ul',
4351                 cls: 'dashboard-menu sidebar-menu'
4352             };
4353             
4354             return cfg;
4355         }
4356         
4357         if (this.form === true) {
4358             cfg = {
4359                 tag: 'form',
4360                 cls: 'navbar-form'
4361             };
4362             
4363             if (this.align === 'right') {
4364                 cfg.cls += ' navbar-right ml-md-auto';
4365             } else {
4366                 cfg.cls += ' navbar-left';
4367             }
4368         }
4369         
4370         if (this.align === 'right') {
4371             cfg.cls += ' navbar-right ml-md-auto';
4372         } else {
4373             cfg.cls += ' mr-auto';
4374         }
4375         
4376         if (this.inverse) {
4377             cfg.cls += ' navbar-inverse';
4378             
4379         }
4380         
4381         
4382         return cfg;
4383     },
4384     /**
4385     * sets the active Navigation item
4386     * @param {Roo.bootstrap.NavItem} the new current navitem
4387     */
4388     setActiveItem : function(item)
4389     {
4390         var prev = false;
4391         Roo.each(this.navItems, function(v){
4392             if (v == item) {
4393                 return ;
4394             }
4395             if (v.isActive()) {
4396                 v.setActive(false, true);
4397                 prev = v;
4398                 
4399             }
4400             
4401         });
4402
4403         item.setActive(true, true);
4404         this.fireEvent('changed', this, item, prev);
4405         
4406         
4407     },
4408     /**
4409     * gets the active Navigation item
4410     * @return {Roo.bootstrap.NavItem} the current navitem
4411     */
4412     getActive : function()
4413     {
4414         
4415         var prev = false;
4416         Roo.each(this.navItems, function(v){
4417             
4418             if (v.isActive()) {
4419                 prev = v;
4420                 
4421             }
4422             
4423         });
4424         return prev;
4425     },
4426     
4427     indexOfNav : function()
4428     {
4429         
4430         var prev = false;
4431         Roo.each(this.navItems, function(v,i){
4432             
4433             if (v.isActive()) {
4434                 prev = i;
4435                 
4436             }
4437             
4438         });
4439         return prev;
4440     },
4441     /**
4442     * adds a Navigation item
4443     * @param {Roo.bootstrap.NavItem} the navitem to add
4444     */
4445     addItem : function(cfg)
4446     {
4447         var cn = new Roo.bootstrap.NavItem(cfg);
4448         this.register(cn);
4449         cn.parentId = this.id;
4450         cn.onRender(this.el, null);
4451         return cn;
4452     },
4453     /**
4454     * register a Navigation item
4455     * @param {Roo.bootstrap.NavItem} the navitem to add
4456     */
4457     register : function(item)
4458     {
4459         this.navItems.push( item);
4460         item.navId = this.navId;
4461     
4462     },
4463     
4464     /**
4465     * clear all the Navigation item
4466     */
4467    
4468     clearAll : function()
4469     {
4470         this.navItems = [];
4471         this.el.dom.innerHTML = '';
4472     },
4473     
4474     getNavItem: function(tabId)
4475     {
4476         var ret = false;
4477         Roo.each(this.navItems, function(e) {
4478             if (e.tabId == tabId) {
4479                ret =  e;
4480                return false;
4481             }
4482             return true;
4483             
4484         });
4485         return ret;
4486     },
4487     
4488     setActiveNext : function()
4489     {
4490         var i = this.indexOfNav(this.getActive());
4491         if (i > this.navItems.length) {
4492             return;
4493         }
4494         this.setActiveItem(this.navItems[i+1]);
4495     },
4496     setActivePrev : function()
4497     {
4498         var i = this.indexOfNav(this.getActive());
4499         if (i  < 1) {
4500             return;
4501         }
4502         this.setActiveItem(this.navItems[i-1]);
4503     },
4504     clearWasActive : function(except) {
4505         Roo.each(this.navItems, function(e) {
4506             if (e.tabId != except.tabId && e.was_active) {
4507                e.was_active = false;
4508                return false;
4509             }
4510             return true;
4511             
4512         });
4513     },
4514     getWasActive : function ()
4515     {
4516         var r = false;
4517         Roo.each(this.navItems, function(e) {
4518             if (e.was_active) {
4519                r = e;
4520                return false;
4521             }
4522             return true;
4523             
4524         });
4525         return r;
4526     }
4527     
4528     
4529 });
4530
4531  
4532 Roo.apply(Roo.bootstrap.NavGroup, {
4533     
4534     groups: {},
4535      /**
4536     * register a Navigation Group
4537     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4538     */
4539     register : function(navgrp)
4540     {
4541         this.groups[navgrp.navId] = navgrp;
4542         
4543     },
4544     /**
4545     * fetch a Navigation Group based on the navigation ID
4546     * @param {string} the navgroup to add
4547     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4548     */
4549     get: function(navId) {
4550         if (typeof(this.groups[navId]) == 'undefined') {
4551             return false;
4552             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4553         }
4554         return this.groups[navId] ;
4555     }
4556     
4557     
4558     
4559 });
4560
4561  /*
4562  * - LGPL
4563  *
4564  * row
4565  * 
4566  */
4567
4568 /**
4569  * @class Roo.bootstrap.NavItem
4570  * @extends Roo.bootstrap.Component
4571  * Bootstrap Navbar.NavItem class
4572  * @cfg {String} href  link to
4573  * @cfg {String} html content of button
4574  * @cfg {String} badge text inside badge
4575  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4576  * @cfg {String} glyphicon DEPRICATED - use fa
4577  * @cfg {String} icon DEPRICATED - use fa
4578  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4579  * @cfg {Boolean} active Is item active
4580  * @cfg {Boolean} disabled Is item disabled
4581  
4582  * @cfg {Boolean} preventDefault (true | false) default false
4583  * @cfg {String} tabId the tab that this item activates.
4584  * @cfg {String} tagtype (a|span) render as a href or span?
4585  * @cfg {Boolean} animateRef (true|false) link to element default false  
4586   
4587  * @constructor
4588  * Create a new Navbar Item
4589  * @param {Object} config The config object
4590  */
4591 Roo.bootstrap.NavItem = function(config){
4592     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4593     this.addEvents({
4594         // raw events
4595         /**
4596          * @event click
4597          * The raw click event for the entire grid.
4598          * @param {Roo.EventObject} e
4599          */
4600         "click" : true,
4601          /**
4602             * @event changed
4603             * Fires when the active item active state changes
4604             * @param {Roo.bootstrap.NavItem} this
4605             * @param {boolean} state the new state
4606              
4607          */
4608         'changed': true,
4609         /**
4610             * @event scrollto
4611             * Fires when scroll to element
4612             * @param {Roo.bootstrap.NavItem} this
4613             * @param {Object} options
4614             * @param {Roo.EventObject} e
4615              
4616          */
4617         'scrollto': true
4618     });
4619    
4620 };
4621
4622 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4623     
4624     href: false,
4625     html: '',
4626     badge: '',
4627     icon: false,
4628     fa : false,
4629     glyphicon: false,
4630     active: false,
4631     preventDefault : false,
4632     tabId : false,
4633     tagtype : 'a',
4634     disabled : false,
4635     animateRef : false,
4636     was_active : false,
4637     
4638     getAutoCreate : function(){
4639          
4640         var cfg = {
4641             tag: 'li',
4642             cls: 'nav-item'
4643             
4644         };
4645         
4646         if (this.active) {
4647             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4648         }
4649         if (this.disabled) {
4650             cfg.cls += ' disabled';
4651         }
4652         
4653         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4654             cfg.cn = [
4655                 {
4656                     tag: this.tagtype,
4657                     href : this.href || "#",
4658                     html: this.html || ''
4659                 }
4660             ];
4661             if (this.tagtype == 'a') {
4662                 cfg.cn[0].cls = 'nav-link';
4663             }
4664             if (this.icon) {
4665                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4666             }
4667             if (this.fa) {
4668                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4669             }
4670             if(this.glyphicon) {
4671                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4672             }
4673             
4674             if (this.menu) {
4675                 
4676                 cfg.cn[0].html += " <span class='caret'></span>";
4677              
4678             }
4679             
4680             if (this.badge !== '') {
4681                  
4682                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4683             }
4684         }
4685         
4686         
4687         
4688         return cfg;
4689     },
4690     initEvents: function() 
4691     {
4692         if (typeof (this.menu) != 'undefined') {
4693             this.menu.parentType = this.xtype;
4694             this.menu.triggerEl = this.el;
4695             this.menu = this.addxtype(Roo.apply({}, this.menu));
4696         }
4697         
4698         this.el.select('a',true).on('click', this.onClick, this);
4699         
4700         if(this.tagtype == 'span'){
4701             this.el.select('span',true).on('click', this.onClick, this);
4702         }
4703        
4704         // at this point parent should be available..
4705         this.parent().register(this);
4706     },
4707     
4708     onClick : function(e)
4709     {
4710         if (e.getTarget('.dropdown-menu-item')) {
4711             // did you click on a menu itemm.... - then don't trigger onclick..
4712             return;
4713         }
4714         
4715         if(
4716                 this.preventDefault || 
4717                 this.href == '#' 
4718         ){
4719             Roo.log("NavItem - prevent Default?");
4720             e.preventDefault();
4721         }
4722         
4723         if (this.disabled) {
4724             return;
4725         }
4726         
4727         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4728         if (tg && tg.transition) {
4729             Roo.log("waiting for the transitionend");
4730             return;
4731         }
4732         
4733         
4734         
4735         //Roo.log("fire event clicked");
4736         if(this.fireEvent('click', this, e) === false){
4737             return;
4738         };
4739         
4740         if(this.tagtype == 'span'){
4741             return;
4742         }
4743         
4744         //Roo.log(this.href);
4745         var ael = this.el.select('a',true).first();
4746         //Roo.log(ael);
4747         
4748         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4749             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4750             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4751                 return; // ignore... - it's a 'hash' to another page.
4752             }
4753             Roo.log("NavItem - prevent Default?");
4754             e.preventDefault();
4755             this.scrollToElement(e);
4756         }
4757         
4758         
4759         var p =  this.parent();
4760    
4761         if (['tabs','pills'].indexOf(p.type)!==-1) {
4762             if (typeof(p.setActiveItem) !== 'undefined') {
4763                 p.setActiveItem(this);
4764             }
4765         }
4766         
4767         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4768         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4769             // remove the collapsed menu expand...
4770             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4771         }
4772     },
4773     
4774     isActive: function () {
4775         return this.active
4776     },
4777     setActive : function(state, fire, is_was_active)
4778     {
4779         if (this.active && !state && this.navId) {
4780             this.was_active = true;
4781             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4782             if (nv) {
4783                 nv.clearWasActive(this);
4784             }
4785             
4786         }
4787         this.active = state;
4788         
4789         if (!state ) {
4790             this.el.removeClass('active');
4791         } else if (!this.el.hasClass('active')) {
4792             this.el.addClass('active');
4793         }
4794         if (fire) {
4795             this.fireEvent('changed', this, state);
4796         }
4797         
4798         // show a panel if it's registered and related..
4799         
4800         if (!this.navId || !this.tabId || !state || is_was_active) {
4801             return;
4802         }
4803         
4804         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4805         if (!tg) {
4806             return;
4807         }
4808         var pan = tg.getPanelByName(this.tabId);
4809         if (!pan) {
4810             return;
4811         }
4812         // if we can not flip to new panel - go back to old nav highlight..
4813         if (false == tg.showPanel(pan)) {
4814             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4815             if (nv) {
4816                 var onav = nv.getWasActive();
4817                 if (onav) {
4818                     onav.setActive(true, false, true);
4819                 }
4820             }
4821             
4822         }
4823         
4824         
4825         
4826     },
4827      // this should not be here...
4828     setDisabled : function(state)
4829     {
4830         this.disabled = state;
4831         if (!state ) {
4832             this.el.removeClass('disabled');
4833         } else if (!this.el.hasClass('disabled')) {
4834             this.el.addClass('disabled');
4835         }
4836         
4837     },
4838     
4839     /**
4840      * Fetch the element to display the tooltip on.
4841      * @return {Roo.Element} defaults to this.el
4842      */
4843     tooltipEl : function()
4844     {
4845         return this.el.select('' + this.tagtype + '', true).first();
4846     },
4847     
4848     scrollToElement : function(e)
4849     {
4850         var c = document.body;
4851         
4852         /*
4853          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4854          */
4855         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4856             c = document.documentElement;
4857         }
4858         
4859         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4860         
4861         if(!target){
4862             return;
4863         }
4864
4865         var o = target.calcOffsetsTo(c);
4866         
4867         var options = {
4868             target : target,
4869             value : o[1]
4870         };
4871         
4872         this.fireEvent('scrollto', this, options, e);
4873         
4874         Roo.get(c).scrollTo('top', options.value, true);
4875         
4876         return;
4877     }
4878 });
4879  
4880
4881  /*
4882  * - LGPL
4883  *
4884  * sidebar item
4885  *
4886  *  li
4887  *    <span> icon </span>
4888  *    <span> text </span>
4889  *    <span>badge </span>
4890  */
4891
4892 /**
4893  * @class Roo.bootstrap.NavSidebarItem
4894  * @extends Roo.bootstrap.NavItem
4895  * Bootstrap Navbar.NavSidebarItem class
4896  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4897  * {Boolean} open is the menu open
4898  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4899  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4900  * {String} buttonSize (sm|md|lg)the extra classes for the button
4901  * {Boolean} showArrow show arrow next to the text (default true)
4902  * @constructor
4903  * Create a new Navbar Button
4904  * @param {Object} config The config object
4905  */
4906 Roo.bootstrap.NavSidebarItem = function(config){
4907     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4908     this.addEvents({
4909         // raw events
4910         /**
4911          * @event click
4912          * The raw click event for the entire grid.
4913          * @param {Roo.EventObject} e
4914          */
4915         "click" : true,
4916          /**
4917             * @event changed
4918             * Fires when the active item active state changes
4919             * @param {Roo.bootstrap.NavSidebarItem} this
4920             * @param {boolean} state the new state
4921              
4922          */
4923         'changed': true
4924     });
4925    
4926 };
4927
4928 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4929     
4930     badgeWeight : 'default',
4931     
4932     open: false,
4933     
4934     buttonView : false,
4935     
4936     buttonWeight : 'default',
4937     
4938     buttonSize : 'md',
4939     
4940     showArrow : true,
4941     
4942     getAutoCreate : function(){
4943         
4944         
4945         var a = {
4946                 tag: 'a',
4947                 href : this.href || '#',
4948                 cls: '',
4949                 html : '',
4950                 cn : []
4951         };
4952         
4953         if(this.buttonView){
4954             a = {
4955                 tag: 'button',
4956                 href : this.href || '#',
4957                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4958                 html : this.html,
4959                 cn : []
4960             };
4961         }
4962         
4963         var cfg = {
4964             tag: 'li',
4965             cls: '',
4966             cn: [ a ]
4967         };
4968         
4969         if (this.active) {
4970             cfg.cls += ' active';
4971         }
4972         
4973         if (this.disabled) {
4974             cfg.cls += ' disabled';
4975         }
4976         if (this.open) {
4977             cfg.cls += ' open x-open';
4978         }
4979         // left icon..
4980         if (this.glyphicon || this.icon) {
4981             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4982             a.cn.push({ tag : 'i', cls : c }) ;
4983         }
4984         
4985         if(!this.buttonView){
4986             var span = {
4987                 tag: 'span',
4988                 html : this.html || ''
4989             };
4990
4991             a.cn.push(span);
4992             
4993         }
4994         
4995         if (this.badge !== '') {
4996             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4997         }
4998         
4999         if (this.menu) {
5000             
5001             if(this.showArrow){
5002                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5003             }
5004             
5005             a.cls += ' dropdown-toggle treeview' ;
5006         }
5007         
5008         return cfg;
5009     },
5010     
5011     initEvents : function()
5012     { 
5013         if (typeof (this.menu) != 'undefined') {
5014             this.menu.parentType = this.xtype;
5015             this.menu.triggerEl = this.el;
5016             this.menu = this.addxtype(Roo.apply({}, this.menu));
5017         }
5018         
5019         this.el.on('click', this.onClick, this);
5020         
5021         if(this.badge !== ''){
5022             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5023         }
5024         
5025     },
5026     
5027     onClick : function(e)
5028     {
5029         if(this.disabled){
5030             e.preventDefault();
5031             return;
5032         }
5033         
5034         if(this.preventDefault){
5035             e.preventDefault();
5036         }
5037         
5038         this.fireEvent('click', this);
5039     },
5040     
5041     disable : function()
5042     {
5043         this.setDisabled(true);
5044     },
5045     
5046     enable : function()
5047     {
5048         this.setDisabled(false);
5049     },
5050     
5051     setDisabled : function(state)
5052     {
5053         if(this.disabled == state){
5054             return;
5055         }
5056         
5057         this.disabled = state;
5058         
5059         if (state) {
5060             this.el.addClass('disabled');
5061             return;
5062         }
5063         
5064         this.el.removeClass('disabled');
5065         
5066         return;
5067     },
5068     
5069     setActive : function(state)
5070     {
5071         if(this.active == state){
5072             return;
5073         }
5074         
5075         this.active = state;
5076         
5077         if (state) {
5078             this.el.addClass('active');
5079             return;
5080         }
5081         
5082         this.el.removeClass('active');
5083         
5084         return;
5085     },
5086     
5087     isActive: function () 
5088     {
5089         return this.active;
5090     },
5091     
5092     setBadge : function(str)
5093     {
5094         if(!this.badgeEl){
5095             return;
5096         }
5097         
5098         this.badgeEl.dom.innerHTML = str;
5099     }
5100     
5101    
5102      
5103  
5104 });
5105  
5106
5107  /*
5108  * - LGPL
5109  *
5110  * row
5111  * 
5112  */
5113
5114 /**
5115  * @class Roo.bootstrap.Row
5116  * @extends Roo.bootstrap.Component
5117  * Bootstrap Row class (contains columns...)
5118  * 
5119  * @constructor
5120  * Create a new Row
5121  * @param {Object} config The config object
5122  */
5123
5124 Roo.bootstrap.Row = function(config){
5125     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5126 };
5127
5128 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5129     
5130     getAutoCreate : function(){
5131        return {
5132             cls: 'row clearfix'
5133        };
5134     }
5135     
5136     
5137 });
5138
5139  
5140
5141  /*
5142  * - LGPL
5143  *
5144  * element
5145  * 
5146  */
5147
5148 /**
5149  * @class Roo.bootstrap.Element
5150  * @extends Roo.bootstrap.Component
5151  * Bootstrap Element class
5152  * @cfg {String} html contents of the element
5153  * @cfg {String} tag tag of the element
5154  * @cfg {String} cls class of the element
5155  * @cfg {Boolean} preventDefault (true|false) default false
5156  * @cfg {Boolean} clickable (true|false) default false
5157  * 
5158  * @constructor
5159  * Create a new Element
5160  * @param {Object} config The config object
5161  */
5162
5163 Roo.bootstrap.Element = function(config){
5164     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5165     
5166     this.addEvents({
5167         // raw events
5168         /**
5169          * @event click
5170          * When a element is chick
5171          * @param {Roo.bootstrap.Element} this
5172          * @param {Roo.EventObject} e
5173          */
5174         "click" : true
5175     });
5176 };
5177
5178 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5179     
5180     tag: 'div',
5181     cls: '',
5182     html: '',
5183     preventDefault: false, 
5184     clickable: false,
5185     
5186     getAutoCreate : function(){
5187         
5188         var cfg = {
5189             tag: this.tag,
5190             // cls: this.cls, double assign in parent class Component.js :: onRender
5191             html: this.html
5192         };
5193         
5194         return cfg;
5195     },
5196     
5197     initEvents: function() 
5198     {
5199         Roo.bootstrap.Element.superclass.initEvents.call(this);
5200         
5201         if(this.clickable){
5202             this.el.on('click', this.onClick, this);
5203         }
5204         
5205     },
5206     
5207     onClick : function(e)
5208     {
5209         if(this.preventDefault){
5210             e.preventDefault();
5211         }
5212         
5213         this.fireEvent('click', this, e);
5214     },
5215     
5216     getValue : function()
5217     {
5218         return this.el.dom.innerHTML;
5219     },
5220     
5221     setValue : function(value)
5222     {
5223         this.el.dom.innerHTML = value;
5224     }
5225    
5226 });
5227
5228  
5229
5230  /*
5231  * - LGPL
5232  *
5233  * pagination
5234  * 
5235  */
5236
5237 /**
5238  * @class Roo.bootstrap.Pagination
5239  * @extends Roo.bootstrap.Component
5240  * Bootstrap Pagination class
5241  * @cfg {String} size xs | sm | md | lg
5242  * @cfg {Boolean} inverse false | true
5243  * 
5244  * @constructor
5245  * Create a new Pagination
5246  * @param {Object} config The config object
5247  */
5248
5249 Roo.bootstrap.Pagination = function(config){
5250     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5251 };
5252
5253 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5254     
5255     cls: false,
5256     size: false,
5257     inverse: false,
5258     
5259     getAutoCreate : function(){
5260         var cfg = {
5261             tag: 'ul',
5262                 cls: 'pagination'
5263         };
5264         if (this.inverse) {
5265             cfg.cls += ' inverse';
5266         }
5267         if (this.html) {
5268             cfg.html=this.html;
5269         }
5270         if (this.cls) {
5271             cfg.cls += " " + this.cls;
5272         }
5273         return cfg;
5274     }
5275    
5276 });
5277
5278  
5279
5280  /*
5281  * - LGPL
5282  *
5283  * Pagination item
5284  * 
5285  */
5286
5287
5288 /**
5289  * @class Roo.bootstrap.PaginationItem
5290  * @extends Roo.bootstrap.Component
5291  * Bootstrap PaginationItem class
5292  * @cfg {String} html text
5293  * @cfg {String} href the link
5294  * @cfg {Boolean} preventDefault (true | false) default true
5295  * @cfg {Boolean} active (true | false) default false
5296  * @cfg {Boolean} disabled default false
5297  * 
5298  * 
5299  * @constructor
5300  * Create a new PaginationItem
5301  * @param {Object} config The config object
5302  */
5303
5304
5305 Roo.bootstrap.PaginationItem = function(config){
5306     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5307     this.addEvents({
5308         // raw events
5309         /**
5310          * @event click
5311          * The raw click event for the entire grid.
5312          * @param {Roo.EventObject} e
5313          */
5314         "click" : true
5315     });
5316 };
5317
5318 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5319     
5320     href : false,
5321     html : false,
5322     preventDefault: true,
5323     active : false,
5324     cls : false,
5325     disabled: false,
5326     
5327     getAutoCreate : function(){
5328         var cfg= {
5329             tag: 'li',
5330             cn: [
5331                 {
5332                     tag : 'a',
5333                     href : this.href ? this.href : '#',
5334                     html : this.html ? this.html : ''
5335                 }
5336             ]
5337         };
5338         
5339         if(this.cls){
5340             cfg.cls = this.cls;
5341         }
5342         
5343         if(this.disabled){
5344             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5345         }
5346         
5347         if(this.active){
5348             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5349         }
5350         
5351         return cfg;
5352     },
5353     
5354     initEvents: function() {
5355         
5356         this.el.on('click', this.onClick, this);
5357         
5358     },
5359     onClick : function(e)
5360     {
5361         Roo.log('PaginationItem on click ');
5362         if(this.preventDefault){
5363             e.preventDefault();
5364         }
5365         
5366         if(this.disabled){
5367             return;
5368         }
5369         
5370         this.fireEvent('click', this, e);
5371     }
5372    
5373 });
5374
5375  
5376
5377  /*
5378  * - LGPL
5379  *
5380  * slider
5381  * 
5382  */
5383
5384
5385 /**
5386  * @class Roo.bootstrap.Slider
5387  * @extends Roo.bootstrap.Component
5388  * Bootstrap Slider class
5389  *    
5390  * @constructor
5391  * Create a new Slider
5392  * @param {Object} config The config object
5393  */
5394
5395 Roo.bootstrap.Slider = function(config){
5396     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5397 };
5398
5399 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5400     
5401     getAutoCreate : function(){
5402         
5403         var cfg = {
5404             tag: 'div',
5405             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5406             cn: [
5407                 {
5408                     tag: 'a',
5409                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5410                 }
5411             ]
5412         };
5413         
5414         return cfg;
5415     }
5416    
5417 });
5418
5419  /*
5420  * Based on:
5421  * Ext JS Library 1.1.1
5422  * Copyright(c) 2006-2007, Ext JS, LLC.
5423  *
5424  * Originally Released Under LGPL - original licence link has changed is not relivant.
5425  *
5426  * Fork - LGPL
5427  * <script type="text/javascript">
5428  */
5429  
5430
5431 /**
5432  * @class Roo.grid.ColumnModel
5433  * @extends Roo.util.Observable
5434  * This is the default implementation of a ColumnModel used by the Grid. It defines
5435  * the columns in the grid.
5436  * <br>Usage:<br>
5437  <pre><code>
5438  var colModel = new Roo.grid.ColumnModel([
5439         {header: "Ticker", width: 60, sortable: true, locked: true},
5440         {header: "Company Name", width: 150, sortable: true},
5441         {header: "Market Cap.", width: 100, sortable: true},
5442         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5443         {header: "Employees", width: 100, sortable: true, resizable: false}
5444  ]);
5445  </code></pre>
5446  * <p>
5447  
5448  * The config options listed for this class are options which may appear in each
5449  * individual column definition.
5450  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5451  * @constructor
5452  * @param {Object} config An Array of column config objects. See this class's
5453  * config objects for details.
5454 */
5455 Roo.grid.ColumnModel = function(config){
5456         /**
5457      * The config passed into the constructor
5458      */
5459     this.config = config;
5460     this.lookup = {};
5461
5462     // if no id, create one
5463     // if the column does not have a dataIndex mapping,
5464     // map it to the order it is in the config
5465     for(var i = 0, len = config.length; i < len; i++){
5466         var c = config[i];
5467         if(typeof c.dataIndex == "undefined"){
5468             c.dataIndex = i;
5469         }
5470         if(typeof c.renderer == "string"){
5471             c.renderer = Roo.util.Format[c.renderer];
5472         }
5473         if(typeof c.id == "undefined"){
5474             c.id = Roo.id();
5475         }
5476         if(c.editor && c.editor.xtype){
5477             c.editor  = Roo.factory(c.editor, Roo.grid);
5478         }
5479         if(c.editor && c.editor.isFormField){
5480             c.editor = new Roo.grid.GridEditor(c.editor);
5481         }
5482         this.lookup[c.id] = c;
5483     }
5484
5485     /**
5486      * The width of columns which have no width specified (defaults to 100)
5487      * @type Number
5488      */
5489     this.defaultWidth = 100;
5490
5491     /**
5492      * Default sortable of columns which have no sortable specified (defaults to false)
5493      * @type Boolean
5494      */
5495     this.defaultSortable = false;
5496
5497     this.addEvents({
5498         /**
5499              * @event widthchange
5500              * Fires when the width of a column changes.
5501              * @param {ColumnModel} this
5502              * @param {Number} columnIndex The column index
5503              * @param {Number} newWidth The new width
5504              */
5505             "widthchange": true,
5506         /**
5507              * @event headerchange
5508              * Fires when the text of a header changes.
5509              * @param {ColumnModel} this
5510              * @param {Number} columnIndex The column index
5511              * @param {Number} newText The new header text
5512              */
5513             "headerchange": true,
5514         /**
5515              * @event hiddenchange
5516              * Fires when a column is hidden or "unhidden".
5517              * @param {ColumnModel} this
5518              * @param {Number} columnIndex The column index
5519              * @param {Boolean} hidden true if hidden, false otherwise
5520              */
5521             "hiddenchange": true,
5522             /**
5523          * @event columnmoved
5524          * Fires when a column is moved.
5525          * @param {ColumnModel} this
5526          * @param {Number} oldIndex
5527          * @param {Number} newIndex
5528          */
5529         "columnmoved" : true,
5530         /**
5531          * @event columlockchange
5532          * Fires when a column's locked state is changed
5533          * @param {ColumnModel} this
5534          * @param {Number} colIndex
5535          * @param {Boolean} locked true if locked
5536          */
5537         "columnlockchange" : true
5538     });
5539     Roo.grid.ColumnModel.superclass.constructor.call(this);
5540 };
5541 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5542     /**
5543      * @cfg {String} header The header text to display in the Grid view.
5544      */
5545     /**
5546      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5547      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5548      * specified, the column's index is used as an index into the Record's data Array.
5549      */
5550     /**
5551      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5552      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5553      */
5554     /**
5555      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5556      * Defaults to the value of the {@link #defaultSortable} property.
5557      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5558      */
5559     /**
5560      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5561      */
5562     /**
5563      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5564      */
5565     /**
5566      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5567      */
5568     /**
5569      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5570      */
5571     /**
5572      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5573      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5574      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5575      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5576      */
5577        /**
5578      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5579      */
5580     /**
5581      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5582      */
5583     /**
5584      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5585      */
5586     /**
5587      * @cfg {String} cursor (Optional)
5588      */
5589     /**
5590      * @cfg {String} tooltip (Optional)
5591      */
5592     /**
5593      * @cfg {Number} xs (Optional)
5594      */
5595     /**
5596      * @cfg {Number} sm (Optional)
5597      */
5598     /**
5599      * @cfg {Number} md (Optional)
5600      */
5601     /**
5602      * @cfg {Number} lg (Optional)
5603      */
5604     /**
5605      * Returns the id of the column at the specified index.
5606      * @param {Number} index The column index
5607      * @return {String} the id
5608      */
5609     getColumnId : function(index){
5610         return this.config[index].id;
5611     },
5612
5613     /**
5614      * Returns the column for a specified id.
5615      * @param {String} id The column id
5616      * @return {Object} the column
5617      */
5618     getColumnById : function(id){
5619         return this.lookup[id];
5620     },
5621
5622     
5623     /**
5624      * Returns the column for a specified dataIndex.
5625      * @param {String} dataIndex The column dataIndex
5626      * @return {Object|Boolean} the column or false if not found
5627      */
5628     getColumnByDataIndex: function(dataIndex){
5629         var index = this.findColumnIndex(dataIndex);
5630         return index > -1 ? this.config[index] : false;
5631     },
5632     
5633     /**
5634      * Returns the index for a specified column id.
5635      * @param {String} id The column id
5636      * @return {Number} the index, or -1 if not found
5637      */
5638     getIndexById : function(id){
5639         for(var i = 0, len = this.config.length; i < len; i++){
5640             if(this.config[i].id == id){
5641                 return i;
5642             }
5643         }
5644         return -1;
5645     },
5646     
5647     /**
5648      * Returns the index for a specified column dataIndex.
5649      * @param {String} dataIndex The column dataIndex
5650      * @return {Number} the index, or -1 if not found
5651      */
5652     
5653     findColumnIndex : function(dataIndex){
5654         for(var i = 0, len = this.config.length; i < len; i++){
5655             if(this.config[i].dataIndex == dataIndex){
5656                 return i;
5657             }
5658         }
5659         return -1;
5660     },
5661     
5662     
5663     moveColumn : function(oldIndex, newIndex){
5664         var c = this.config[oldIndex];
5665         this.config.splice(oldIndex, 1);
5666         this.config.splice(newIndex, 0, c);
5667         this.dataMap = null;
5668         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5669     },
5670
5671     isLocked : function(colIndex){
5672         return this.config[colIndex].locked === true;
5673     },
5674
5675     setLocked : function(colIndex, value, suppressEvent){
5676         if(this.isLocked(colIndex) == value){
5677             return;
5678         }
5679         this.config[colIndex].locked = value;
5680         if(!suppressEvent){
5681             this.fireEvent("columnlockchange", this, colIndex, value);
5682         }
5683     },
5684
5685     getTotalLockedWidth : function(){
5686         var totalWidth = 0;
5687         for(var i = 0; i < this.config.length; i++){
5688             if(this.isLocked(i) && !this.isHidden(i)){
5689                 this.totalWidth += this.getColumnWidth(i);
5690             }
5691         }
5692         return totalWidth;
5693     },
5694
5695     getLockedCount : function(){
5696         for(var i = 0, len = this.config.length; i < len; i++){
5697             if(!this.isLocked(i)){
5698                 return i;
5699             }
5700         }
5701         
5702         return this.config.length;
5703     },
5704
5705     /**
5706      * Returns the number of columns.
5707      * @return {Number}
5708      */
5709     getColumnCount : function(visibleOnly){
5710         if(visibleOnly === true){
5711             var c = 0;
5712             for(var i = 0, len = this.config.length; i < len; i++){
5713                 if(!this.isHidden(i)){
5714                     c++;
5715                 }
5716             }
5717             return c;
5718         }
5719         return this.config.length;
5720     },
5721
5722     /**
5723      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5724      * @param {Function} fn
5725      * @param {Object} scope (optional)
5726      * @return {Array} result
5727      */
5728     getColumnsBy : function(fn, scope){
5729         var r = [];
5730         for(var i = 0, len = this.config.length; i < len; i++){
5731             var c = this.config[i];
5732             if(fn.call(scope||this, c, i) === true){
5733                 r[r.length] = c;
5734             }
5735         }
5736         return r;
5737     },
5738
5739     /**
5740      * Returns true if the specified column is sortable.
5741      * @param {Number} col The column index
5742      * @return {Boolean}
5743      */
5744     isSortable : function(col){
5745         if(typeof this.config[col].sortable == "undefined"){
5746             return this.defaultSortable;
5747         }
5748         return this.config[col].sortable;
5749     },
5750
5751     /**
5752      * Returns the rendering (formatting) function defined for the column.
5753      * @param {Number} col The column index.
5754      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5755      */
5756     getRenderer : function(col){
5757         if(!this.config[col].renderer){
5758             return Roo.grid.ColumnModel.defaultRenderer;
5759         }
5760         return this.config[col].renderer;
5761     },
5762
5763     /**
5764      * Sets the rendering (formatting) function for a column.
5765      * @param {Number} col The column index
5766      * @param {Function} fn The function to use to process the cell's raw data
5767      * to return HTML markup for the grid view. The render function is called with
5768      * the following parameters:<ul>
5769      * <li>Data value.</li>
5770      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5771      * <li>css A CSS style string to apply to the table cell.</li>
5772      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5773      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5774      * <li>Row index</li>
5775      * <li>Column index</li>
5776      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5777      */
5778     setRenderer : function(col, fn){
5779         this.config[col].renderer = fn;
5780     },
5781
5782     /**
5783      * Returns the width for the specified column.
5784      * @param {Number} col The column index
5785      * @return {Number}
5786      */
5787     getColumnWidth : function(col){
5788         return this.config[col].width * 1 || this.defaultWidth;
5789     },
5790
5791     /**
5792      * Sets the width for a column.
5793      * @param {Number} col The column index
5794      * @param {Number} width The new width
5795      */
5796     setColumnWidth : function(col, width, suppressEvent){
5797         this.config[col].width = width;
5798         this.totalWidth = null;
5799         if(!suppressEvent){
5800              this.fireEvent("widthchange", this, col, width);
5801         }
5802     },
5803
5804     /**
5805      * Returns the total width of all columns.
5806      * @param {Boolean} includeHidden True to include hidden column widths
5807      * @return {Number}
5808      */
5809     getTotalWidth : function(includeHidden){
5810         if(!this.totalWidth){
5811             this.totalWidth = 0;
5812             for(var i = 0, len = this.config.length; i < len; i++){
5813                 if(includeHidden || !this.isHidden(i)){
5814                     this.totalWidth += this.getColumnWidth(i);
5815                 }
5816             }
5817         }
5818         return this.totalWidth;
5819     },
5820
5821     /**
5822      * Returns the header for the specified column.
5823      * @param {Number} col The column index
5824      * @return {String}
5825      */
5826     getColumnHeader : function(col){
5827         return this.config[col].header;
5828     },
5829
5830     /**
5831      * Sets the header for a column.
5832      * @param {Number} col The column index
5833      * @param {String} header The new header
5834      */
5835     setColumnHeader : function(col, header){
5836         this.config[col].header = header;
5837         this.fireEvent("headerchange", this, col, header);
5838     },
5839
5840     /**
5841      * Returns the tooltip for the specified column.
5842      * @param {Number} col The column index
5843      * @return {String}
5844      */
5845     getColumnTooltip : function(col){
5846             return this.config[col].tooltip;
5847     },
5848     /**
5849      * Sets the tooltip for a column.
5850      * @param {Number} col The column index
5851      * @param {String} tooltip The new tooltip
5852      */
5853     setColumnTooltip : function(col, tooltip){
5854             this.config[col].tooltip = tooltip;
5855     },
5856
5857     /**
5858      * Returns the dataIndex for the specified column.
5859      * @param {Number} col The column index
5860      * @return {Number}
5861      */
5862     getDataIndex : function(col){
5863         return this.config[col].dataIndex;
5864     },
5865
5866     /**
5867      * Sets the dataIndex for a column.
5868      * @param {Number} col The column index
5869      * @param {Number} dataIndex The new dataIndex
5870      */
5871     setDataIndex : function(col, dataIndex){
5872         this.config[col].dataIndex = dataIndex;
5873     },
5874
5875     
5876     
5877     /**
5878      * Returns true if the cell is editable.
5879      * @param {Number} colIndex The column index
5880      * @param {Number} rowIndex The row index - this is nto actually used..?
5881      * @return {Boolean}
5882      */
5883     isCellEditable : function(colIndex, rowIndex){
5884         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5885     },
5886
5887     /**
5888      * Returns the editor defined for the cell/column.
5889      * return false or null to disable editing.
5890      * @param {Number} colIndex The column index
5891      * @param {Number} rowIndex The row index
5892      * @return {Object}
5893      */
5894     getCellEditor : function(colIndex, rowIndex){
5895         return this.config[colIndex].editor;
5896     },
5897
5898     /**
5899      * Sets if a column is editable.
5900      * @param {Number} col The column index
5901      * @param {Boolean} editable True if the column is editable
5902      */
5903     setEditable : function(col, editable){
5904         this.config[col].editable = editable;
5905     },
5906
5907
5908     /**
5909      * Returns true if the column is hidden.
5910      * @param {Number} colIndex The column index
5911      * @return {Boolean}
5912      */
5913     isHidden : function(colIndex){
5914         return this.config[colIndex].hidden;
5915     },
5916
5917
5918     /**
5919      * Returns true if the column width cannot be changed
5920      */
5921     isFixed : function(colIndex){
5922         return this.config[colIndex].fixed;
5923     },
5924
5925     /**
5926      * Returns true if the column can be resized
5927      * @return {Boolean}
5928      */
5929     isResizable : function(colIndex){
5930         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5931     },
5932     /**
5933      * Sets if a column is hidden.
5934      * @param {Number} colIndex The column index
5935      * @param {Boolean} hidden True if the column is hidden
5936      */
5937     setHidden : function(colIndex, hidden){
5938         this.config[colIndex].hidden = hidden;
5939         this.totalWidth = null;
5940         this.fireEvent("hiddenchange", this, colIndex, hidden);
5941     },
5942
5943     /**
5944      * Sets the editor for a column.
5945      * @param {Number} col The column index
5946      * @param {Object} editor The editor object
5947      */
5948     setEditor : function(col, editor){
5949         this.config[col].editor = editor;
5950     }
5951 });
5952
5953 Roo.grid.ColumnModel.defaultRenderer = function(value)
5954 {
5955     if(typeof value == "object") {
5956         return value;
5957     }
5958         if(typeof value == "string" && value.length < 1){
5959             return "&#160;";
5960         }
5961     
5962         return String.format("{0}", value);
5963 };
5964
5965 // Alias for backwards compatibility
5966 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5967 /*
5968  * Based on:
5969  * Ext JS Library 1.1.1
5970  * Copyright(c) 2006-2007, Ext JS, LLC.
5971  *
5972  * Originally Released Under LGPL - original licence link has changed is not relivant.
5973  *
5974  * Fork - LGPL
5975  * <script type="text/javascript">
5976  */
5977  
5978 /**
5979  * @class Roo.LoadMask
5980  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5981  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5982  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5983  * element's UpdateManager load indicator and will be destroyed after the initial load.
5984  * @constructor
5985  * Create a new LoadMask
5986  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5987  * @param {Object} config The config object
5988  */
5989 Roo.LoadMask = function(el, config){
5990     this.el = Roo.get(el);
5991     Roo.apply(this, config);
5992     if(this.store){
5993         this.store.on('beforeload', this.onBeforeLoad, this);
5994         this.store.on('load', this.onLoad, this);
5995         this.store.on('loadexception', this.onLoadException, this);
5996         this.removeMask = false;
5997     }else{
5998         var um = this.el.getUpdateManager();
5999         um.showLoadIndicator = false; // disable the default indicator
6000         um.on('beforeupdate', this.onBeforeLoad, this);
6001         um.on('update', this.onLoad, this);
6002         um.on('failure', this.onLoad, this);
6003         this.removeMask = true;
6004     }
6005 };
6006
6007 Roo.LoadMask.prototype = {
6008     /**
6009      * @cfg {Boolean} removeMask
6010      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6011      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6012      */
6013     /**
6014      * @cfg {String} msg
6015      * The text to display in a centered loading message box (defaults to 'Loading...')
6016      */
6017     msg : 'Loading...',
6018     /**
6019      * @cfg {String} msgCls
6020      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6021      */
6022     msgCls : 'x-mask-loading',
6023
6024     /**
6025      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6026      * @type Boolean
6027      */
6028     disabled: false,
6029
6030     /**
6031      * Disables the mask to prevent it from being displayed
6032      */
6033     disable : function(){
6034        this.disabled = true;
6035     },
6036
6037     /**
6038      * Enables the mask so that it can be displayed
6039      */
6040     enable : function(){
6041         this.disabled = false;
6042     },
6043     
6044     onLoadException : function()
6045     {
6046         Roo.log(arguments);
6047         
6048         if (typeof(arguments[3]) != 'undefined') {
6049             Roo.MessageBox.alert("Error loading",arguments[3]);
6050         } 
6051         /*
6052         try {
6053             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6054                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6055             }   
6056         } catch(e) {
6057             
6058         }
6059         */
6060     
6061         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6062     },
6063     // private
6064     onLoad : function()
6065     {
6066         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6067     },
6068
6069     // private
6070     onBeforeLoad : function(){
6071         if(!this.disabled){
6072             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6073         }
6074     },
6075
6076     // private
6077     destroy : function(){
6078         if(this.store){
6079             this.store.un('beforeload', this.onBeforeLoad, this);
6080             this.store.un('load', this.onLoad, this);
6081             this.store.un('loadexception', this.onLoadException, this);
6082         }else{
6083             var um = this.el.getUpdateManager();
6084             um.un('beforeupdate', this.onBeforeLoad, this);
6085             um.un('update', this.onLoad, this);
6086             um.un('failure', this.onLoad, this);
6087         }
6088     }
6089 };/*
6090  * - LGPL
6091  *
6092  * table
6093  * 
6094  */
6095
6096 /**
6097  * @class Roo.bootstrap.Table
6098  * @extends Roo.bootstrap.Component
6099  * Bootstrap Table class
6100  * @cfg {String} cls table class
6101  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6102  * @cfg {String} bgcolor Specifies the background color for a table
6103  * @cfg {Number} border Specifies whether the table cells should have borders or not
6104  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6105  * @cfg {Number} cellspacing Specifies the space between cells
6106  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6107  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6108  * @cfg {String} sortable Specifies that the table should be sortable
6109  * @cfg {String} summary Specifies a summary of the content of a table
6110  * @cfg {Number} width Specifies the width of a table
6111  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6112  * 
6113  * @cfg {boolean} striped Should the rows be alternative striped
6114  * @cfg {boolean} bordered Add borders to the table
6115  * @cfg {boolean} hover Add hover highlighting
6116  * @cfg {boolean} condensed Format condensed
6117  * @cfg {boolean} responsive Format condensed
6118  * @cfg {Boolean} loadMask (true|false) default false
6119  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6120  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6121  * @cfg {Boolean} rowSelection (true|false) default false
6122  * @cfg {Boolean} cellSelection (true|false) default false
6123  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6124  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6125  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6126  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6127  
6128  * 
6129  * @constructor
6130  * Create a new Table
6131  * @param {Object} config The config object
6132  */
6133
6134 Roo.bootstrap.Table = function(config){
6135     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6136     
6137   
6138     
6139     // BC...
6140     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6141     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6142     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6143     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6144     
6145     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6146     if (this.sm) {
6147         this.sm.grid = this;
6148         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6149         this.sm = this.selModel;
6150         this.sm.xmodule = this.xmodule || false;
6151     }
6152     
6153     if (this.cm && typeof(this.cm.config) == 'undefined') {
6154         this.colModel = new Roo.grid.ColumnModel(this.cm);
6155         this.cm = this.colModel;
6156         this.cm.xmodule = this.xmodule || false;
6157     }
6158     if (this.store) {
6159         this.store= Roo.factory(this.store, Roo.data);
6160         this.ds = this.store;
6161         this.ds.xmodule = this.xmodule || false;
6162          
6163     }
6164     if (this.footer && this.store) {
6165         this.footer.dataSource = this.ds;
6166         this.footer = Roo.factory(this.footer);
6167     }
6168     
6169     /** @private */
6170     this.addEvents({
6171         /**
6172          * @event cellclick
6173          * Fires when a cell is clicked
6174          * @param {Roo.bootstrap.Table} this
6175          * @param {Roo.Element} el
6176          * @param {Number} rowIndex
6177          * @param {Number} columnIndex
6178          * @param {Roo.EventObject} e
6179          */
6180         "cellclick" : true,
6181         /**
6182          * @event celldblclick
6183          * Fires when a cell is double clicked
6184          * @param {Roo.bootstrap.Table} this
6185          * @param {Roo.Element} el
6186          * @param {Number} rowIndex
6187          * @param {Number} columnIndex
6188          * @param {Roo.EventObject} e
6189          */
6190         "celldblclick" : true,
6191         /**
6192          * @event rowclick
6193          * Fires when a row is clicked
6194          * @param {Roo.bootstrap.Table} this
6195          * @param {Roo.Element} el
6196          * @param {Number} rowIndex
6197          * @param {Roo.EventObject} e
6198          */
6199         "rowclick" : true,
6200         /**
6201          * @event rowdblclick
6202          * Fires when a row is double clicked
6203          * @param {Roo.bootstrap.Table} this
6204          * @param {Roo.Element} el
6205          * @param {Number} rowIndex
6206          * @param {Roo.EventObject} e
6207          */
6208         "rowdblclick" : true,
6209         /**
6210          * @event mouseover
6211          * Fires when a mouseover occur
6212          * @param {Roo.bootstrap.Table} this
6213          * @param {Roo.Element} el
6214          * @param {Number} rowIndex
6215          * @param {Number} columnIndex
6216          * @param {Roo.EventObject} e
6217          */
6218         "mouseover" : true,
6219         /**
6220          * @event mouseout
6221          * Fires when a mouseout occur
6222          * @param {Roo.bootstrap.Table} this
6223          * @param {Roo.Element} el
6224          * @param {Number} rowIndex
6225          * @param {Number} columnIndex
6226          * @param {Roo.EventObject} e
6227          */
6228         "mouseout" : true,
6229         /**
6230          * @event rowclass
6231          * Fires when a row is rendered, so you can change add a style to it.
6232          * @param {Roo.bootstrap.Table} this
6233          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6234          */
6235         'rowclass' : true,
6236           /**
6237          * @event rowsrendered
6238          * Fires when all the  rows have been rendered
6239          * @param {Roo.bootstrap.Table} this
6240          */
6241         'rowsrendered' : true,
6242         /**
6243          * @event contextmenu
6244          * The raw contextmenu event for the entire grid.
6245          * @param {Roo.EventObject} e
6246          */
6247         "contextmenu" : true,
6248         /**
6249          * @event rowcontextmenu
6250          * Fires when a row is right clicked
6251          * @param {Roo.bootstrap.Table} this
6252          * @param {Number} rowIndex
6253          * @param {Roo.EventObject} e
6254          */
6255         "rowcontextmenu" : true,
6256         /**
6257          * @event cellcontextmenu
6258          * Fires when a cell is right clicked
6259          * @param {Roo.bootstrap.Table} this
6260          * @param {Number} rowIndex
6261          * @param {Number} cellIndex
6262          * @param {Roo.EventObject} e
6263          */
6264          "cellcontextmenu" : true,
6265          /**
6266          * @event headercontextmenu
6267          * Fires when a header is right clicked
6268          * @param {Roo.bootstrap.Table} this
6269          * @param {Number} columnIndex
6270          * @param {Roo.EventObject} e
6271          */
6272         "headercontextmenu" : true
6273     });
6274 };
6275
6276 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6277     
6278     cls: false,
6279     align: false,
6280     bgcolor: false,
6281     border: false,
6282     cellpadding: false,
6283     cellspacing: false,
6284     frame: false,
6285     rules: false,
6286     sortable: false,
6287     summary: false,
6288     width: false,
6289     striped : false,
6290     scrollBody : false,
6291     bordered: false,
6292     hover:  false,
6293     condensed : false,
6294     responsive : false,
6295     sm : false,
6296     cm : false,
6297     store : false,
6298     loadMask : false,
6299     footerShow : true,
6300     headerShow : true,
6301   
6302     rowSelection : false,
6303     cellSelection : false,
6304     layout : false,
6305     
6306     // Roo.Element - the tbody
6307     mainBody: false,
6308     // Roo.Element - thead element
6309     mainHead: false,
6310     
6311     container: false, // used by gridpanel...
6312     
6313     lazyLoad : false,
6314     
6315     CSS : Roo.util.CSS,
6316     
6317     auto_hide_footer : false,
6318     
6319     getAutoCreate : function()
6320     {
6321         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6322         
6323         cfg = {
6324             tag: 'table',
6325             cls : 'table',
6326             cn : []
6327         };
6328         if (this.scrollBody) {
6329             cfg.cls += ' table-body-fixed';
6330         }    
6331         if (this.striped) {
6332             cfg.cls += ' table-striped';
6333         }
6334         
6335         if (this.hover) {
6336             cfg.cls += ' table-hover';
6337         }
6338         if (this.bordered) {
6339             cfg.cls += ' table-bordered';
6340         }
6341         if (this.condensed) {
6342             cfg.cls += ' table-condensed';
6343         }
6344         if (this.responsive) {
6345             cfg.cls += ' table-responsive';
6346         }
6347         
6348         if (this.cls) {
6349             cfg.cls+=  ' ' +this.cls;
6350         }
6351         
6352         // this lot should be simplifed...
6353         var _t = this;
6354         var cp = [
6355             'align',
6356             'bgcolor',
6357             'border',
6358             'cellpadding',
6359             'cellspacing',
6360             'frame',
6361             'rules',
6362             'sortable',
6363             'summary',
6364             'width'
6365         ].forEach(function(k) {
6366             if (_t[k]) {
6367                 cfg[k] = _t[k];
6368             }
6369         });
6370         
6371         
6372         if (this.layout) {
6373             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6374         }
6375         
6376         if(this.store || this.cm){
6377             if(this.headerShow){
6378                 cfg.cn.push(this.renderHeader());
6379             }
6380             
6381             cfg.cn.push(this.renderBody());
6382             
6383             if(this.footerShow){
6384                 cfg.cn.push(this.renderFooter());
6385             }
6386             // where does this come from?
6387             //cfg.cls+=  ' TableGrid';
6388         }
6389         
6390         return { cn : [ cfg ] };
6391     },
6392     
6393     initEvents : function()
6394     {   
6395         if(!this.store || !this.cm){
6396             return;
6397         }
6398         if (this.selModel) {
6399             this.selModel.initEvents();
6400         }
6401         
6402         
6403         //Roo.log('initEvents with ds!!!!');
6404         
6405         this.mainBody = this.el.select('tbody', true).first();
6406         this.mainHead = this.el.select('thead', true).first();
6407         this.mainFoot = this.el.select('tfoot', true).first();
6408         
6409         
6410         
6411         var _this = this;
6412         
6413         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6414             e.on('click', _this.sort, _this);
6415         });
6416         
6417         this.mainBody.on("click", this.onClick, this);
6418         this.mainBody.on("dblclick", this.onDblClick, this);
6419         
6420         // why is this done????? = it breaks dialogs??
6421         //this.parent().el.setStyle('position', 'relative');
6422         
6423         
6424         if (this.footer) {
6425             this.footer.parentId = this.id;
6426             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6427             
6428             if(this.lazyLoad){
6429                 this.el.select('tfoot tr td').first().addClass('hide');
6430             }
6431         } 
6432         
6433         if(this.loadMask) {
6434             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6435         }
6436         
6437         this.store.on('load', this.onLoad, this);
6438         this.store.on('beforeload', this.onBeforeLoad, this);
6439         this.store.on('update', this.onUpdate, this);
6440         this.store.on('add', this.onAdd, this);
6441         this.store.on("clear", this.clear, this);
6442         
6443         this.el.on("contextmenu", this.onContextMenu, this);
6444         
6445         this.mainBody.on('scroll', this.onBodyScroll, this);
6446         
6447         this.cm.on("headerchange", this.onHeaderChange, this);
6448         
6449         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6450         
6451     },
6452     
6453     onContextMenu : function(e, t)
6454     {
6455         this.processEvent("contextmenu", e);
6456     },
6457     
6458     processEvent : function(name, e)
6459     {
6460         if (name != 'touchstart' ) {
6461             this.fireEvent(name, e);    
6462         }
6463         
6464         var t = e.getTarget();
6465         
6466         var cell = Roo.get(t);
6467         
6468         if(!cell){
6469             return;
6470         }
6471         
6472         if(cell.findParent('tfoot', false, true)){
6473             return;
6474         }
6475         
6476         if(cell.findParent('thead', false, true)){
6477             
6478             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6479                 cell = Roo.get(t).findParent('th', false, true);
6480                 if (!cell) {
6481                     Roo.log("failed to find th in thead?");
6482                     Roo.log(e.getTarget());
6483                     return;
6484                 }
6485             }
6486             
6487             var cellIndex = cell.dom.cellIndex;
6488             
6489             var ename = name == 'touchstart' ? 'click' : name;
6490             this.fireEvent("header" + ename, this, cellIndex, e);
6491             
6492             return;
6493         }
6494         
6495         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6496             cell = Roo.get(t).findParent('td', false, true);
6497             if (!cell) {
6498                 Roo.log("failed to find th in tbody?");
6499                 Roo.log(e.getTarget());
6500                 return;
6501             }
6502         }
6503         
6504         var row = cell.findParent('tr', false, true);
6505         var cellIndex = cell.dom.cellIndex;
6506         var rowIndex = row.dom.rowIndex - 1;
6507         
6508         if(row !== false){
6509             
6510             this.fireEvent("row" + name, this, rowIndex, e);
6511             
6512             if(cell !== false){
6513             
6514                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6515             }
6516         }
6517         
6518     },
6519     
6520     onMouseover : function(e, el)
6521     {
6522         var cell = Roo.get(el);
6523         
6524         if(!cell){
6525             return;
6526         }
6527         
6528         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6529             cell = cell.findParent('td', false, true);
6530         }
6531         
6532         var row = cell.findParent('tr', false, true);
6533         var cellIndex = cell.dom.cellIndex;
6534         var rowIndex = row.dom.rowIndex - 1; // start from 0
6535         
6536         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6537         
6538     },
6539     
6540     onMouseout : function(e, el)
6541     {
6542         var cell = Roo.get(el);
6543         
6544         if(!cell){
6545             return;
6546         }
6547         
6548         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6549             cell = cell.findParent('td', false, true);
6550         }
6551         
6552         var row = cell.findParent('tr', false, true);
6553         var cellIndex = cell.dom.cellIndex;
6554         var rowIndex = row.dom.rowIndex - 1; // start from 0
6555         
6556         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6557         
6558     },
6559     
6560     onClick : function(e, el)
6561     {
6562         var cell = Roo.get(el);
6563         
6564         if(!cell || (!this.cellSelection && !this.rowSelection)){
6565             return;
6566         }
6567         
6568         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6569             cell = cell.findParent('td', false, true);
6570         }
6571         
6572         if(!cell || typeof(cell) == 'undefined'){
6573             return;
6574         }
6575         
6576         var row = cell.findParent('tr', false, true);
6577         
6578         if(!row || typeof(row) == 'undefined'){
6579             return;
6580         }
6581         
6582         var cellIndex = cell.dom.cellIndex;
6583         var rowIndex = this.getRowIndex(row);
6584         
6585         // why??? - should these not be based on SelectionModel?
6586         if(this.cellSelection){
6587             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6588         }
6589         
6590         if(this.rowSelection){
6591             this.fireEvent('rowclick', this, row, rowIndex, e);
6592         }
6593         
6594         
6595     },
6596         
6597     onDblClick : function(e,el)
6598     {
6599         var cell = Roo.get(el);
6600         
6601         if(!cell || (!this.cellSelection && !this.rowSelection)){
6602             return;
6603         }
6604         
6605         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6606             cell = cell.findParent('td', false, true);
6607         }
6608         
6609         if(!cell || typeof(cell) == 'undefined'){
6610             return;
6611         }
6612         
6613         var row = cell.findParent('tr', false, true);
6614         
6615         if(!row || typeof(row) == 'undefined'){
6616             return;
6617         }
6618         
6619         var cellIndex = cell.dom.cellIndex;
6620         var rowIndex = this.getRowIndex(row);
6621         
6622         if(this.cellSelection){
6623             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6624         }
6625         
6626         if(this.rowSelection){
6627             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6628         }
6629     },
6630     
6631     sort : function(e,el)
6632     {
6633         var col = Roo.get(el);
6634         
6635         if(!col.hasClass('sortable')){
6636             return;
6637         }
6638         
6639         var sort = col.attr('sort');
6640         var dir = 'ASC';
6641         
6642         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6643             dir = 'DESC';
6644         }
6645         
6646         this.store.sortInfo = {field : sort, direction : dir};
6647         
6648         if (this.footer) {
6649             Roo.log("calling footer first");
6650             this.footer.onClick('first');
6651         } else {
6652         
6653             this.store.load({ params : { start : 0 } });
6654         }
6655     },
6656     
6657     renderHeader : function()
6658     {
6659         var header = {
6660             tag: 'thead',
6661             cn : []
6662         };
6663         
6664         var cm = this.cm;
6665         this.totalWidth = 0;
6666         
6667         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6668             
6669             var config = cm.config[i];
6670             
6671             var c = {
6672                 tag: 'th',
6673                 cls : 'x-hcol-' + i,
6674                 style : '',
6675                 html: cm.getColumnHeader(i)
6676             };
6677             
6678             var hh = '';
6679             
6680             if(typeof(config.sortable) != 'undefined' && config.sortable){
6681                 c.cls = 'sortable';
6682                 c.html = '<i class="glyphicon"></i>' + c.html;
6683             }
6684             
6685             if(typeof(config.lgHeader) != 'undefined'){
6686                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6687             }
6688             
6689             if(typeof(config.mdHeader) != 'undefined'){
6690                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6691             }
6692             
6693             if(typeof(config.smHeader) != 'undefined'){
6694                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6695             }
6696             
6697             if(typeof(config.xsHeader) != 'undefined'){
6698                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6699             }
6700             
6701             if(hh.length){
6702                 c.html = hh;
6703             }
6704             
6705             if(typeof(config.tooltip) != 'undefined'){
6706                 c.tooltip = config.tooltip;
6707             }
6708             
6709             if(typeof(config.colspan) != 'undefined'){
6710                 c.colspan = config.colspan;
6711             }
6712             
6713             if(typeof(config.hidden) != 'undefined' && config.hidden){
6714                 c.style += ' display:none;';
6715             }
6716             
6717             if(typeof(config.dataIndex) != 'undefined'){
6718                 c.sort = config.dataIndex;
6719             }
6720             
6721            
6722             
6723             if(typeof(config.align) != 'undefined' && config.align.length){
6724                 c.style += ' text-align:' + config.align + ';';
6725             }
6726             
6727             if(typeof(config.width) != 'undefined'){
6728                 c.style += ' width:' + config.width + 'px;';
6729                 this.totalWidth += config.width;
6730             } else {
6731                 this.totalWidth += 100; // assume minimum of 100 per column?
6732             }
6733             
6734             if(typeof(config.cls) != 'undefined'){
6735                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6736             }
6737             
6738             ['xs','sm','md','lg'].map(function(size){
6739                 
6740                 if(typeof(config[size]) == 'undefined'){
6741                     return;
6742                 }
6743                 
6744                 if (!config[size]) { // 0 = hidden
6745                     c.cls += ' hidden-' + size;
6746                     return;
6747                 }
6748                 
6749                 c.cls += ' col-' + size + '-' + config[size];
6750
6751             });
6752             
6753             header.cn.push(c)
6754         }
6755         
6756         return header;
6757     },
6758     
6759     renderBody : function()
6760     {
6761         var body = {
6762             tag: 'tbody',
6763             cn : [
6764                 {
6765                     tag: 'tr',
6766                     cn : [
6767                         {
6768                             tag : 'td',
6769                             colspan :  this.cm.getColumnCount()
6770                         }
6771                     ]
6772                 }
6773             ]
6774         };
6775         
6776         return body;
6777     },
6778     
6779     renderFooter : function()
6780     {
6781         var footer = {
6782             tag: 'tfoot',
6783             cn : [
6784                 {
6785                     tag: 'tr',
6786                     cn : [
6787                         {
6788                             tag : 'td',
6789                             colspan :  this.cm.getColumnCount()
6790                         }
6791                     ]
6792                 }
6793             ]
6794         };
6795         
6796         return footer;
6797     },
6798     
6799     
6800     
6801     onLoad : function()
6802     {
6803 //        Roo.log('ds onload');
6804         this.clear();
6805         
6806         var _this = this;
6807         var cm = this.cm;
6808         var ds = this.store;
6809         
6810         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6811             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6812             if (_this.store.sortInfo) {
6813                     
6814                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6815                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6816                 }
6817                 
6818                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6819                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6820                 }
6821             }
6822         });
6823         
6824         var tbody =  this.mainBody;
6825               
6826         if(ds.getCount() > 0){
6827             ds.data.each(function(d,rowIndex){
6828                 var row =  this.renderRow(cm, ds, rowIndex);
6829                 
6830                 tbody.createChild(row);
6831                 
6832                 var _this = this;
6833                 
6834                 if(row.cellObjects.length){
6835                     Roo.each(row.cellObjects, function(r){
6836                         _this.renderCellObject(r);
6837                     })
6838                 }
6839                 
6840             }, this);
6841         }
6842         
6843         var tfoot = this.el.select('tfoot', true).first();
6844         
6845         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6846             
6847             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6848             
6849             var total = this.ds.getTotalCount();
6850             
6851             if(this.footer.pageSize < total){
6852                 this.mainFoot.show();
6853             }
6854         }
6855         
6856         Roo.each(this.el.select('tbody td', true).elements, function(e){
6857             e.on('mouseover', _this.onMouseover, _this);
6858         });
6859         
6860         Roo.each(this.el.select('tbody td', true).elements, function(e){
6861             e.on('mouseout', _this.onMouseout, _this);
6862         });
6863         this.fireEvent('rowsrendered', this);
6864         
6865         this.autoSize();
6866     },
6867     
6868     
6869     onUpdate : function(ds,record)
6870     {
6871         this.refreshRow(record);
6872         this.autoSize();
6873     },
6874     
6875     onRemove : function(ds, record, index, isUpdate){
6876         if(isUpdate !== true){
6877             this.fireEvent("beforerowremoved", this, index, record);
6878         }
6879         var bt = this.mainBody.dom;
6880         
6881         var rows = this.el.select('tbody > tr', true).elements;
6882         
6883         if(typeof(rows[index]) != 'undefined'){
6884             bt.removeChild(rows[index].dom);
6885         }
6886         
6887 //        if(bt.rows[index]){
6888 //            bt.removeChild(bt.rows[index]);
6889 //        }
6890         
6891         if(isUpdate !== true){
6892             //this.stripeRows(index);
6893             //this.syncRowHeights(index, index);
6894             //this.layout();
6895             this.fireEvent("rowremoved", this, index, record);
6896         }
6897     },
6898     
6899     onAdd : function(ds, records, rowIndex)
6900     {
6901         //Roo.log('on Add called');
6902         // - note this does not handle multiple adding very well..
6903         var bt = this.mainBody.dom;
6904         for (var i =0 ; i < records.length;i++) {
6905             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6906             //Roo.log(records[i]);
6907             //Roo.log(this.store.getAt(rowIndex+i));
6908             this.insertRow(this.store, rowIndex + i, false);
6909             return;
6910         }
6911         
6912     },
6913     
6914     
6915     refreshRow : function(record){
6916         var ds = this.store, index;
6917         if(typeof record == 'number'){
6918             index = record;
6919             record = ds.getAt(index);
6920         }else{
6921             index = ds.indexOf(record);
6922         }
6923         this.insertRow(ds, index, true);
6924         this.autoSize();
6925         this.onRemove(ds, record, index+1, true);
6926         this.autoSize();
6927         //this.syncRowHeights(index, index);
6928         //this.layout();
6929         this.fireEvent("rowupdated", this, index, record);
6930     },
6931     
6932     insertRow : function(dm, rowIndex, isUpdate){
6933         
6934         if(!isUpdate){
6935             this.fireEvent("beforerowsinserted", this, rowIndex);
6936         }
6937             //var s = this.getScrollState();
6938         var row = this.renderRow(this.cm, this.store, rowIndex);
6939         // insert before rowIndex..
6940         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6941         
6942         var _this = this;
6943                 
6944         if(row.cellObjects.length){
6945             Roo.each(row.cellObjects, function(r){
6946                 _this.renderCellObject(r);
6947             })
6948         }
6949             
6950         if(!isUpdate){
6951             this.fireEvent("rowsinserted", this, rowIndex);
6952             //this.syncRowHeights(firstRow, lastRow);
6953             //this.stripeRows(firstRow);
6954             //this.layout();
6955         }
6956         
6957     },
6958     
6959     
6960     getRowDom : function(rowIndex)
6961     {
6962         var rows = this.el.select('tbody > tr', true).elements;
6963         
6964         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6965         
6966     },
6967     // returns the object tree for a tr..
6968   
6969     
6970     renderRow : function(cm, ds, rowIndex) 
6971     {
6972         var d = ds.getAt(rowIndex);
6973         
6974         var row = {
6975             tag : 'tr',
6976             cls : 'x-row-' + rowIndex,
6977             cn : []
6978         };
6979             
6980         var cellObjects = [];
6981         
6982         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6983             var config = cm.config[i];
6984             
6985             var renderer = cm.getRenderer(i);
6986             var value = '';
6987             var id = false;
6988             
6989             if(typeof(renderer) !== 'undefined'){
6990                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6991             }
6992             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6993             // and are rendered into the cells after the row is rendered - using the id for the element.
6994             
6995             if(typeof(value) === 'object'){
6996                 id = Roo.id();
6997                 cellObjects.push({
6998                     container : id,
6999                     cfg : value 
7000                 })
7001             }
7002             
7003             var rowcfg = {
7004                 record: d,
7005                 rowIndex : rowIndex,
7006                 colIndex : i,
7007                 rowClass : ''
7008             };
7009
7010             this.fireEvent('rowclass', this, rowcfg);
7011             
7012             var td = {
7013                 tag: 'td',
7014                 cls : rowcfg.rowClass + ' x-col-' + i,
7015                 style: '',
7016                 html: (typeof(value) === 'object') ? '' : value
7017             };
7018             
7019             if (id) {
7020                 td.id = id;
7021             }
7022             
7023             if(typeof(config.colspan) != 'undefined'){
7024                 td.colspan = config.colspan;
7025             }
7026             
7027             if(typeof(config.hidden) != 'undefined' && config.hidden){
7028                 td.style += ' display:none;';
7029             }
7030             
7031             if(typeof(config.align) != 'undefined' && config.align.length){
7032                 td.style += ' text-align:' + config.align + ';';
7033             }
7034             if(typeof(config.valign) != 'undefined' && config.valign.length){
7035                 td.style += ' vertical-align:' + config.valign + ';';
7036             }
7037             
7038             if(typeof(config.width) != 'undefined'){
7039                 td.style += ' width:' +  config.width + 'px;';
7040             }
7041             
7042             if(typeof(config.cursor) != 'undefined'){
7043                 td.style += ' cursor:' +  config.cursor + ';';
7044             }
7045             
7046             if(typeof(config.cls) != 'undefined'){
7047                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7048             }
7049             
7050             ['xs','sm','md','lg'].map(function(size){
7051                 
7052                 if(typeof(config[size]) == 'undefined'){
7053                     return;
7054                 }
7055                 
7056                 if (!config[size]) { // 0 = hidden
7057                     td.cls += ' hidden-' + size;
7058                     return;
7059                 }
7060                 
7061                 td.cls += ' col-' + size + '-' + config[size];
7062
7063             });
7064             
7065             row.cn.push(td);
7066            
7067         }
7068         
7069         row.cellObjects = cellObjects;
7070         
7071         return row;
7072           
7073     },
7074     
7075     
7076     
7077     onBeforeLoad : function()
7078     {
7079         
7080     },
7081      /**
7082      * Remove all rows
7083      */
7084     clear : function()
7085     {
7086         this.el.select('tbody', true).first().dom.innerHTML = '';
7087     },
7088     /**
7089      * Show or hide a row.
7090      * @param {Number} rowIndex to show or hide
7091      * @param {Boolean} state hide
7092      */
7093     setRowVisibility : function(rowIndex, state)
7094     {
7095         var bt = this.mainBody.dom;
7096         
7097         var rows = this.el.select('tbody > tr', true).elements;
7098         
7099         if(typeof(rows[rowIndex]) == 'undefined'){
7100             return;
7101         }
7102         rows[rowIndex].dom.style.display = state ? '' : 'none';
7103     },
7104     
7105     
7106     getSelectionModel : function(){
7107         if(!this.selModel){
7108             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7109         }
7110         return this.selModel;
7111     },
7112     /*
7113      * Render the Roo.bootstrap object from renderder
7114      */
7115     renderCellObject : function(r)
7116     {
7117         var _this = this;
7118         
7119         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7120         
7121         var t = r.cfg.render(r.container);
7122         
7123         if(r.cfg.cn){
7124             Roo.each(r.cfg.cn, function(c){
7125                 var child = {
7126                     container: t.getChildContainer(),
7127                     cfg: c
7128                 };
7129                 _this.renderCellObject(child);
7130             })
7131         }
7132     },
7133     
7134     getRowIndex : function(row)
7135     {
7136         var rowIndex = -1;
7137         
7138         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7139             if(el != row){
7140                 return;
7141             }
7142             
7143             rowIndex = index;
7144         });
7145         
7146         return rowIndex;
7147     },
7148      /**
7149      * Returns the grid's underlying element = used by panel.Grid
7150      * @return {Element} The element
7151      */
7152     getGridEl : function(){
7153         return this.el;
7154     },
7155      /**
7156      * Forces a resize - used by panel.Grid
7157      * @return {Element} The element
7158      */
7159     autoSize : function()
7160     {
7161         //var ctr = Roo.get(this.container.dom.parentElement);
7162         var ctr = Roo.get(this.el.dom);
7163         
7164         var thd = this.getGridEl().select('thead',true).first();
7165         var tbd = this.getGridEl().select('tbody', true).first();
7166         var tfd = this.getGridEl().select('tfoot', true).first();
7167         
7168         var cw = ctr.getWidth();
7169         
7170         if (tbd) {
7171             
7172             tbd.setSize(ctr.getWidth(),
7173                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7174             );
7175             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7176             cw -= barsize;
7177         }
7178         cw = Math.max(cw, this.totalWidth);
7179         this.getGridEl().select('tr',true).setWidth(cw);
7180         // resize 'expandable coloumn?
7181         
7182         return; // we doe not have a view in this design..
7183         
7184     },
7185     onBodyScroll: function()
7186     {
7187         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7188         if(this.mainHead){
7189             this.mainHead.setStyle({
7190                 'position' : 'relative',
7191                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7192             });
7193         }
7194         
7195         if(this.lazyLoad){
7196             
7197             var scrollHeight = this.mainBody.dom.scrollHeight;
7198             
7199             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7200             
7201             var height = this.mainBody.getHeight();
7202             
7203             if(scrollHeight - height == scrollTop) {
7204                 
7205                 var total = this.ds.getTotalCount();
7206                 
7207                 if(this.footer.cursor + this.footer.pageSize < total){
7208                     
7209                     this.footer.ds.load({
7210                         params : {
7211                             start : this.footer.cursor + this.footer.pageSize,
7212                             limit : this.footer.pageSize
7213                         },
7214                         add : true
7215                     });
7216                 }
7217             }
7218             
7219         }
7220     },
7221     
7222     onHeaderChange : function()
7223     {
7224         var header = this.renderHeader();
7225         var table = this.el.select('table', true).first();
7226         
7227         this.mainHead.remove();
7228         this.mainHead = table.createChild(header, this.mainBody, false);
7229     },
7230     
7231     onHiddenChange : function(colModel, colIndex, hidden)
7232     {
7233         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7234         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7235         
7236         this.CSS.updateRule(thSelector, "display", "");
7237         this.CSS.updateRule(tdSelector, "display", "");
7238         
7239         if(hidden){
7240             this.CSS.updateRule(thSelector, "display", "none");
7241             this.CSS.updateRule(tdSelector, "display", "none");
7242         }
7243         
7244         this.onHeaderChange();
7245         this.onLoad();
7246     },
7247     
7248     setColumnWidth: function(col_index, width)
7249     {
7250         // width = "md-2 xs-2..."
7251         if(!this.colModel.config[col_index]) {
7252             return;
7253         }
7254         
7255         var w = width.split(" ");
7256         
7257         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7258         
7259         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7260         
7261         
7262         for(var j = 0; j < w.length; j++) {
7263             
7264             if(!w[j]) {
7265                 continue;
7266             }
7267             
7268             var size_cls = w[j].split("-");
7269             
7270             if(!Number.isInteger(size_cls[1] * 1)) {
7271                 continue;
7272             }
7273             
7274             if(!this.colModel.config[col_index][size_cls[0]]) {
7275                 continue;
7276             }
7277             
7278             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7279                 continue;
7280             }
7281             
7282             h_row[0].classList.replace(
7283                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7284                 "col-"+size_cls[0]+"-"+size_cls[1]
7285             );
7286             
7287             for(var i = 0; i < rows.length; i++) {
7288                 
7289                 var size_cls = w[j].split("-");
7290                 
7291                 if(!Number.isInteger(size_cls[1] * 1)) {
7292                     continue;
7293                 }
7294                 
7295                 if(!this.colModel.config[col_index][size_cls[0]]) {
7296                     continue;
7297                 }
7298                 
7299                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7300                     continue;
7301                 }
7302                 
7303                 rows[i].classList.replace(
7304                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7305                     "col-"+size_cls[0]+"-"+size_cls[1]
7306                 );
7307             }
7308             
7309             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7310         }
7311     }
7312 });
7313
7314  
7315
7316  /*
7317  * - LGPL
7318  *
7319  * table cell
7320  * 
7321  */
7322
7323 /**
7324  * @class Roo.bootstrap.TableCell
7325  * @extends Roo.bootstrap.Component
7326  * Bootstrap TableCell class
7327  * @cfg {String} html cell contain text
7328  * @cfg {String} cls cell class
7329  * @cfg {String} tag cell tag (td|th) default td
7330  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7331  * @cfg {String} align Aligns the content in a cell
7332  * @cfg {String} axis Categorizes cells
7333  * @cfg {String} bgcolor Specifies the background color of a cell
7334  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7335  * @cfg {Number} colspan Specifies the number of columns a cell should span
7336  * @cfg {String} headers Specifies one or more header cells a cell is related to
7337  * @cfg {Number} height Sets the height of a cell
7338  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7339  * @cfg {Number} rowspan Sets the number of rows a cell should span
7340  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7341  * @cfg {String} valign Vertical aligns the content in a cell
7342  * @cfg {Number} width Specifies the width of a cell
7343  * 
7344  * @constructor
7345  * Create a new TableCell
7346  * @param {Object} config The config object
7347  */
7348
7349 Roo.bootstrap.TableCell = function(config){
7350     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7351 };
7352
7353 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7354     
7355     html: false,
7356     cls: false,
7357     tag: false,
7358     abbr: false,
7359     align: false,
7360     axis: false,
7361     bgcolor: false,
7362     charoff: false,
7363     colspan: false,
7364     headers: false,
7365     height: false,
7366     nowrap: false,
7367     rowspan: false,
7368     scope: false,
7369     valign: false,
7370     width: false,
7371     
7372     
7373     getAutoCreate : function(){
7374         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7375         
7376         cfg = {
7377             tag: 'td'
7378         };
7379         
7380         if(this.tag){
7381             cfg.tag = this.tag;
7382         }
7383         
7384         if (this.html) {
7385             cfg.html=this.html
7386         }
7387         if (this.cls) {
7388             cfg.cls=this.cls
7389         }
7390         if (this.abbr) {
7391             cfg.abbr=this.abbr
7392         }
7393         if (this.align) {
7394             cfg.align=this.align
7395         }
7396         if (this.axis) {
7397             cfg.axis=this.axis
7398         }
7399         if (this.bgcolor) {
7400             cfg.bgcolor=this.bgcolor
7401         }
7402         if (this.charoff) {
7403             cfg.charoff=this.charoff
7404         }
7405         if (this.colspan) {
7406             cfg.colspan=this.colspan
7407         }
7408         if (this.headers) {
7409             cfg.headers=this.headers
7410         }
7411         if (this.height) {
7412             cfg.height=this.height
7413         }
7414         if (this.nowrap) {
7415             cfg.nowrap=this.nowrap
7416         }
7417         if (this.rowspan) {
7418             cfg.rowspan=this.rowspan
7419         }
7420         if (this.scope) {
7421             cfg.scope=this.scope
7422         }
7423         if (this.valign) {
7424             cfg.valign=this.valign
7425         }
7426         if (this.width) {
7427             cfg.width=this.width
7428         }
7429         
7430         
7431         return cfg;
7432     }
7433    
7434 });
7435
7436  
7437
7438  /*
7439  * - LGPL
7440  *
7441  * table row
7442  * 
7443  */
7444
7445 /**
7446  * @class Roo.bootstrap.TableRow
7447  * @extends Roo.bootstrap.Component
7448  * Bootstrap TableRow class
7449  * @cfg {String} cls row class
7450  * @cfg {String} align Aligns the content in a table row
7451  * @cfg {String} bgcolor Specifies a background color for a table row
7452  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7453  * @cfg {String} valign Vertical aligns the content in a table row
7454  * 
7455  * @constructor
7456  * Create a new TableRow
7457  * @param {Object} config The config object
7458  */
7459
7460 Roo.bootstrap.TableRow = function(config){
7461     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7462 };
7463
7464 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7465     
7466     cls: false,
7467     align: false,
7468     bgcolor: false,
7469     charoff: false,
7470     valign: false,
7471     
7472     getAutoCreate : function(){
7473         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7474         
7475         cfg = {
7476             tag: 'tr'
7477         };
7478             
7479         if(this.cls){
7480             cfg.cls = this.cls;
7481         }
7482         if(this.align){
7483             cfg.align = this.align;
7484         }
7485         if(this.bgcolor){
7486             cfg.bgcolor = this.bgcolor;
7487         }
7488         if(this.charoff){
7489             cfg.charoff = this.charoff;
7490         }
7491         if(this.valign){
7492             cfg.valign = this.valign;
7493         }
7494         
7495         return cfg;
7496     }
7497    
7498 });
7499
7500  
7501
7502  /*
7503  * - LGPL
7504  *
7505  * table body
7506  * 
7507  */
7508
7509 /**
7510  * @class Roo.bootstrap.TableBody
7511  * @extends Roo.bootstrap.Component
7512  * Bootstrap TableBody class
7513  * @cfg {String} cls element class
7514  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7515  * @cfg {String} align Aligns the content inside the element
7516  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7517  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7518  * 
7519  * @constructor
7520  * Create a new TableBody
7521  * @param {Object} config The config object
7522  */
7523
7524 Roo.bootstrap.TableBody = function(config){
7525     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7526 };
7527
7528 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7529     
7530     cls: false,
7531     tag: false,
7532     align: false,
7533     charoff: false,
7534     valign: false,
7535     
7536     getAutoCreate : function(){
7537         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7538         
7539         cfg = {
7540             tag: 'tbody'
7541         };
7542             
7543         if (this.cls) {
7544             cfg.cls=this.cls
7545         }
7546         if(this.tag){
7547             cfg.tag = this.tag;
7548         }
7549         
7550         if(this.align){
7551             cfg.align = this.align;
7552         }
7553         if(this.charoff){
7554             cfg.charoff = this.charoff;
7555         }
7556         if(this.valign){
7557             cfg.valign = this.valign;
7558         }
7559         
7560         return cfg;
7561     }
7562     
7563     
7564 //    initEvents : function()
7565 //    {
7566 //        
7567 //        if(!this.store){
7568 //            return;
7569 //        }
7570 //        
7571 //        this.store = Roo.factory(this.store, Roo.data);
7572 //        this.store.on('load', this.onLoad, this);
7573 //        
7574 //        this.store.load();
7575 //        
7576 //    },
7577 //    
7578 //    onLoad: function () 
7579 //    {   
7580 //        this.fireEvent('load', this);
7581 //    }
7582 //    
7583 //   
7584 });
7585
7586  
7587
7588  /*
7589  * Based on:
7590  * Ext JS Library 1.1.1
7591  * Copyright(c) 2006-2007, Ext JS, LLC.
7592  *
7593  * Originally Released Under LGPL - original licence link has changed is not relivant.
7594  *
7595  * Fork - LGPL
7596  * <script type="text/javascript">
7597  */
7598
7599 // as we use this in bootstrap.
7600 Roo.namespace('Roo.form');
7601  /**
7602  * @class Roo.form.Action
7603  * Internal Class used to handle form actions
7604  * @constructor
7605  * @param {Roo.form.BasicForm} el The form element or its id
7606  * @param {Object} config Configuration options
7607  */
7608
7609  
7610  
7611 // define the action interface
7612 Roo.form.Action = function(form, options){
7613     this.form = form;
7614     this.options = options || {};
7615 };
7616 /**
7617  * Client Validation Failed
7618  * @const 
7619  */
7620 Roo.form.Action.CLIENT_INVALID = 'client';
7621 /**
7622  * Server Validation Failed
7623  * @const 
7624  */
7625 Roo.form.Action.SERVER_INVALID = 'server';
7626  /**
7627  * Connect to Server Failed
7628  * @const 
7629  */
7630 Roo.form.Action.CONNECT_FAILURE = 'connect';
7631 /**
7632  * Reading Data from Server Failed
7633  * @const 
7634  */
7635 Roo.form.Action.LOAD_FAILURE = 'load';
7636
7637 Roo.form.Action.prototype = {
7638     type : 'default',
7639     failureType : undefined,
7640     response : undefined,
7641     result : undefined,
7642
7643     // interface method
7644     run : function(options){
7645
7646     },
7647
7648     // interface method
7649     success : function(response){
7650
7651     },
7652
7653     // interface method
7654     handleResponse : function(response){
7655
7656     },
7657
7658     // default connection failure
7659     failure : function(response){
7660         
7661         this.response = response;
7662         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7663         this.form.afterAction(this, false);
7664     },
7665
7666     processResponse : function(response){
7667         this.response = response;
7668         if(!response.responseText){
7669             return true;
7670         }
7671         this.result = this.handleResponse(response);
7672         return this.result;
7673     },
7674
7675     // utility functions used internally
7676     getUrl : function(appendParams){
7677         var url = this.options.url || this.form.url || this.form.el.dom.action;
7678         if(appendParams){
7679             var p = this.getParams();
7680             if(p){
7681                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7682             }
7683         }
7684         return url;
7685     },
7686
7687     getMethod : function(){
7688         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7689     },
7690
7691     getParams : function(){
7692         var bp = this.form.baseParams;
7693         var p = this.options.params;
7694         if(p){
7695             if(typeof p == "object"){
7696                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7697             }else if(typeof p == 'string' && bp){
7698                 p += '&' + Roo.urlEncode(bp);
7699             }
7700         }else if(bp){
7701             p = Roo.urlEncode(bp);
7702         }
7703         return p;
7704     },
7705
7706     createCallback : function(){
7707         return {
7708             success: this.success,
7709             failure: this.failure,
7710             scope: this,
7711             timeout: (this.form.timeout*1000),
7712             upload: this.form.fileUpload ? this.success : undefined
7713         };
7714     }
7715 };
7716
7717 Roo.form.Action.Submit = function(form, options){
7718     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7719 };
7720
7721 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7722     type : 'submit',
7723
7724     haveProgress : false,
7725     uploadComplete : false,
7726     
7727     // uploadProgress indicator.
7728     uploadProgress : function()
7729     {
7730         if (!this.form.progressUrl) {
7731             return;
7732         }
7733         
7734         if (!this.haveProgress) {
7735             Roo.MessageBox.progress("Uploading", "Uploading");
7736         }
7737         if (this.uploadComplete) {
7738            Roo.MessageBox.hide();
7739            return;
7740         }
7741         
7742         this.haveProgress = true;
7743    
7744         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7745         
7746         var c = new Roo.data.Connection();
7747         c.request({
7748             url : this.form.progressUrl,
7749             params: {
7750                 id : uid
7751             },
7752             method: 'GET',
7753             success : function(req){
7754                //console.log(data);
7755                 var rdata = false;
7756                 var edata;
7757                 try  {
7758                    rdata = Roo.decode(req.responseText)
7759                 } catch (e) {
7760                     Roo.log("Invalid data from server..");
7761                     Roo.log(edata);
7762                     return;
7763                 }
7764                 if (!rdata || !rdata.success) {
7765                     Roo.log(rdata);
7766                     Roo.MessageBox.alert(Roo.encode(rdata));
7767                     return;
7768                 }
7769                 var data = rdata.data;
7770                 
7771                 if (this.uploadComplete) {
7772                    Roo.MessageBox.hide();
7773                    return;
7774                 }
7775                    
7776                 if (data){
7777                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7778                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7779                     );
7780                 }
7781                 this.uploadProgress.defer(2000,this);
7782             },
7783        
7784             failure: function(data) {
7785                 Roo.log('progress url failed ');
7786                 Roo.log(data);
7787             },
7788             scope : this
7789         });
7790            
7791     },
7792     
7793     
7794     run : function()
7795     {
7796         // run get Values on the form, so it syncs any secondary forms.
7797         this.form.getValues();
7798         
7799         var o = this.options;
7800         var method = this.getMethod();
7801         var isPost = method == 'POST';
7802         if(o.clientValidation === false || this.form.isValid()){
7803             
7804             if (this.form.progressUrl) {
7805                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7806                     (new Date() * 1) + '' + Math.random());
7807                     
7808             } 
7809             
7810             
7811             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7812                 form:this.form.el.dom,
7813                 url:this.getUrl(!isPost),
7814                 method: method,
7815                 params:isPost ? this.getParams() : null,
7816                 isUpload: this.form.fileUpload
7817             }));
7818             
7819             this.uploadProgress();
7820
7821         }else if (o.clientValidation !== false){ // client validation failed
7822             this.failureType = Roo.form.Action.CLIENT_INVALID;
7823             this.form.afterAction(this, false);
7824         }
7825     },
7826
7827     success : function(response)
7828     {
7829         this.uploadComplete= true;
7830         if (this.haveProgress) {
7831             Roo.MessageBox.hide();
7832         }
7833         
7834         
7835         var result = this.processResponse(response);
7836         if(result === true || result.success){
7837             this.form.afterAction(this, true);
7838             return;
7839         }
7840         if(result.errors){
7841             this.form.markInvalid(result.errors);
7842             this.failureType = Roo.form.Action.SERVER_INVALID;
7843         }
7844         this.form.afterAction(this, false);
7845     },
7846     failure : function(response)
7847     {
7848         this.uploadComplete= true;
7849         if (this.haveProgress) {
7850             Roo.MessageBox.hide();
7851         }
7852         
7853         this.response = response;
7854         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7855         this.form.afterAction(this, false);
7856     },
7857     
7858     handleResponse : function(response){
7859         if(this.form.errorReader){
7860             var rs = this.form.errorReader.read(response);
7861             var errors = [];
7862             if(rs.records){
7863                 for(var i = 0, len = rs.records.length; i < len; i++) {
7864                     var r = rs.records[i];
7865                     errors[i] = r.data;
7866                 }
7867             }
7868             if(errors.length < 1){
7869                 errors = null;
7870             }
7871             return {
7872                 success : rs.success,
7873                 errors : errors
7874             };
7875         }
7876         var ret = false;
7877         try {
7878             ret = Roo.decode(response.responseText);
7879         } catch (e) {
7880             ret = {
7881                 success: false,
7882                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7883                 errors : []
7884             };
7885         }
7886         return ret;
7887         
7888     }
7889 });
7890
7891
7892 Roo.form.Action.Load = function(form, options){
7893     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7894     this.reader = this.form.reader;
7895 };
7896
7897 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7898     type : 'load',
7899
7900     run : function(){
7901         
7902         Roo.Ajax.request(Roo.apply(
7903                 this.createCallback(), {
7904                     method:this.getMethod(),
7905                     url:this.getUrl(false),
7906                     params:this.getParams()
7907         }));
7908     },
7909
7910     success : function(response){
7911         
7912         var result = this.processResponse(response);
7913         if(result === true || !result.success || !result.data){
7914             this.failureType = Roo.form.Action.LOAD_FAILURE;
7915             this.form.afterAction(this, false);
7916             return;
7917         }
7918         this.form.clearInvalid();
7919         this.form.setValues(result.data);
7920         this.form.afterAction(this, true);
7921     },
7922
7923     handleResponse : function(response){
7924         if(this.form.reader){
7925             var rs = this.form.reader.read(response);
7926             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7927             return {
7928                 success : rs.success,
7929                 data : data
7930             };
7931         }
7932         return Roo.decode(response.responseText);
7933     }
7934 });
7935
7936 Roo.form.Action.ACTION_TYPES = {
7937     'load' : Roo.form.Action.Load,
7938     'submit' : Roo.form.Action.Submit
7939 };/*
7940  * - LGPL
7941  *
7942  * form
7943  *
7944  */
7945
7946 /**
7947  * @class Roo.bootstrap.Form
7948  * @extends Roo.bootstrap.Component
7949  * Bootstrap Form class
7950  * @cfg {String} method  GET | POST (default POST)
7951  * @cfg {String} labelAlign top | left (default top)
7952  * @cfg {String} align left  | right - for navbars
7953  * @cfg {Boolean} loadMask load mask when submit (default true)
7954
7955  *
7956  * @constructor
7957  * Create a new Form
7958  * @param {Object} config The config object
7959  */
7960
7961
7962 Roo.bootstrap.Form = function(config){
7963     
7964     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7965     
7966     Roo.bootstrap.Form.popover.apply();
7967     
7968     this.addEvents({
7969         /**
7970          * @event clientvalidation
7971          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7972          * @param {Form} this
7973          * @param {Boolean} valid true if the form has passed client-side validation
7974          */
7975         clientvalidation: true,
7976         /**
7977          * @event beforeaction
7978          * Fires before any action is performed. Return false to cancel the action.
7979          * @param {Form} this
7980          * @param {Action} action The action to be performed
7981          */
7982         beforeaction: true,
7983         /**
7984          * @event actionfailed
7985          * Fires when an action fails.
7986          * @param {Form} this
7987          * @param {Action} action The action that failed
7988          */
7989         actionfailed : true,
7990         /**
7991          * @event actioncomplete
7992          * Fires when an action is completed.
7993          * @param {Form} this
7994          * @param {Action} action The action that completed
7995          */
7996         actioncomplete : true
7997     });
7998 };
7999
8000 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8001
8002      /**
8003      * @cfg {String} method
8004      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8005      */
8006     method : 'POST',
8007     /**
8008      * @cfg {String} url
8009      * The URL to use for form actions if one isn't supplied in the action options.
8010      */
8011     /**
8012      * @cfg {Boolean} fileUpload
8013      * Set to true if this form is a file upload.
8014      */
8015
8016     /**
8017      * @cfg {Object} baseParams
8018      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8019      */
8020
8021     /**
8022      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8023      */
8024     timeout: 30,
8025     /**
8026      * @cfg {Sting} align (left|right) for navbar forms
8027      */
8028     align : 'left',
8029
8030     // private
8031     activeAction : null,
8032
8033     /**
8034      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8035      * element by passing it or its id or mask the form itself by passing in true.
8036      * @type Mixed
8037      */
8038     waitMsgTarget : false,
8039
8040     loadMask : true,
8041     
8042     /**
8043      * @cfg {Boolean} errorMask (true|false) default false
8044      */
8045     errorMask : false,
8046     
8047     /**
8048      * @cfg {Number} maskOffset Default 100
8049      */
8050     maskOffset : 100,
8051     
8052     /**
8053      * @cfg {Boolean} maskBody
8054      */
8055     maskBody : false,
8056
8057     getAutoCreate : function(){
8058
8059         var cfg = {
8060             tag: 'form',
8061             method : this.method || 'POST',
8062             id : this.id || Roo.id(),
8063             cls : ''
8064         };
8065         if (this.parent().xtype.match(/^Nav/)) {
8066             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8067
8068         }
8069
8070         if (this.labelAlign == 'left' ) {
8071             cfg.cls += ' form-horizontal';
8072         }
8073
8074
8075         return cfg;
8076     },
8077     initEvents : function()
8078     {
8079         this.el.on('submit', this.onSubmit, this);
8080         // this was added as random key presses on the form where triggering form submit.
8081         this.el.on('keypress', function(e) {
8082             if (e.getCharCode() != 13) {
8083                 return true;
8084             }
8085             // we might need to allow it for textareas.. and some other items.
8086             // check e.getTarget().
8087
8088             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8089                 return true;
8090             }
8091
8092             Roo.log("keypress blocked");
8093
8094             e.preventDefault();
8095             return false;
8096         });
8097         
8098     },
8099     // private
8100     onSubmit : function(e){
8101         e.stopEvent();
8102     },
8103
8104      /**
8105      * Returns true if client-side validation on the form is successful.
8106      * @return Boolean
8107      */
8108     isValid : function(){
8109         var items = this.getItems();
8110         var valid = true;
8111         var target = false;
8112         
8113         items.each(function(f){
8114             
8115             if(f.validate()){
8116                 return;
8117             }
8118             
8119             Roo.log('invalid field: ' + f.name);
8120             
8121             valid = false;
8122
8123             if(!target && f.el.isVisible(true)){
8124                 target = f;
8125             }
8126            
8127         });
8128         
8129         if(this.errorMask && !valid){
8130             Roo.bootstrap.Form.popover.mask(this, target);
8131         }
8132         
8133         return valid;
8134     },
8135     
8136     /**
8137      * Returns true if any fields in this form have changed since their original load.
8138      * @return Boolean
8139      */
8140     isDirty : function(){
8141         var dirty = false;
8142         var items = this.getItems();
8143         items.each(function(f){
8144            if(f.isDirty()){
8145                dirty = true;
8146                return false;
8147            }
8148            return true;
8149         });
8150         return dirty;
8151     },
8152      /**
8153      * Performs a predefined action (submit or load) or custom actions you define on this form.
8154      * @param {String} actionName The name of the action type
8155      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8156      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8157      * accept other config options):
8158      * <pre>
8159 Property          Type             Description
8160 ----------------  ---------------  ----------------------------------------------------------------------------------
8161 url               String           The url for the action (defaults to the form's url)
8162 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8163 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8164 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8165                                    validate the form on the client (defaults to false)
8166      * </pre>
8167      * @return {BasicForm} this
8168      */
8169     doAction : function(action, options){
8170         if(typeof action == 'string'){
8171             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8172         }
8173         if(this.fireEvent('beforeaction', this, action) !== false){
8174             this.beforeAction(action);
8175             action.run.defer(100, action);
8176         }
8177         return this;
8178     },
8179
8180     // private
8181     beforeAction : function(action){
8182         var o = action.options;
8183         
8184         if(this.loadMask){
8185             
8186             if(this.maskBody){
8187                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8188             } else {
8189                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8190             }
8191         }
8192         // not really supported yet.. ??
8193
8194         //if(this.waitMsgTarget === true){
8195         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8196         //}else if(this.waitMsgTarget){
8197         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8198         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8199         //}else {
8200         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8201        // }
8202
8203     },
8204
8205     // private
8206     afterAction : function(action, success){
8207         this.activeAction = null;
8208         var o = action.options;
8209
8210         if(this.loadMask){
8211             
8212             if(this.maskBody){
8213                 Roo.get(document.body).unmask();
8214             } else {
8215                 this.el.unmask();
8216             }
8217         }
8218         
8219         //if(this.waitMsgTarget === true){
8220 //            this.el.unmask();
8221         //}else if(this.waitMsgTarget){
8222         //    this.waitMsgTarget.unmask();
8223         //}else{
8224         //    Roo.MessageBox.updateProgress(1);
8225         //    Roo.MessageBox.hide();
8226        // }
8227         //
8228         if(success){
8229             if(o.reset){
8230                 this.reset();
8231             }
8232             Roo.callback(o.success, o.scope, [this, action]);
8233             this.fireEvent('actioncomplete', this, action);
8234
8235         }else{
8236
8237             // failure condition..
8238             // we have a scenario where updates need confirming.
8239             // eg. if a locking scenario exists..
8240             // we look for { errors : { needs_confirm : true }} in the response.
8241             if (
8242                 (typeof(action.result) != 'undefined')  &&
8243                 (typeof(action.result.errors) != 'undefined')  &&
8244                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8245            ){
8246                 var _t = this;
8247                 Roo.log("not supported yet");
8248                  /*
8249
8250                 Roo.MessageBox.confirm(
8251                     "Change requires confirmation",
8252                     action.result.errorMsg,
8253                     function(r) {
8254                         if (r != 'yes') {
8255                             return;
8256                         }
8257                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8258                     }
8259
8260                 );
8261                 */
8262
8263
8264                 return;
8265             }
8266
8267             Roo.callback(o.failure, o.scope, [this, action]);
8268             // show an error message if no failed handler is set..
8269             if (!this.hasListener('actionfailed')) {
8270                 Roo.log("need to add dialog support");
8271                 /*
8272                 Roo.MessageBox.alert("Error",
8273                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8274                         action.result.errorMsg :
8275                         "Saving Failed, please check your entries or try again"
8276                 );
8277                 */
8278             }
8279
8280             this.fireEvent('actionfailed', this, action);
8281         }
8282
8283     },
8284     /**
8285      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8286      * @param {String} id The value to search for
8287      * @return Field
8288      */
8289     findField : function(id){
8290         var items = this.getItems();
8291         var field = items.get(id);
8292         if(!field){
8293              items.each(function(f){
8294                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8295                     field = f;
8296                     return false;
8297                 }
8298                 return true;
8299             });
8300         }
8301         return field || null;
8302     },
8303      /**
8304      * Mark fields in this form invalid in bulk.
8305      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8306      * @return {BasicForm} this
8307      */
8308     markInvalid : function(errors){
8309         if(errors instanceof Array){
8310             for(var i = 0, len = errors.length; i < len; i++){
8311                 var fieldError = errors[i];
8312                 var f = this.findField(fieldError.id);
8313                 if(f){
8314                     f.markInvalid(fieldError.msg);
8315                 }
8316             }
8317         }else{
8318             var field, id;
8319             for(id in errors){
8320                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8321                     field.markInvalid(errors[id]);
8322                 }
8323             }
8324         }
8325         //Roo.each(this.childForms || [], function (f) {
8326         //    f.markInvalid(errors);
8327         //});
8328
8329         return this;
8330     },
8331
8332     /**
8333      * Set values for fields in this form in bulk.
8334      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8335      * @return {BasicForm} this
8336      */
8337     setValues : function(values){
8338         if(values instanceof Array){ // array of objects
8339             for(var i = 0, len = values.length; i < len; i++){
8340                 var v = values[i];
8341                 var f = this.findField(v.id);
8342                 if(f){
8343                     f.setValue(v.value);
8344                     if(this.trackResetOnLoad){
8345                         f.originalValue = f.getValue();
8346                     }
8347                 }
8348             }
8349         }else{ // object hash
8350             var field, id;
8351             for(id in values){
8352                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8353
8354                     if (field.setFromData &&
8355                         field.valueField &&
8356                         field.displayField &&
8357                         // combos' with local stores can
8358                         // be queried via setValue()
8359                         // to set their value..
8360                         (field.store && !field.store.isLocal)
8361                         ) {
8362                         // it's a combo
8363                         var sd = { };
8364                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8365                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8366                         field.setFromData(sd);
8367
8368                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8369                         
8370                         field.setFromData(values);
8371                         
8372                     } else {
8373                         field.setValue(values[id]);
8374                     }
8375
8376
8377                     if(this.trackResetOnLoad){
8378                         field.originalValue = field.getValue();
8379                     }
8380                 }
8381             }
8382         }
8383
8384         //Roo.each(this.childForms || [], function (f) {
8385         //    f.setValues(values);
8386         //});
8387
8388         return this;
8389     },
8390
8391     /**
8392      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8393      * they are returned as an array.
8394      * @param {Boolean} asString
8395      * @return {Object}
8396      */
8397     getValues : function(asString){
8398         //if (this.childForms) {
8399             // copy values from the child forms
8400         //    Roo.each(this.childForms, function (f) {
8401         //        this.setValues(f.getValues());
8402         //    }, this);
8403         //}
8404
8405
8406
8407         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8408         if(asString === true){
8409             return fs;
8410         }
8411         return Roo.urlDecode(fs);
8412     },
8413
8414     /**
8415      * Returns the fields in this form as an object with key/value pairs.
8416      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8417      * @return {Object}
8418      */
8419     getFieldValues : function(with_hidden)
8420     {
8421         var items = this.getItems();
8422         var ret = {};
8423         items.each(function(f){
8424             
8425             if (!f.getName()) {
8426                 return;
8427             }
8428             
8429             var v = f.getValue();
8430             
8431             if (f.inputType =='radio') {
8432                 if (typeof(ret[f.getName()]) == 'undefined') {
8433                     ret[f.getName()] = ''; // empty..
8434                 }
8435
8436                 if (!f.el.dom.checked) {
8437                     return;
8438
8439                 }
8440                 v = f.el.dom.value;
8441
8442             }
8443             
8444             if(f.xtype == 'MoneyField'){
8445                 ret[f.currencyName] = f.getCurrency();
8446             }
8447
8448             // not sure if this supported any more..
8449             if ((typeof(v) == 'object') && f.getRawValue) {
8450                 v = f.getRawValue() ; // dates..
8451             }
8452             // combo boxes where name != hiddenName...
8453             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8454                 ret[f.name] = f.getRawValue();
8455             }
8456             ret[f.getName()] = v;
8457         });
8458
8459         return ret;
8460     },
8461
8462     /**
8463      * Clears all invalid messages in this form.
8464      * @return {BasicForm} this
8465      */
8466     clearInvalid : function(){
8467         var items = this.getItems();
8468
8469         items.each(function(f){
8470            f.clearInvalid();
8471         });
8472
8473         return this;
8474     },
8475
8476     /**
8477      * Resets this form.
8478      * @return {BasicForm} this
8479      */
8480     reset : function(){
8481         var items = this.getItems();
8482         items.each(function(f){
8483             f.reset();
8484         });
8485
8486         Roo.each(this.childForms || [], function (f) {
8487             f.reset();
8488         });
8489
8490
8491         return this;
8492     },
8493     
8494     getItems : function()
8495     {
8496         var r=new Roo.util.MixedCollection(false, function(o){
8497             return o.id || (o.id = Roo.id());
8498         });
8499         var iter = function(el) {
8500             if (el.inputEl) {
8501                 r.add(el);
8502             }
8503             if (!el.items) {
8504                 return;
8505             }
8506             Roo.each(el.items,function(e) {
8507                 iter(e);
8508             });
8509         };
8510
8511         iter(this);
8512         return r;
8513     },
8514     
8515     hideFields : function(items)
8516     {
8517         Roo.each(items, function(i){
8518             
8519             var f = this.findField(i);
8520             
8521             if(!f){
8522                 return;
8523             }
8524             
8525             f.hide();
8526             
8527         }, this);
8528     },
8529     
8530     showFields : function(items)
8531     {
8532         Roo.each(items, function(i){
8533             
8534             var f = this.findField(i);
8535             
8536             if(!f){
8537                 return;
8538             }
8539             
8540             f.show();
8541             
8542         }, this);
8543     }
8544
8545 });
8546
8547 Roo.apply(Roo.bootstrap.Form, {
8548     
8549     popover : {
8550         
8551         padding : 5,
8552         
8553         isApplied : false,
8554         
8555         isMasked : false,
8556         
8557         form : false,
8558         
8559         target : false,
8560         
8561         toolTip : false,
8562         
8563         intervalID : false,
8564         
8565         maskEl : false,
8566         
8567         apply : function()
8568         {
8569             if(this.isApplied){
8570                 return;
8571             }
8572             
8573             this.maskEl = {
8574                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8575                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8576                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8577                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8578             };
8579             
8580             this.maskEl.top.enableDisplayMode("block");
8581             this.maskEl.left.enableDisplayMode("block");
8582             this.maskEl.bottom.enableDisplayMode("block");
8583             this.maskEl.right.enableDisplayMode("block");
8584             
8585             this.toolTip = new Roo.bootstrap.Tooltip({
8586                 cls : 'roo-form-error-popover',
8587                 alignment : {
8588                     'left' : ['r-l', [-2,0], 'right'],
8589                     'right' : ['l-r', [2,0], 'left'],
8590                     'bottom' : ['tl-bl', [0,2], 'top'],
8591                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8592                 }
8593             });
8594             
8595             this.toolTip.render(Roo.get(document.body));
8596
8597             this.toolTip.el.enableDisplayMode("block");
8598             
8599             Roo.get(document.body).on('click', function(){
8600                 this.unmask();
8601             }, this);
8602             
8603             Roo.get(document.body).on('touchstart', function(){
8604                 this.unmask();
8605             }, this);
8606             
8607             this.isApplied = true
8608         },
8609         
8610         mask : function(form, target)
8611         {
8612             this.form = form;
8613             
8614             this.target = target;
8615             
8616             if(!this.form.errorMask || !target.el){
8617                 return;
8618             }
8619             
8620             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8621             
8622             Roo.log(scrollable);
8623             
8624             var ot = this.target.el.calcOffsetsTo(scrollable);
8625             
8626             var scrollTo = ot[1] - this.form.maskOffset;
8627             
8628             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8629             
8630             scrollable.scrollTo('top', scrollTo);
8631             
8632             var box = this.target.el.getBox();
8633             Roo.log(box);
8634             var zIndex = Roo.bootstrap.Modal.zIndex++;
8635
8636             
8637             this.maskEl.top.setStyle('position', 'absolute');
8638             this.maskEl.top.setStyle('z-index', zIndex);
8639             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8640             this.maskEl.top.setLeft(0);
8641             this.maskEl.top.setTop(0);
8642             this.maskEl.top.show();
8643             
8644             this.maskEl.left.setStyle('position', 'absolute');
8645             this.maskEl.left.setStyle('z-index', zIndex);
8646             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8647             this.maskEl.left.setLeft(0);
8648             this.maskEl.left.setTop(box.y - this.padding);
8649             this.maskEl.left.show();
8650
8651             this.maskEl.bottom.setStyle('position', 'absolute');
8652             this.maskEl.bottom.setStyle('z-index', zIndex);
8653             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8654             this.maskEl.bottom.setLeft(0);
8655             this.maskEl.bottom.setTop(box.bottom + this.padding);
8656             this.maskEl.bottom.show();
8657
8658             this.maskEl.right.setStyle('position', 'absolute');
8659             this.maskEl.right.setStyle('z-index', zIndex);
8660             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8661             this.maskEl.right.setLeft(box.right + this.padding);
8662             this.maskEl.right.setTop(box.y - this.padding);
8663             this.maskEl.right.show();
8664
8665             this.toolTip.bindEl = this.target.el;
8666
8667             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8668
8669             var tip = this.target.blankText;
8670
8671             if(this.target.getValue() !== '' ) {
8672                 
8673                 if (this.target.invalidText.length) {
8674                     tip = this.target.invalidText;
8675                 } else if (this.target.regexText.length){
8676                     tip = this.target.regexText;
8677                 }
8678             }
8679
8680             this.toolTip.show(tip);
8681
8682             this.intervalID = window.setInterval(function() {
8683                 Roo.bootstrap.Form.popover.unmask();
8684             }, 10000);
8685
8686             window.onwheel = function(){ return false;};
8687             
8688             (function(){ this.isMasked = true; }).defer(500, this);
8689             
8690         },
8691         
8692         unmask : function()
8693         {
8694             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8695                 return;
8696             }
8697             
8698             this.maskEl.top.setStyle('position', 'absolute');
8699             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8700             this.maskEl.top.hide();
8701
8702             this.maskEl.left.setStyle('position', 'absolute');
8703             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8704             this.maskEl.left.hide();
8705
8706             this.maskEl.bottom.setStyle('position', 'absolute');
8707             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8708             this.maskEl.bottom.hide();
8709
8710             this.maskEl.right.setStyle('position', 'absolute');
8711             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8712             this.maskEl.right.hide();
8713             
8714             this.toolTip.hide();
8715             
8716             this.toolTip.el.hide();
8717             
8718             window.onwheel = function(){ return true;};
8719             
8720             if(this.intervalID){
8721                 window.clearInterval(this.intervalID);
8722                 this.intervalID = false;
8723             }
8724             
8725             this.isMasked = false;
8726             
8727         }
8728         
8729     }
8730     
8731 });
8732
8733 /*
8734  * Based on:
8735  * Ext JS Library 1.1.1
8736  * Copyright(c) 2006-2007, Ext JS, LLC.
8737  *
8738  * Originally Released Under LGPL - original licence link has changed is not relivant.
8739  *
8740  * Fork - LGPL
8741  * <script type="text/javascript">
8742  */
8743 /**
8744  * @class Roo.form.VTypes
8745  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8746  * @singleton
8747  */
8748 Roo.form.VTypes = function(){
8749     // closure these in so they are only created once.
8750     var alpha = /^[a-zA-Z_]+$/;
8751     var alphanum = /^[a-zA-Z0-9_]+$/;
8752     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8753     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8754
8755     // All these messages and functions are configurable
8756     return {
8757         /**
8758          * The function used to validate email addresses
8759          * @param {String} value The email address
8760          */
8761         'email' : function(v){
8762             return email.test(v);
8763         },
8764         /**
8765          * The error text to display when the email validation function returns false
8766          * @type String
8767          */
8768         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8769         /**
8770          * The keystroke filter mask to be applied on email input
8771          * @type RegExp
8772          */
8773         'emailMask' : /[a-z0-9_\.\-@]/i,
8774
8775         /**
8776          * The function used to validate URLs
8777          * @param {String} value The URL
8778          */
8779         'url' : function(v){
8780             return url.test(v);
8781         },
8782         /**
8783          * The error text to display when the url validation function returns false
8784          * @type String
8785          */
8786         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8787         
8788         /**
8789          * The function used to validate alpha values
8790          * @param {String} value The value
8791          */
8792         'alpha' : function(v){
8793             return alpha.test(v);
8794         },
8795         /**
8796          * The error text to display when the alpha validation function returns false
8797          * @type String
8798          */
8799         'alphaText' : 'This field should only contain letters and _',
8800         /**
8801          * The keystroke filter mask to be applied on alpha input
8802          * @type RegExp
8803          */
8804         'alphaMask' : /[a-z_]/i,
8805
8806         /**
8807          * The function used to validate alphanumeric values
8808          * @param {String} value The value
8809          */
8810         'alphanum' : function(v){
8811             return alphanum.test(v);
8812         },
8813         /**
8814          * The error text to display when the alphanumeric validation function returns false
8815          * @type String
8816          */
8817         'alphanumText' : 'This field should only contain letters, numbers and _',
8818         /**
8819          * The keystroke filter mask to be applied on alphanumeric input
8820          * @type RegExp
8821          */
8822         'alphanumMask' : /[a-z0-9_]/i
8823     };
8824 }();/*
8825  * - LGPL
8826  *
8827  * Input
8828  * 
8829  */
8830
8831 /**
8832  * @class Roo.bootstrap.Input
8833  * @extends Roo.bootstrap.Component
8834  * Bootstrap Input class
8835  * @cfg {Boolean} disabled is it disabled
8836  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8837  * @cfg {String} name name of the input
8838  * @cfg {string} fieldLabel - the label associated
8839  * @cfg {string} placeholder - placeholder to put in text.
8840  * @cfg {string}  before - input group add on before
8841  * @cfg {string} after - input group add on after
8842  * @cfg {string} size - (lg|sm) or leave empty..
8843  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8844  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8845  * @cfg {Number} md colspan out of 12 for computer-sized screens
8846  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8847  * @cfg {string} value default value of the input
8848  * @cfg {Number} labelWidth set the width of label 
8849  * @cfg {Number} labellg set the width of label (1-12)
8850  * @cfg {Number} labelmd set the width of label (1-12)
8851  * @cfg {Number} labelsm set the width of label (1-12)
8852  * @cfg {Number} labelxs set the width of label (1-12)
8853  * @cfg {String} labelAlign (top|left)
8854  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8855  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8856  * @cfg {String} indicatorpos (left|right) default left
8857  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8858  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8859
8860  * @cfg {String} align (left|center|right) Default left
8861  * @cfg {Boolean} forceFeedback (true|false) Default false
8862  * 
8863  * @constructor
8864  * Create a new Input
8865  * @param {Object} config The config object
8866  */
8867
8868 Roo.bootstrap.Input = function(config){
8869     
8870     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8871     
8872     this.addEvents({
8873         /**
8874          * @event focus
8875          * Fires when this field receives input focus.
8876          * @param {Roo.form.Field} this
8877          */
8878         focus : true,
8879         /**
8880          * @event blur
8881          * Fires when this field loses input focus.
8882          * @param {Roo.form.Field} this
8883          */
8884         blur : true,
8885         /**
8886          * @event specialkey
8887          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8888          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8889          * @param {Roo.form.Field} this
8890          * @param {Roo.EventObject} e The event object
8891          */
8892         specialkey : true,
8893         /**
8894          * @event change
8895          * Fires just before the field blurs if the field value has changed.
8896          * @param {Roo.form.Field} this
8897          * @param {Mixed} newValue The new value
8898          * @param {Mixed} oldValue The original value
8899          */
8900         change : true,
8901         /**
8902          * @event invalid
8903          * Fires after the field has been marked as invalid.
8904          * @param {Roo.form.Field} this
8905          * @param {String} msg The validation message
8906          */
8907         invalid : true,
8908         /**
8909          * @event valid
8910          * Fires after the field has been validated with no errors.
8911          * @param {Roo.form.Field} this
8912          */
8913         valid : true,
8914          /**
8915          * @event keyup
8916          * Fires after the key up
8917          * @param {Roo.form.Field} this
8918          * @param {Roo.EventObject}  e The event Object
8919          */
8920         keyup : true
8921     });
8922 };
8923
8924 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8925      /**
8926      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8927       automatic validation (defaults to "keyup").
8928      */
8929     validationEvent : "keyup",
8930      /**
8931      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8932      */
8933     validateOnBlur : true,
8934     /**
8935      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8936      */
8937     validationDelay : 250,
8938      /**
8939      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8940      */
8941     focusClass : "x-form-focus",  // not needed???
8942     
8943        
8944     /**
8945      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8946      */
8947     invalidClass : "has-warning",
8948     
8949     /**
8950      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8951      */
8952     validClass : "has-success",
8953     
8954     /**
8955      * @cfg {Boolean} hasFeedback (true|false) default true
8956      */
8957     hasFeedback : true,
8958     
8959     /**
8960      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8961      */
8962     invalidFeedbackClass : "glyphicon-warning-sign",
8963     
8964     /**
8965      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8966      */
8967     validFeedbackClass : "glyphicon-ok",
8968     
8969     /**
8970      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8971      */
8972     selectOnFocus : false,
8973     
8974      /**
8975      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8976      */
8977     maskRe : null,
8978        /**
8979      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8980      */
8981     vtype : null,
8982     
8983       /**
8984      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8985      */
8986     disableKeyFilter : false,
8987     
8988        /**
8989      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8990      */
8991     disabled : false,
8992      /**
8993      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8994      */
8995     allowBlank : true,
8996     /**
8997      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8998      */
8999     blankText : "Please complete this mandatory field",
9000     
9001      /**
9002      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9003      */
9004     minLength : 0,
9005     /**
9006      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9007      */
9008     maxLength : Number.MAX_VALUE,
9009     /**
9010      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9011      */
9012     minLengthText : "The minimum length for this field is {0}",
9013     /**
9014      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9015      */
9016     maxLengthText : "The maximum length for this field is {0}",
9017   
9018     
9019     /**
9020      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9021      * If available, this function will be called only after the basic validators all return true, and will be passed the
9022      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9023      */
9024     validator : null,
9025     /**
9026      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9027      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9028      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9029      */
9030     regex : null,
9031     /**
9032      * @cfg {String} regexText -- Depricated - use Invalid Text
9033      */
9034     regexText : "",
9035     
9036     /**
9037      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9038      */
9039     invalidText : "",
9040     
9041     
9042     
9043     autocomplete: false,
9044     
9045     
9046     fieldLabel : '',
9047     inputType : 'text',
9048     
9049     name : false,
9050     placeholder: false,
9051     before : false,
9052     after : false,
9053     size : false,
9054     hasFocus : false,
9055     preventMark: false,
9056     isFormField : true,
9057     value : '',
9058     labelWidth : 2,
9059     labelAlign : false,
9060     readOnly : false,
9061     align : false,
9062     formatedValue : false,
9063     forceFeedback : false,
9064     
9065     indicatorpos : 'left',
9066     
9067     labellg : 0,
9068     labelmd : 0,
9069     labelsm : 0,
9070     labelxs : 0,
9071     
9072     capture : '',
9073     accept : '',
9074     
9075     parentLabelAlign : function()
9076     {
9077         var parent = this;
9078         while (parent.parent()) {
9079             parent = parent.parent();
9080             if (typeof(parent.labelAlign) !='undefined') {
9081                 return parent.labelAlign;
9082             }
9083         }
9084         return 'left';
9085         
9086     },
9087     
9088     getAutoCreate : function()
9089     {
9090         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9091         
9092         var id = Roo.id();
9093         
9094         var cfg = {};
9095         
9096         if(this.inputType != 'hidden'){
9097             cfg.cls = 'form-group' //input-group
9098         }
9099         
9100         var input =  {
9101             tag: 'input',
9102             id : id,
9103             type : this.inputType,
9104             value : this.value,
9105             cls : 'form-control',
9106             placeholder : this.placeholder || '',
9107             autocomplete : this.autocomplete || 'new-password'
9108         };
9109         
9110         if(this.capture.length){
9111             input.capture = this.capture;
9112         }
9113         
9114         if(this.accept.length){
9115             input.accept = this.accept + "/*";
9116         }
9117         
9118         if(this.align){
9119             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9120         }
9121         
9122         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9123             input.maxLength = this.maxLength;
9124         }
9125         
9126         if (this.disabled) {
9127             input.disabled=true;
9128         }
9129         
9130         if (this.readOnly) {
9131             input.readonly=true;
9132         }
9133         
9134         if (this.name) {
9135             input.name = this.name;
9136         }
9137         
9138         if (this.size) {
9139             input.cls += ' input-' + this.size;
9140         }
9141         
9142         var settings=this;
9143         ['xs','sm','md','lg'].map(function(size){
9144             if (settings[size]) {
9145                 cfg.cls += ' col-' + size + '-' + settings[size];
9146             }
9147         });
9148         
9149         var inputblock = input;
9150         
9151         var feedback = {
9152             tag: 'span',
9153             cls: 'glyphicon form-control-feedback'
9154         };
9155             
9156         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9157             
9158             inputblock = {
9159                 cls : 'has-feedback',
9160                 cn :  [
9161                     input,
9162                     feedback
9163                 ] 
9164             };  
9165         }
9166         
9167         if (this.before || this.after) {
9168             
9169             inputblock = {
9170                 cls : 'input-group',
9171                 cn :  [] 
9172             };
9173             
9174             if (this.before && typeof(this.before) == 'string') {
9175                 
9176                 inputblock.cn.push({
9177                     tag :'span',
9178                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9179                     html : this.before
9180                 });
9181             }
9182             if (this.before && typeof(this.before) == 'object') {
9183                 this.before = Roo.factory(this.before);
9184                 
9185                 inputblock.cn.push({
9186                     tag :'span',
9187                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9188                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9189                 });
9190             }
9191             
9192             inputblock.cn.push(input);
9193             
9194             if (this.after && typeof(this.after) == 'string') {
9195                 inputblock.cn.push({
9196                     tag :'span',
9197                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9198                     html : this.after
9199                 });
9200             }
9201             if (this.after && typeof(this.after) == 'object') {
9202                 this.after = Roo.factory(this.after);
9203                 
9204                 inputblock.cn.push({
9205                     tag :'span',
9206                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9207                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9208                 });
9209             }
9210             
9211             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9212                 inputblock.cls += ' has-feedback';
9213                 inputblock.cn.push(feedback);
9214             }
9215         };
9216         var indicator = {
9217             tag : 'i',
9218             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9219             tooltip : 'This field is required'
9220         };
9221         if (Roo.bootstrap.version == 4) {
9222             indicator = {
9223                 tag : 'i',
9224                 style : 'display-none'
9225             };
9226         }
9227         if (align ==='left' && this.fieldLabel.length) {
9228             
9229             cfg.cls += ' roo-form-group-label-left row';
9230             
9231             cfg.cn = [
9232                 indicator,
9233                 {
9234                     tag: 'label',
9235                     'for' :  id,
9236                     cls : 'control-label col-form-label',
9237                     html : this.fieldLabel
9238
9239                 },
9240                 {
9241                     cls : "", 
9242                     cn: [
9243                         inputblock
9244                     ]
9245                 }
9246             ];
9247             
9248             var labelCfg = cfg.cn[1];
9249             var contentCfg = cfg.cn[2];
9250             
9251             if(this.indicatorpos == 'right'){
9252                 cfg.cn = [
9253                     {
9254                         tag: 'label',
9255                         'for' :  id,
9256                         cls : 'control-label col-form-label',
9257                         cn : [
9258                             {
9259                                 tag : 'span',
9260                                 html : this.fieldLabel
9261                             },
9262                             indicator
9263                         ]
9264                     },
9265                     {
9266                         cls : "",
9267                         cn: [
9268                             inputblock
9269                         ]
9270                     }
9271
9272                 ];
9273                 
9274                 labelCfg = cfg.cn[0];
9275                 contentCfg = cfg.cn[1];
9276             
9277             }
9278             
9279             if(this.labelWidth > 12){
9280                 labelCfg.style = "width: " + this.labelWidth + 'px';
9281             }
9282             
9283             if(this.labelWidth < 13 && this.labelmd == 0){
9284                 this.labelmd = this.labelWidth;
9285             }
9286             
9287             if(this.labellg > 0){
9288                 labelCfg.cls += ' col-lg-' + this.labellg;
9289                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9290             }
9291             
9292             if(this.labelmd > 0){
9293                 labelCfg.cls += ' col-md-' + this.labelmd;
9294                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9295             }
9296             
9297             if(this.labelsm > 0){
9298                 labelCfg.cls += ' col-sm-' + this.labelsm;
9299                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9300             }
9301             
9302             if(this.labelxs > 0){
9303                 labelCfg.cls += ' col-xs-' + this.labelxs;
9304                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9305             }
9306             
9307             
9308         } else if ( this.fieldLabel.length) {
9309                 
9310             cfg.cn = [
9311                 {
9312                     tag : 'i',
9313                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9314                     tooltip : 'This field is required'
9315                 },
9316                 {
9317                     tag: 'label',
9318                    //cls : 'input-group-addon',
9319                     html : this.fieldLabel
9320
9321                 },
9322
9323                inputblock
9324
9325            ];
9326            
9327            if(this.indicatorpos == 'right'){
9328                 
9329                 cfg.cn = [
9330                     {
9331                         tag: 'label',
9332                        //cls : 'input-group-addon',
9333                         html : this.fieldLabel
9334
9335                     },
9336                     {
9337                         tag : 'i',
9338                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9339                         tooltip : 'This field is required'
9340                     },
9341
9342                    inputblock
9343
9344                ];
9345
9346             }
9347
9348         } else {
9349             
9350             cfg.cn = [
9351
9352                     inputblock
9353
9354             ];
9355                 
9356                 
9357         };
9358         
9359         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9360            cfg.cls += ' navbar-form';
9361         }
9362         
9363         if (this.parentType === 'NavGroup') {
9364            cfg.cls += ' navbar-form';
9365            cfg.tag = 'li';
9366         }
9367         
9368         return cfg;
9369         
9370     },
9371     /**
9372      * return the real input element.
9373      */
9374     inputEl: function ()
9375     {
9376         return this.el.select('input.form-control',true).first();
9377     },
9378     
9379     tooltipEl : function()
9380     {
9381         return this.inputEl();
9382     },
9383     
9384     indicatorEl : function()
9385     {
9386         if (Roo.bootstrap.version == 4) {
9387             return false; // not enabled in v4 yet.
9388         }
9389         
9390         var indicator = this.el.select('i.roo-required-indicator',true).first();
9391         
9392         if(!indicator){
9393             return false;
9394         }
9395         
9396         return indicator;
9397         
9398     },
9399     
9400     setDisabled : function(v)
9401     {
9402         var i  = this.inputEl().dom;
9403         if (!v) {
9404             i.removeAttribute('disabled');
9405             return;
9406             
9407         }
9408         i.setAttribute('disabled','true');
9409     },
9410     initEvents : function()
9411     {
9412           
9413         this.inputEl().on("keydown" , this.fireKey,  this);
9414         this.inputEl().on("focus", this.onFocus,  this);
9415         this.inputEl().on("blur", this.onBlur,  this);
9416         
9417         this.inputEl().relayEvent('keyup', this);
9418         
9419         this.indicator = this.indicatorEl();
9420         
9421         if(this.indicator){
9422             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9423         }
9424  
9425         // reference to original value for reset
9426         this.originalValue = this.getValue();
9427         //Roo.form.TextField.superclass.initEvents.call(this);
9428         if(this.validationEvent == 'keyup'){
9429             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9430             this.inputEl().on('keyup', this.filterValidation, this);
9431         }
9432         else if(this.validationEvent !== false){
9433             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9434         }
9435         
9436         if(this.selectOnFocus){
9437             this.on("focus", this.preFocus, this);
9438             
9439         }
9440         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9441             this.inputEl().on("keypress", this.filterKeys, this);
9442         } else {
9443             this.inputEl().relayEvent('keypress', this);
9444         }
9445        /* if(this.grow){
9446             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9447             this.el.on("click", this.autoSize,  this);
9448         }
9449         */
9450         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9451             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9452         }
9453         
9454         if (typeof(this.before) == 'object') {
9455             this.before.render(this.el.select('.roo-input-before',true).first());
9456         }
9457         if (typeof(this.after) == 'object') {
9458             this.after.render(this.el.select('.roo-input-after',true).first());
9459         }
9460         
9461         this.inputEl().on('change', this.onChange, this);
9462         
9463     },
9464     filterValidation : function(e){
9465         if(!e.isNavKeyPress()){
9466             this.validationTask.delay(this.validationDelay);
9467         }
9468     },
9469      /**
9470      * Validates the field value
9471      * @return {Boolean} True if the value is valid, else false
9472      */
9473     validate : function(){
9474         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9475         if(this.disabled || this.validateValue(this.getRawValue())){
9476             this.markValid();
9477             return true;
9478         }
9479         
9480         this.markInvalid();
9481         return false;
9482     },
9483     
9484     
9485     /**
9486      * Validates a value according to the field's validation rules and marks the field as invalid
9487      * if the validation fails
9488      * @param {Mixed} value The value to validate
9489      * @return {Boolean} True if the value is valid, else false
9490      */
9491     validateValue : function(value)
9492     {
9493         if(this.getVisibilityEl().hasClass('hidden')){
9494             return true;
9495         }
9496         
9497         if(value.length < 1)  { // if it's blank
9498             if(this.allowBlank){
9499                 return true;
9500             }
9501             return false;
9502         }
9503         
9504         if(value.length < this.minLength){
9505             return false;
9506         }
9507         if(value.length > this.maxLength){
9508             return false;
9509         }
9510         if(this.vtype){
9511             var vt = Roo.form.VTypes;
9512             if(!vt[this.vtype](value, this)){
9513                 return false;
9514             }
9515         }
9516         if(typeof this.validator == "function"){
9517             var msg = this.validator(value);
9518             if(msg !== true){
9519                 return false;
9520             }
9521             if (typeof(msg) == 'string') {
9522                 this.invalidText = msg;
9523             }
9524         }
9525         
9526         if(this.regex && !this.regex.test(value)){
9527             return false;
9528         }
9529         
9530         return true;
9531     },
9532     
9533      // private
9534     fireKey : function(e){
9535         //Roo.log('field ' + e.getKey());
9536         if(e.isNavKeyPress()){
9537             this.fireEvent("specialkey", this, e);
9538         }
9539     },
9540     focus : function (selectText){
9541         if(this.rendered){
9542             this.inputEl().focus();
9543             if(selectText === true){
9544                 this.inputEl().dom.select();
9545             }
9546         }
9547         return this;
9548     } ,
9549     
9550     onFocus : function(){
9551         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9552            // this.el.addClass(this.focusClass);
9553         }
9554         if(!this.hasFocus){
9555             this.hasFocus = true;
9556             this.startValue = this.getValue();
9557             this.fireEvent("focus", this);
9558         }
9559     },
9560     
9561     beforeBlur : Roo.emptyFn,
9562
9563     
9564     // private
9565     onBlur : function(){
9566         this.beforeBlur();
9567         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9568             //this.el.removeClass(this.focusClass);
9569         }
9570         this.hasFocus = false;
9571         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9572             this.validate();
9573         }
9574         var v = this.getValue();
9575         if(String(v) !== String(this.startValue)){
9576             this.fireEvent('change', this, v, this.startValue);
9577         }
9578         this.fireEvent("blur", this);
9579     },
9580     
9581     onChange : function(e)
9582     {
9583         var v = this.getValue();
9584         if(String(v) !== String(this.startValue)){
9585             this.fireEvent('change', this, v, this.startValue);
9586         }
9587         
9588     },
9589     
9590     /**
9591      * Resets the current field value to the originally loaded value and clears any validation messages
9592      */
9593     reset : function(){
9594         this.setValue(this.originalValue);
9595         this.validate();
9596     },
9597      /**
9598      * Returns the name of the field
9599      * @return {Mixed} name The name field
9600      */
9601     getName: function(){
9602         return this.name;
9603     },
9604      /**
9605      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9606      * @return {Mixed} value The field value
9607      */
9608     getValue : function(){
9609         
9610         var v = this.inputEl().getValue();
9611         
9612         return v;
9613     },
9614     /**
9615      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9616      * @return {Mixed} value The field value
9617      */
9618     getRawValue : function(){
9619         var v = this.inputEl().getValue();
9620         
9621         return v;
9622     },
9623     
9624     /**
9625      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9626      * @param {Mixed} value The value to set
9627      */
9628     setRawValue : function(v){
9629         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9630     },
9631     
9632     selectText : function(start, end){
9633         var v = this.getRawValue();
9634         if(v.length > 0){
9635             start = start === undefined ? 0 : start;
9636             end = end === undefined ? v.length : end;
9637             var d = this.inputEl().dom;
9638             if(d.setSelectionRange){
9639                 d.setSelectionRange(start, end);
9640             }else if(d.createTextRange){
9641                 var range = d.createTextRange();
9642                 range.moveStart("character", start);
9643                 range.moveEnd("character", v.length-end);
9644                 range.select();
9645             }
9646         }
9647     },
9648     
9649     /**
9650      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9651      * @param {Mixed} value The value to set
9652      */
9653     setValue : function(v){
9654         this.value = v;
9655         if(this.rendered){
9656             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9657             this.validate();
9658         }
9659     },
9660     
9661     /*
9662     processValue : function(value){
9663         if(this.stripCharsRe){
9664             var newValue = value.replace(this.stripCharsRe, '');
9665             if(newValue !== value){
9666                 this.setRawValue(newValue);
9667                 return newValue;
9668             }
9669         }
9670         return value;
9671     },
9672   */
9673     preFocus : function(){
9674         
9675         if(this.selectOnFocus){
9676             this.inputEl().dom.select();
9677         }
9678     },
9679     filterKeys : function(e){
9680         var k = e.getKey();
9681         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9682             return;
9683         }
9684         var c = e.getCharCode(), cc = String.fromCharCode(c);
9685         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9686             return;
9687         }
9688         if(!this.maskRe.test(cc)){
9689             e.stopEvent();
9690         }
9691     },
9692      /**
9693      * Clear any invalid styles/messages for this field
9694      */
9695     clearInvalid : function(){
9696         
9697         if(!this.el || this.preventMark){ // not rendered
9698             return;
9699         }
9700         
9701      
9702         this.el.removeClass(this.invalidClass);
9703         
9704         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9705             
9706             var feedback = this.el.select('.form-control-feedback', true).first();
9707             
9708             if(feedback){
9709                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9710             }
9711             
9712         }
9713         
9714         if(this.indicator){
9715             this.indicator.removeClass('visible');
9716             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9717         }
9718         
9719         this.fireEvent('valid', this);
9720     },
9721     
9722      /**
9723      * Mark this field as valid
9724      */
9725     markValid : function()
9726     {
9727         if(!this.el  || this.preventMark){ // not rendered...
9728             return;
9729         }
9730         
9731         this.el.removeClass([this.invalidClass, this.validClass]);
9732         
9733         var feedback = this.el.select('.form-control-feedback', true).first();
9734             
9735         if(feedback){
9736             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9737         }
9738         
9739         if(this.indicator){
9740             this.indicator.removeClass('visible');
9741             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9742         }
9743         
9744         if(this.disabled){
9745             return;
9746         }
9747         
9748         if(this.allowBlank && !this.getRawValue().length){
9749             return;
9750         }
9751         
9752         this.el.addClass(this.validClass);
9753         
9754         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9755             
9756             var feedback = this.el.select('.form-control-feedback', true).first();
9757             
9758             if(feedback){
9759                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9760                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9761             }
9762             
9763         }
9764         
9765         this.fireEvent('valid', this);
9766     },
9767     
9768      /**
9769      * Mark this field as invalid
9770      * @param {String} msg The validation message
9771      */
9772     markInvalid : function(msg)
9773     {
9774         if(!this.el  || this.preventMark){ // not rendered
9775             return;
9776         }
9777         
9778         this.el.removeClass([this.invalidClass, this.validClass]);
9779         
9780         var feedback = this.el.select('.form-control-feedback', true).first();
9781             
9782         if(feedback){
9783             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9784         }
9785
9786         if(this.disabled){
9787             return;
9788         }
9789         
9790         if(this.allowBlank && !this.getRawValue().length){
9791             return;
9792         }
9793         
9794         if(this.indicator){
9795             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9796             this.indicator.addClass('visible');
9797         }
9798         
9799         this.el.addClass(this.invalidClass);
9800         
9801         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9802             
9803             var feedback = this.el.select('.form-control-feedback', true).first();
9804             
9805             if(feedback){
9806                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9807                 
9808                 if(this.getValue().length || this.forceFeedback){
9809                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9810                 }
9811                 
9812             }
9813             
9814         }
9815         
9816         this.fireEvent('invalid', this, msg);
9817     },
9818     // private
9819     SafariOnKeyDown : function(event)
9820     {
9821         // this is a workaround for a password hang bug on chrome/ webkit.
9822         if (this.inputEl().dom.type != 'password') {
9823             return;
9824         }
9825         
9826         var isSelectAll = false;
9827         
9828         if(this.inputEl().dom.selectionEnd > 0){
9829             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9830         }
9831         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9832             event.preventDefault();
9833             this.setValue('');
9834             return;
9835         }
9836         
9837         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9838             
9839             event.preventDefault();
9840             // this is very hacky as keydown always get's upper case.
9841             //
9842             var cc = String.fromCharCode(event.getCharCode());
9843             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9844             
9845         }
9846     },
9847     adjustWidth : function(tag, w){
9848         tag = tag.toLowerCase();
9849         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9850             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9851                 if(tag == 'input'){
9852                     return w + 2;
9853                 }
9854                 if(tag == 'textarea'){
9855                     return w-2;
9856                 }
9857             }else if(Roo.isOpera){
9858                 if(tag == 'input'){
9859                     return w + 2;
9860                 }
9861                 if(tag == 'textarea'){
9862                     return w-2;
9863                 }
9864             }
9865         }
9866         return w;
9867     },
9868     
9869     setFieldLabel : function(v)
9870     {
9871         if(!this.rendered){
9872             return;
9873         }
9874         
9875         if(this.indicatorEl()){
9876             var ar = this.el.select('label > span',true);
9877             
9878             if (ar.elements.length) {
9879                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9880                 this.fieldLabel = v;
9881                 return;
9882             }
9883             
9884             var br = this.el.select('label',true);
9885             
9886             if(br.elements.length) {
9887                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9888                 this.fieldLabel = v;
9889                 return;
9890             }
9891             
9892             Roo.log('Cannot Found any of label > span || label in input');
9893             return;
9894         }
9895         
9896         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9897         this.fieldLabel = v;
9898         
9899         
9900     }
9901 });
9902
9903  
9904 /*
9905  * - LGPL
9906  *
9907  * Input
9908  * 
9909  */
9910
9911 /**
9912  * @class Roo.bootstrap.TextArea
9913  * @extends Roo.bootstrap.Input
9914  * Bootstrap TextArea class
9915  * @cfg {Number} cols Specifies the visible width of a text area
9916  * @cfg {Number} rows Specifies the visible number of lines in a text area
9917  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9918  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9919  * @cfg {string} html text
9920  * 
9921  * @constructor
9922  * Create a new TextArea
9923  * @param {Object} config The config object
9924  */
9925
9926 Roo.bootstrap.TextArea = function(config){
9927     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9928    
9929 };
9930
9931 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9932      
9933     cols : false,
9934     rows : 5,
9935     readOnly : false,
9936     warp : 'soft',
9937     resize : false,
9938     value: false,
9939     html: false,
9940     
9941     getAutoCreate : function(){
9942         
9943         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9944         
9945         var id = Roo.id();
9946         
9947         var cfg = {};
9948         
9949         if(this.inputType != 'hidden'){
9950             cfg.cls = 'form-group' //input-group
9951         }
9952         
9953         var input =  {
9954             tag: 'textarea',
9955             id : id,
9956             warp : this.warp,
9957             rows : this.rows,
9958             value : this.value || '',
9959             html: this.html || '',
9960             cls : 'form-control',
9961             placeholder : this.placeholder || '' 
9962             
9963         };
9964         
9965         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9966             input.maxLength = this.maxLength;
9967         }
9968         
9969         if(this.resize){
9970             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9971         }
9972         
9973         if(this.cols){
9974             input.cols = this.cols;
9975         }
9976         
9977         if (this.readOnly) {
9978             input.readonly = true;
9979         }
9980         
9981         if (this.name) {
9982             input.name = this.name;
9983         }
9984         
9985         if (this.size) {
9986             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9987         }
9988         
9989         var settings=this;
9990         ['xs','sm','md','lg'].map(function(size){
9991             if (settings[size]) {
9992                 cfg.cls += ' col-' + size + '-' + settings[size];
9993             }
9994         });
9995         
9996         var inputblock = input;
9997         
9998         if(this.hasFeedback && !this.allowBlank){
9999             
10000             var feedback = {
10001                 tag: 'span',
10002                 cls: 'glyphicon form-control-feedback'
10003             };
10004
10005             inputblock = {
10006                 cls : 'has-feedback',
10007                 cn :  [
10008                     input,
10009                     feedback
10010                 ] 
10011             };  
10012         }
10013         
10014         
10015         if (this.before || this.after) {
10016             
10017             inputblock = {
10018                 cls : 'input-group',
10019                 cn :  [] 
10020             };
10021             if (this.before) {
10022                 inputblock.cn.push({
10023                     tag :'span',
10024                     cls : 'input-group-addon',
10025                     html : this.before
10026                 });
10027             }
10028             
10029             inputblock.cn.push(input);
10030             
10031             if(this.hasFeedback && !this.allowBlank){
10032                 inputblock.cls += ' has-feedback';
10033                 inputblock.cn.push(feedback);
10034             }
10035             
10036             if (this.after) {
10037                 inputblock.cn.push({
10038                     tag :'span',
10039                     cls : 'input-group-addon',
10040                     html : this.after
10041                 });
10042             }
10043             
10044         }
10045         
10046         if (align ==='left' && this.fieldLabel.length) {
10047             cfg.cn = [
10048                 {
10049                     tag: 'label',
10050                     'for' :  id,
10051                     cls : 'control-label',
10052                     html : this.fieldLabel
10053                 },
10054                 {
10055                     cls : "",
10056                     cn: [
10057                         inputblock
10058                     ]
10059                 }
10060
10061             ];
10062             
10063             if(this.labelWidth > 12){
10064                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10065             }
10066
10067             if(this.labelWidth < 13 && this.labelmd == 0){
10068                 this.labelmd = this.labelWidth;
10069             }
10070
10071             if(this.labellg > 0){
10072                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10073                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10074             }
10075
10076             if(this.labelmd > 0){
10077                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10078                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10079             }
10080
10081             if(this.labelsm > 0){
10082                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10083                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10084             }
10085
10086             if(this.labelxs > 0){
10087                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10088                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10089             }
10090             
10091         } else if ( this.fieldLabel.length) {
10092             cfg.cn = [
10093
10094                {
10095                    tag: 'label',
10096                    //cls : 'input-group-addon',
10097                    html : this.fieldLabel
10098
10099                },
10100
10101                inputblock
10102
10103            ];
10104
10105         } else {
10106
10107             cfg.cn = [
10108
10109                 inputblock
10110
10111             ];
10112                 
10113         }
10114         
10115         if (this.disabled) {
10116             input.disabled=true;
10117         }
10118         
10119         return cfg;
10120         
10121     },
10122     /**
10123      * return the real textarea element.
10124      */
10125     inputEl: function ()
10126     {
10127         return this.el.select('textarea.form-control',true).first();
10128     },
10129     
10130     /**
10131      * Clear any invalid styles/messages for this field
10132      */
10133     clearInvalid : function()
10134     {
10135         
10136         if(!this.el || this.preventMark){ // not rendered
10137             return;
10138         }
10139         
10140         var label = this.el.select('label', true).first();
10141         var icon = this.el.select('i.fa-star', true).first();
10142         
10143         if(label && icon){
10144             icon.remove();
10145         }
10146         
10147         this.el.removeClass(this.invalidClass);
10148         
10149         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10150             
10151             var feedback = this.el.select('.form-control-feedback', true).first();
10152             
10153             if(feedback){
10154                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10155             }
10156             
10157         }
10158         
10159         this.fireEvent('valid', this);
10160     },
10161     
10162      /**
10163      * Mark this field as valid
10164      */
10165     markValid : function()
10166     {
10167         if(!this.el  || this.preventMark){ // not rendered
10168             return;
10169         }
10170         
10171         this.el.removeClass([this.invalidClass, this.validClass]);
10172         
10173         var feedback = this.el.select('.form-control-feedback', true).first();
10174             
10175         if(feedback){
10176             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10177         }
10178
10179         if(this.disabled || this.allowBlank){
10180             return;
10181         }
10182         
10183         var label = this.el.select('label', true).first();
10184         var icon = this.el.select('i.fa-star', true).first();
10185         
10186         if(label && icon){
10187             icon.remove();
10188         }
10189         
10190         this.el.addClass(this.validClass);
10191         
10192         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10193             
10194             var feedback = this.el.select('.form-control-feedback', true).first();
10195             
10196             if(feedback){
10197                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10198                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10199             }
10200             
10201         }
10202         
10203         this.fireEvent('valid', this);
10204     },
10205     
10206      /**
10207      * Mark this field as invalid
10208      * @param {String} msg The validation message
10209      */
10210     markInvalid : function(msg)
10211     {
10212         if(!this.el  || this.preventMark){ // not rendered
10213             return;
10214         }
10215         
10216         this.el.removeClass([this.invalidClass, this.validClass]);
10217         
10218         var feedback = this.el.select('.form-control-feedback', true).first();
10219             
10220         if(feedback){
10221             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10222         }
10223
10224         if(this.disabled || this.allowBlank){
10225             return;
10226         }
10227         
10228         var label = this.el.select('label', true).first();
10229         var icon = this.el.select('i.fa-star', true).first();
10230         
10231         if(!this.getValue().length && label && !icon){
10232             this.el.createChild({
10233                 tag : 'i',
10234                 cls : 'text-danger fa fa-lg fa-star',
10235                 tooltip : 'This field is required',
10236                 style : 'margin-right:5px;'
10237             }, label, true);
10238         }
10239
10240         this.el.addClass(this.invalidClass);
10241         
10242         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10243             
10244             var feedback = this.el.select('.form-control-feedback', true).first();
10245             
10246             if(feedback){
10247                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10248                 
10249                 if(this.getValue().length || this.forceFeedback){
10250                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10251                 }
10252                 
10253             }
10254             
10255         }
10256         
10257         this.fireEvent('invalid', this, msg);
10258     }
10259 });
10260
10261  
10262 /*
10263  * - LGPL
10264  *
10265  * trigger field - base class for combo..
10266  * 
10267  */
10268  
10269 /**
10270  * @class Roo.bootstrap.TriggerField
10271  * @extends Roo.bootstrap.Input
10272  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10273  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10274  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10275  * for which you can provide a custom implementation.  For example:
10276  * <pre><code>
10277 var trigger = new Roo.bootstrap.TriggerField();
10278 trigger.onTriggerClick = myTriggerFn;
10279 trigger.applyTo('my-field');
10280 </code></pre>
10281  *
10282  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10283  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10284  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10285  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10286  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10287
10288  * @constructor
10289  * Create a new TriggerField.
10290  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10291  * to the base TextField)
10292  */
10293 Roo.bootstrap.TriggerField = function(config){
10294     this.mimicing = false;
10295     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10296 };
10297
10298 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10299     /**
10300      * @cfg {String} triggerClass A CSS class to apply to the trigger
10301      */
10302      /**
10303      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10304      */
10305     hideTrigger:false,
10306
10307     /**
10308      * @cfg {Boolean} removable (true|false) special filter default false
10309      */
10310     removable : false,
10311     
10312     /** @cfg {Boolean} grow @hide */
10313     /** @cfg {Number} growMin @hide */
10314     /** @cfg {Number} growMax @hide */
10315
10316     /**
10317      * @hide 
10318      * @method
10319      */
10320     autoSize: Roo.emptyFn,
10321     // private
10322     monitorTab : true,
10323     // private
10324     deferHeight : true,
10325
10326     
10327     actionMode : 'wrap',
10328     
10329     caret : false,
10330     
10331     
10332     getAutoCreate : function(){
10333        
10334         var align = this.labelAlign || this.parentLabelAlign();
10335         
10336         var id = Roo.id();
10337         
10338         var cfg = {
10339             cls: 'form-group' //input-group
10340         };
10341         
10342         
10343         var input =  {
10344             tag: 'input',
10345             id : id,
10346             type : this.inputType,
10347             cls : 'form-control',
10348             autocomplete: 'new-password',
10349             placeholder : this.placeholder || '' 
10350             
10351         };
10352         if (this.name) {
10353             input.name = this.name;
10354         }
10355         if (this.size) {
10356             input.cls += ' input-' + this.size;
10357         }
10358         
10359         if (this.disabled) {
10360             input.disabled=true;
10361         }
10362         
10363         var inputblock = input;
10364         
10365         if(this.hasFeedback && !this.allowBlank){
10366             
10367             var feedback = {
10368                 tag: 'span',
10369                 cls: 'glyphicon form-control-feedback'
10370             };
10371             
10372             if(this.removable && !this.editable && !this.tickable){
10373                 inputblock = {
10374                     cls : 'has-feedback',
10375                     cn :  [
10376                         inputblock,
10377                         {
10378                             tag: 'button',
10379                             html : 'x',
10380                             cls : 'roo-combo-removable-btn close'
10381                         },
10382                         feedback
10383                     ] 
10384                 };
10385             } else {
10386                 inputblock = {
10387                     cls : 'has-feedback',
10388                     cn :  [
10389                         inputblock,
10390                         feedback
10391                     ] 
10392                 };
10393             }
10394
10395         } else {
10396             if(this.removable && !this.editable && !this.tickable){
10397                 inputblock = {
10398                     cls : 'roo-removable',
10399                     cn :  [
10400                         inputblock,
10401                         {
10402                             tag: 'button',
10403                             html : 'x',
10404                             cls : 'roo-combo-removable-btn close'
10405                         }
10406                     ] 
10407                 };
10408             }
10409         }
10410         
10411         if (this.before || this.after) {
10412             
10413             inputblock = {
10414                 cls : 'input-group',
10415                 cn :  [] 
10416             };
10417             if (this.before) {
10418                 inputblock.cn.push({
10419                     tag :'span',
10420                     cls : 'input-group-addon input-group-prepend input-group-text',
10421                     html : this.before
10422                 });
10423             }
10424             
10425             inputblock.cn.push(input);
10426             
10427             if(this.hasFeedback && !this.allowBlank){
10428                 inputblock.cls += ' has-feedback';
10429                 inputblock.cn.push(feedback);
10430             }
10431             
10432             if (this.after) {
10433                 inputblock.cn.push({
10434                     tag :'span',
10435                     cls : 'input-group-addon input-group-append input-group-text',
10436                     html : this.after
10437                 });
10438             }
10439             
10440         };
10441         
10442       
10443         
10444         var ibwrap = inputblock;
10445         
10446         if(this.multiple){
10447             ibwrap = {
10448                 tag: 'ul',
10449                 cls: 'roo-select2-choices',
10450                 cn:[
10451                     {
10452                         tag: 'li',
10453                         cls: 'roo-select2-search-field',
10454                         cn: [
10455
10456                             inputblock
10457                         ]
10458                     }
10459                 ]
10460             };
10461                 
10462         }
10463         
10464         var combobox = {
10465             cls: 'roo-select2-container input-group',
10466             cn: [
10467                  {
10468                     tag: 'input',
10469                     type : 'hidden',
10470                     cls: 'form-hidden-field'
10471                 },
10472                 ibwrap
10473             ]
10474         };
10475         
10476         if(!this.multiple && this.showToggleBtn){
10477             
10478             var caret = {
10479                         tag: 'span',
10480                         cls: 'caret'
10481              };
10482             if (this.caret != false) {
10483                 caret = {
10484                      tag: 'i',
10485                      cls: 'fa fa-' + this.caret
10486                 };
10487                 
10488             }
10489             
10490             combobox.cn.push({
10491                 tag :'span',
10492                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10493                 cn : [
10494                     caret,
10495                     {
10496                         tag: 'span',
10497                         cls: 'combobox-clear',
10498                         cn  : [
10499                             {
10500                                 tag : 'i',
10501                                 cls: 'icon-remove'
10502                             }
10503                         ]
10504                     }
10505                 ]
10506
10507             })
10508         }
10509         
10510         if(this.multiple){
10511             combobox.cls += ' roo-select2-container-multi';
10512         }
10513          var indicator = {
10514             tag : 'i',
10515             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10516             tooltip : 'This field is required'
10517         };
10518         if (Roo.bootstrap.version == 4) {
10519             indicator = {
10520                 tag : 'i',
10521                 style : 'display:none'
10522             };
10523         }
10524         
10525         
10526         if (align ==='left' && this.fieldLabel.length) {
10527             
10528             cfg.cls += ' roo-form-group-label-left row';
10529
10530             cfg.cn = [
10531                 indicator,
10532                 {
10533                     tag: 'label',
10534                     'for' :  id,
10535                     cls : 'control-label',
10536                     html : this.fieldLabel
10537
10538                 },
10539                 {
10540                     cls : "", 
10541                     cn: [
10542                         combobox
10543                     ]
10544                 }
10545
10546             ];
10547             
10548             var labelCfg = cfg.cn[1];
10549             var contentCfg = cfg.cn[2];
10550             
10551             if(this.indicatorpos == 'right'){
10552                 cfg.cn = [
10553                     {
10554                         tag: 'label',
10555                         'for' :  id,
10556                         cls : 'control-label',
10557                         cn : [
10558                             {
10559                                 tag : 'span',
10560                                 html : this.fieldLabel
10561                             },
10562                             indicator
10563                         ]
10564                     },
10565                     {
10566                         cls : "", 
10567                         cn: [
10568                             combobox
10569                         ]
10570                     }
10571
10572                 ];
10573                 
10574                 labelCfg = cfg.cn[0];
10575                 contentCfg = cfg.cn[1];
10576             }
10577             
10578             if(this.labelWidth > 12){
10579                 labelCfg.style = "width: " + this.labelWidth + 'px';
10580             }
10581             
10582             if(this.labelWidth < 13 && this.labelmd == 0){
10583                 this.labelmd = this.labelWidth;
10584             }
10585             
10586             if(this.labellg > 0){
10587                 labelCfg.cls += ' col-lg-' + this.labellg;
10588                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10589             }
10590             
10591             if(this.labelmd > 0){
10592                 labelCfg.cls += ' col-md-' + this.labelmd;
10593                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10594             }
10595             
10596             if(this.labelsm > 0){
10597                 labelCfg.cls += ' col-sm-' + this.labelsm;
10598                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10599             }
10600             
10601             if(this.labelxs > 0){
10602                 labelCfg.cls += ' col-xs-' + this.labelxs;
10603                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10604             }
10605             
10606         } else if ( this.fieldLabel.length) {
10607 //                Roo.log(" label");
10608             cfg.cn = [
10609                 indicator,
10610                {
10611                    tag: 'label',
10612                    //cls : 'input-group-addon',
10613                    html : this.fieldLabel
10614
10615                },
10616
10617                combobox
10618
10619             ];
10620             
10621             if(this.indicatorpos == 'right'){
10622                 
10623                 cfg.cn = [
10624                     {
10625                        tag: 'label',
10626                        cn : [
10627                            {
10628                                tag : 'span',
10629                                html : this.fieldLabel
10630                            },
10631                            indicator
10632                        ]
10633
10634                     },
10635                     combobox
10636
10637                 ];
10638
10639             }
10640
10641         } else {
10642             
10643 //                Roo.log(" no label && no align");
10644                 cfg = combobox
10645                      
10646                 
10647         }
10648         
10649         var settings=this;
10650         ['xs','sm','md','lg'].map(function(size){
10651             if (settings[size]) {
10652                 cfg.cls += ' col-' + size + '-' + settings[size];
10653             }
10654         });
10655         
10656         return cfg;
10657         
10658     },
10659     
10660     
10661     
10662     // private
10663     onResize : function(w, h){
10664 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10665 //        if(typeof w == 'number'){
10666 //            var x = w - this.trigger.getWidth();
10667 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10668 //            this.trigger.setStyle('left', x+'px');
10669 //        }
10670     },
10671
10672     // private
10673     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10674
10675     // private
10676     getResizeEl : function(){
10677         return this.inputEl();
10678     },
10679
10680     // private
10681     getPositionEl : function(){
10682         return this.inputEl();
10683     },
10684
10685     // private
10686     alignErrorIcon : function(){
10687         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10688     },
10689
10690     // private
10691     initEvents : function(){
10692         
10693         this.createList();
10694         
10695         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10696         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10697         if(!this.multiple && this.showToggleBtn){
10698             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10699             if(this.hideTrigger){
10700                 this.trigger.setDisplayed(false);
10701             }
10702             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10703         }
10704         
10705         if(this.multiple){
10706             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10707         }
10708         
10709         if(this.removable && !this.editable && !this.tickable){
10710             var close = this.closeTriggerEl();
10711             
10712             if(close){
10713                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10714                 close.on('click', this.removeBtnClick, this, close);
10715             }
10716         }
10717         
10718         //this.trigger.addClassOnOver('x-form-trigger-over');
10719         //this.trigger.addClassOnClick('x-form-trigger-click');
10720         
10721         //if(!this.width){
10722         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10723         //}
10724     },
10725     
10726     closeTriggerEl : function()
10727     {
10728         var close = this.el.select('.roo-combo-removable-btn', true).first();
10729         return close ? close : false;
10730     },
10731     
10732     removeBtnClick : function(e, h, el)
10733     {
10734         e.preventDefault();
10735         
10736         if(this.fireEvent("remove", this) !== false){
10737             this.reset();
10738             this.fireEvent("afterremove", this)
10739         }
10740     },
10741     
10742     createList : function()
10743     {
10744         this.list = Roo.get(document.body).createChild({
10745             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10746             cls: 'typeahead typeahead-long dropdown-menu',
10747             style: 'display:none'
10748         });
10749         
10750         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10751         
10752     },
10753
10754     // private
10755     initTrigger : function(){
10756        
10757     },
10758
10759     // private
10760     onDestroy : function(){
10761         if(this.trigger){
10762             this.trigger.removeAllListeners();
10763           //  this.trigger.remove();
10764         }
10765         //if(this.wrap){
10766         //    this.wrap.remove();
10767         //}
10768         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10769     },
10770
10771     // private
10772     onFocus : function(){
10773         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10774         /*
10775         if(!this.mimicing){
10776             this.wrap.addClass('x-trigger-wrap-focus');
10777             this.mimicing = true;
10778             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10779             if(this.monitorTab){
10780                 this.el.on("keydown", this.checkTab, this);
10781             }
10782         }
10783         */
10784     },
10785
10786     // private
10787     checkTab : function(e){
10788         if(e.getKey() == e.TAB){
10789             this.triggerBlur();
10790         }
10791     },
10792
10793     // private
10794     onBlur : function(){
10795         // do nothing
10796     },
10797
10798     // private
10799     mimicBlur : function(e, t){
10800         /*
10801         if(!this.wrap.contains(t) && this.validateBlur()){
10802             this.triggerBlur();
10803         }
10804         */
10805     },
10806
10807     // private
10808     triggerBlur : function(){
10809         this.mimicing = false;
10810         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10811         if(this.monitorTab){
10812             this.el.un("keydown", this.checkTab, this);
10813         }
10814         //this.wrap.removeClass('x-trigger-wrap-focus');
10815         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10816     },
10817
10818     // private
10819     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10820     validateBlur : function(e, t){
10821         return true;
10822     },
10823
10824     // private
10825     onDisable : function(){
10826         this.inputEl().dom.disabled = true;
10827         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10828         //if(this.wrap){
10829         //    this.wrap.addClass('x-item-disabled');
10830         //}
10831     },
10832
10833     // private
10834     onEnable : function(){
10835         this.inputEl().dom.disabled = false;
10836         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10837         //if(this.wrap){
10838         //    this.el.removeClass('x-item-disabled');
10839         //}
10840     },
10841
10842     // private
10843     onShow : function(){
10844         var ae = this.getActionEl();
10845         
10846         if(ae){
10847             ae.dom.style.display = '';
10848             ae.dom.style.visibility = 'visible';
10849         }
10850     },
10851
10852     // private
10853     
10854     onHide : function(){
10855         var ae = this.getActionEl();
10856         ae.dom.style.display = 'none';
10857     },
10858
10859     /**
10860      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10861      * by an implementing function.
10862      * @method
10863      * @param {EventObject} e
10864      */
10865     onTriggerClick : Roo.emptyFn
10866 });
10867  /*
10868  * Based on:
10869  * Ext JS Library 1.1.1
10870  * Copyright(c) 2006-2007, Ext JS, LLC.
10871  *
10872  * Originally Released Under LGPL - original licence link has changed is not relivant.
10873  *
10874  * Fork - LGPL
10875  * <script type="text/javascript">
10876  */
10877
10878
10879 /**
10880  * @class Roo.data.SortTypes
10881  * @singleton
10882  * Defines the default sorting (casting?) comparison functions used when sorting data.
10883  */
10884 Roo.data.SortTypes = {
10885     /**
10886      * Default sort that does nothing
10887      * @param {Mixed} s The value being converted
10888      * @return {Mixed} The comparison value
10889      */
10890     none : function(s){
10891         return s;
10892     },
10893     
10894     /**
10895      * The regular expression used to strip tags
10896      * @type {RegExp}
10897      * @property
10898      */
10899     stripTagsRE : /<\/?[^>]+>/gi,
10900     
10901     /**
10902      * Strips all HTML tags to sort on text only
10903      * @param {Mixed} s The value being converted
10904      * @return {String} The comparison value
10905      */
10906     asText : function(s){
10907         return String(s).replace(this.stripTagsRE, "");
10908     },
10909     
10910     /**
10911      * Strips all HTML tags to sort on text only - Case insensitive
10912      * @param {Mixed} s The value being converted
10913      * @return {String} The comparison value
10914      */
10915     asUCText : function(s){
10916         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10917     },
10918     
10919     /**
10920      * Case insensitive string
10921      * @param {Mixed} s The value being converted
10922      * @return {String} The comparison value
10923      */
10924     asUCString : function(s) {
10925         return String(s).toUpperCase();
10926     },
10927     
10928     /**
10929      * Date sorting
10930      * @param {Mixed} s The value being converted
10931      * @return {Number} The comparison value
10932      */
10933     asDate : function(s) {
10934         if(!s){
10935             return 0;
10936         }
10937         if(s instanceof Date){
10938             return s.getTime();
10939         }
10940         return Date.parse(String(s));
10941     },
10942     
10943     /**
10944      * Float sorting
10945      * @param {Mixed} s The value being converted
10946      * @return {Float} The comparison value
10947      */
10948     asFloat : function(s) {
10949         var val = parseFloat(String(s).replace(/,/g, ""));
10950         if(isNaN(val)) {
10951             val = 0;
10952         }
10953         return val;
10954     },
10955     
10956     /**
10957      * Integer sorting
10958      * @param {Mixed} s The value being converted
10959      * @return {Number} The comparison value
10960      */
10961     asInt : function(s) {
10962         var val = parseInt(String(s).replace(/,/g, ""));
10963         if(isNaN(val)) {
10964             val = 0;
10965         }
10966         return val;
10967     }
10968 };/*
10969  * Based on:
10970  * Ext JS Library 1.1.1
10971  * Copyright(c) 2006-2007, Ext JS, LLC.
10972  *
10973  * Originally Released Under LGPL - original licence link has changed is not relivant.
10974  *
10975  * Fork - LGPL
10976  * <script type="text/javascript">
10977  */
10978
10979 /**
10980 * @class Roo.data.Record
10981  * Instances of this class encapsulate both record <em>definition</em> information, and record
10982  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10983  * to access Records cached in an {@link Roo.data.Store} object.<br>
10984  * <p>
10985  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10986  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10987  * objects.<br>
10988  * <p>
10989  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10990  * @constructor
10991  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10992  * {@link #create}. The parameters are the same.
10993  * @param {Array} data An associative Array of data values keyed by the field name.
10994  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10995  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10996  * not specified an integer id is generated.
10997  */
10998 Roo.data.Record = function(data, id){
10999     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11000     this.data = data;
11001 };
11002
11003 /**
11004  * Generate a constructor for a specific record layout.
11005  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11006  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11007  * Each field definition object may contain the following properties: <ul>
11008  * <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,
11009  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11010  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11011  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11012  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11013  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11014  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11015  * this may be omitted.</p></li>
11016  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11017  * <ul><li>auto (Default, implies no conversion)</li>
11018  * <li>string</li>
11019  * <li>int</li>
11020  * <li>float</li>
11021  * <li>boolean</li>
11022  * <li>date</li></ul></p></li>
11023  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11024  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11025  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11026  * by the Reader into an object that will be stored in the Record. It is passed the
11027  * following parameters:<ul>
11028  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11029  * </ul></p></li>
11030  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11031  * </ul>
11032  * <br>usage:<br><pre><code>
11033 var TopicRecord = Roo.data.Record.create(
11034     {name: 'title', mapping: 'topic_title'},
11035     {name: 'author', mapping: 'username'},
11036     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11037     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11038     {name: 'lastPoster', mapping: 'user2'},
11039     {name: 'excerpt', mapping: 'post_text'}
11040 );
11041
11042 var myNewRecord = new TopicRecord({
11043     title: 'Do my job please',
11044     author: 'noobie',
11045     totalPosts: 1,
11046     lastPost: new Date(),
11047     lastPoster: 'Animal',
11048     excerpt: 'No way dude!'
11049 });
11050 myStore.add(myNewRecord);
11051 </code></pre>
11052  * @method create
11053  * @static
11054  */
11055 Roo.data.Record.create = function(o){
11056     var f = function(){
11057         f.superclass.constructor.apply(this, arguments);
11058     };
11059     Roo.extend(f, Roo.data.Record);
11060     var p = f.prototype;
11061     p.fields = new Roo.util.MixedCollection(false, function(field){
11062         return field.name;
11063     });
11064     for(var i = 0, len = o.length; i < len; i++){
11065         p.fields.add(new Roo.data.Field(o[i]));
11066     }
11067     f.getField = function(name){
11068         return p.fields.get(name);  
11069     };
11070     return f;
11071 };
11072
11073 Roo.data.Record.AUTO_ID = 1000;
11074 Roo.data.Record.EDIT = 'edit';
11075 Roo.data.Record.REJECT = 'reject';
11076 Roo.data.Record.COMMIT = 'commit';
11077
11078 Roo.data.Record.prototype = {
11079     /**
11080      * Readonly flag - true if this record has been modified.
11081      * @type Boolean
11082      */
11083     dirty : false,
11084     editing : false,
11085     error: null,
11086     modified: null,
11087
11088     // private
11089     join : function(store){
11090         this.store = store;
11091     },
11092
11093     /**
11094      * Set the named field to the specified value.
11095      * @param {String} name The name of the field to set.
11096      * @param {Object} value The value to set the field to.
11097      */
11098     set : function(name, value){
11099         if(this.data[name] == value){
11100             return;
11101         }
11102         this.dirty = true;
11103         if(!this.modified){
11104             this.modified = {};
11105         }
11106         if(typeof this.modified[name] == 'undefined'){
11107             this.modified[name] = this.data[name];
11108         }
11109         this.data[name] = value;
11110         if(!this.editing && this.store){
11111             this.store.afterEdit(this);
11112         }       
11113     },
11114
11115     /**
11116      * Get the value of the named field.
11117      * @param {String} name The name of the field to get the value of.
11118      * @return {Object} The value of the field.
11119      */
11120     get : function(name){
11121         return this.data[name]; 
11122     },
11123
11124     // private
11125     beginEdit : function(){
11126         this.editing = true;
11127         this.modified = {}; 
11128     },
11129
11130     // private
11131     cancelEdit : function(){
11132         this.editing = false;
11133         delete this.modified;
11134     },
11135
11136     // private
11137     endEdit : function(){
11138         this.editing = false;
11139         if(this.dirty && this.store){
11140             this.store.afterEdit(this);
11141         }
11142     },
11143
11144     /**
11145      * Usually called by the {@link Roo.data.Store} which owns the Record.
11146      * Rejects all changes made to the Record since either creation, or the last commit operation.
11147      * Modified fields are reverted to their original values.
11148      * <p>
11149      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11150      * of reject operations.
11151      */
11152     reject : function(){
11153         var m = this.modified;
11154         for(var n in m){
11155             if(typeof m[n] != "function"){
11156                 this.data[n] = m[n];
11157             }
11158         }
11159         this.dirty = false;
11160         delete this.modified;
11161         this.editing = false;
11162         if(this.store){
11163             this.store.afterReject(this);
11164         }
11165     },
11166
11167     /**
11168      * Usually called by the {@link Roo.data.Store} which owns the Record.
11169      * Commits all changes made to the Record since either creation, or the last commit operation.
11170      * <p>
11171      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11172      * of commit operations.
11173      */
11174     commit : function(){
11175         this.dirty = false;
11176         delete this.modified;
11177         this.editing = false;
11178         if(this.store){
11179             this.store.afterCommit(this);
11180         }
11181     },
11182
11183     // private
11184     hasError : function(){
11185         return this.error != null;
11186     },
11187
11188     // private
11189     clearError : function(){
11190         this.error = null;
11191     },
11192
11193     /**
11194      * Creates a copy of this record.
11195      * @param {String} id (optional) A new record id if you don't want to use this record's id
11196      * @return {Record}
11197      */
11198     copy : function(newId) {
11199         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11200     }
11201 };/*
11202  * Based on:
11203  * Ext JS Library 1.1.1
11204  * Copyright(c) 2006-2007, Ext JS, LLC.
11205  *
11206  * Originally Released Under LGPL - original licence link has changed is not relivant.
11207  *
11208  * Fork - LGPL
11209  * <script type="text/javascript">
11210  */
11211
11212
11213
11214 /**
11215  * @class Roo.data.Store
11216  * @extends Roo.util.Observable
11217  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11218  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11219  * <p>
11220  * 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
11221  * has no knowledge of the format of the data returned by the Proxy.<br>
11222  * <p>
11223  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11224  * instances from the data object. These records are cached and made available through accessor functions.
11225  * @constructor
11226  * Creates a new Store.
11227  * @param {Object} config A config object containing the objects needed for the Store to access data,
11228  * and read the data into Records.
11229  */
11230 Roo.data.Store = function(config){
11231     this.data = new Roo.util.MixedCollection(false);
11232     this.data.getKey = function(o){
11233         return o.id;
11234     };
11235     this.baseParams = {};
11236     // private
11237     this.paramNames = {
11238         "start" : "start",
11239         "limit" : "limit",
11240         "sort" : "sort",
11241         "dir" : "dir",
11242         "multisort" : "_multisort"
11243     };
11244
11245     if(config && config.data){
11246         this.inlineData = config.data;
11247         delete config.data;
11248     }
11249
11250     Roo.apply(this, config);
11251     
11252     if(this.reader){ // reader passed
11253         this.reader = Roo.factory(this.reader, Roo.data);
11254         this.reader.xmodule = this.xmodule || false;
11255         if(!this.recordType){
11256             this.recordType = this.reader.recordType;
11257         }
11258         if(this.reader.onMetaChange){
11259             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11260         }
11261     }
11262
11263     if(this.recordType){
11264         this.fields = this.recordType.prototype.fields;
11265     }
11266     this.modified = [];
11267
11268     this.addEvents({
11269         /**
11270          * @event datachanged
11271          * Fires when the data cache has changed, and a widget which is using this Store
11272          * as a Record cache should refresh its view.
11273          * @param {Store} this
11274          */
11275         datachanged : true,
11276         /**
11277          * @event metachange
11278          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11279          * @param {Store} this
11280          * @param {Object} meta The JSON metadata
11281          */
11282         metachange : true,
11283         /**
11284          * @event add
11285          * Fires when Records have been added to the Store
11286          * @param {Store} this
11287          * @param {Roo.data.Record[]} records The array of Records added
11288          * @param {Number} index The index at which the record(s) were added
11289          */
11290         add : true,
11291         /**
11292          * @event remove
11293          * Fires when a Record has been removed from the Store
11294          * @param {Store} this
11295          * @param {Roo.data.Record} record The Record that was removed
11296          * @param {Number} index The index at which the record was removed
11297          */
11298         remove : true,
11299         /**
11300          * @event update
11301          * Fires when a Record has been updated
11302          * @param {Store} this
11303          * @param {Roo.data.Record} record The Record that was updated
11304          * @param {String} operation The update operation being performed.  Value may be one of:
11305          * <pre><code>
11306  Roo.data.Record.EDIT
11307  Roo.data.Record.REJECT
11308  Roo.data.Record.COMMIT
11309          * </code></pre>
11310          */
11311         update : true,
11312         /**
11313          * @event clear
11314          * Fires when the data cache has been cleared.
11315          * @param {Store} this
11316          */
11317         clear : true,
11318         /**
11319          * @event beforeload
11320          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11321          * the load action will be canceled.
11322          * @param {Store} this
11323          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11324          */
11325         beforeload : true,
11326         /**
11327          * @event beforeloadadd
11328          * Fires after a new set of Records has been loaded.
11329          * @param {Store} this
11330          * @param {Roo.data.Record[]} records The Records that were loaded
11331          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11332          */
11333         beforeloadadd : true,
11334         /**
11335          * @event load
11336          * Fires after a new set of Records has been loaded, before they are added to the store.
11337          * @param {Store} this
11338          * @param {Roo.data.Record[]} records The Records that were loaded
11339          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11340          * @params {Object} return from reader
11341          */
11342         load : true,
11343         /**
11344          * @event loadexception
11345          * Fires if an exception occurs in the Proxy during loading.
11346          * Called with the signature of the Proxy's "loadexception" event.
11347          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11348          * 
11349          * @param {Proxy} 
11350          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11351          * @param {Object} load options 
11352          * @param {Object} jsonData from your request (normally this contains the Exception)
11353          */
11354         loadexception : true
11355     });
11356     
11357     if(this.proxy){
11358         this.proxy = Roo.factory(this.proxy, Roo.data);
11359         this.proxy.xmodule = this.xmodule || false;
11360         this.relayEvents(this.proxy,  ["loadexception"]);
11361     }
11362     this.sortToggle = {};
11363     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11364
11365     Roo.data.Store.superclass.constructor.call(this);
11366
11367     if(this.inlineData){
11368         this.loadData(this.inlineData);
11369         delete this.inlineData;
11370     }
11371 };
11372
11373 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11374      /**
11375     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11376     * without a remote query - used by combo/forms at present.
11377     */
11378     
11379     /**
11380     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11381     */
11382     /**
11383     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11384     */
11385     /**
11386     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11387     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11388     */
11389     /**
11390     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11391     * on any HTTP request
11392     */
11393     /**
11394     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11395     */
11396     /**
11397     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11398     */
11399     multiSort: false,
11400     /**
11401     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11402     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11403     */
11404     remoteSort : false,
11405
11406     /**
11407     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11408      * loaded or when a record is removed. (defaults to false).
11409     */
11410     pruneModifiedRecords : false,
11411
11412     // private
11413     lastOptions : null,
11414
11415     /**
11416      * Add Records to the Store and fires the add event.
11417      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11418      */
11419     add : function(records){
11420         records = [].concat(records);
11421         for(var i = 0, len = records.length; i < len; i++){
11422             records[i].join(this);
11423         }
11424         var index = this.data.length;
11425         this.data.addAll(records);
11426         this.fireEvent("add", this, records, index);
11427     },
11428
11429     /**
11430      * Remove a Record from the Store and fires the remove event.
11431      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11432      */
11433     remove : function(record){
11434         var index = this.data.indexOf(record);
11435         this.data.removeAt(index);
11436  
11437         if(this.pruneModifiedRecords){
11438             this.modified.remove(record);
11439         }
11440         this.fireEvent("remove", this, record, index);
11441     },
11442
11443     /**
11444      * Remove all Records from the Store and fires the clear event.
11445      */
11446     removeAll : function(){
11447         this.data.clear();
11448         if(this.pruneModifiedRecords){
11449             this.modified = [];
11450         }
11451         this.fireEvent("clear", this);
11452     },
11453
11454     /**
11455      * Inserts Records to the Store at the given index and fires the add event.
11456      * @param {Number} index The start index at which to insert the passed Records.
11457      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11458      */
11459     insert : function(index, records){
11460         records = [].concat(records);
11461         for(var i = 0, len = records.length; i < len; i++){
11462             this.data.insert(index, records[i]);
11463             records[i].join(this);
11464         }
11465         this.fireEvent("add", this, records, index);
11466     },
11467
11468     /**
11469      * Get the index within the cache of the passed Record.
11470      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11471      * @return {Number} The index of the passed Record. Returns -1 if not found.
11472      */
11473     indexOf : function(record){
11474         return this.data.indexOf(record);
11475     },
11476
11477     /**
11478      * Get the index within the cache of the Record with the passed id.
11479      * @param {String} id The id of the Record to find.
11480      * @return {Number} The index of the Record. Returns -1 if not found.
11481      */
11482     indexOfId : function(id){
11483         return this.data.indexOfKey(id);
11484     },
11485
11486     /**
11487      * Get the Record with the specified id.
11488      * @param {String} id The id of the Record to find.
11489      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11490      */
11491     getById : function(id){
11492         return this.data.key(id);
11493     },
11494
11495     /**
11496      * Get the Record at the specified index.
11497      * @param {Number} index The index of the Record to find.
11498      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11499      */
11500     getAt : function(index){
11501         return this.data.itemAt(index);
11502     },
11503
11504     /**
11505      * Returns a range of Records between specified indices.
11506      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11507      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11508      * @return {Roo.data.Record[]} An array of Records
11509      */
11510     getRange : function(start, end){
11511         return this.data.getRange(start, end);
11512     },
11513
11514     // private
11515     storeOptions : function(o){
11516         o = Roo.apply({}, o);
11517         delete o.callback;
11518         delete o.scope;
11519         this.lastOptions = o;
11520     },
11521
11522     /**
11523      * Loads the Record cache from the configured Proxy using the configured Reader.
11524      * <p>
11525      * If using remote paging, then the first load call must specify the <em>start</em>
11526      * and <em>limit</em> properties in the options.params property to establish the initial
11527      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11528      * <p>
11529      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11530      * and this call will return before the new data has been loaded. Perform any post-processing
11531      * in a callback function, or in a "load" event handler.</strong>
11532      * <p>
11533      * @param {Object} options An object containing properties which control loading options:<ul>
11534      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11535      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11536      * passed the following arguments:<ul>
11537      * <li>r : Roo.data.Record[]</li>
11538      * <li>options: Options object from the load call</li>
11539      * <li>success: Boolean success indicator</li></ul></li>
11540      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11541      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11542      * </ul>
11543      */
11544     load : function(options){
11545         options = options || {};
11546         if(this.fireEvent("beforeload", this, options) !== false){
11547             this.storeOptions(options);
11548             var p = Roo.apply(options.params || {}, this.baseParams);
11549             // if meta was not loaded from remote source.. try requesting it.
11550             if (!this.reader.metaFromRemote) {
11551                 p._requestMeta = 1;
11552             }
11553             if(this.sortInfo && this.remoteSort){
11554                 var pn = this.paramNames;
11555                 p[pn["sort"]] = this.sortInfo.field;
11556                 p[pn["dir"]] = this.sortInfo.direction;
11557             }
11558             if (this.multiSort) {
11559                 var pn = this.paramNames;
11560                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11561             }
11562             
11563             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11564         }
11565     },
11566
11567     /**
11568      * Reloads the Record cache from the configured Proxy using the configured Reader and
11569      * the options from the last load operation performed.
11570      * @param {Object} options (optional) An object containing properties which may override the options
11571      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11572      * the most recently used options are reused).
11573      */
11574     reload : function(options){
11575         this.load(Roo.applyIf(options||{}, this.lastOptions));
11576     },
11577
11578     // private
11579     // Called as a callback by the Reader during a load operation.
11580     loadRecords : function(o, options, success){
11581         if(!o || success === false){
11582             if(success !== false){
11583                 this.fireEvent("load", this, [], options, o);
11584             }
11585             if(options.callback){
11586                 options.callback.call(options.scope || this, [], options, false);
11587             }
11588             return;
11589         }
11590         // if data returned failure - throw an exception.
11591         if (o.success === false) {
11592             // show a message if no listener is registered.
11593             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11594                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11595             }
11596             // loadmask wil be hooked into this..
11597             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11598             return;
11599         }
11600         var r = o.records, t = o.totalRecords || r.length;
11601         
11602         this.fireEvent("beforeloadadd", this, r, options, o);
11603         
11604         if(!options || options.add !== true){
11605             if(this.pruneModifiedRecords){
11606                 this.modified = [];
11607             }
11608             for(var i = 0, len = r.length; i < len; i++){
11609                 r[i].join(this);
11610             }
11611             if(this.snapshot){
11612                 this.data = this.snapshot;
11613                 delete this.snapshot;
11614             }
11615             this.data.clear();
11616             this.data.addAll(r);
11617             this.totalLength = t;
11618             this.applySort();
11619             this.fireEvent("datachanged", this);
11620         }else{
11621             this.totalLength = Math.max(t, this.data.length+r.length);
11622             this.add(r);
11623         }
11624         
11625         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11626                 
11627             var e = new Roo.data.Record({});
11628
11629             e.set(this.parent.displayField, this.parent.emptyTitle);
11630             e.set(this.parent.valueField, '');
11631
11632             this.insert(0, e);
11633         }
11634             
11635         this.fireEvent("load", this, r, options, o);
11636         if(options.callback){
11637             options.callback.call(options.scope || this, r, options, true);
11638         }
11639     },
11640
11641
11642     /**
11643      * Loads data from a passed data block. A Reader which understands the format of the data
11644      * must have been configured in the constructor.
11645      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11646      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11647      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11648      */
11649     loadData : function(o, append){
11650         var r = this.reader.readRecords(o);
11651         this.loadRecords(r, {add: append}, true);
11652     },
11653
11654     /**
11655      * Gets the number of cached records.
11656      * <p>
11657      * <em>If using paging, this may not be the total size of the dataset. If the data object
11658      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11659      * the data set size</em>
11660      */
11661     getCount : function(){
11662         return this.data.length || 0;
11663     },
11664
11665     /**
11666      * Gets the total number of records in the dataset as returned by the server.
11667      * <p>
11668      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11669      * the dataset size</em>
11670      */
11671     getTotalCount : function(){
11672         return this.totalLength || 0;
11673     },
11674
11675     /**
11676      * Returns the sort state of the Store as an object with two properties:
11677      * <pre><code>
11678  field {String} The name of the field by which the Records are sorted
11679  direction {String} The sort order, "ASC" or "DESC"
11680      * </code></pre>
11681      */
11682     getSortState : function(){
11683         return this.sortInfo;
11684     },
11685
11686     // private
11687     applySort : function(){
11688         if(this.sortInfo && !this.remoteSort){
11689             var s = this.sortInfo, f = s.field;
11690             var st = this.fields.get(f).sortType;
11691             var fn = function(r1, r2){
11692                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11693                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11694             };
11695             this.data.sort(s.direction, fn);
11696             if(this.snapshot && this.snapshot != this.data){
11697                 this.snapshot.sort(s.direction, fn);
11698             }
11699         }
11700     },
11701
11702     /**
11703      * Sets the default sort column and order to be used by the next load operation.
11704      * @param {String} fieldName The name of the field to sort by.
11705      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11706      */
11707     setDefaultSort : function(field, dir){
11708         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11709     },
11710
11711     /**
11712      * Sort the Records.
11713      * If remote sorting is used, the sort is performed on the server, and the cache is
11714      * reloaded. If local sorting is used, the cache is sorted internally.
11715      * @param {String} fieldName The name of the field to sort by.
11716      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11717      */
11718     sort : function(fieldName, dir){
11719         var f = this.fields.get(fieldName);
11720         if(!dir){
11721             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11722             
11723             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11724                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11725             }else{
11726                 dir = f.sortDir;
11727             }
11728         }
11729         this.sortToggle[f.name] = dir;
11730         this.sortInfo = {field: f.name, direction: dir};
11731         if(!this.remoteSort){
11732             this.applySort();
11733             this.fireEvent("datachanged", this);
11734         }else{
11735             this.load(this.lastOptions);
11736         }
11737     },
11738
11739     /**
11740      * Calls the specified function for each of the Records in the cache.
11741      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11742      * Returning <em>false</em> aborts and exits the iteration.
11743      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11744      */
11745     each : function(fn, scope){
11746         this.data.each(fn, scope);
11747     },
11748
11749     /**
11750      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11751      * (e.g., during paging).
11752      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11753      */
11754     getModifiedRecords : function(){
11755         return this.modified;
11756     },
11757
11758     // private
11759     createFilterFn : function(property, value, anyMatch){
11760         if(!value.exec){ // not a regex
11761             value = String(value);
11762             if(value.length == 0){
11763                 return false;
11764             }
11765             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11766         }
11767         return function(r){
11768             return value.test(r.data[property]);
11769         };
11770     },
11771
11772     /**
11773      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11774      * @param {String} property A field on your records
11775      * @param {Number} start The record index to start at (defaults to 0)
11776      * @param {Number} end The last record index to include (defaults to length - 1)
11777      * @return {Number} The sum
11778      */
11779     sum : function(property, start, end){
11780         var rs = this.data.items, v = 0;
11781         start = start || 0;
11782         end = (end || end === 0) ? end : rs.length-1;
11783
11784         for(var i = start; i <= end; i++){
11785             v += (rs[i].data[property] || 0);
11786         }
11787         return v;
11788     },
11789
11790     /**
11791      * Filter the records by a specified property.
11792      * @param {String} field A field on your records
11793      * @param {String/RegExp} value Either a string that the field
11794      * should start with or a RegExp to test against the field
11795      * @param {Boolean} anyMatch True to match any part not just the beginning
11796      */
11797     filter : function(property, value, anyMatch){
11798         var fn = this.createFilterFn(property, value, anyMatch);
11799         return fn ? this.filterBy(fn) : this.clearFilter();
11800     },
11801
11802     /**
11803      * Filter by a function. The specified function will be called with each
11804      * record in this data source. If the function returns true the record is included,
11805      * otherwise it is filtered.
11806      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11807      * @param {Object} scope (optional) The scope of the function (defaults to this)
11808      */
11809     filterBy : function(fn, scope){
11810         this.snapshot = this.snapshot || this.data;
11811         this.data = this.queryBy(fn, scope||this);
11812         this.fireEvent("datachanged", this);
11813     },
11814
11815     /**
11816      * Query the records by a specified property.
11817      * @param {String} field A field on your records
11818      * @param {String/RegExp} value Either a string that the field
11819      * should start with or a RegExp to test against the field
11820      * @param {Boolean} anyMatch True to match any part not just the beginning
11821      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11822      */
11823     query : function(property, value, anyMatch){
11824         var fn = this.createFilterFn(property, value, anyMatch);
11825         return fn ? this.queryBy(fn) : this.data.clone();
11826     },
11827
11828     /**
11829      * Query by a function. The specified function will be called with each
11830      * record in this data source. If the function returns true the record is included
11831      * in the results.
11832      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11833      * @param {Object} scope (optional) The scope of the function (defaults to this)
11834       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11835      **/
11836     queryBy : function(fn, scope){
11837         var data = this.snapshot || this.data;
11838         return data.filterBy(fn, scope||this);
11839     },
11840
11841     /**
11842      * Collects unique values for a particular dataIndex from this store.
11843      * @param {String} dataIndex The property to collect
11844      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11845      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11846      * @return {Array} An array of the unique values
11847      **/
11848     collect : function(dataIndex, allowNull, bypassFilter){
11849         var d = (bypassFilter === true && this.snapshot) ?
11850                 this.snapshot.items : this.data.items;
11851         var v, sv, r = [], l = {};
11852         for(var i = 0, len = d.length; i < len; i++){
11853             v = d[i].data[dataIndex];
11854             sv = String(v);
11855             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11856                 l[sv] = true;
11857                 r[r.length] = v;
11858             }
11859         }
11860         return r;
11861     },
11862
11863     /**
11864      * Revert to a view of the Record cache with no filtering applied.
11865      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11866      */
11867     clearFilter : function(suppressEvent){
11868         if(this.snapshot && this.snapshot != this.data){
11869             this.data = this.snapshot;
11870             delete this.snapshot;
11871             if(suppressEvent !== true){
11872                 this.fireEvent("datachanged", this);
11873             }
11874         }
11875     },
11876
11877     // private
11878     afterEdit : function(record){
11879         if(this.modified.indexOf(record) == -1){
11880             this.modified.push(record);
11881         }
11882         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11883     },
11884     
11885     // private
11886     afterReject : function(record){
11887         this.modified.remove(record);
11888         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11889     },
11890
11891     // private
11892     afterCommit : function(record){
11893         this.modified.remove(record);
11894         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11895     },
11896
11897     /**
11898      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11899      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11900      */
11901     commitChanges : function(){
11902         var m = this.modified.slice(0);
11903         this.modified = [];
11904         for(var i = 0, len = m.length; i < len; i++){
11905             m[i].commit();
11906         }
11907     },
11908
11909     /**
11910      * Cancel outstanding changes on all changed records.
11911      */
11912     rejectChanges : function(){
11913         var m = this.modified.slice(0);
11914         this.modified = [];
11915         for(var i = 0, len = m.length; i < len; i++){
11916             m[i].reject();
11917         }
11918     },
11919
11920     onMetaChange : function(meta, rtype, o){
11921         this.recordType = rtype;
11922         this.fields = rtype.prototype.fields;
11923         delete this.snapshot;
11924         this.sortInfo = meta.sortInfo || this.sortInfo;
11925         this.modified = [];
11926         this.fireEvent('metachange', this, this.reader.meta);
11927     },
11928     
11929     moveIndex : function(data, type)
11930     {
11931         var index = this.indexOf(data);
11932         
11933         var newIndex = index + type;
11934         
11935         this.remove(data);
11936         
11937         this.insert(newIndex, data);
11938         
11939     }
11940 });/*
11941  * Based on:
11942  * Ext JS Library 1.1.1
11943  * Copyright(c) 2006-2007, Ext JS, LLC.
11944  *
11945  * Originally Released Under LGPL - original licence link has changed is not relivant.
11946  *
11947  * Fork - LGPL
11948  * <script type="text/javascript">
11949  */
11950
11951 /**
11952  * @class Roo.data.SimpleStore
11953  * @extends Roo.data.Store
11954  * Small helper class to make creating Stores from Array data easier.
11955  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11956  * @cfg {Array} fields An array of field definition objects, or field name strings.
11957  * @cfg {Array} data The multi-dimensional array of data
11958  * @constructor
11959  * @param {Object} config
11960  */
11961 Roo.data.SimpleStore = function(config){
11962     Roo.data.SimpleStore.superclass.constructor.call(this, {
11963         isLocal : true,
11964         reader: new Roo.data.ArrayReader({
11965                 id: config.id
11966             },
11967             Roo.data.Record.create(config.fields)
11968         ),
11969         proxy : new Roo.data.MemoryProxy(config.data)
11970     });
11971     this.load();
11972 };
11973 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11974  * Based on:
11975  * Ext JS Library 1.1.1
11976  * Copyright(c) 2006-2007, Ext JS, LLC.
11977  *
11978  * Originally Released Under LGPL - original licence link has changed is not relivant.
11979  *
11980  * Fork - LGPL
11981  * <script type="text/javascript">
11982  */
11983
11984 /**
11985 /**
11986  * @extends Roo.data.Store
11987  * @class Roo.data.JsonStore
11988  * Small helper class to make creating Stores for JSON data easier. <br/>
11989 <pre><code>
11990 var store = new Roo.data.JsonStore({
11991     url: 'get-images.php',
11992     root: 'images',
11993     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11994 });
11995 </code></pre>
11996  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11997  * JsonReader and HttpProxy (unless inline data is provided).</b>
11998  * @cfg {Array} fields An array of field definition objects, or field name strings.
11999  * @constructor
12000  * @param {Object} config
12001  */
12002 Roo.data.JsonStore = function(c){
12003     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12004         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12005         reader: new Roo.data.JsonReader(c, c.fields)
12006     }));
12007 };
12008 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12009  * Based on:
12010  * Ext JS Library 1.1.1
12011  * Copyright(c) 2006-2007, Ext JS, LLC.
12012  *
12013  * Originally Released Under LGPL - original licence link has changed is not relivant.
12014  *
12015  * Fork - LGPL
12016  * <script type="text/javascript">
12017  */
12018
12019  
12020 Roo.data.Field = function(config){
12021     if(typeof config == "string"){
12022         config = {name: config};
12023     }
12024     Roo.apply(this, config);
12025     
12026     if(!this.type){
12027         this.type = "auto";
12028     }
12029     
12030     var st = Roo.data.SortTypes;
12031     // named sortTypes are supported, here we look them up
12032     if(typeof this.sortType == "string"){
12033         this.sortType = st[this.sortType];
12034     }
12035     
12036     // set default sortType for strings and dates
12037     if(!this.sortType){
12038         switch(this.type){
12039             case "string":
12040                 this.sortType = st.asUCString;
12041                 break;
12042             case "date":
12043                 this.sortType = st.asDate;
12044                 break;
12045             default:
12046                 this.sortType = st.none;
12047         }
12048     }
12049
12050     // define once
12051     var stripRe = /[\$,%]/g;
12052
12053     // prebuilt conversion function for this field, instead of
12054     // switching every time we're reading a value
12055     if(!this.convert){
12056         var cv, dateFormat = this.dateFormat;
12057         switch(this.type){
12058             case "":
12059             case "auto":
12060             case undefined:
12061                 cv = function(v){ return v; };
12062                 break;
12063             case "string":
12064                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12065                 break;
12066             case "int":
12067                 cv = function(v){
12068                     return v !== undefined && v !== null && v !== '' ?
12069                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12070                     };
12071                 break;
12072             case "float":
12073                 cv = function(v){
12074                     return v !== undefined && v !== null && v !== '' ?
12075                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12076                     };
12077                 break;
12078             case "bool":
12079             case "boolean":
12080                 cv = function(v){ return v === true || v === "true" || v == 1; };
12081                 break;
12082             case "date":
12083                 cv = function(v){
12084                     if(!v){
12085                         return '';
12086                     }
12087                     if(v instanceof Date){
12088                         return v;
12089                     }
12090                     if(dateFormat){
12091                         if(dateFormat == "timestamp"){
12092                             return new Date(v*1000);
12093                         }
12094                         return Date.parseDate(v, dateFormat);
12095                     }
12096                     var parsed = Date.parse(v);
12097                     return parsed ? new Date(parsed) : null;
12098                 };
12099              break;
12100             
12101         }
12102         this.convert = cv;
12103     }
12104 };
12105
12106 Roo.data.Field.prototype = {
12107     dateFormat: null,
12108     defaultValue: "",
12109     mapping: null,
12110     sortType : null,
12111     sortDir : "ASC"
12112 };/*
12113  * Based on:
12114  * Ext JS Library 1.1.1
12115  * Copyright(c) 2006-2007, Ext JS, LLC.
12116  *
12117  * Originally Released Under LGPL - original licence link has changed is not relivant.
12118  *
12119  * Fork - LGPL
12120  * <script type="text/javascript">
12121  */
12122  
12123 // Base class for reading structured data from a data source.  This class is intended to be
12124 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12125
12126 /**
12127  * @class Roo.data.DataReader
12128  * Base class for reading structured data from a data source.  This class is intended to be
12129  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12130  */
12131
12132 Roo.data.DataReader = function(meta, recordType){
12133     
12134     this.meta = meta;
12135     
12136     this.recordType = recordType instanceof Array ? 
12137         Roo.data.Record.create(recordType) : recordType;
12138 };
12139
12140 Roo.data.DataReader.prototype = {
12141      /**
12142      * Create an empty record
12143      * @param {Object} data (optional) - overlay some values
12144      * @return {Roo.data.Record} record created.
12145      */
12146     newRow :  function(d) {
12147         var da =  {};
12148         this.recordType.prototype.fields.each(function(c) {
12149             switch( c.type) {
12150                 case 'int' : da[c.name] = 0; break;
12151                 case 'date' : da[c.name] = new Date(); break;
12152                 case 'float' : da[c.name] = 0.0; break;
12153                 case 'boolean' : da[c.name] = false; break;
12154                 default : da[c.name] = ""; break;
12155             }
12156             
12157         });
12158         return new this.recordType(Roo.apply(da, d));
12159     }
12160     
12161 };/*
12162  * Based on:
12163  * Ext JS Library 1.1.1
12164  * Copyright(c) 2006-2007, Ext JS, LLC.
12165  *
12166  * Originally Released Under LGPL - original licence link has changed is not relivant.
12167  *
12168  * Fork - LGPL
12169  * <script type="text/javascript">
12170  */
12171
12172 /**
12173  * @class Roo.data.DataProxy
12174  * @extends Roo.data.Observable
12175  * This class is an abstract base class for implementations which provide retrieval of
12176  * unformatted data objects.<br>
12177  * <p>
12178  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12179  * (of the appropriate type which knows how to parse the data object) to provide a block of
12180  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12181  * <p>
12182  * Custom implementations must implement the load method as described in
12183  * {@link Roo.data.HttpProxy#load}.
12184  */
12185 Roo.data.DataProxy = function(){
12186     this.addEvents({
12187         /**
12188          * @event beforeload
12189          * Fires before a network request is made to retrieve a data object.
12190          * @param {Object} This DataProxy object.
12191          * @param {Object} params The params parameter to the load function.
12192          */
12193         beforeload : true,
12194         /**
12195          * @event load
12196          * Fires before the load method's callback is called.
12197          * @param {Object} This DataProxy object.
12198          * @param {Object} o The data object.
12199          * @param {Object} arg The callback argument object passed to the load function.
12200          */
12201         load : true,
12202         /**
12203          * @event loadexception
12204          * Fires if an Exception occurs during data retrieval.
12205          * @param {Object} This DataProxy object.
12206          * @param {Object} o The data object.
12207          * @param {Object} arg The callback argument object passed to the load function.
12208          * @param {Object} e The Exception.
12209          */
12210         loadexception : true
12211     });
12212     Roo.data.DataProxy.superclass.constructor.call(this);
12213 };
12214
12215 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12216
12217     /**
12218      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12219      */
12220 /*
12221  * Based on:
12222  * Ext JS Library 1.1.1
12223  * Copyright(c) 2006-2007, Ext JS, LLC.
12224  *
12225  * Originally Released Under LGPL - original licence link has changed is not relivant.
12226  *
12227  * Fork - LGPL
12228  * <script type="text/javascript">
12229  */
12230 /**
12231  * @class Roo.data.MemoryProxy
12232  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12233  * to the Reader when its load method is called.
12234  * @constructor
12235  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12236  */
12237 Roo.data.MemoryProxy = function(data){
12238     if (data.data) {
12239         data = data.data;
12240     }
12241     Roo.data.MemoryProxy.superclass.constructor.call(this);
12242     this.data = data;
12243 };
12244
12245 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12246     
12247     /**
12248      * Load data from the requested source (in this case an in-memory
12249      * data object passed to the constructor), read the data object into
12250      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12251      * process that block using the passed callback.
12252      * @param {Object} params This parameter is not used by the MemoryProxy class.
12253      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12254      * object into a block of Roo.data.Records.
12255      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12256      * The function must be passed <ul>
12257      * <li>The Record block object</li>
12258      * <li>The "arg" argument from the load function</li>
12259      * <li>A boolean success indicator</li>
12260      * </ul>
12261      * @param {Object} scope The scope in which to call the callback
12262      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12263      */
12264     load : function(params, reader, callback, scope, arg){
12265         params = params || {};
12266         var result;
12267         try {
12268             result = reader.readRecords(this.data);
12269         }catch(e){
12270             this.fireEvent("loadexception", this, arg, null, e);
12271             callback.call(scope, null, arg, false);
12272             return;
12273         }
12274         callback.call(scope, result, arg, true);
12275     },
12276     
12277     // private
12278     update : function(params, records){
12279         
12280     }
12281 });/*
12282  * Based on:
12283  * Ext JS Library 1.1.1
12284  * Copyright(c) 2006-2007, Ext JS, LLC.
12285  *
12286  * Originally Released Under LGPL - original licence link has changed is not relivant.
12287  *
12288  * Fork - LGPL
12289  * <script type="text/javascript">
12290  */
12291 /**
12292  * @class Roo.data.HttpProxy
12293  * @extends Roo.data.DataProxy
12294  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12295  * configured to reference a certain URL.<br><br>
12296  * <p>
12297  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12298  * from which the running page was served.<br><br>
12299  * <p>
12300  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12301  * <p>
12302  * Be aware that to enable the browser to parse an XML document, the server must set
12303  * the Content-Type header in the HTTP response to "text/xml".
12304  * @constructor
12305  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12306  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12307  * will be used to make the request.
12308  */
12309 Roo.data.HttpProxy = function(conn){
12310     Roo.data.HttpProxy.superclass.constructor.call(this);
12311     // is conn a conn config or a real conn?
12312     this.conn = conn;
12313     this.useAjax = !conn || !conn.events;
12314   
12315 };
12316
12317 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12318     // thse are take from connection...
12319     
12320     /**
12321      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12322      */
12323     /**
12324      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12325      * extra parameters to each request made by this object. (defaults to undefined)
12326      */
12327     /**
12328      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12329      *  to each request made by this object. (defaults to undefined)
12330      */
12331     /**
12332      * @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)
12333      */
12334     /**
12335      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12336      */
12337      /**
12338      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12339      * @type Boolean
12340      */
12341   
12342
12343     /**
12344      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12345      * @type Boolean
12346      */
12347     /**
12348      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12349      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12350      * a finer-grained basis than the DataProxy events.
12351      */
12352     getConnection : function(){
12353         return this.useAjax ? Roo.Ajax : this.conn;
12354     },
12355
12356     /**
12357      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12358      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12359      * process that block using the passed callback.
12360      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12361      * for the request to the remote server.
12362      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12363      * object into a block of Roo.data.Records.
12364      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12365      * The function must be passed <ul>
12366      * <li>The Record block object</li>
12367      * <li>The "arg" argument from the load function</li>
12368      * <li>A boolean success indicator</li>
12369      * </ul>
12370      * @param {Object} scope The scope in which to call the callback
12371      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12372      */
12373     load : function(params, reader, callback, scope, arg){
12374         if(this.fireEvent("beforeload", this, params) !== false){
12375             var  o = {
12376                 params : params || {},
12377                 request: {
12378                     callback : callback,
12379                     scope : scope,
12380                     arg : arg
12381                 },
12382                 reader: reader,
12383                 callback : this.loadResponse,
12384                 scope: this
12385             };
12386             if(this.useAjax){
12387                 Roo.applyIf(o, this.conn);
12388                 if(this.activeRequest){
12389                     Roo.Ajax.abort(this.activeRequest);
12390                 }
12391                 this.activeRequest = Roo.Ajax.request(o);
12392             }else{
12393                 this.conn.request(o);
12394             }
12395         }else{
12396             callback.call(scope||this, null, arg, false);
12397         }
12398     },
12399
12400     // private
12401     loadResponse : function(o, success, response){
12402         delete this.activeRequest;
12403         if(!success){
12404             this.fireEvent("loadexception", this, o, response);
12405             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12406             return;
12407         }
12408         var result;
12409         try {
12410             result = o.reader.read(response);
12411         }catch(e){
12412             this.fireEvent("loadexception", this, o, response, e);
12413             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12414             return;
12415         }
12416         
12417         this.fireEvent("load", this, o, o.request.arg);
12418         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12419     },
12420
12421     // private
12422     update : function(dataSet){
12423
12424     },
12425
12426     // private
12427     updateResponse : function(dataSet){
12428
12429     }
12430 });/*
12431  * Based on:
12432  * Ext JS Library 1.1.1
12433  * Copyright(c) 2006-2007, Ext JS, LLC.
12434  *
12435  * Originally Released Under LGPL - original licence link has changed is not relivant.
12436  *
12437  * Fork - LGPL
12438  * <script type="text/javascript">
12439  */
12440
12441 /**
12442  * @class Roo.data.ScriptTagProxy
12443  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12444  * other than the originating domain of the running page.<br><br>
12445  * <p>
12446  * <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
12447  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12448  * <p>
12449  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12450  * source code that is used as the source inside a &lt;script> tag.<br><br>
12451  * <p>
12452  * In order for the browser to process the returned data, the server must wrap the data object
12453  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12454  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12455  * depending on whether the callback name was passed:
12456  * <p>
12457  * <pre><code>
12458 boolean scriptTag = false;
12459 String cb = request.getParameter("callback");
12460 if (cb != null) {
12461     scriptTag = true;
12462     response.setContentType("text/javascript");
12463 } else {
12464     response.setContentType("application/x-json");
12465 }
12466 Writer out = response.getWriter();
12467 if (scriptTag) {
12468     out.write(cb + "(");
12469 }
12470 out.print(dataBlock.toJsonString());
12471 if (scriptTag) {
12472     out.write(");");
12473 }
12474 </pre></code>
12475  *
12476  * @constructor
12477  * @param {Object} config A configuration object.
12478  */
12479 Roo.data.ScriptTagProxy = function(config){
12480     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12481     Roo.apply(this, config);
12482     this.head = document.getElementsByTagName("head")[0];
12483 };
12484
12485 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12486
12487 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12488     /**
12489      * @cfg {String} url The URL from which to request the data object.
12490      */
12491     /**
12492      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12493      */
12494     timeout : 30000,
12495     /**
12496      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12497      * the server the name of the callback function set up by the load call to process the returned data object.
12498      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12499      * javascript output which calls this named function passing the data object as its only parameter.
12500      */
12501     callbackParam : "callback",
12502     /**
12503      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12504      * name to the request.
12505      */
12506     nocache : true,
12507
12508     /**
12509      * Load data from the configured URL, read the data object into
12510      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12511      * process that block using the passed callback.
12512      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12513      * for the request to the remote server.
12514      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12515      * object into a block of Roo.data.Records.
12516      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12517      * The function must be passed <ul>
12518      * <li>The Record block object</li>
12519      * <li>The "arg" argument from the load function</li>
12520      * <li>A boolean success indicator</li>
12521      * </ul>
12522      * @param {Object} scope The scope in which to call the callback
12523      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12524      */
12525     load : function(params, reader, callback, scope, arg){
12526         if(this.fireEvent("beforeload", this, params) !== false){
12527
12528             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12529
12530             var url = this.url;
12531             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12532             if(this.nocache){
12533                 url += "&_dc=" + (new Date().getTime());
12534             }
12535             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12536             var trans = {
12537                 id : transId,
12538                 cb : "stcCallback"+transId,
12539                 scriptId : "stcScript"+transId,
12540                 params : params,
12541                 arg : arg,
12542                 url : url,
12543                 callback : callback,
12544                 scope : scope,
12545                 reader : reader
12546             };
12547             var conn = this;
12548
12549             window[trans.cb] = function(o){
12550                 conn.handleResponse(o, trans);
12551             };
12552
12553             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12554
12555             if(this.autoAbort !== false){
12556                 this.abort();
12557             }
12558
12559             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12560
12561             var script = document.createElement("script");
12562             script.setAttribute("src", url);
12563             script.setAttribute("type", "text/javascript");
12564             script.setAttribute("id", trans.scriptId);
12565             this.head.appendChild(script);
12566
12567             this.trans = trans;
12568         }else{
12569             callback.call(scope||this, null, arg, false);
12570         }
12571     },
12572
12573     // private
12574     isLoading : function(){
12575         return this.trans ? true : false;
12576     },
12577
12578     /**
12579      * Abort the current server request.
12580      */
12581     abort : function(){
12582         if(this.isLoading()){
12583             this.destroyTrans(this.trans);
12584         }
12585     },
12586
12587     // private
12588     destroyTrans : function(trans, isLoaded){
12589         this.head.removeChild(document.getElementById(trans.scriptId));
12590         clearTimeout(trans.timeoutId);
12591         if(isLoaded){
12592             window[trans.cb] = undefined;
12593             try{
12594                 delete window[trans.cb];
12595             }catch(e){}
12596         }else{
12597             // if hasn't been loaded, wait for load to remove it to prevent script error
12598             window[trans.cb] = function(){
12599                 window[trans.cb] = undefined;
12600                 try{
12601                     delete window[trans.cb];
12602                 }catch(e){}
12603             };
12604         }
12605     },
12606
12607     // private
12608     handleResponse : function(o, trans){
12609         this.trans = false;
12610         this.destroyTrans(trans, true);
12611         var result;
12612         try {
12613             result = trans.reader.readRecords(o);
12614         }catch(e){
12615             this.fireEvent("loadexception", this, o, trans.arg, e);
12616             trans.callback.call(trans.scope||window, null, trans.arg, false);
12617             return;
12618         }
12619         this.fireEvent("load", this, o, trans.arg);
12620         trans.callback.call(trans.scope||window, result, trans.arg, true);
12621     },
12622
12623     // private
12624     handleFailure : function(trans){
12625         this.trans = false;
12626         this.destroyTrans(trans, false);
12627         this.fireEvent("loadexception", this, null, trans.arg);
12628         trans.callback.call(trans.scope||window, null, trans.arg, false);
12629     }
12630 });/*
12631  * Based on:
12632  * Ext JS Library 1.1.1
12633  * Copyright(c) 2006-2007, Ext JS, LLC.
12634  *
12635  * Originally Released Under LGPL - original licence link has changed is not relivant.
12636  *
12637  * Fork - LGPL
12638  * <script type="text/javascript">
12639  */
12640
12641 /**
12642  * @class Roo.data.JsonReader
12643  * @extends Roo.data.DataReader
12644  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12645  * based on mappings in a provided Roo.data.Record constructor.
12646  * 
12647  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12648  * in the reply previously. 
12649  * 
12650  * <p>
12651  * Example code:
12652  * <pre><code>
12653 var RecordDef = Roo.data.Record.create([
12654     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12655     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12656 ]);
12657 var myReader = new Roo.data.JsonReader({
12658     totalProperty: "results",    // The property which contains the total dataset size (optional)
12659     root: "rows",                // The property which contains an Array of row objects
12660     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12661 }, RecordDef);
12662 </code></pre>
12663  * <p>
12664  * This would consume a JSON file like this:
12665  * <pre><code>
12666 { 'results': 2, 'rows': [
12667     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12668     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12669 }
12670 </code></pre>
12671  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12672  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12673  * paged from the remote server.
12674  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12675  * @cfg {String} root name of the property which contains the Array of row objects.
12676  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12677  * @cfg {Array} fields Array of field definition objects
12678  * @constructor
12679  * Create a new JsonReader
12680  * @param {Object} meta Metadata configuration options
12681  * @param {Object} recordType Either an Array of field definition objects,
12682  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12683  */
12684 Roo.data.JsonReader = function(meta, recordType){
12685     
12686     meta = meta || {};
12687     // set some defaults:
12688     Roo.applyIf(meta, {
12689         totalProperty: 'total',
12690         successProperty : 'success',
12691         root : 'data',
12692         id : 'id'
12693     });
12694     
12695     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12696 };
12697 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12698     
12699     /**
12700      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12701      * Used by Store query builder to append _requestMeta to params.
12702      * 
12703      */
12704     metaFromRemote : false,
12705     /**
12706      * This method is only used by a DataProxy which has retrieved data from a remote server.
12707      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12708      * @return {Object} data A data block which is used by an Roo.data.Store object as
12709      * a cache of Roo.data.Records.
12710      */
12711     read : function(response){
12712         var json = response.responseText;
12713        
12714         var o = /* eval:var:o */ eval("("+json+")");
12715         if(!o) {
12716             throw {message: "JsonReader.read: Json object not found"};
12717         }
12718         
12719         if(o.metaData){
12720             
12721             delete this.ef;
12722             this.metaFromRemote = true;
12723             this.meta = o.metaData;
12724             this.recordType = Roo.data.Record.create(o.metaData.fields);
12725             this.onMetaChange(this.meta, this.recordType, o);
12726         }
12727         return this.readRecords(o);
12728     },
12729
12730     // private function a store will implement
12731     onMetaChange : function(meta, recordType, o){
12732
12733     },
12734
12735     /**
12736          * @ignore
12737          */
12738     simpleAccess: function(obj, subsc) {
12739         return obj[subsc];
12740     },
12741
12742         /**
12743          * @ignore
12744          */
12745     getJsonAccessor: function(){
12746         var re = /[\[\.]/;
12747         return function(expr) {
12748             try {
12749                 return(re.test(expr))
12750                     ? new Function("obj", "return obj." + expr)
12751                     : function(obj){
12752                         return obj[expr];
12753                     };
12754             } catch(e){}
12755             return Roo.emptyFn;
12756         };
12757     }(),
12758
12759     /**
12760      * Create a data block containing Roo.data.Records from an XML document.
12761      * @param {Object} o An object which contains an Array of row objects in the property specified
12762      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12763      * which contains the total size of the dataset.
12764      * @return {Object} data A data block which is used by an Roo.data.Store object as
12765      * a cache of Roo.data.Records.
12766      */
12767     readRecords : function(o){
12768         /**
12769          * After any data loads, the raw JSON data is available for further custom processing.
12770          * @type Object
12771          */
12772         this.o = o;
12773         var s = this.meta, Record = this.recordType,
12774             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12775
12776 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12777         if (!this.ef) {
12778             if(s.totalProperty) {
12779                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12780                 }
12781                 if(s.successProperty) {
12782                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12783                 }
12784                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12785                 if (s.id) {
12786                         var g = this.getJsonAccessor(s.id);
12787                         this.getId = function(rec) {
12788                                 var r = g(rec);  
12789                                 return (r === undefined || r === "") ? null : r;
12790                         };
12791                 } else {
12792                         this.getId = function(){return null;};
12793                 }
12794             this.ef = [];
12795             for(var jj = 0; jj < fl; jj++){
12796                 f = fi[jj];
12797                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12798                 this.ef[jj] = this.getJsonAccessor(map);
12799             }
12800         }
12801
12802         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12803         if(s.totalProperty){
12804             var vt = parseInt(this.getTotal(o), 10);
12805             if(!isNaN(vt)){
12806                 totalRecords = vt;
12807             }
12808         }
12809         if(s.successProperty){
12810             var vs = this.getSuccess(o);
12811             if(vs === false || vs === 'false'){
12812                 success = false;
12813             }
12814         }
12815         var records = [];
12816         for(var i = 0; i < c; i++){
12817                 var n = root[i];
12818             var values = {};
12819             var id = this.getId(n);
12820             for(var j = 0; j < fl; j++){
12821                 f = fi[j];
12822             var v = this.ef[j](n);
12823             if (!f.convert) {
12824                 Roo.log('missing convert for ' + f.name);
12825                 Roo.log(f);
12826                 continue;
12827             }
12828             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12829             }
12830             var record = new Record(values, id);
12831             record.json = n;
12832             records[i] = record;
12833         }
12834         return {
12835             raw : o,
12836             success : success,
12837             records : records,
12838             totalRecords : totalRecords
12839         };
12840     }
12841 });/*
12842  * Based on:
12843  * Ext JS Library 1.1.1
12844  * Copyright(c) 2006-2007, Ext JS, LLC.
12845  *
12846  * Originally Released Under LGPL - original licence link has changed is not relivant.
12847  *
12848  * Fork - LGPL
12849  * <script type="text/javascript">
12850  */
12851
12852 /**
12853  * @class Roo.data.ArrayReader
12854  * @extends Roo.data.DataReader
12855  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12856  * Each element of that Array represents a row of data fields. The
12857  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12858  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12859  * <p>
12860  * Example code:.
12861  * <pre><code>
12862 var RecordDef = Roo.data.Record.create([
12863     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12864     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12865 ]);
12866 var myReader = new Roo.data.ArrayReader({
12867     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12868 }, RecordDef);
12869 </code></pre>
12870  * <p>
12871  * This would consume an Array like this:
12872  * <pre><code>
12873 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12874   </code></pre>
12875  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12876  * @constructor
12877  * Create a new JsonReader
12878  * @param {Object} meta Metadata configuration options.
12879  * @param {Object} recordType Either an Array of field definition objects
12880  * as specified to {@link Roo.data.Record#create},
12881  * or an {@link Roo.data.Record} object
12882  * created using {@link Roo.data.Record#create}.
12883  */
12884 Roo.data.ArrayReader = function(meta, recordType){
12885     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12886 };
12887
12888 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12889     /**
12890      * Create a data block containing Roo.data.Records from an XML document.
12891      * @param {Object} o An Array of row objects which represents the dataset.
12892      * @return {Object} data A data block which is used by an Roo.data.Store object as
12893      * a cache of Roo.data.Records.
12894      */
12895     readRecords : function(o){
12896         var sid = this.meta ? this.meta.id : null;
12897         var recordType = this.recordType, fields = recordType.prototype.fields;
12898         var records = [];
12899         var root = o;
12900             for(var i = 0; i < root.length; i++){
12901                     var n = root[i];
12902                 var values = {};
12903                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12904                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12905                 var f = fields.items[j];
12906                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12907                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12908                 v = f.convert(v);
12909                 values[f.name] = v;
12910             }
12911                 var record = new recordType(values, id);
12912                 record.json = n;
12913                 records[records.length] = record;
12914             }
12915             return {
12916                 records : records,
12917                 totalRecords : records.length
12918             };
12919     }
12920 });/*
12921  * - LGPL
12922  * * 
12923  */
12924
12925 /**
12926  * @class Roo.bootstrap.ComboBox
12927  * @extends Roo.bootstrap.TriggerField
12928  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12929  * @cfg {Boolean} append (true|false) default false
12930  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12931  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12932  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12933  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12934  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12935  * @cfg {Boolean} animate default true
12936  * @cfg {Boolean} emptyResultText only for touch device
12937  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12938  * @cfg {String} emptyTitle default ''
12939  * @constructor
12940  * Create a new ComboBox.
12941  * @param {Object} config Configuration options
12942  */
12943 Roo.bootstrap.ComboBox = function(config){
12944     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12945     this.addEvents({
12946         /**
12947          * @event expand
12948          * Fires when the dropdown list is expanded
12949         * @param {Roo.bootstrap.ComboBox} combo This combo box
12950         */
12951         'expand' : true,
12952         /**
12953          * @event collapse
12954          * Fires when the dropdown list is collapsed
12955         * @param {Roo.bootstrap.ComboBox} combo This combo box
12956         */
12957         'collapse' : true,
12958         /**
12959          * @event beforeselect
12960          * Fires before a list item is selected. Return false to cancel the selection.
12961         * @param {Roo.bootstrap.ComboBox} combo This combo box
12962         * @param {Roo.data.Record} record The data record returned from the underlying store
12963         * @param {Number} index The index of the selected item in the dropdown list
12964         */
12965         'beforeselect' : true,
12966         /**
12967          * @event select
12968          * Fires when a list item is selected
12969         * @param {Roo.bootstrap.ComboBox} combo This combo box
12970         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12971         * @param {Number} index The index of the selected item in the dropdown list
12972         */
12973         'select' : true,
12974         /**
12975          * @event beforequery
12976          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12977          * The event object passed has these properties:
12978         * @param {Roo.bootstrap.ComboBox} combo This combo box
12979         * @param {String} query The query
12980         * @param {Boolean} forceAll true to force "all" query
12981         * @param {Boolean} cancel true to cancel the query
12982         * @param {Object} e The query event object
12983         */
12984         'beforequery': true,
12985          /**
12986          * @event add
12987          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12988         * @param {Roo.bootstrap.ComboBox} combo This combo box
12989         */
12990         'add' : true,
12991         /**
12992          * @event edit
12993          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12994         * @param {Roo.bootstrap.ComboBox} combo This combo box
12995         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12996         */
12997         'edit' : true,
12998         /**
12999          * @event remove
13000          * Fires when the remove value from the combobox array
13001         * @param {Roo.bootstrap.ComboBox} combo This combo box
13002         */
13003         'remove' : true,
13004         /**
13005          * @event afterremove
13006          * Fires when the remove value from the combobox array
13007         * @param {Roo.bootstrap.ComboBox} combo This combo box
13008         */
13009         'afterremove' : true,
13010         /**
13011          * @event specialfilter
13012          * Fires when specialfilter
13013             * @param {Roo.bootstrap.ComboBox} combo This combo box
13014             */
13015         'specialfilter' : true,
13016         /**
13017          * @event tick
13018          * Fires when tick the element
13019             * @param {Roo.bootstrap.ComboBox} combo This combo box
13020             */
13021         'tick' : true,
13022         /**
13023          * @event touchviewdisplay
13024          * Fires when touch view require special display (default is using displayField)
13025             * @param {Roo.bootstrap.ComboBox} combo This combo box
13026             * @param {Object} cfg set html .
13027             */
13028         'touchviewdisplay' : true
13029         
13030     });
13031     
13032     this.item = [];
13033     this.tickItems = [];
13034     
13035     this.selectedIndex = -1;
13036     if(this.mode == 'local'){
13037         if(config.queryDelay === undefined){
13038             this.queryDelay = 10;
13039         }
13040         if(config.minChars === undefined){
13041             this.minChars = 0;
13042         }
13043     }
13044 };
13045
13046 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13047      
13048     /**
13049      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13050      * rendering into an Roo.Editor, defaults to false)
13051      */
13052     /**
13053      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13054      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13055      */
13056     /**
13057      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13058      */
13059     /**
13060      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13061      * the dropdown list (defaults to undefined, with no header element)
13062      */
13063
13064      /**
13065      * @cfg {String/Roo.Template} tpl The template to use to render the output
13066      */
13067      
13068      /**
13069      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13070      */
13071     listWidth: undefined,
13072     /**
13073      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13074      * mode = 'remote' or 'text' if mode = 'local')
13075      */
13076     displayField: undefined,
13077     
13078     /**
13079      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13080      * mode = 'remote' or 'value' if mode = 'local'). 
13081      * Note: use of a valueField requires the user make a selection
13082      * in order for a value to be mapped.
13083      */
13084     valueField: undefined,
13085     /**
13086      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13087      */
13088     modalTitle : '',
13089     
13090     /**
13091      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13092      * field's data value (defaults to the underlying DOM element's name)
13093      */
13094     hiddenName: undefined,
13095     /**
13096      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13097      */
13098     listClass: '',
13099     /**
13100      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13101      */
13102     selectedClass: 'active',
13103     
13104     /**
13105      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13106      */
13107     shadow:'sides',
13108     /**
13109      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13110      * anchor positions (defaults to 'tl-bl')
13111      */
13112     listAlign: 'tl-bl?',
13113     /**
13114      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13115      */
13116     maxHeight: 300,
13117     /**
13118      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13119      * query specified by the allQuery config option (defaults to 'query')
13120      */
13121     triggerAction: 'query',
13122     /**
13123      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13124      * (defaults to 4, does not apply if editable = false)
13125      */
13126     minChars : 4,
13127     /**
13128      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13129      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13130      */
13131     typeAhead: false,
13132     /**
13133      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13134      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13135      */
13136     queryDelay: 500,
13137     /**
13138      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13139      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13140      */
13141     pageSize: 0,
13142     /**
13143      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13144      * when editable = true (defaults to false)
13145      */
13146     selectOnFocus:false,
13147     /**
13148      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13149      */
13150     queryParam: 'query',
13151     /**
13152      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13153      * when mode = 'remote' (defaults to 'Loading...')
13154      */
13155     loadingText: 'Loading...',
13156     /**
13157      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13158      */
13159     resizable: false,
13160     /**
13161      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13162      */
13163     handleHeight : 8,
13164     /**
13165      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13166      * traditional select (defaults to true)
13167      */
13168     editable: true,
13169     /**
13170      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13171      */
13172     allQuery: '',
13173     /**
13174      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13175      */
13176     mode: 'remote',
13177     /**
13178      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13179      * listWidth has a higher value)
13180      */
13181     minListWidth : 70,
13182     /**
13183      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13184      * allow the user to set arbitrary text into the field (defaults to false)
13185      */
13186     forceSelection:false,
13187     /**
13188      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13189      * if typeAhead = true (defaults to 250)
13190      */
13191     typeAheadDelay : 250,
13192     /**
13193      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13194      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13195      */
13196     valueNotFoundText : undefined,
13197     /**
13198      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13199      */
13200     blockFocus : false,
13201     
13202     /**
13203      * @cfg {Boolean} disableClear Disable showing of clear button.
13204      */
13205     disableClear : false,
13206     /**
13207      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13208      */
13209     alwaysQuery : false,
13210     
13211     /**
13212      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13213      */
13214     multiple : false,
13215     
13216     /**
13217      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13218      */
13219     invalidClass : "has-warning",
13220     
13221     /**
13222      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13223      */
13224     validClass : "has-success",
13225     
13226     /**
13227      * @cfg {Boolean} specialFilter (true|false) special filter default false
13228      */
13229     specialFilter : false,
13230     
13231     /**
13232      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13233      */
13234     mobileTouchView : true,
13235     
13236     /**
13237      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13238      */
13239     useNativeIOS : false,
13240     
13241     /**
13242      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13243      */
13244     mobile_restrict_height : false,
13245     
13246     ios_options : false,
13247     
13248     //private
13249     addicon : false,
13250     editicon: false,
13251     
13252     page: 0,
13253     hasQuery: false,
13254     append: false,
13255     loadNext: false,
13256     autoFocus : true,
13257     tickable : false,
13258     btnPosition : 'right',
13259     triggerList : true,
13260     showToggleBtn : true,
13261     animate : true,
13262     emptyResultText: 'Empty',
13263     triggerText : 'Select',
13264     emptyTitle : '',
13265     
13266     // element that contains real text value.. (when hidden is used..)
13267     
13268     getAutoCreate : function()
13269     {   
13270         var cfg = false;
13271         //render
13272         /*
13273          * Render classic select for iso
13274          */
13275         
13276         if(Roo.isIOS && this.useNativeIOS){
13277             cfg = this.getAutoCreateNativeIOS();
13278             return cfg;
13279         }
13280         
13281         /*
13282          * Touch Devices
13283          */
13284         
13285         if(Roo.isTouch && this.mobileTouchView){
13286             cfg = this.getAutoCreateTouchView();
13287             return cfg;;
13288         }
13289         
13290         /*
13291          *  Normal ComboBox
13292          */
13293         if(!this.tickable){
13294             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13295             return cfg;
13296         }
13297         
13298         /*
13299          *  ComboBox with tickable selections
13300          */
13301              
13302         var align = this.labelAlign || this.parentLabelAlign();
13303         
13304         cfg = {
13305             cls : 'form-group roo-combobox-tickable' //input-group
13306         };
13307         
13308         var btn_text_select = '';
13309         var btn_text_done = '';
13310         var btn_text_cancel = '';
13311         
13312         if (this.btn_text_show) {
13313             btn_text_select = 'Select';
13314             btn_text_done = 'Done';
13315             btn_text_cancel = 'Cancel'; 
13316         }
13317         
13318         var buttons = {
13319             tag : 'div',
13320             cls : 'tickable-buttons',
13321             cn : [
13322                 {
13323                     tag : 'button',
13324                     type : 'button',
13325                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13326                     //html : this.triggerText
13327                     html: btn_text_select
13328                 },
13329                 {
13330                     tag : 'button',
13331                     type : 'button',
13332                     name : 'ok',
13333                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13334                     //html : 'Done'
13335                     html: btn_text_done
13336                 },
13337                 {
13338                     tag : 'button',
13339                     type : 'button',
13340                     name : 'cancel',
13341                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13342                     //html : 'Cancel'
13343                     html: btn_text_cancel
13344                 }
13345             ]
13346         };
13347         
13348         if(this.editable){
13349             buttons.cn.unshift({
13350                 tag: 'input',
13351                 cls: 'roo-select2-search-field-input'
13352             });
13353         }
13354         
13355         var _this = this;
13356         
13357         Roo.each(buttons.cn, function(c){
13358             if (_this.size) {
13359                 c.cls += ' btn-' + _this.size;
13360             }
13361
13362             if (_this.disabled) {
13363                 c.disabled = true;
13364             }
13365         });
13366         
13367         var box = {
13368             tag: 'div',
13369             cn: [
13370                 {
13371                     tag: 'input',
13372                     type : 'hidden',
13373                     cls: 'form-hidden-field'
13374                 },
13375                 {
13376                     tag: 'ul',
13377                     cls: 'roo-select2-choices',
13378                     cn:[
13379                         {
13380                             tag: 'li',
13381                             cls: 'roo-select2-search-field',
13382                             cn: [
13383                                 buttons
13384                             ]
13385                         }
13386                     ]
13387                 }
13388             ]
13389         };
13390         
13391         var combobox = {
13392             cls: 'roo-select2-container input-group roo-select2-container-multi',
13393             cn: [
13394                 
13395                 box
13396 //                {
13397 //                    tag: 'ul',
13398 //                    cls: 'typeahead typeahead-long dropdown-menu',
13399 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13400 //                }
13401             ]
13402         };
13403         
13404         if(this.hasFeedback && !this.allowBlank){
13405             
13406             var feedback = {
13407                 tag: 'span',
13408                 cls: 'glyphicon form-control-feedback'
13409             };
13410
13411             combobox.cn.push(feedback);
13412         }
13413         
13414         var indicator = {
13415             tag : 'i',
13416             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13417             tooltip : 'This field is required'
13418         };
13419         if (Roo.bootstrap.version == 4) {
13420             indicator = {
13421                 tag : 'i',
13422                 style : 'display:none'
13423             };
13424         }
13425         if (align ==='left' && this.fieldLabel.length) {
13426             
13427             cfg.cls += ' roo-form-group-label-left row';
13428             
13429             cfg.cn = [
13430                 indicator,
13431                 {
13432                     tag: 'label',
13433                     'for' :  id,
13434                     cls : 'control-label col-form-label',
13435                     html : this.fieldLabel
13436
13437                 },
13438                 {
13439                     cls : "", 
13440                     cn: [
13441                         combobox
13442                     ]
13443                 }
13444
13445             ];
13446             
13447             var labelCfg = cfg.cn[1];
13448             var contentCfg = cfg.cn[2];
13449             
13450
13451             if(this.indicatorpos == 'right'){
13452                 
13453                 cfg.cn = [
13454                     {
13455                         tag: 'label',
13456                         'for' :  id,
13457                         cls : 'control-label col-form-label',
13458                         cn : [
13459                             {
13460                                 tag : 'span',
13461                                 html : this.fieldLabel
13462                             },
13463                             indicator
13464                         ]
13465                     },
13466                     {
13467                         cls : "",
13468                         cn: [
13469                             combobox
13470                         ]
13471                     }
13472
13473                 ];
13474                 
13475                 
13476                 
13477                 labelCfg = cfg.cn[0];
13478                 contentCfg = cfg.cn[1];
13479             
13480             }
13481             
13482             if(this.labelWidth > 12){
13483                 labelCfg.style = "width: " + this.labelWidth + 'px';
13484             }
13485             
13486             if(this.labelWidth < 13 && this.labelmd == 0){
13487                 this.labelmd = this.labelWidth;
13488             }
13489             
13490             if(this.labellg > 0){
13491                 labelCfg.cls += ' col-lg-' + this.labellg;
13492                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13493             }
13494             
13495             if(this.labelmd > 0){
13496                 labelCfg.cls += ' col-md-' + this.labelmd;
13497                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13498             }
13499             
13500             if(this.labelsm > 0){
13501                 labelCfg.cls += ' col-sm-' + this.labelsm;
13502                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13503             }
13504             
13505             if(this.labelxs > 0){
13506                 labelCfg.cls += ' col-xs-' + this.labelxs;
13507                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13508             }
13509                 
13510                 
13511         } else if ( this.fieldLabel.length) {
13512 //                Roo.log(" label");
13513                  cfg.cn = [
13514                    indicator,
13515                     {
13516                         tag: 'label',
13517                         //cls : 'input-group-addon',
13518                         html : this.fieldLabel
13519                     },
13520                     combobox
13521                 ];
13522                 
13523                 if(this.indicatorpos == 'right'){
13524                     cfg.cn = [
13525                         {
13526                             tag: 'label',
13527                             //cls : 'input-group-addon',
13528                             html : this.fieldLabel
13529                         },
13530                         indicator,
13531                         combobox
13532                     ];
13533                     
13534                 }
13535
13536         } else {
13537             
13538 //                Roo.log(" no label && no align");
13539                 cfg = combobox
13540                      
13541                 
13542         }
13543          
13544         var settings=this;
13545         ['xs','sm','md','lg'].map(function(size){
13546             if (settings[size]) {
13547                 cfg.cls += ' col-' + size + '-' + settings[size];
13548             }
13549         });
13550         
13551         return cfg;
13552         
13553     },
13554     
13555     _initEventsCalled : false,
13556     
13557     // private
13558     initEvents: function()
13559     {   
13560         if (this._initEventsCalled) { // as we call render... prevent looping...
13561             return;
13562         }
13563         this._initEventsCalled = true;
13564         
13565         if (!this.store) {
13566             throw "can not find store for combo";
13567         }
13568         
13569         this.indicator = this.indicatorEl();
13570         
13571         this.store = Roo.factory(this.store, Roo.data);
13572         this.store.parent = this;
13573         
13574         // if we are building from html. then this element is so complex, that we can not really
13575         // use the rendered HTML.
13576         // so we have to trash and replace the previous code.
13577         if (Roo.XComponent.build_from_html) {
13578             // remove this element....
13579             var e = this.el.dom, k=0;
13580             while (e ) { e = e.previousSibling;  ++k;}
13581
13582             this.el.remove();
13583             
13584             this.el=false;
13585             this.rendered = false;
13586             
13587             this.render(this.parent().getChildContainer(true), k);
13588         }
13589         
13590         if(Roo.isIOS && this.useNativeIOS){
13591             this.initIOSView();
13592             return;
13593         }
13594         
13595         /*
13596          * Touch Devices
13597          */
13598         
13599         if(Roo.isTouch && this.mobileTouchView){
13600             this.initTouchView();
13601             return;
13602         }
13603         
13604         if(this.tickable){
13605             this.initTickableEvents();
13606             return;
13607         }
13608         
13609         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13610         
13611         if(this.hiddenName){
13612             
13613             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13614             
13615             this.hiddenField.dom.value =
13616                 this.hiddenValue !== undefined ? this.hiddenValue :
13617                 this.value !== undefined ? this.value : '';
13618
13619             // prevent input submission
13620             this.el.dom.removeAttribute('name');
13621             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13622              
13623              
13624         }
13625         //if(Roo.isGecko){
13626         //    this.el.dom.setAttribute('autocomplete', 'off');
13627         //}
13628         
13629         var cls = 'x-combo-list';
13630         
13631         //this.list = new Roo.Layer({
13632         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13633         //});
13634         
13635         var _this = this;
13636         
13637         (function(){
13638             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13639             _this.list.setWidth(lw);
13640         }).defer(100);
13641         
13642         this.list.on('mouseover', this.onViewOver, this);
13643         this.list.on('mousemove', this.onViewMove, this);
13644         this.list.on('scroll', this.onViewScroll, this);
13645         
13646         /*
13647         this.list.swallowEvent('mousewheel');
13648         this.assetHeight = 0;
13649
13650         if(this.title){
13651             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13652             this.assetHeight += this.header.getHeight();
13653         }
13654
13655         this.innerList = this.list.createChild({cls:cls+'-inner'});
13656         this.innerList.on('mouseover', this.onViewOver, this);
13657         this.innerList.on('mousemove', this.onViewMove, this);
13658         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13659         
13660         if(this.allowBlank && !this.pageSize && !this.disableClear){
13661             this.footer = this.list.createChild({cls:cls+'-ft'});
13662             this.pageTb = new Roo.Toolbar(this.footer);
13663            
13664         }
13665         if(this.pageSize){
13666             this.footer = this.list.createChild({cls:cls+'-ft'});
13667             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13668                     {pageSize: this.pageSize});
13669             
13670         }
13671         
13672         if (this.pageTb && this.allowBlank && !this.disableClear) {
13673             var _this = this;
13674             this.pageTb.add(new Roo.Toolbar.Fill(), {
13675                 cls: 'x-btn-icon x-btn-clear',
13676                 text: '&#160;',
13677                 handler: function()
13678                 {
13679                     _this.collapse();
13680                     _this.clearValue();
13681                     _this.onSelect(false, -1);
13682                 }
13683             });
13684         }
13685         if (this.footer) {
13686             this.assetHeight += this.footer.getHeight();
13687         }
13688         */
13689             
13690         if(!this.tpl){
13691             this.tpl = Roo.bootstrap.version == 4 ?
13692                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13693                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13694         }
13695
13696         this.view = new Roo.View(this.list, this.tpl, {
13697             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13698         });
13699         //this.view.wrapEl.setDisplayed(false);
13700         this.view.on('click', this.onViewClick, this);
13701         
13702         
13703         this.store.on('beforeload', this.onBeforeLoad, this);
13704         this.store.on('load', this.onLoad, this);
13705         this.store.on('loadexception', this.onLoadException, this);
13706         /*
13707         if(this.resizable){
13708             this.resizer = new Roo.Resizable(this.list,  {
13709                pinned:true, handles:'se'
13710             });
13711             this.resizer.on('resize', function(r, w, h){
13712                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13713                 this.listWidth = w;
13714                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13715                 this.restrictHeight();
13716             }, this);
13717             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13718         }
13719         */
13720         if(!this.editable){
13721             this.editable = true;
13722             this.setEditable(false);
13723         }
13724         
13725         /*
13726         
13727         if (typeof(this.events.add.listeners) != 'undefined') {
13728             
13729             this.addicon = this.wrap.createChild(
13730                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13731        
13732             this.addicon.on('click', function(e) {
13733                 this.fireEvent('add', this);
13734             }, this);
13735         }
13736         if (typeof(this.events.edit.listeners) != 'undefined') {
13737             
13738             this.editicon = this.wrap.createChild(
13739                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13740             if (this.addicon) {
13741                 this.editicon.setStyle('margin-left', '40px');
13742             }
13743             this.editicon.on('click', function(e) {
13744                 
13745                 // we fire even  if inothing is selected..
13746                 this.fireEvent('edit', this, this.lastData );
13747                 
13748             }, this);
13749         }
13750         */
13751         
13752         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13753             "up" : function(e){
13754                 this.inKeyMode = true;
13755                 this.selectPrev();
13756             },
13757
13758             "down" : function(e){
13759                 if(!this.isExpanded()){
13760                     this.onTriggerClick();
13761                 }else{
13762                     this.inKeyMode = true;
13763                     this.selectNext();
13764                 }
13765             },
13766
13767             "enter" : function(e){
13768 //                this.onViewClick();
13769                 //return true;
13770                 this.collapse();
13771                 
13772                 if(this.fireEvent("specialkey", this, e)){
13773                     this.onViewClick(false);
13774                 }
13775                 
13776                 return true;
13777             },
13778
13779             "esc" : function(e){
13780                 this.collapse();
13781             },
13782
13783             "tab" : function(e){
13784                 this.collapse();
13785                 
13786                 if(this.fireEvent("specialkey", this, e)){
13787                     this.onViewClick(false);
13788                 }
13789                 
13790                 return true;
13791             },
13792
13793             scope : this,
13794
13795             doRelay : function(foo, bar, hname){
13796                 if(hname == 'down' || this.scope.isExpanded()){
13797                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13798                 }
13799                 return true;
13800             },
13801
13802             forceKeyDown: true
13803         });
13804         
13805         
13806         this.queryDelay = Math.max(this.queryDelay || 10,
13807                 this.mode == 'local' ? 10 : 250);
13808         
13809         
13810         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13811         
13812         if(this.typeAhead){
13813             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13814         }
13815         if(this.editable !== false){
13816             this.inputEl().on("keyup", this.onKeyUp, this);
13817         }
13818         if(this.forceSelection){
13819             this.inputEl().on('blur', this.doForce, this);
13820         }
13821         
13822         if(this.multiple){
13823             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13824             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13825         }
13826     },
13827     
13828     initTickableEvents: function()
13829     {   
13830         this.createList();
13831         
13832         if(this.hiddenName){
13833             
13834             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13835             
13836             this.hiddenField.dom.value =
13837                 this.hiddenValue !== undefined ? this.hiddenValue :
13838                 this.value !== undefined ? this.value : '';
13839
13840             // prevent input submission
13841             this.el.dom.removeAttribute('name');
13842             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13843              
13844              
13845         }
13846         
13847 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13848         
13849         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13850         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13851         if(this.triggerList){
13852             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13853         }
13854          
13855         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13856         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13857         
13858         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13859         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13860         
13861         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13862         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13863         
13864         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13865         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13866         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13867         
13868         this.okBtn.hide();
13869         this.cancelBtn.hide();
13870         
13871         var _this = this;
13872         
13873         (function(){
13874             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13875             _this.list.setWidth(lw);
13876         }).defer(100);
13877         
13878         this.list.on('mouseover', this.onViewOver, this);
13879         this.list.on('mousemove', this.onViewMove, this);
13880         
13881         this.list.on('scroll', this.onViewScroll, this);
13882         
13883         if(!this.tpl){
13884             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13885                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13886         }
13887
13888         this.view = new Roo.View(this.list, this.tpl, {
13889             singleSelect:true,
13890             tickable:true,
13891             parent:this,
13892             store: this.store,
13893             selectedClass: this.selectedClass
13894         });
13895         
13896         //this.view.wrapEl.setDisplayed(false);
13897         this.view.on('click', this.onViewClick, this);
13898         
13899         
13900         
13901         this.store.on('beforeload', this.onBeforeLoad, this);
13902         this.store.on('load', this.onLoad, this);
13903         this.store.on('loadexception', this.onLoadException, this);
13904         
13905         if(this.editable){
13906             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13907                 "up" : function(e){
13908                     this.inKeyMode = true;
13909                     this.selectPrev();
13910                 },
13911
13912                 "down" : function(e){
13913                     this.inKeyMode = true;
13914                     this.selectNext();
13915                 },
13916
13917                 "enter" : function(e){
13918                     if(this.fireEvent("specialkey", this, e)){
13919                         this.onViewClick(false);
13920                     }
13921                     
13922                     return true;
13923                 },
13924
13925                 "esc" : function(e){
13926                     this.onTickableFooterButtonClick(e, false, false);
13927                 },
13928
13929                 "tab" : function(e){
13930                     this.fireEvent("specialkey", this, e);
13931                     
13932                     this.onTickableFooterButtonClick(e, false, false);
13933                     
13934                     return true;
13935                 },
13936
13937                 scope : this,
13938
13939                 doRelay : function(e, fn, key){
13940                     if(this.scope.isExpanded()){
13941                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13942                     }
13943                     return true;
13944                 },
13945
13946                 forceKeyDown: true
13947             });
13948         }
13949         
13950         this.queryDelay = Math.max(this.queryDelay || 10,
13951                 this.mode == 'local' ? 10 : 250);
13952         
13953         
13954         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13955         
13956         if(this.typeAhead){
13957             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13958         }
13959         
13960         if(this.editable !== false){
13961             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13962         }
13963         
13964         this.indicator = this.indicatorEl();
13965         
13966         if(this.indicator){
13967             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13968             this.indicator.hide();
13969         }
13970         
13971     },
13972
13973     onDestroy : function(){
13974         if(this.view){
13975             this.view.setStore(null);
13976             this.view.el.removeAllListeners();
13977             this.view.el.remove();
13978             this.view.purgeListeners();
13979         }
13980         if(this.list){
13981             this.list.dom.innerHTML  = '';
13982         }
13983         
13984         if(this.store){
13985             this.store.un('beforeload', this.onBeforeLoad, this);
13986             this.store.un('load', this.onLoad, this);
13987             this.store.un('loadexception', this.onLoadException, this);
13988         }
13989         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13990     },
13991
13992     // private
13993     fireKey : function(e){
13994         if(e.isNavKeyPress() && !this.list.isVisible()){
13995             this.fireEvent("specialkey", this, e);
13996         }
13997     },
13998
13999     // private
14000     onResize: function(w, h){
14001 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14002 //        
14003 //        if(typeof w != 'number'){
14004 //            // we do not handle it!?!?
14005 //            return;
14006 //        }
14007 //        var tw = this.trigger.getWidth();
14008 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14009 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14010 //        var x = w - tw;
14011 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14012 //            
14013 //        //this.trigger.setStyle('left', x+'px');
14014 //        
14015 //        if(this.list && this.listWidth === undefined){
14016 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14017 //            this.list.setWidth(lw);
14018 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14019 //        }
14020         
14021     
14022         
14023     },
14024
14025     /**
14026      * Allow or prevent the user from directly editing the field text.  If false is passed,
14027      * the user will only be able to select from the items defined in the dropdown list.  This method
14028      * is the runtime equivalent of setting the 'editable' config option at config time.
14029      * @param {Boolean} value True to allow the user to directly edit the field text
14030      */
14031     setEditable : function(value){
14032         if(value == this.editable){
14033             return;
14034         }
14035         this.editable = value;
14036         if(!value){
14037             this.inputEl().dom.setAttribute('readOnly', true);
14038             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14039             this.inputEl().addClass('x-combo-noedit');
14040         }else{
14041             this.inputEl().dom.setAttribute('readOnly', false);
14042             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14043             this.inputEl().removeClass('x-combo-noedit');
14044         }
14045     },
14046
14047     // private
14048     
14049     onBeforeLoad : function(combo,opts){
14050         if(!this.hasFocus){
14051             return;
14052         }
14053          if (!opts.add) {
14054             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14055          }
14056         this.restrictHeight();
14057         this.selectedIndex = -1;
14058     },
14059
14060     // private
14061     onLoad : function(){
14062         
14063         this.hasQuery = false;
14064         
14065         if(!this.hasFocus){
14066             return;
14067         }
14068         
14069         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14070             this.loading.hide();
14071         }
14072         
14073         if(this.store.getCount() > 0){
14074             
14075             this.expand();
14076             this.restrictHeight();
14077             if(this.lastQuery == this.allQuery){
14078                 if(this.editable && !this.tickable){
14079                     this.inputEl().dom.select();
14080                 }
14081                 
14082                 if(
14083                     !this.selectByValue(this.value, true) &&
14084                     this.autoFocus && 
14085                     (
14086                         !this.store.lastOptions ||
14087                         typeof(this.store.lastOptions.add) == 'undefined' || 
14088                         this.store.lastOptions.add != true
14089                     )
14090                 ){
14091                     this.select(0, true);
14092                 }
14093             }else{
14094                 if(this.autoFocus){
14095                     this.selectNext();
14096                 }
14097                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14098                     this.taTask.delay(this.typeAheadDelay);
14099                 }
14100             }
14101         }else{
14102             this.onEmptyResults();
14103         }
14104         
14105         //this.el.focus();
14106     },
14107     // private
14108     onLoadException : function()
14109     {
14110         this.hasQuery = false;
14111         
14112         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14113             this.loading.hide();
14114         }
14115         
14116         if(this.tickable && this.editable){
14117             return;
14118         }
14119         
14120         this.collapse();
14121         // only causes errors at present
14122         //Roo.log(this.store.reader.jsonData);
14123         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14124             // fixme
14125             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14126         //}
14127         
14128         
14129     },
14130     // private
14131     onTypeAhead : function(){
14132         if(this.store.getCount() > 0){
14133             var r = this.store.getAt(0);
14134             var newValue = r.data[this.displayField];
14135             var len = newValue.length;
14136             var selStart = this.getRawValue().length;
14137             
14138             if(selStart != len){
14139                 this.setRawValue(newValue);
14140                 this.selectText(selStart, newValue.length);
14141             }
14142         }
14143     },
14144
14145     // private
14146     onSelect : function(record, index){
14147         
14148         if(this.fireEvent('beforeselect', this, record, index) !== false){
14149         
14150             this.setFromData(index > -1 ? record.data : false);
14151             
14152             this.collapse();
14153             this.fireEvent('select', this, record, index);
14154         }
14155     },
14156
14157     /**
14158      * Returns the currently selected field value or empty string if no value is set.
14159      * @return {String} value The selected value
14160      */
14161     getValue : function()
14162     {
14163         if(Roo.isIOS && this.useNativeIOS){
14164             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14165         }
14166         
14167         if(this.multiple){
14168             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14169         }
14170         
14171         if(this.valueField){
14172             return typeof this.value != 'undefined' ? this.value : '';
14173         }else{
14174             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14175         }
14176     },
14177     
14178     getRawValue : function()
14179     {
14180         if(Roo.isIOS && this.useNativeIOS){
14181             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14182         }
14183         
14184         var v = this.inputEl().getValue();
14185         
14186         return v;
14187     },
14188
14189     /**
14190      * Clears any text/value currently set in the field
14191      */
14192     clearValue : function(){
14193         
14194         if(this.hiddenField){
14195             this.hiddenField.dom.value = '';
14196         }
14197         this.value = '';
14198         this.setRawValue('');
14199         this.lastSelectionText = '';
14200         this.lastData = false;
14201         
14202         var close = this.closeTriggerEl();
14203         
14204         if(close){
14205             close.hide();
14206         }
14207         
14208         this.validate();
14209         
14210     },
14211
14212     /**
14213      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14214      * will be displayed in the field.  If the value does not match the data value of an existing item,
14215      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14216      * Otherwise the field will be blank (although the value will still be set).
14217      * @param {String} value The value to match
14218      */
14219     setValue : function(v)
14220     {
14221         if(Roo.isIOS && this.useNativeIOS){
14222             this.setIOSValue(v);
14223             return;
14224         }
14225         
14226         if(this.multiple){
14227             this.syncValue();
14228             return;
14229         }
14230         
14231         var text = v;
14232         if(this.valueField){
14233             var r = this.findRecord(this.valueField, v);
14234             if(r){
14235                 text = r.data[this.displayField];
14236             }else if(this.valueNotFoundText !== undefined){
14237                 text = this.valueNotFoundText;
14238             }
14239         }
14240         this.lastSelectionText = text;
14241         if(this.hiddenField){
14242             this.hiddenField.dom.value = v;
14243         }
14244         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14245         this.value = v;
14246         
14247         var close = this.closeTriggerEl();
14248         
14249         if(close){
14250             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14251         }
14252         
14253         this.validate();
14254     },
14255     /**
14256      * @property {Object} the last set data for the element
14257      */
14258     
14259     lastData : false,
14260     /**
14261      * Sets the value of the field based on a object which is related to the record format for the store.
14262      * @param {Object} value the value to set as. or false on reset?
14263      */
14264     setFromData : function(o){
14265         
14266         if(this.multiple){
14267             this.addItem(o);
14268             return;
14269         }
14270             
14271         var dv = ''; // display value
14272         var vv = ''; // value value..
14273         this.lastData = o;
14274         if (this.displayField) {
14275             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14276         } else {
14277             // this is an error condition!!!
14278             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14279         }
14280         
14281         if(this.valueField){
14282             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14283         }
14284         
14285         var close = this.closeTriggerEl();
14286         
14287         if(close){
14288             if(dv.length || vv * 1 > 0){
14289                 close.show() ;
14290                 this.blockFocus=true;
14291             } else {
14292                 close.hide();
14293             }             
14294         }
14295         
14296         if(this.hiddenField){
14297             this.hiddenField.dom.value = vv;
14298             
14299             this.lastSelectionText = dv;
14300             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14301             this.value = vv;
14302             return;
14303         }
14304         // no hidden field.. - we store the value in 'value', but still display
14305         // display field!!!!
14306         this.lastSelectionText = dv;
14307         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14308         this.value = vv;
14309         
14310         
14311         
14312     },
14313     // private
14314     reset : function(){
14315         // overridden so that last data is reset..
14316         
14317         if(this.multiple){
14318             this.clearItem();
14319             return;
14320         }
14321         
14322         this.setValue(this.originalValue);
14323         //this.clearInvalid();
14324         this.lastData = false;
14325         if (this.view) {
14326             this.view.clearSelections();
14327         }
14328         
14329         this.validate();
14330     },
14331     // private
14332     findRecord : function(prop, value){
14333         var record;
14334         if(this.store.getCount() > 0){
14335             this.store.each(function(r){
14336                 if(r.data[prop] == value){
14337                     record = r;
14338                     return false;
14339                 }
14340                 return true;
14341             });
14342         }
14343         return record;
14344     },
14345     
14346     getName: function()
14347     {
14348         // returns hidden if it's set..
14349         if (!this.rendered) {return ''};
14350         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14351         
14352     },
14353     // private
14354     onViewMove : function(e, t){
14355         this.inKeyMode = false;
14356     },
14357
14358     // private
14359     onViewOver : function(e, t){
14360         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14361             return;
14362         }
14363         var item = this.view.findItemFromChild(t);
14364         
14365         if(item){
14366             var index = this.view.indexOf(item);
14367             this.select(index, false);
14368         }
14369     },
14370
14371     // private
14372     onViewClick : function(view, doFocus, el, e)
14373     {
14374         var index = this.view.getSelectedIndexes()[0];
14375         
14376         var r = this.store.getAt(index);
14377         
14378         if(this.tickable){
14379             
14380             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14381                 return;
14382             }
14383             
14384             var rm = false;
14385             var _this = this;
14386             
14387             Roo.each(this.tickItems, function(v,k){
14388                 
14389                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14390                     Roo.log(v);
14391                     _this.tickItems.splice(k, 1);
14392                     
14393                     if(typeof(e) == 'undefined' && view == false){
14394                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14395                     }
14396                     
14397                     rm = true;
14398                     return;
14399                 }
14400             });
14401             
14402             if(rm){
14403                 return;
14404             }
14405             
14406             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14407                 this.tickItems.push(r.data);
14408             }
14409             
14410             if(typeof(e) == 'undefined' && view == false){
14411                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14412             }
14413                     
14414             return;
14415         }
14416         
14417         if(r){
14418             this.onSelect(r, index);
14419         }
14420         if(doFocus !== false && !this.blockFocus){
14421             this.inputEl().focus();
14422         }
14423     },
14424
14425     // private
14426     restrictHeight : function(){
14427         //this.innerList.dom.style.height = '';
14428         //var inner = this.innerList.dom;
14429         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14430         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14431         //this.list.beginUpdate();
14432         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14433         this.list.alignTo(this.inputEl(), this.listAlign);
14434         this.list.alignTo(this.inputEl(), this.listAlign);
14435         //this.list.endUpdate();
14436     },
14437
14438     // private
14439     onEmptyResults : function(){
14440         
14441         if(this.tickable && this.editable){
14442             this.hasFocus = false;
14443             this.restrictHeight();
14444             return;
14445         }
14446         
14447         this.collapse();
14448     },
14449
14450     /**
14451      * Returns true if the dropdown list is expanded, else false.
14452      */
14453     isExpanded : function(){
14454         return this.list.isVisible();
14455     },
14456
14457     /**
14458      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14459      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14460      * @param {String} value The data value of the item to select
14461      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14462      * selected item if it is not currently in view (defaults to true)
14463      * @return {Boolean} True if the value matched an item in the list, else false
14464      */
14465     selectByValue : function(v, scrollIntoView){
14466         if(v !== undefined && v !== null){
14467             var r = this.findRecord(this.valueField || this.displayField, v);
14468             if(r){
14469                 this.select(this.store.indexOf(r), scrollIntoView);
14470                 return true;
14471             }
14472         }
14473         return false;
14474     },
14475
14476     /**
14477      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14478      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14479      * @param {Number} index The zero-based index of the list item to select
14480      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14481      * selected item if it is not currently in view (defaults to true)
14482      */
14483     select : function(index, scrollIntoView){
14484         this.selectedIndex = index;
14485         this.view.select(index);
14486         if(scrollIntoView !== false){
14487             var el = this.view.getNode(index);
14488             /*
14489              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14490              */
14491             if(el){
14492                 this.list.scrollChildIntoView(el, false);
14493             }
14494         }
14495     },
14496
14497     // private
14498     selectNext : function(){
14499         var ct = this.store.getCount();
14500         if(ct > 0){
14501             if(this.selectedIndex == -1){
14502                 this.select(0);
14503             }else if(this.selectedIndex < ct-1){
14504                 this.select(this.selectedIndex+1);
14505             }
14506         }
14507     },
14508
14509     // private
14510     selectPrev : function(){
14511         var ct = this.store.getCount();
14512         if(ct > 0){
14513             if(this.selectedIndex == -1){
14514                 this.select(0);
14515             }else if(this.selectedIndex != 0){
14516                 this.select(this.selectedIndex-1);
14517             }
14518         }
14519     },
14520
14521     // private
14522     onKeyUp : function(e){
14523         if(this.editable !== false && !e.isSpecialKey()){
14524             this.lastKey = e.getKey();
14525             this.dqTask.delay(this.queryDelay);
14526         }
14527     },
14528
14529     // private
14530     validateBlur : function(){
14531         return !this.list || !this.list.isVisible();   
14532     },
14533
14534     // private
14535     initQuery : function(){
14536         
14537         var v = this.getRawValue();
14538         
14539         if(this.tickable && this.editable){
14540             v = this.tickableInputEl().getValue();
14541         }
14542         
14543         this.doQuery(v);
14544     },
14545
14546     // private
14547     doForce : function(){
14548         if(this.inputEl().dom.value.length > 0){
14549             this.inputEl().dom.value =
14550                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14551              
14552         }
14553     },
14554
14555     /**
14556      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14557      * query allowing the query action to be canceled if needed.
14558      * @param {String} query The SQL query to execute
14559      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14560      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14561      * saved in the current store (defaults to false)
14562      */
14563     doQuery : function(q, forceAll){
14564         
14565         if(q === undefined || q === null){
14566             q = '';
14567         }
14568         var qe = {
14569             query: q,
14570             forceAll: forceAll,
14571             combo: this,
14572             cancel:false
14573         };
14574         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14575             return false;
14576         }
14577         q = qe.query;
14578         
14579         forceAll = qe.forceAll;
14580         if(forceAll === true || (q.length >= this.minChars)){
14581             
14582             this.hasQuery = true;
14583             
14584             if(this.lastQuery != q || this.alwaysQuery){
14585                 this.lastQuery = q;
14586                 if(this.mode == 'local'){
14587                     this.selectedIndex = -1;
14588                     if(forceAll){
14589                         this.store.clearFilter();
14590                     }else{
14591                         
14592                         if(this.specialFilter){
14593                             this.fireEvent('specialfilter', this);
14594                             this.onLoad();
14595                             return;
14596                         }
14597                         
14598                         this.store.filter(this.displayField, q);
14599                     }
14600                     
14601                     this.store.fireEvent("datachanged", this.store);
14602                     
14603                     this.onLoad();
14604                     
14605                     
14606                 }else{
14607                     
14608                     this.store.baseParams[this.queryParam] = q;
14609                     
14610                     var options = {params : this.getParams(q)};
14611                     
14612                     if(this.loadNext){
14613                         options.add = true;
14614                         options.params.start = this.page * this.pageSize;
14615                     }
14616                     
14617                     this.store.load(options);
14618                     
14619                     /*
14620                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14621                      *  we should expand the list on onLoad
14622                      *  so command out it
14623                      */
14624 //                    this.expand();
14625                 }
14626             }else{
14627                 this.selectedIndex = -1;
14628                 this.onLoad();   
14629             }
14630         }
14631         
14632         this.loadNext = false;
14633     },
14634     
14635     // private
14636     getParams : function(q){
14637         var p = {};
14638         //p[this.queryParam] = q;
14639         
14640         if(this.pageSize){
14641             p.start = 0;
14642             p.limit = this.pageSize;
14643         }
14644         return p;
14645     },
14646
14647     /**
14648      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14649      */
14650     collapse : function(){
14651         if(!this.isExpanded()){
14652             return;
14653         }
14654         
14655         this.list.hide();
14656         
14657         this.hasFocus = false;
14658         
14659         if(this.tickable){
14660             this.okBtn.hide();
14661             this.cancelBtn.hide();
14662             this.trigger.show();
14663             
14664             if(this.editable){
14665                 this.tickableInputEl().dom.value = '';
14666                 this.tickableInputEl().blur();
14667             }
14668             
14669         }
14670         
14671         Roo.get(document).un('mousedown', this.collapseIf, this);
14672         Roo.get(document).un('mousewheel', this.collapseIf, this);
14673         if (!this.editable) {
14674             Roo.get(document).un('keydown', this.listKeyPress, this);
14675         }
14676         this.fireEvent('collapse', this);
14677         
14678         this.validate();
14679     },
14680
14681     // private
14682     collapseIf : function(e){
14683         var in_combo  = e.within(this.el);
14684         var in_list =  e.within(this.list);
14685         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14686         
14687         if (in_combo || in_list || is_list) {
14688             //e.stopPropagation();
14689             return;
14690         }
14691         
14692         if(this.tickable){
14693             this.onTickableFooterButtonClick(e, false, false);
14694         }
14695
14696         this.collapse();
14697         
14698     },
14699
14700     /**
14701      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14702      */
14703     expand : function(){
14704        
14705         if(this.isExpanded() || !this.hasFocus){
14706             return;
14707         }
14708         
14709         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14710         this.list.setWidth(lw);
14711         
14712         Roo.log('expand');
14713         
14714         this.list.show();
14715         
14716         this.restrictHeight();
14717         
14718         if(this.tickable){
14719             
14720             this.tickItems = Roo.apply([], this.item);
14721             
14722             this.okBtn.show();
14723             this.cancelBtn.show();
14724             this.trigger.hide();
14725             
14726             if(this.editable){
14727                 this.tickableInputEl().focus();
14728             }
14729             
14730         }
14731         
14732         Roo.get(document).on('mousedown', this.collapseIf, this);
14733         Roo.get(document).on('mousewheel', this.collapseIf, this);
14734         if (!this.editable) {
14735             Roo.get(document).on('keydown', this.listKeyPress, this);
14736         }
14737         
14738         this.fireEvent('expand', this);
14739     },
14740
14741     // private
14742     // Implements the default empty TriggerField.onTriggerClick function
14743     onTriggerClick : function(e)
14744     {
14745         Roo.log('trigger click');
14746         
14747         if(this.disabled || !this.triggerList){
14748             return;
14749         }
14750         
14751         this.page = 0;
14752         this.loadNext = false;
14753         
14754         if(this.isExpanded()){
14755             this.collapse();
14756             if (!this.blockFocus) {
14757                 this.inputEl().focus();
14758             }
14759             
14760         }else {
14761             this.hasFocus = true;
14762             if(this.triggerAction == 'all') {
14763                 this.doQuery(this.allQuery, true);
14764             } else {
14765                 this.doQuery(this.getRawValue());
14766             }
14767             if (!this.blockFocus) {
14768                 this.inputEl().focus();
14769             }
14770         }
14771     },
14772     
14773     onTickableTriggerClick : function(e)
14774     {
14775         if(this.disabled){
14776             return;
14777         }
14778         
14779         this.page = 0;
14780         this.loadNext = false;
14781         this.hasFocus = true;
14782         
14783         if(this.triggerAction == 'all') {
14784             this.doQuery(this.allQuery, true);
14785         } else {
14786             this.doQuery(this.getRawValue());
14787         }
14788     },
14789     
14790     onSearchFieldClick : function(e)
14791     {
14792         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14793             this.onTickableFooterButtonClick(e, false, false);
14794             return;
14795         }
14796         
14797         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14798             return;
14799         }
14800         
14801         this.page = 0;
14802         this.loadNext = false;
14803         this.hasFocus = true;
14804         
14805         if(this.triggerAction == 'all') {
14806             this.doQuery(this.allQuery, true);
14807         } else {
14808             this.doQuery(this.getRawValue());
14809         }
14810     },
14811     
14812     listKeyPress : function(e)
14813     {
14814         //Roo.log('listkeypress');
14815         // scroll to first matching element based on key pres..
14816         if (e.isSpecialKey()) {
14817             return false;
14818         }
14819         var k = String.fromCharCode(e.getKey()).toUpperCase();
14820         //Roo.log(k);
14821         var match  = false;
14822         var csel = this.view.getSelectedNodes();
14823         var cselitem = false;
14824         if (csel.length) {
14825             var ix = this.view.indexOf(csel[0]);
14826             cselitem  = this.store.getAt(ix);
14827             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14828                 cselitem = false;
14829             }
14830             
14831         }
14832         
14833         this.store.each(function(v) { 
14834             if (cselitem) {
14835                 // start at existing selection.
14836                 if (cselitem.id == v.id) {
14837                     cselitem = false;
14838                 }
14839                 return true;
14840             }
14841                 
14842             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14843                 match = this.store.indexOf(v);
14844                 return false;
14845             }
14846             return true;
14847         }, this);
14848         
14849         if (match === false) {
14850             return true; // no more action?
14851         }
14852         // scroll to?
14853         this.view.select(match);
14854         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14855         sn.scrollIntoView(sn.dom.parentNode, false);
14856     },
14857     
14858     onViewScroll : function(e, t){
14859         
14860         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){
14861             return;
14862         }
14863         
14864         this.hasQuery = true;
14865         
14866         this.loading = this.list.select('.loading', true).first();
14867         
14868         if(this.loading === null){
14869             this.list.createChild({
14870                 tag: 'div',
14871                 cls: 'loading roo-select2-more-results roo-select2-active',
14872                 html: 'Loading more results...'
14873             });
14874             
14875             this.loading = this.list.select('.loading', true).first();
14876             
14877             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14878             
14879             this.loading.hide();
14880         }
14881         
14882         this.loading.show();
14883         
14884         var _combo = this;
14885         
14886         this.page++;
14887         this.loadNext = true;
14888         
14889         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14890         
14891         return;
14892     },
14893     
14894     addItem : function(o)
14895     {   
14896         var dv = ''; // display value
14897         
14898         if (this.displayField) {
14899             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14900         } else {
14901             // this is an error condition!!!
14902             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14903         }
14904         
14905         if(!dv.length){
14906             return;
14907         }
14908         
14909         var choice = this.choices.createChild({
14910             tag: 'li',
14911             cls: 'roo-select2-search-choice',
14912             cn: [
14913                 {
14914                     tag: 'div',
14915                     html: dv
14916                 },
14917                 {
14918                     tag: 'a',
14919                     href: '#',
14920                     cls: 'roo-select2-search-choice-close fa fa-times',
14921                     tabindex: '-1'
14922                 }
14923             ]
14924             
14925         }, this.searchField);
14926         
14927         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14928         
14929         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14930         
14931         this.item.push(o);
14932         
14933         this.lastData = o;
14934         
14935         this.syncValue();
14936         
14937         this.inputEl().dom.value = '';
14938         
14939         this.validate();
14940     },
14941     
14942     onRemoveItem : function(e, _self, o)
14943     {
14944         e.preventDefault();
14945         
14946         this.lastItem = Roo.apply([], this.item);
14947         
14948         var index = this.item.indexOf(o.data) * 1;
14949         
14950         if( index < 0){
14951             Roo.log('not this item?!');
14952             return;
14953         }
14954         
14955         this.item.splice(index, 1);
14956         o.item.remove();
14957         
14958         this.syncValue();
14959         
14960         this.fireEvent('remove', this, e);
14961         
14962         this.validate();
14963         
14964     },
14965     
14966     syncValue : function()
14967     {
14968         if(!this.item.length){
14969             this.clearValue();
14970             return;
14971         }
14972             
14973         var value = [];
14974         var _this = this;
14975         Roo.each(this.item, function(i){
14976             if(_this.valueField){
14977                 value.push(i[_this.valueField]);
14978                 return;
14979             }
14980
14981             value.push(i);
14982         });
14983
14984         this.value = value.join(',');
14985
14986         if(this.hiddenField){
14987             this.hiddenField.dom.value = this.value;
14988         }
14989         
14990         this.store.fireEvent("datachanged", this.store);
14991         
14992         this.validate();
14993     },
14994     
14995     clearItem : function()
14996     {
14997         if(!this.multiple){
14998             return;
14999         }
15000         
15001         this.item = [];
15002         
15003         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15004            c.remove();
15005         });
15006         
15007         this.syncValue();
15008         
15009         this.validate();
15010         
15011         if(this.tickable && !Roo.isTouch){
15012             this.view.refresh();
15013         }
15014     },
15015     
15016     inputEl: function ()
15017     {
15018         if(Roo.isIOS && this.useNativeIOS){
15019             return this.el.select('select.roo-ios-select', true).first();
15020         }
15021         
15022         if(Roo.isTouch && this.mobileTouchView){
15023             return this.el.select('input.form-control',true).first();
15024         }
15025         
15026         if(this.tickable){
15027             return this.searchField;
15028         }
15029         
15030         return this.el.select('input.form-control',true).first();
15031     },
15032     
15033     onTickableFooterButtonClick : function(e, btn, el)
15034     {
15035         e.preventDefault();
15036         
15037         this.lastItem = Roo.apply([], this.item);
15038         
15039         if(btn && btn.name == 'cancel'){
15040             this.tickItems = Roo.apply([], this.item);
15041             this.collapse();
15042             return;
15043         }
15044         
15045         this.clearItem();
15046         
15047         var _this = this;
15048         
15049         Roo.each(this.tickItems, function(o){
15050             _this.addItem(o);
15051         });
15052         
15053         this.collapse();
15054         
15055     },
15056     
15057     validate : function()
15058     {
15059         if(this.getVisibilityEl().hasClass('hidden')){
15060             return true;
15061         }
15062         
15063         var v = this.getRawValue();
15064         
15065         if(this.multiple){
15066             v = this.getValue();
15067         }
15068         
15069         if(this.disabled || this.allowBlank || v.length){
15070             this.markValid();
15071             return true;
15072         }
15073         
15074         this.markInvalid();
15075         return false;
15076     },
15077     
15078     tickableInputEl : function()
15079     {
15080         if(!this.tickable || !this.editable){
15081             return this.inputEl();
15082         }
15083         
15084         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15085     },
15086     
15087     
15088     getAutoCreateTouchView : function()
15089     {
15090         var id = Roo.id();
15091         
15092         var cfg = {
15093             cls: 'form-group' //input-group
15094         };
15095         
15096         var input =  {
15097             tag: 'input',
15098             id : id,
15099             type : this.inputType,
15100             cls : 'form-control x-combo-noedit',
15101             autocomplete: 'new-password',
15102             placeholder : this.placeholder || '',
15103             readonly : true
15104         };
15105         
15106         if (this.name) {
15107             input.name = this.name;
15108         }
15109         
15110         if (this.size) {
15111             input.cls += ' input-' + this.size;
15112         }
15113         
15114         if (this.disabled) {
15115             input.disabled = true;
15116         }
15117         
15118         var inputblock = {
15119             cls : '',
15120             cn : [
15121                 input
15122             ]
15123         };
15124         
15125         if(this.before){
15126             inputblock.cls += ' input-group';
15127             
15128             inputblock.cn.unshift({
15129                 tag :'span',
15130                 cls : 'input-group-addon input-group-prepend input-group-text',
15131                 html : this.before
15132             });
15133         }
15134         
15135         if(this.removable && !this.multiple){
15136             inputblock.cls += ' roo-removable';
15137             
15138             inputblock.cn.push({
15139                 tag: 'button',
15140                 html : 'x',
15141                 cls : 'roo-combo-removable-btn close'
15142             });
15143         }
15144
15145         if(this.hasFeedback && !this.allowBlank){
15146             
15147             inputblock.cls += ' has-feedback';
15148             
15149             inputblock.cn.push({
15150                 tag: 'span',
15151                 cls: 'glyphicon form-control-feedback'
15152             });
15153             
15154         }
15155         
15156         if (this.after) {
15157             
15158             inputblock.cls += (this.before) ? '' : ' input-group';
15159             
15160             inputblock.cn.push({
15161                 tag :'span',
15162                 cls : 'input-group-addon input-group-append input-group-text',
15163                 html : this.after
15164             });
15165         }
15166
15167         
15168         var ibwrap = inputblock;
15169         
15170         if(this.multiple){
15171             ibwrap = {
15172                 tag: 'ul',
15173                 cls: 'roo-select2-choices',
15174                 cn:[
15175                     {
15176                         tag: 'li',
15177                         cls: 'roo-select2-search-field',
15178                         cn: [
15179
15180                             inputblock
15181                         ]
15182                     }
15183                 ]
15184             };
15185         
15186             
15187         }
15188         
15189         var combobox = {
15190             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15191             cn: [
15192                 {
15193                     tag: 'input',
15194                     type : 'hidden',
15195                     cls: 'form-hidden-field'
15196                 },
15197                 ibwrap
15198             ]
15199         };
15200         
15201         if(!this.multiple && this.showToggleBtn){
15202             
15203             var caret = {
15204                         tag: 'span',
15205                         cls: 'caret'
15206             };
15207             
15208             if (this.caret != false) {
15209                 caret = {
15210                      tag: 'i',
15211                      cls: 'fa fa-' + this.caret
15212                 };
15213                 
15214             }
15215             
15216             combobox.cn.push({
15217                 tag :'span',
15218                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15219                 cn : [
15220                     caret,
15221                     {
15222                         tag: 'span',
15223                         cls: 'combobox-clear',
15224                         cn  : [
15225                             {
15226                                 tag : 'i',
15227                                 cls: 'icon-remove'
15228                             }
15229                         ]
15230                     }
15231                 ]
15232
15233             })
15234         }
15235         
15236         if(this.multiple){
15237             combobox.cls += ' roo-select2-container-multi';
15238         }
15239         
15240         var align = this.labelAlign || this.parentLabelAlign();
15241         
15242         if (align ==='left' && this.fieldLabel.length) {
15243
15244             cfg.cn = [
15245                 {
15246                    tag : 'i',
15247                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15248                    tooltip : 'This field is required'
15249                 },
15250                 {
15251                     tag: 'label',
15252                     cls : 'control-label col-form-label',
15253                     html : this.fieldLabel
15254
15255                 },
15256                 {
15257                     cls : '', 
15258                     cn: [
15259                         combobox
15260                     ]
15261                 }
15262             ];
15263             
15264             var labelCfg = cfg.cn[1];
15265             var contentCfg = cfg.cn[2];
15266             
15267
15268             if(this.indicatorpos == 'right'){
15269                 cfg.cn = [
15270                     {
15271                         tag: 'label',
15272                         'for' :  id,
15273                         cls : 'control-label col-form-label',
15274                         cn : [
15275                             {
15276                                 tag : 'span',
15277                                 html : this.fieldLabel
15278                             },
15279                             {
15280                                 tag : 'i',
15281                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15282                                 tooltip : 'This field is required'
15283                             }
15284                         ]
15285                     },
15286                     {
15287                         cls : "",
15288                         cn: [
15289                             combobox
15290                         ]
15291                     }
15292
15293                 ];
15294                 
15295                 labelCfg = cfg.cn[0];
15296                 contentCfg = cfg.cn[1];
15297             }
15298             
15299            
15300             
15301             if(this.labelWidth > 12){
15302                 labelCfg.style = "width: " + this.labelWidth + 'px';
15303             }
15304             
15305             if(this.labelWidth < 13 && this.labelmd == 0){
15306                 this.labelmd = this.labelWidth;
15307             }
15308             
15309             if(this.labellg > 0){
15310                 labelCfg.cls += ' col-lg-' + this.labellg;
15311                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15312             }
15313             
15314             if(this.labelmd > 0){
15315                 labelCfg.cls += ' col-md-' + this.labelmd;
15316                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15317             }
15318             
15319             if(this.labelsm > 0){
15320                 labelCfg.cls += ' col-sm-' + this.labelsm;
15321                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15322             }
15323             
15324             if(this.labelxs > 0){
15325                 labelCfg.cls += ' col-xs-' + this.labelxs;
15326                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15327             }
15328                 
15329                 
15330         } else if ( this.fieldLabel.length) {
15331             cfg.cn = [
15332                 {
15333                    tag : 'i',
15334                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15335                    tooltip : 'This field is required'
15336                 },
15337                 {
15338                     tag: 'label',
15339                     cls : 'control-label',
15340                     html : this.fieldLabel
15341
15342                 },
15343                 {
15344                     cls : '', 
15345                     cn: [
15346                         combobox
15347                     ]
15348                 }
15349             ];
15350             
15351             if(this.indicatorpos == 'right'){
15352                 cfg.cn = [
15353                     {
15354                         tag: 'label',
15355                         cls : 'control-label',
15356                         html : this.fieldLabel,
15357                         cn : [
15358                             {
15359                                tag : 'i',
15360                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15361                                tooltip : 'This field is required'
15362                             }
15363                         ]
15364                     },
15365                     {
15366                         cls : '', 
15367                         cn: [
15368                             combobox
15369                         ]
15370                     }
15371                 ];
15372             }
15373         } else {
15374             cfg.cn = combobox;    
15375         }
15376         
15377         
15378         var settings = this;
15379         
15380         ['xs','sm','md','lg'].map(function(size){
15381             if (settings[size]) {
15382                 cfg.cls += ' col-' + size + '-' + settings[size];
15383             }
15384         });
15385         
15386         return cfg;
15387     },
15388     
15389     initTouchView : function()
15390     {
15391         this.renderTouchView();
15392         
15393         this.touchViewEl.on('scroll', function(){
15394             this.el.dom.scrollTop = 0;
15395         }, this);
15396         
15397         this.originalValue = this.getValue();
15398         
15399         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15400         
15401         this.inputEl().on("click", this.showTouchView, this);
15402         if (this.triggerEl) {
15403             this.triggerEl.on("click", this.showTouchView, this);
15404         }
15405         
15406         
15407         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15408         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15409         
15410         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15411         
15412         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15413         this.store.on('load', this.onTouchViewLoad, this);
15414         this.store.on('loadexception', this.onTouchViewLoadException, this);
15415         
15416         if(this.hiddenName){
15417             
15418             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15419             
15420             this.hiddenField.dom.value =
15421                 this.hiddenValue !== undefined ? this.hiddenValue :
15422                 this.value !== undefined ? this.value : '';
15423         
15424             this.el.dom.removeAttribute('name');
15425             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15426         }
15427         
15428         if(this.multiple){
15429             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15430             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15431         }
15432         
15433         if(this.removable && !this.multiple){
15434             var close = this.closeTriggerEl();
15435             if(close){
15436                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15437                 close.on('click', this.removeBtnClick, this, close);
15438             }
15439         }
15440         /*
15441          * fix the bug in Safari iOS8
15442          */
15443         this.inputEl().on("focus", function(e){
15444             document.activeElement.blur();
15445         }, this);
15446         
15447         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15448         
15449         return;
15450         
15451         
15452     },
15453     
15454     renderTouchView : function()
15455     {
15456         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15457         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15458         
15459         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15460         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15461         
15462         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15463         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15464         this.touchViewBodyEl.setStyle('overflow', 'auto');
15465         
15466         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15467         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15468         
15469         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15470         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15471         
15472     },
15473     
15474     showTouchView : function()
15475     {
15476         if(this.disabled){
15477             return;
15478         }
15479         
15480         this.touchViewHeaderEl.hide();
15481
15482         if(this.modalTitle.length){
15483             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15484             this.touchViewHeaderEl.show();
15485         }
15486
15487         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15488         this.touchViewEl.show();
15489
15490         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15491         
15492         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15493         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15494
15495         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15496
15497         if(this.modalTitle.length){
15498             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15499         }
15500         
15501         this.touchViewBodyEl.setHeight(bodyHeight);
15502
15503         if(this.animate){
15504             var _this = this;
15505             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15506         }else{
15507             this.touchViewEl.addClass('in');
15508         }
15509         
15510         if(this._touchViewMask){
15511             Roo.get(document.body).addClass("x-body-masked");
15512             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15513             this._touchViewMask.setStyle('z-index', 10000);
15514             this._touchViewMask.addClass('show');
15515         }
15516         
15517         this.doTouchViewQuery();
15518         
15519     },
15520     
15521     hideTouchView : function()
15522     {
15523         this.touchViewEl.removeClass('in');
15524
15525         if(this.animate){
15526             var _this = this;
15527             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15528         }else{
15529             this.touchViewEl.setStyle('display', 'none');
15530         }
15531         
15532         if(this._touchViewMask){
15533             this._touchViewMask.removeClass('show');
15534             Roo.get(document.body).removeClass("x-body-masked");
15535         }
15536     },
15537     
15538     setTouchViewValue : function()
15539     {
15540         if(this.multiple){
15541             this.clearItem();
15542         
15543             var _this = this;
15544
15545             Roo.each(this.tickItems, function(o){
15546                 this.addItem(o);
15547             }, this);
15548         }
15549         
15550         this.hideTouchView();
15551     },
15552     
15553     doTouchViewQuery : function()
15554     {
15555         var qe = {
15556             query: '',
15557             forceAll: true,
15558             combo: this,
15559             cancel:false
15560         };
15561         
15562         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15563             return false;
15564         }
15565         
15566         if(!this.alwaysQuery || this.mode == 'local'){
15567             this.onTouchViewLoad();
15568             return;
15569         }
15570         
15571         this.store.load();
15572     },
15573     
15574     onTouchViewBeforeLoad : function(combo,opts)
15575     {
15576         return;
15577     },
15578
15579     // private
15580     onTouchViewLoad : function()
15581     {
15582         if(this.store.getCount() < 1){
15583             this.onTouchViewEmptyResults();
15584             return;
15585         }
15586         
15587         this.clearTouchView();
15588         
15589         var rawValue = this.getRawValue();
15590         
15591         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15592         
15593         this.tickItems = [];
15594         
15595         this.store.data.each(function(d, rowIndex){
15596             var row = this.touchViewListGroup.createChild(template);
15597             
15598             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15599                 row.addClass(d.data.cls);
15600             }
15601             
15602             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15603                 var cfg = {
15604                     data : d.data,
15605                     html : d.data[this.displayField]
15606                 };
15607                 
15608                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15609                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15610                 }
15611             }
15612             row.removeClass('selected');
15613             if(!this.multiple && this.valueField &&
15614                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15615             {
15616                 // radio buttons..
15617                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15618                 row.addClass('selected');
15619             }
15620             
15621             if(this.multiple && this.valueField &&
15622                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15623             {
15624                 
15625                 // checkboxes...
15626                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15627                 this.tickItems.push(d.data);
15628             }
15629             
15630             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15631             
15632         }, this);
15633         
15634         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15635         
15636         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15637
15638         if(this.modalTitle.length){
15639             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15640         }
15641
15642         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15643         
15644         if(this.mobile_restrict_height && listHeight < bodyHeight){
15645             this.touchViewBodyEl.setHeight(listHeight);
15646         }
15647         
15648         var _this = this;
15649         
15650         if(firstChecked && listHeight > bodyHeight){
15651             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15652         }
15653         
15654     },
15655     
15656     onTouchViewLoadException : function()
15657     {
15658         this.hideTouchView();
15659     },
15660     
15661     onTouchViewEmptyResults : function()
15662     {
15663         this.clearTouchView();
15664         
15665         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15666         
15667         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15668         
15669     },
15670     
15671     clearTouchView : function()
15672     {
15673         this.touchViewListGroup.dom.innerHTML = '';
15674     },
15675     
15676     onTouchViewClick : function(e, el, o)
15677     {
15678         e.preventDefault();
15679         
15680         var row = o.row;
15681         var rowIndex = o.rowIndex;
15682         
15683         var r = this.store.getAt(rowIndex);
15684         
15685         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15686             
15687             if(!this.multiple){
15688                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15689                     c.dom.removeAttribute('checked');
15690                 }, this);
15691
15692                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15693
15694                 this.setFromData(r.data);
15695
15696                 var close = this.closeTriggerEl();
15697
15698                 if(close){
15699                     close.show();
15700                 }
15701
15702                 this.hideTouchView();
15703
15704                 this.fireEvent('select', this, r, rowIndex);
15705
15706                 return;
15707             }
15708
15709             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15710                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15711                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15712                 return;
15713             }
15714
15715             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15716             this.addItem(r.data);
15717             this.tickItems.push(r.data);
15718         }
15719     },
15720     
15721     getAutoCreateNativeIOS : function()
15722     {
15723         var cfg = {
15724             cls: 'form-group' //input-group,
15725         };
15726         
15727         var combobox =  {
15728             tag: 'select',
15729             cls : 'roo-ios-select'
15730         };
15731         
15732         if (this.name) {
15733             combobox.name = this.name;
15734         }
15735         
15736         if (this.disabled) {
15737             combobox.disabled = true;
15738         }
15739         
15740         var settings = this;
15741         
15742         ['xs','sm','md','lg'].map(function(size){
15743             if (settings[size]) {
15744                 cfg.cls += ' col-' + size + '-' + settings[size];
15745             }
15746         });
15747         
15748         cfg.cn = combobox;
15749         
15750         return cfg;
15751         
15752     },
15753     
15754     initIOSView : function()
15755     {
15756         this.store.on('load', this.onIOSViewLoad, this);
15757         
15758         return;
15759     },
15760     
15761     onIOSViewLoad : function()
15762     {
15763         if(this.store.getCount() < 1){
15764             return;
15765         }
15766         
15767         this.clearIOSView();
15768         
15769         if(this.allowBlank) {
15770             
15771             var default_text = '-- SELECT --';
15772             
15773             if(this.placeholder.length){
15774                 default_text = this.placeholder;
15775             }
15776             
15777             if(this.emptyTitle.length){
15778                 default_text += ' - ' + this.emptyTitle + ' -';
15779             }
15780             
15781             var opt = this.inputEl().createChild({
15782                 tag: 'option',
15783                 value : 0,
15784                 html : default_text
15785             });
15786             
15787             var o = {};
15788             o[this.valueField] = 0;
15789             o[this.displayField] = default_text;
15790             
15791             this.ios_options.push({
15792                 data : o,
15793                 el : opt
15794             });
15795             
15796         }
15797         
15798         this.store.data.each(function(d, rowIndex){
15799             
15800             var html = '';
15801             
15802             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15803                 html = d.data[this.displayField];
15804             }
15805             
15806             var value = '';
15807             
15808             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15809                 value = d.data[this.valueField];
15810             }
15811             
15812             var option = {
15813                 tag: 'option',
15814                 value : value,
15815                 html : html
15816             };
15817             
15818             if(this.value == d.data[this.valueField]){
15819                 option['selected'] = true;
15820             }
15821             
15822             var opt = this.inputEl().createChild(option);
15823             
15824             this.ios_options.push({
15825                 data : d.data,
15826                 el : opt
15827             });
15828             
15829         }, this);
15830         
15831         this.inputEl().on('change', function(){
15832            this.fireEvent('select', this);
15833         }, this);
15834         
15835     },
15836     
15837     clearIOSView: function()
15838     {
15839         this.inputEl().dom.innerHTML = '';
15840         
15841         this.ios_options = [];
15842     },
15843     
15844     setIOSValue: function(v)
15845     {
15846         this.value = v;
15847         
15848         if(!this.ios_options){
15849             return;
15850         }
15851         
15852         Roo.each(this.ios_options, function(opts){
15853            
15854            opts.el.dom.removeAttribute('selected');
15855            
15856            if(opts.data[this.valueField] != v){
15857                return;
15858            }
15859            
15860            opts.el.dom.setAttribute('selected', true);
15861            
15862         }, this);
15863     }
15864
15865     /** 
15866     * @cfg {Boolean} grow 
15867     * @hide 
15868     */
15869     /** 
15870     * @cfg {Number} growMin 
15871     * @hide 
15872     */
15873     /** 
15874     * @cfg {Number} growMax 
15875     * @hide 
15876     */
15877     /**
15878      * @hide
15879      * @method autoSize
15880      */
15881 });
15882
15883 Roo.apply(Roo.bootstrap.ComboBox,  {
15884     
15885     header : {
15886         tag: 'div',
15887         cls: 'modal-header',
15888         cn: [
15889             {
15890                 tag: 'h4',
15891                 cls: 'modal-title'
15892             }
15893         ]
15894     },
15895     
15896     body : {
15897         tag: 'div',
15898         cls: 'modal-body',
15899         cn: [
15900             {
15901                 tag: 'ul',
15902                 cls: 'list-group'
15903             }
15904         ]
15905     },
15906     
15907     listItemRadio : {
15908         tag: 'li',
15909         cls: 'list-group-item',
15910         cn: [
15911             {
15912                 tag: 'span',
15913                 cls: 'roo-combobox-list-group-item-value'
15914             },
15915             {
15916                 tag: 'div',
15917                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15918                 cn: [
15919                     {
15920                         tag: 'input',
15921                         type: 'radio'
15922                     },
15923                     {
15924                         tag: 'label'
15925                     }
15926                 ]
15927             }
15928         ]
15929     },
15930     
15931     listItemCheckbox : {
15932         tag: 'li',
15933         cls: 'list-group-item',
15934         cn: [
15935             {
15936                 tag: 'span',
15937                 cls: 'roo-combobox-list-group-item-value'
15938             },
15939             {
15940                 tag: 'div',
15941                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15942                 cn: [
15943                     {
15944                         tag: 'input',
15945                         type: 'checkbox'
15946                     },
15947                     {
15948                         tag: 'label'
15949                     }
15950                 ]
15951             }
15952         ]
15953     },
15954     
15955     emptyResult : {
15956         tag: 'div',
15957         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15958     },
15959     
15960     footer : {
15961         tag: 'div',
15962         cls: 'modal-footer',
15963         cn: [
15964             {
15965                 tag: 'div',
15966                 cls: 'row',
15967                 cn: [
15968                     {
15969                         tag: 'div',
15970                         cls: 'col-xs-6 text-left',
15971                         cn: {
15972                             tag: 'button',
15973                             cls: 'btn btn-danger roo-touch-view-cancel',
15974                             html: 'Cancel'
15975                         }
15976                     },
15977                     {
15978                         tag: 'div',
15979                         cls: 'col-xs-6 text-right',
15980                         cn: {
15981                             tag: 'button',
15982                             cls: 'btn btn-success roo-touch-view-ok',
15983                             html: 'OK'
15984                         }
15985                     }
15986                 ]
15987             }
15988         ]
15989         
15990     }
15991 });
15992
15993 Roo.apply(Roo.bootstrap.ComboBox,  {
15994     
15995     touchViewTemplate : {
15996         tag: 'div',
15997         cls: 'modal fade roo-combobox-touch-view',
15998         cn: [
15999             {
16000                 tag: 'div',
16001                 cls: 'modal-dialog',
16002                 style : 'position:fixed', // we have to fix position....
16003                 cn: [
16004                     {
16005                         tag: 'div',
16006                         cls: 'modal-content',
16007                         cn: [
16008                             Roo.bootstrap.ComboBox.header,
16009                             Roo.bootstrap.ComboBox.body,
16010                             Roo.bootstrap.ComboBox.footer
16011                         ]
16012                     }
16013                 ]
16014             }
16015         ]
16016     }
16017 });/*
16018  * Based on:
16019  * Ext JS Library 1.1.1
16020  * Copyright(c) 2006-2007, Ext JS, LLC.
16021  *
16022  * Originally Released Under LGPL - original licence link has changed is not relivant.
16023  *
16024  * Fork - LGPL
16025  * <script type="text/javascript">
16026  */
16027
16028 /**
16029  * @class Roo.View
16030  * @extends Roo.util.Observable
16031  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16032  * This class also supports single and multi selection modes. <br>
16033  * Create a data model bound view:
16034  <pre><code>
16035  var store = new Roo.data.Store(...);
16036
16037  var view = new Roo.View({
16038     el : "my-element",
16039     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16040  
16041     singleSelect: true,
16042     selectedClass: "ydataview-selected",
16043     store: store
16044  });
16045
16046  // listen for node click?
16047  view.on("click", function(vw, index, node, e){
16048  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16049  });
16050
16051  // load XML data
16052  dataModel.load("foobar.xml");
16053  </code></pre>
16054  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16055  * <br><br>
16056  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16057  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16058  * 
16059  * Note: old style constructor is still suported (container, template, config)
16060  * 
16061  * @constructor
16062  * Create a new View
16063  * @param {Object} config The config object
16064  * 
16065  */
16066 Roo.View = function(config, depreciated_tpl, depreciated_config){
16067     
16068     this.parent = false;
16069     
16070     if (typeof(depreciated_tpl) == 'undefined') {
16071         // new way.. - universal constructor.
16072         Roo.apply(this, config);
16073         this.el  = Roo.get(this.el);
16074     } else {
16075         // old format..
16076         this.el  = Roo.get(config);
16077         this.tpl = depreciated_tpl;
16078         Roo.apply(this, depreciated_config);
16079     }
16080     this.wrapEl  = this.el.wrap().wrap();
16081     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16082     
16083     
16084     if(typeof(this.tpl) == "string"){
16085         this.tpl = new Roo.Template(this.tpl);
16086     } else {
16087         // support xtype ctors..
16088         this.tpl = new Roo.factory(this.tpl, Roo);
16089     }
16090     
16091     
16092     this.tpl.compile();
16093     
16094     /** @private */
16095     this.addEvents({
16096         /**
16097          * @event beforeclick
16098          * Fires before a click is processed. Returns false to cancel the default action.
16099          * @param {Roo.View} this
16100          * @param {Number} index The index of the target node
16101          * @param {HTMLElement} node The target node
16102          * @param {Roo.EventObject} e The raw event object
16103          */
16104             "beforeclick" : true,
16105         /**
16106          * @event click
16107          * Fires when a template node is clicked.
16108          * @param {Roo.View} this
16109          * @param {Number} index The index of the target node
16110          * @param {HTMLElement} node The target node
16111          * @param {Roo.EventObject} e The raw event object
16112          */
16113             "click" : true,
16114         /**
16115          * @event dblclick
16116          * Fires when a template node is double clicked.
16117          * @param {Roo.View} this
16118          * @param {Number} index The index of the target node
16119          * @param {HTMLElement} node The target node
16120          * @param {Roo.EventObject} e The raw event object
16121          */
16122             "dblclick" : true,
16123         /**
16124          * @event contextmenu
16125          * Fires when a template node is right clicked.
16126          * @param {Roo.View} this
16127          * @param {Number} index The index of the target node
16128          * @param {HTMLElement} node The target node
16129          * @param {Roo.EventObject} e The raw event object
16130          */
16131             "contextmenu" : true,
16132         /**
16133          * @event selectionchange
16134          * Fires when the selected nodes change.
16135          * @param {Roo.View} this
16136          * @param {Array} selections Array of the selected nodes
16137          */
16138             "selectionchange" : true,
16139     
16140         /**
16141          * @event beforeselect
16142          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16143          * @param {Roo.View} this
16144          * @param {HTMLElement} node The node to be selected
16145          * @param {Array} selections Array of currently selected nodes
16146          */
16147             "beforeselect" : true,
16148         /**
16149          * @event preparedata
16150          * Fires on every row to render, to allow you to change the data.
16151          * @param {Roo.View} this
16152          * @param {Object} data to be rendered (change this)
16153          */
16154           "preparedata" : true
16155           
16156           
16157         });
16158
16159
16160
16161     this.el.on({
16162         "click": this.onClick,
16163         "dblclick": this.onDblClick,
16164         "contextmenu": this.onContextMenu,
16165         scope:this
16166     });
16167
16168     this.selections = [];
16169     this.nodes = [];
16170     this.cmp = new Roo.CompositeElementLite([]);
16171     if(this.store){
16172         this.store = Roo.factory(this.store, Roo.data);
16173         this.setStore(this.store, true);
16174     }
16175     
16176     if ( this.footer && this.footer.xtype) {
16177            
16178          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16179         
16180         this.footer.dataSource = this.store;
16181         this.footer.container = fctr;
16182         this.footer = Roo.factory(this.footer, Roo);
16183         fctr.insertFirst(this.el);
16184         
16185         // this is a bit insane - as the paging toolbar seems to detach the el..
16186 //        dom.parentNode.parentNode.parentNode
16187          // they get detached?
16188     }
16189     
16190     
16191     Roo.View.superclass.constructor.call(this);
16192     
16193     
16194 };
16195
16196 Roo.extend(Roo.View, Roo.util.Observable, {
16197     
16198      /**
16199      * @cfg {Roo.data.Store} store Data store to load data from.
16200      */
16201     store : false,
16202     
16203     /**
16204      * @cfg {String|Roo.Element} el The container element.
16205      */
16206     el : '',
16207     
16208     /**
16209      * @cfg {String|Roo.Template} tpl The template used by this View 
16210      */
16211     tpl : false,
16212     /**
16213      * @cfg {String} dataName the named area of the template to use as the data area
16214      *                          Works with domtemplates roo-name="name"
16215      */
16216     dataName: false,
16217     /**
16218      * @cfg {String} selectedClass The css class to add to selected nodes
16219      */
16220     selectedClass : "x-view-selected",
16221      /**
16222      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16223      */
16224     emptyText : "",
16225     
16226     /**
16227      * @cfg {String} text to display on mask (default Loading)
16228      */
16229     mask : false,
16230     /**
16231      * @cfg {Boolean} multiSelect Allow multiple selection
16232      */
16233     multiSelect : false,
16234     /**
16235      * @cfg {Boolean} singleSelect Allow single selection
16236      */
16237     singleSelect:  false,
16238     
16239     /**
16240      * @cfg {Boolean} toggleSelect - selecting 
16241      */
16242     toggleSelect : false,
16243     
16244     /**
16245      * @cfg {Boolean} tickable - selecting 
16246      */
16247     tickable : false,
16248     
16249     /**
16250      * Returns the element this view is bound to.
16251      * @return {Roo.Element}
16252      */
16253     getEl : function(){
16254         return this.wrapEl;
16255     },
16256     
16257     
16258
16259     /**
16260      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16261      */
16262     refresh : function(){
16263         //Roo.log('refresh');
16264         var t = this.tpl;
16265         
16266         // if we are using something like 'domtemplate', then
16267         // the what gets used is:
16268         // t.applySubtemplate(NAME, data, wrapping data..)
16269         // the outer template then get' applied with
16270         //     the store 'extra data'
16271         // and the body get's added to the
16272         //      roo-name="data" node?
16273         //      <span class='roo-tpl-{name}'></span> ?????
16274         
16275         
16276         
16277         this.clearSelections();
16278         this.el.update("");
16279         var html = [];
16280         var records = this.store.getRange();
16281         if(records.length < 1) {
16282             
16283             // is this valid??  = should it render a template??
16284             
16285             this.el.update(this.emptyText);
16286             return;
16287         }
16288         var el = this.el;
16289         if (this.dataName) {
16290             this.el.update(t.apply(this.store.meta)); //????
16291             el = this.el.child('.roo-tpl-' + this.dataName);
16292         }
16293         
16294         for(var i = 0, len = records.length; i < len; i++){
16295             var data = this.prepareData(records[i].data, i, records[i]);
16296             this.fireEvent("preparedata", this, data, i, records[i]);
16297             
16298             var d = Roo.apply({}, data);
16299             
16300             if(this.tickable){
16301                 Roo.apply(d, {'roo-id' : Roo.id()});
16302                 
16303                 var _this = this;
16304             
16305                 Roo.each(this.parent.item, function(item){
16306                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16307                         return;
16308                     }
16309                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16310                 });
16311             }
16312             
16313             html[html.length] = Roo.util.Format.trim(
16314                 this.dataName ?
16315                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16316                     t.apply(d)
16317             );
16318         }
16319         
16320         
16321         
16322         el.update(html.join(""));
16323         this.nodes = el.dom.childNodes;
16324         this.updateIndexes(0);
16325     },
16326     
16327
16328     /**
16329      * Function to override to reformat the data that is sent to
16330      * the template for each node.
16331      * DEPRICATED - use the preparedata event handler.
16332      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16333      * a JSON object for an UpdateManager bound view).
16334      */
16335     prepareData : function(data, index, record)
16336     {
16337         this.fireEvent("preparedata", this, data, index, record);
16338         return data;
16339     },
16340
16341     onUpdate : function(ds, record){
16342         // Roo.log('on update');   
16343         this.clearSelections();
16344         var index = this.store.indexOf(record);
16345         var n = this.nodes[index];
16346         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16347         n.parentNode.removeChild(n);
16348         this.updateIndexes(index, index);
16349     },
16350
16351     
16352     
16353 // --------- FIXME     
16354     onAdd : function(ds, records, index)
16355     {
16356         //Roo.log(['on Add', ds, records, index] );        
16357         this.clearSelections();
16358         if(this.nodes.length == 0){
16359             this.refresh();
16360             return;
16361         }
16362         var n = this.nodes[index];
16363         for(var i = 0, len = records.length; i < len; i++){
16364             var d = this.prepareData(records[i].data, i, records[i]);
16365             if(n){
16366                 this.tpl.insertBefore(n, d);
16367             }else{
16368                 
16369                 this.tpl.append(this.el, d);
16370             }
16371         }
16372         this.updateIndexes(index);
16373     },
16374
16375     onRemove : function(ds, record, index){
16376        // Roo.log('onRemove');
16377         this.clearSelections();
16378         var el = this.dataName  ?
16379             this.el.child('.roo-tpl-' + this.dataName) :
16380             this.el; 
16381         
16382         el.dom.removeChild(this.nodes[index]);
16383         this.updateIndexes(index);
16384     },
16385
16386     /**
16387      * Refresh an individual node.
16388      * @param {Number} index
16389      */
16390     refreshNode : function(index){
16391         this.onUpdate(this.store, this.store.getAt(index));
16392     },
16393
16394     updateIndexes : function(startIndex, endIndex){
16395         var ns = this.nodes;
16396         startIndex = startIndex || 0;
16397         endIndex = endIndex || ns.length - 1;
16398         for(var i = startIndex; i <= endIndex; i++){
16399             ns[i].nodeIndex = i;
16400         }
16401     },
16402
16403     /**
16404      * Changes the data store this view uses and refresh the view.
16405      * @param {Store} store
16406      */
16407     setStore : function(store, initial){
16408         if(!initial && this.store){
16409             this.store.un("datachanged", this.refresh);
16410             this.store.un("add", this.onAdd);
16411             this.store.un("remove", this.onRemove);
16412             this.store.un("update", this.onUpdate);
16413             this.store.un("clear", this.refresh);
16414             this.store.un("beforeload", this.onBeforeLoad);
16415             this.store.un("load", this.onLoad);
16416             this.store.un("loadexception", this.onLoad);
16417         }
16418         if(store){
16419           
16420             store.on("datachanged", this.refresh, this);
16421             store.on("add", this.onAdd, this);
16422             store.on("remove", this.onRemove, this);
16423             store.on("update", this.onUpdate, this);
16424             store.on("clear", this.refresh, this);
16425             store.on("beforeload", this.onBeforeLoad, this);
16426             store.on("load", this.onLoad, this);
16427             store.on("loadexception", this.onLoad, this);
16428         }
16429         
16430         if(store){
16431             this.refresh();
16432         }
16433     },
16434     /**
16435      * onbeforeLoad - masks the loading area.
16436      *
16437      */
16438     onBeforeLoad : function(store,opts)
16439     {
16440          //Roo.log('onBeforeLoad');   
16441         if (!opts.add) {
16442             this.el.update("");
16443         }
16444         this.el.mask(this.mask ? this.mask : "Loading" ); 
16445     },
16446     onLoad : function ()
16447     {
16448         this.el.unmask();
16449     },
16450     
16451
16452     /**
16453      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16454      * @param {HTMLElement} node
16455      * @return {HTMLElement} The template node
16456      */
16457     findItemFromChild : function(node){
16458         var el = this.dataName  ?
16459             this.el.child('.roo-tpl-' + this.dataName,true) :
16460             this.el.dom; 
16461         
16462         if(!node || node.parentNode == el){
16463                     return node;
16464             }
16465             var p = node.parentNode;
16466             while(p && p != el){
16467             if(p.parentNode == el){
16468                 return p;
16469             }
16470             p = p.parentNode;
16471         }
16472             return null;
16473     },
16474
16475     /** @ignore */
16476     onClick : function(e){
16477         var item = this.findItemFromChild(e.getTarget());
16478         if(item){
16479             var index = this.indexOf(item);
16480             if(this.onItemClick(item, index, e) !== false){
16481                 this.fireEvent("click", this, index, item, e);
16482             }
16483         }else{
16484             this.clearSelections();
16485         }
16486     },
16487
16488     /** @ignore */
16489     onContextMenu : function(e){
16490         var item = this.findItemFromChild(e.getTarget());
16491         if(item){
16492             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16493         }
16494     },
16495
16496     /** @ignore */
16497     onDblClick : function(e){
16498         var item = this.findItemFromChild(e.getTarget());
16499         if(item){
16500             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16501         }
16502     },
16503
16504     onItemClick : function(item, index, e)
16505     {
16506         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16507             return false;
16508         }
16509         if (this.toggleSelect) {
16510             var m = this.isSelected(item) ? 'unselect' : 'select';
16511             //Roo.log(m);
16512             var _t = this;
16513             _t[m](item, true, false);
16514             return true;
16515         }
16516         if(this.multiSelect || this.singleSelect){
16517             if(this.multiSelect && e.shiftKey && this.lastSelection){
16518                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16519             }else{
16520                 this.select(item, this.multiSelect && e.ctrlKey);
16521                 this.lastSelection = item;
16522             }
16523             
16524             if(!this.tickable){
16525                 e.preventDefault();
16526             }
16527             
16528         }
16529         return true;
16530     },
16531
16532     /**
16533      * Get the number of selected nodes.
16534      * @return {Number}
16535      */
16536     getSelectionCount : function(){
16537         return this.selections.length;
16538     },
16539
16540     /**
16541      * Get the currently selected nodes.
16542      * @return {Array} An array of HTMLElements
16543      */
16544     getSelectedNodes : function(){
16545         return this.selections;
16546     },
16547
16548     /**
16549      * Get the indexes of the selected nodes.
16550      * @return {Array}
16551      */
16552     getSelectedIndexes : function(){
16553         var indexes = [], s = this.selections;
16554         for(var i = 0, len = s.length; i < len; i++){
16555             indexes.push(s[i].nodeIndex);
16556         }
16557         return indexes;
16558     },
16559
16560     /**
16561      * Clear all selections
16562      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16563      */
16564     clearSelections : function(suppressEvent){
16565         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16566             this.cmp.elements = this.selections;
16567             this.cmp.removeClass(this.selectedClass);
16568             this.selections = [];
16569             if(!suppressEvent){
16570                 this.fireEvent("selectionchange", this, this.selections);
16571             }
16572         }
16573     },
16574
16575     /**
16576      * Returns true if the passed node is selected
16577      * @param {HTMLElement/Number} node The node or node index
16578      * @return {Boolean}
16579      */
16580     isSelected : function(node){
16581         var s = this.selections;
16582         if(s.length < 1){
16583             return false;
16584         }
16585         node = this.getNode(node);
16586         return s.indexOf(node) !== -1;
16587     },
16588
16589     /**
16590      * Selects nodes.
16591      * @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
16592      * @param {Boolean} keepExisting (optional) true to keep existing selections
16593      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16594      */
16595     select : function(nodeInfo, keepExisting, suppressEvent){
16596         if(nodeInfo instanceof Array){
16597             if(!keepExisting){
16598                 this.clearSelections(true);
16599             }
16600             for(var i = 0, len = nodeInfo.length; i < len; i++){
16601                 this.select(nodeInfo[i], true, true);
16602             }
16603             return;
16604         } 
16605         var node = this.getNode(nodeInfo);
16606         if(!node || this.isSelected(node)){
16607             return; // already selected.
16608         }
16609         if(!keepExisting){
16610             this.clearSelections(true);
16611         }
16612         
16613         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16614             Roo.fly(node).addClass(this.selectedClass);
16615             this.selections.push(node);
16616             if(!suppressEvent){
16617                 this.fireEvent("selectionchange", this, this.selections);
16618             }
16619         }
16620         
16621         
16622     },
16623       /**
16624      * Unselects nodes.
16625      * @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
16626      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16627      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16628      */
16629     unselect : function(nodeInfo, keepExisting, suppressEvent)
16630     {
16631         if(nodeInfo instanceof Array){
16632             Roo.each(this.selections, function(s) {
16633                 this.unselect(s, nodeInfo);
16634             }, this);
16635             return;
16636         }
16637         var node = this.getNode(nodeInfo);
16638         if(!node || !this.isSelected(node)){
16639             //Roo.log("not selected");
16640             return; // not selected.
16641         }
16642         // fireevent???
16643         var ns = [];
16644         Roo.each(this.selections, function(s) {
16645             if (s == node ) {
16646                 Roo.fly(node).removeClass(this.selectedClass);
16647
16648                 return;
16649             }
16650             ns.push(s);
16651         },this);
16652         
16653         this.selections= ns;
16654         this.fireEvent("selectionchange", this, this.selections);
16655     },
16656
16657     /**
16658      * Gets a template node.
16659      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16660      * @return {HTMLElement} The node or null if it wasn't found
16661      */
16662     getNode : function(nodeInfo){
16663         if(typeof nodeInfo == "string"){
16664             return document.getElementById(nodeInfo);
16665         }else if(typeof nodeInfo == "number"){
16666             return this.nodes[nodeInfo];
16667         }
16668         return nodeInfo;
16669     },
16670
16671     /**
16672      * Gets a range template nodes.
16673      * @param {Number} startIndex
16674      * @param {Number} endIndex
16675      * @return {Array} An array of nodes
16676      */
16677     getNodes : function(start, end){
16678         var ns = this.nodes;
16679         start = start || 0;
16680         end = typeof end == "undefined" ? ns.length - 1 : end;
16681         var nodes = [];
16682         if(start <= end){
16683             for(var i = start; i <= end; i++){
16684                 nodes.push(ns[i]);
16685             }
16686         } else{
16687             for(var i = start; i >= end; i--){
16688                 nodes.push(ns[i]);
16689             }
16690         }
16691         return nodes;
16692     },
16693
16694     /**
16695      * Finds the index of the passed node
16696      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16697      * @return {Number} The index of the node or -1
16698      */
16699     indexOf : function(node){
16700         node = this.getNode(node);
16701         if(typeof node.nodeIndex == "number"){
16702             return node.nodeIndex;
16703         }
16704         var ns = this.nodes;
16705         for(var i = 0, len = ns.length; i < len; i++){
16706             if(ns[i] == node){
16707                 return i;
16708             }
16709         }
16710         return -1;
16711     }
16712 });
16713 /*
16714  * - LGPL
16715  *
16716  * based on jquery fullcalendar
16717  * 
16718  */
16719
16720 Roo.bootstrap = Roo.bootstrap || {};
16721 /**
16722  * @class Roo.bootstrap.Calendar
16723  * @extends Roo.bootstrap.Component
16724  * Bootstrap Calendar class
16725  * @cfg {Boolean} loadMask (true|false) default false
16726  * @cfg {Object} header generate the user specific header of the calendar, default false
16727
16728  * @constructor
16729  * Create a new Container
16730  * @param {Object} config The config object
16731  */
16732
16733
16734
16735 Roo.bootstrap.Calendar = function(config){
16736     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16737      this.addEvents({
16738         /**
16739              * @event select
16740              * Fires when a date is selected
16741              * @param {DatePicker} this
16742              * @param {Date} date The selected date
16743              */
16744         'select': true,
16745         /**
16746              * @event monthchange
16747              * Fires when the displayed month changes 
16748              * @param {DatePicker} this
16749              * @param {Date} date The selected month
16750              */
16751         'monthchange': true,
16752         /**
16753              * @event evententer
16754              * Fires when mouse over an event
16755              * @param {Calendar} this
16756              * @param {event} Event
16757              */
16758         'evententer': true,
16759         /**
16760              * @event eventleave
16761              * Fires when the mouse leaves an
16762              * @param {Calendar} this
16763              * @param {event}
16764              */
16765         'eventleave': true,
16766         /**
16767              * @event eventclick
16768              * Fires when the mouse click an
16769              * @param {Calendar} this
16770              * @param {event}
16771              */
16772         'eventclick': true
16773         
16774     });
16775
16776 };
16777
16778 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16779     
16780      /**
16781      * @cfg {Number} startDay
16782      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16783      */
16784     startDay : 0,
16785     
16786     loadMask : false,
16787     
16788     header : false,
16789       
16790     getAutoCreate : function(){
16791         
16792         
16793         var fc_button = function(name, corner, style, content ) {
16794             return Roo.apply({},{
16795                 tag : 'span',
16796                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16797                          (corner.length ?
16798                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16799                             ''
16800                         ),
16801                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16802                 unselectable: 'on'
16803             });
16804         };
16805         
16806         var header = {};
16807         
16808         if(!this.header){
16809             header = {
16810                 tag : 'table',
16811                 cls : 'fc-header',
16812                 style : 'width:100%',
16813                 cn : [
16814                     {
16815                         tag: 'tr',
16816                         cn : [
16817                             {
16818                                 tag : 'td',
16819                                 cls : 'fc-header-left',
16820                                 cn : [
16821                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16822                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16823                                     { tag: 'span', cls: 'fc-header-space' },
16824                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16825
16826
16827                                 ]
16828                             },
16829
16830                             {
16831                                 tag : 'td',
16832                                 cls : 'fc-header-center',
16833                                 cn : [
16834                                     {
16835                                         tag: 'span',
16836                                         cls: 'fc-header-title',
16837                                         cn : {
16838                                             tag: 'H2',
16839                                             html : 'month / year'
16840                                         }
16841                                     }
16842
16843                                 ]
16844                             },
16845                             {
16846                                 tag : 'td',
16847                                 cls : 'fc-header-right',
16848                                 cn : [
16849                               /*      fc_button('month', 'left', '', 'month' ),
16850                                     fc_button('week', '', '', 'week' ),
16851                                     fc_button('day', 'right', '', 'day' )
16852                                 */    
16853
16854                                 ]
16855                             }
16856
16857                         ]
16858                     }
16859                 ]
16860             };
16861         }
16862         
16863         header = this.header;
16864         
16865        
16866         var cal_heads = function() {
16867             var ret = [];
16868             // fixme - handle this.
16869             
16870             for (var i =0; i < Date.dayNames.length; i++) {
16871                 var d = Date.dayNames[i];
16872                 ret.push({
16873                     tag: 'th',
16874                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16875                     html : d.substring(0,3)
16876                 });
16877                 
16878             }
16879             ret[0].cls += ' fc-first';
16880             ret[6].cls += ' fc-last';
16881             return ret;
16882         };
16883         var cal_cell = function(n) {
16884             return  {
16885                 tag: 'td',
16886                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16887                 cn : [
16888                     {
16889                         cn : [
16890                             {
16891                                 cls: 'fc-day-number',
16892                                 html: 'D'
16893                             },
16894                             {
16895                                 cls: 'fc-day-content',
16896                              
16897                                 cn : [
16898                                      {
16899                                         style: 'position: relative;' // height: 17px;
16900                                     }
16901                                 ]
16902                             }
16903                             
16904                             
16905                         ]
16906                     }
16907                 ]
16908                 
16909             }
16910         };
16911         var cal_rows = function() {
16912             
16913             var ret = [];
16914             for (var r = 0; r < 6; r++) {
16915                 var row= {
16916                     tag : 'tr',
16917                     cls : 'fc-week',
16918                     cn : []
16919                 };
16920                 
16921                 for (var i =0; i < Date.dayNames.length; i++) {
16922                     var d = Date.dayNames[i];
16923                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16924
16925                 }
16926                 row.cn[0].cls+=' fc-first';
16927                 row.cn[0].cn[0].style = 'min-height:90px';
16928                 row.cn[6].cls+=' fc-last';
16929                 ret.push(row);
16930                 
16931             }
16932             ret[0].cls += ' fc-first';
16933             ret[4].cls += ' fc-prev-last';
16934             ret[5].cls += ' fc-last';
16935             return ret;
16936             
16937         };
16938         
16939         var cal_table = {
16940             tag: 'table',
16941             cls: 'fc-border-separate',
16942             style : 'width:100%',
16943             cellspacing  : 0,
16944             cn : [
16945                 { 
16946                     tag: 'thead',
16947                     cn : [
16948                         { 
16949                             tag: 'tr',
16950                             cls : 'fc-first fc-last',
16951                             cn : cal_heads()
16952                         }
16953                     ]
16954                 },
16955                 { 
16956                     tag: 'tbody',
16957                     cn : cal_rows()
16958                 }
16959                   
16960             ]
16961         };
16962          
16963          var cfg = {
16964             cls : 'fc fc-ltr',
16965             cn : [
16966                 header,
16967                 {
16968                     cls : 'fc-content',
16969                     style : "position: relative;",
16970                     cn : [
16971                         {
16972                             cls : 'fc-view fc-view-month fc-grid',
16973                             style : 'position: relative',
16974                             unselectable : 'on',
16975                             cn : [
16976                                 {
16977                                     cls : 'fc-event-container',
16978                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16979                                 },
16980                                 cal_table
16981                             ]
16982                         }
16983                     ]
16984     
16985                 }
16986            ] 
16987             
16988         };
16989         
16990          
16991         
16992         return cfg;
16993     },
16994     
16995     
16996     initEvents : function()
16997     {
16998         if(!this.store){
16999             throw "can not find store for calendar";
17000         }
17001         
17002         var mark = {
17003             tag: "div",
17004             cls:"x-dlg-mask",
17005             style: "text-align:center",
17006             cn: [
17007                 {
17008                     tag: "div",
17009                     style: "background-color:white;width:50%;margin:250 auto",
17010                     cn: [
17011                         {
17012                             tag: "img",
17013                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17014                         },
17015                         {
17016                             tag: "span",
17017                             html: "Loading"
17018                         }
17019                         
17020                     ]
17021                 }
17022             ]
17023         };
17024         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17025         
17026         var size = this.el.select('.fc-content', true).first().getSize();
17027         this.maskEl.setSize(size.width, size.height);
17028         this.maskEl.enableDisplayMode("block");
17029         if(!this.loadMask){
17030             this.maskEl.hide();
17031         }
17032         
17033         this.store = Roo.factory(this.store, Roo.data);
17034         this.store.on('load', this.onLoad, this);
17035         this.store.on('beforeload', this.onBeforeLoad, this);
17036         
17037         this.resize();
17038         
17039         this.cells = this.el.select('.fc-day',true);
17040         //Roo.log(this.cells);
17041         this.textNodes = this.el.query('.fc-day-number');
17042         this.cells.addClassOnOver('fc-state-hover');
17043         
17044         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17045         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17046         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17047         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17048         
17049         this.on('monthchange', this.onMonthChange, this);
17050         
17051         this.update(new Date().clearTime());
17052     },
17053     
17054     resize : function() {
17055         var sz  = this.el.getSize();
17056         
17057         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17058         this.el.select('.fc-day-content div',true).setHeight(34);
17059     },
17060     
17061     
17062     // private
17063     showPrevMonth : function(e){
17064         this.update(this.activeDate.add("mo", -1));
17065     },
17066     showToday : function(e){
17067         this.update(new Date().clearTime());
17068     },
17069     // private
17070     showNextMonth : function(e){
17071         this.update(this.activeDate.add("mo", 1));
17072     },
17073
17074     // private
17075     showPrevYear : function(){
17076         this.update(this.activeDate.add("y", -1));
17077     },
17078
17079     // private
17080     showNextYear : function(){
17081         this.update(this.activeDate.add("y", 1));
17082     },
17083
17084     
17085    // private
17086     update : function(date)
17087     {
17088         var vd = this.activeDate;
17089         this.activeDate = date;
17090 //        if(vd && this.el){
17091 //            var t = date.getTime();
17092 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17093 //                Roo.log('using add remove');
17094 //                
17095 //                this.fireEvent('monthchange', this, date);
17096 //                
17097 //                this.cells.removeClass("fc-state-highlight");
17098 //                this.cells.each(function(c){
17099 //                   if(c.dateValue == t){
17100 //                       c.addClass("fc-state-highlight");
17101 //                       setTimeout(function(){
17102 //                            try{c.dom.firstChild.focus();}catch(e){}
17103 //                       }, 50);
17104 //                       return false;
17105 //                   }
17106 //                   return true;
17107 //                });
17108 //                return;
17109 //            }
17110 //        }
17111         
17112         var days = date.getDaysInMonth();
17113         
17114         var firstOfMonth = date.getFirstDateOfMonth();
17115         var startingPos = firstOfMonth.getDay()-this.startDay;
17116         
17117         if(startingPos < this.startDay){
17118             startingPos += 7;
17119         }
17120         
17121         var pm = date.add(Date.MONTH, -1);
17122         var prevStart = pm.getDaysInMonth()-startingPos;
17123 //        
17124         this.cells = this.el.select('.fc-day',true);
17125         this.textNodes = this.el.query('.fc-day-number');
17126         this.cells.addClassOnOver('fc-state-hover');
17127         
17128         var cells = this.cells.elements;
17129         var textEls = this.textNodes;
17130         
17131         Roo.each(cells, function(cell){
17132             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17133         });
17134         
17135         days += startingPos;
17136
17137         // convert everything to numbers so it's fast
17138         var day = 86400000;
17139         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17140         //Roo.log(d);
17141         //Roo.log(pm);
17142         //Roo.log(prevStart);
17143         
17144         var today = new Date().clearTime().getTime();
17145         var sel = date.clearTime().getTime();
17146         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17147         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17148         var ddMatch = this.disabledDatesRE;
17149         var ddText = this.disabledDatesText;
17150         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17151         var ddaysText = this.disabledDaysText;
17152         var format = this.format;
17153         
17154         var setCellClass = function(cal, cell){
17155             cell.row = 0;
17156             cell.events = [];
17157             cell.more = [];
17158             //Roo.log('set Cell Class');
17159             cell.title = "";
17160             var t = d.getTime();
17161             
17162             //Roo.log(d);
17163             
17164             cell.dateValue = t;
17165             if(t == today){
17166                 cell.className += " fc-today";
17167                 cell.className += " fc-state-highlight";
17168                 cell.title = cal.todayText;
17169             }
17170             if(t == sel){
17171                 // disable highlight in other month..
17172                 //cell.className += " fc-state-highlight";
17173                 
17174             }
17175             // disabling
17176             if(t < min) {
17177                 cell.className = " fc-state-disabled";
17178                 cell.title = cal.minText;
17179                 return;
17180             }
17181             if(t > max) {
17182                 cell.className = " fc-state-disabled";
17183                 cell.title = cal.maxText;
17184                 return;
17185             }
17186             if(ddays){
17187                 if(ddays.indexOf(d.getDay()) != -1){
17188                     cell.title = ddaysText;
17189                     cell.className = " fc-state-disabled";
17190                 }
17191             }
17192             if(ddMatch && format){
17193                 var fvalue = d.dateFormat(format);
17194                 if(ddMatch.test(fvalue)){
17195                     cell.title = ddText.replace("%0", fvalue);
17196                     cell.className = " fc-state-disabled";
17197                 }
17198             }
17199             
17200             if (!cell.initialClassName) {
17201                 cell.initialClassName = cell.dom.className;
17202             }
17203             
17204             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17205         };
17206
17207         var i = 0;
17208         
17209         for(; i < startingPos; i++) {
17210             textEls[i].innerHTML = (++prevStart);
17211             d.setDate(d.getDate()+1);
17212             
17213             cells[i].className = "fc-past fc-other-month";
17214             setCellClass(this, cells[i]);
17215         }
17216         
17217         var intDay = 0;
17218         
17219         for(; i < days; i++){
17220             intDay = i - startingPos + 1;
17221             textEls[i].innerHTML = (intDay);
17222             d.setDate(d.getDate()+1);
17223             
17224             cells[i].className = ''; // "x-date-active";
17225             setCellClass(this, cells[i]);
17226         }
17227         var extraDays = 0;
17228         
17229         for(; i < 42; i++) {
17230             textEls[i].innerHTML = (++extraDays);
17231             d.setDate(d.getDate()+1);
17232             
17233             cells[i].className = "fc-future fc-other-month";
17234             setCellClass(this, cells[i]);
17235         }
17236         
17237         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17238         
17239         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17240         
17241         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17242         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17243         
17244         if(totalRows != 6){
17245             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17246             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17247         }
17248         
17249         this.fireEvent('monthchange', this, date);
17250         
17251         
17252         /*
17253         if(!this.internalRender){
17254             var main = this.el.dom.firstChild;
17255             var w = main.offsetWidth;
17256             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17257             Roo.fly(main).setWidth(w);
17258             this.internalRender = true;
17259             // opera does not respect the auto grow header center column
17260             // then, after it gets a width opera refuses to recalculate
17261             // without a second pass
17262             if(Roo.isOpera && !this.secondPass){
17263                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17264                 this.secondPass = true;
17265                 this.update.defer(10, this, [date]);
17266             }
17267         }
17268         */
17269         
17270     },
17271     
17272     findCell : function(dt) {
17273         dt = dt.clearTime().getTime();
17274         var ret = false;
17275         this.cells.each(function(c){
17276             //Roo.log("check " +c.dateValue + '?=' + dt);
17277             if(c.dateValue == dt){
17278                 ret = c;
17279                 return false;
17280             }
17281             return true;
17282         });
17283         
17284         return ret;
17285     },
17286     
17287     findCells : function(ev) {
17288         var s = ev.start.clone().clearTime().getTime();
17289        // Roo.log(s);
17290         var e= ev.end.clone().clearTime().getTime();
17291        // Roo.log(e);
17292         var ret = [];
17293         this.cells.each(function(c){
17294              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17295             
17296             if(c.dateValue > e){
17297                 return ;
17298             }
17299             if(c.dateValue < s){
17300                 return ;
17301             }
17302             ret.push(c);
17303         });
17304         
17305         return ret;    
17306     },
17307     
17308 //    findBestRow: function(cells)
17309 //    {
17310 //        var ret = 0;
17311 //        
17312 //        for (var i =0 ; i < cells.length;i++) {
17313 //            ret  = Math.max(cells[i].rows || 0,ret);
17314 //        }
17315 //        return ret;
17316 //        
17317 //    },
17318     
17319     
17320     addItem : function(ev)
17321     {
17322         // look for vertical location slot in
17323         var cells = this.findCells(ev);
17324         
17325 //        ev.row = this.findBestRow(cells);
17326         
17327         // work out the location.
17328         
17329         var crow = false;
17330         var rows = [];
17331         for(var i =0; i < cells.length; i++) {
17332             
17333             cells[i].row = cells[0].row;
17334             
17335             if(i == 0){
17336                 cells[i].row = cells[i].row + 1;
17337             }
17338             
17339             if (!crow) {
17340                 crow = {
17341                     start : cells[i],
17342                     end :  cells[i]
17343                 };
17344                 continue;
17345             }
17346             if (crow.start.getY() == cells[i].getY()) {
17347                 // on same row.
17348                 crow.end = cells[i];
17349                 continue;
17350             }
17351             // different row.
17352             rows.push(crow);
17353             crow = {
17354                 start: cells[i],
17355                 end : cells[i]
17356             };
17357             
17358         }
17359         
17360         rows.push(crow);
17361         ev.els = [];
17362         ev.rows = rows;
17363         ev.cells = cells;
17364         
17365         cells[0].events.push(ev);
17366         
17367         this.calevents.push(ev);
17368     },
17369     
17370     clearEvents: function() {
17371         
17372         if(!this.calevents){
17373             return;
17374         }
17375         
17376         Roo.each(this.cells.elements, function(c){
17377             c.row = 0;
17378             c.events = [];
17379             c.more = [];
17380         });
17381         
17382         Roo.each(this.calevents, function(e) {
17383             Roo.each(e.els, function(el) {
17384                 el.un('mouseenter' ,this.onEventEnter, this);
17385                 el.un('mouseleave' ,this.onEventLeave, this);
17386                 el.remove();
17387             },this);
17388         },this);
17389         
17390         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17391             e.remove();
17392         });
17393         
17394     },
17395     
17396     renderEvents: function()
17397     {   
17398         var _this = this;
17399         
17400         this.cells.each(function(c) {
17401             
17402             if(c.row < 5){
17403                 return;
17404             }
17405             
17406             var ev = c.events;
17407             
17408             var r = 4;
17409             if(c.row != c.events.length){
17410                 r = 4 - (4 - (c.row - c.events.length));
17411             }
17412             
17413             c.events = ev.slice(0, r);
17414             c.more = ev.slice(r);
17415             
17416             if(c.more.length && c.more.length == 1){
17417                 c.events.push(c.more.pop());
17418             }
17419             
17420             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17421             
17422         });
17423             
17424         this.cells.each(function(c) {
17425             
17426             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17427             
17428             
17429             for (var e = 0; e < c.events.length; e++){
17430                 var ev = c.events[e];
17431                 var rows = ev.rows;
17432                 
17433                 for(var i = 0; i < rows.length; i++) {
17434                 
17435                     // how many rows should it span..
17436
17437                     var  cfg = {
17438                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17439                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17440
17441                         unselectable : "on",
17442                         cn : [
17443                             {
17444                                 cls: 'fc-event-inner',
17445                                 cn : [
17446     //                                {
17447     //                                  tag:'span',
17448     //                                  cls: 'fc-event-time',
17449     //                                  html : cells.length > 1 ? '' : ev.time
17450     //                                },
17451                                     {
17452                                       tag:'span',
17453                                       cls: 'fc-event-title',
17454                                       html : String.format('{0}', ev.title)
17455                                     }
17456
17457
17458                                 ]
17459                             },
17460                             {
17461                                 cls: 'ui-resizable-handle ui-resizable-e',
17462                                 html : '&nbsp;&nbsp;&nbsp'
17463                             }
17464
17465                         ]
17466                     };
17467
17468                     if (i == 0) {
17469                         cfg.cls += ' fc-event-start';
17470                     }
17471                     if ((i+1) == rows.length) {
17472                         cfg.cls += ' fc-event-end';
17473                     }
17474
17475                     var ctr = _this.el.select('.fc-event-container',true).first();
17476                     var cg = ctr.createChild(cfg);
17477
17478                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17479                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17480
17481                     var r = (c.more.length) ? 1 : 0;
17482                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17483                     cg.setWidth(ebox.right - sbox.x -2);
17484
17485                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17486                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17487                     cg.on('click', _this.onEventClick, _this, ev);
17488
17489                     ev.els.push(cg);
17490                     
17491                 }
17492                 
17493             }
17494             
17495             
17496             if(c.more.length){
17497                 var  cfg = {
17498                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17499                     style : 'position: absolute',
17500                     unselectable : "on",
17501                     cn : [
17502                         {
17503                             cls: 'fc-event-inner',
17504                             cn : [
17505                                 {
17506                                   tag:'span',
17507                                   cls: 'fc-event-title',
17508                                   html : 'More'
17509                                 }
17510
17511
17512                             ]
17513                         },
17514                         {
17515                             cls: 'ui-resizable-handle ui-resizable-e',
17516                             html : '&nbsp;&nbsp;&nbsp'
17517                         }
17518
17519                     ]
17520                 };
17521
17522                 var ctr = _this.el.select('.fc-event-container',true).first();
17523                 var cg = ctr.createChild(cfg);
17524
17525                 var sbox = c.select('.fc-day-content',true).first().getBox();
17526                 var ebox = c.select('.fc-day-content',true).first().getBox();
17527                 //Roo.log(cg);
17528                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17529                 cg.setWidth(ebox.right - sbox.x -2);
17530
17531                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17532                 
17533             }
17534             
17535         });
17536         
17537         
17538         
17539     },
17540     
17541     onEventEnter: function (e, el,event,d) {
17542         this.fireEvent('evententer', this, el, event);
17543     },
17544     
17545     onEventLeave: function (e, el,event,d) {
17546         this.fireEvent('eventleave', this, el, event);
17547     },
17548     
17549     onEventClick: function (e, el,event,d) {
17550         this.fireEvent('eventclick', this, el, event);
17551     },
17552     
17553     onMonthChange: function () {
17554         this.store.load();
17555     },
17556     
17557     onMoreEventClick: function(e, el, more)
17558     {
17559         var _this = this;
17560         
17561         this.calpopover.placement = 'right';
17562         this.calpopover.setTitle('More');
17563         
17564         this.calpopover.setContent('');
17565         
17566         var ctr = this.calpopover.el.select('.popover-content', true).first();
17567         
17568         Roo.each(more, function(m){
17569             var cfg = {
17570                 cls : 'fc-event-hori fc-event-draggable',
17571                 html : m.title
17572             };
17573             var cg = ctr.createChild(cfg);
17574             
17575             cg.on('click', _this.onEventClick, _this, m);
17576         });
17577         
17578         this.calpopover.show(el);
17579         
17580         
17581     },
17582     
17583     onLoad: function () 
17584     {   
17585         this.calevents = [];
17586         var cal = this;
17587         
17588         if(this.store.getCount() > 0){
17589             this.store.data.each(function(d){
17590                cal.addItem({
17591                     id : d.data.id,
17592                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17593                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17594                     time : d.data.start_time,
17595                     title : d.data.title,
17596                     description : d.data.description,
17597                     venue : d.data.venue
17598                 });
17599             });
17600         }
17601         
17602         this.renderEvents();
17603         
17604         if(this.calevents.length && this.loadMask){
17605             this.maskEl.hide();
17606         }
17607     },
17608     
17609     onBeforeLoad: function()
17610     {
17611         this.clearEvents();
17612         if(this.loadMask){
17613             this.maskEl.show();
17614         }
17615     }
17616 });
17617
17618  
17619  /*
17620  * - LGPL
17621  *
17622  * element
17623  * 
17624  */
17625
17626 /**
17627  * @class Roo.bootstrap.Popover
17628  * @extends Roo.bootstrap.Component
17629  * Bootstrap Popover class
17630  * @cfg {String} html contents of the popover   (or false to use children..)
17631  * @cfg {String} title of popover (or false to hide)
17632  * @cfg {String} placement how it is placed
17633  * @cfg {String} trigger click || hover (or false to trigger manually)
17634  * @cfg {String} over what (parent or false to trigger manually.)
17635  * @cfg {Number} delay - delay before showing
17636  
17637  * @constructor
17638  * Create a new Popover
17639  * @param {Object} config The config object
17640  */
17641
17642 Roo.bootstrap.Popover = function(config){
17643     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17644     
17645     this.addEvents({
17646         // raw events
17647          /**
17648          * @event show
17649          * After the popover show
17650          * 
17651          * @param {Roo.bootstrap.Popover} this
17652          */
17653         "show" : true,
17654         /**
17655          * @event hide
17656          * After the popover hide
17657          * 
17658          * @param {Roo.bootstrap.Popover} this
17659          */
17660         "hide" : true
17661     });
17662 };
17663
17664 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17665     
17666     title: 'Fill in a title',
17667     html: false,
17668     
17669     placement : 'right',
17670     trigger : 'hover', // hover
17671     
17672     delay : 0,
17673     
17674     over: 'parent',
17675     
17676     can_build_overlaid : false,
17677     
17678     getChildContainer : function()
17679     {
17680         return this.el.select('.popover-content',true).first();
17681     },
17682     
17683     getAutoCreate : function(){
17684          
17685         var cfg = {
17686            cls : 'popover roo-dynamic',
17687            style: 'display:block',
17688            cn : [
17689                 {
17690                     cls : 'arrow'
17691                 },
17692                 {
17693                     cls : 'popover-inner',
17694                     cn : [
17695                         {
17696                             tag: 'h3',
17697                             cls: 'popover-title popover-header',
17698                             html : this.title
17699                         },
17700                         {
17701                             cls : 'popover-content popover-body',
17702                             html : this.html
17703                         }
17704                     ]
17705                     
17706                 }
17707            ]
17708         };
17709         
17710         return cfg;
17711     },
17712     setTitle: function(str)
17713     {
17714         this.title = str;
17715         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17716     },
17717     setContent: function(str)
17718     {
17719         this.html = str;
17720         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17721     },
17722     // as it get's added to the bottom of the page.
17723     onRender : function(ct, position)
17724     {
17725         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17726         if(!this.el){
17727             var cfg = Roo.apply({},  this.getAutoCreate());
17728             cfg.id = Roo.id();
17729             
17730             if (this.cls) {
17731                 cfg.cls += ' ' + this.cls;
17732             }
17733             if (this.style) {
17734                 cfg.style = this.style;
17735             }
17736             //Roo.log("adding to ");
17737             this.el = Roo.get(document.body).createChild(cfg, position);
17738 //            Roo.log(this.el);
17739         }
17740         this.initEvents();
17741     },
17742     
17743     initEvents : function()
17744     {
17745         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17746         this.el.enableDisplayMode('block');
17747         this.el.hide();
17748         if (this.over === false) {
17749             return; 
17750         }
17751         if (this.triggers === false) {
17752             return;
17753         }
17754         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17755         var triggers = this.trigger ? this.trigger.split(' ') : [];
17756         Roo.each(triggers, function(trigger) {
17757         
17758             if (trigger == 'click') {
17759                 on_el.on('click', this.toggle, this);
17760             } else if (trigger != 'manual') {
17761                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17762                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17763       
17764                 on_el.on(eventIn  ,this.enter, this);
17765                 on_el.on(eventOut, this.leave, this);
17766             }
17767         }, this);
17768         
17769     },
17770     
17771     
17772     // private
17773     timeout : null,
17774     hoverState : null,
17775     
17776     toggle : function () {
17777         this.hoverState == 'in' ? this.leave() : this.enter();
17778     },
17779     
17780     enter : function () {
17781         
17782         clearTimeout(this.timeout);
17783     
17784         this.hoverState = 'in';
17785     
17786         if (!this.delay || !this.delay.show) {
17787             this.show();
17788             return;
17789         }
17790         var _t = this;
17791         this.timeout = setTimeout(function () {
17792             if (_t.hoverState == 'in') {
17793                 _t.show();
17794             }
17795         }, this.delay.show)
17796     },
17797     
17798     leave : function() {
17799         clearTimeout(this.timeout);
17800     
17801         this.hoverState = 'out';
17802     
17803         if (!this.delay || !this.delay.hide) {
17804             this.hide();
17805             return;
17806         }
17807         var _t = this;
17808         this.timeout = setTimeout(function () {
17809             if (_t.hoverState == 'out') {
17810                 _t.hide();
17811             }
17812         }, this.delay.hide)
17813     },
17814     
17815     show : function (on_el)
17816     {
17817         if (!on_el) {
17818             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17819         }
17820         
17821         // set content.
17822         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17823         if (this.html !== false) {
17824             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17825         }
17826         this.el.removeClass([
17827             'fade','top','bottom', 'left', 'right','in',
17828             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17829         ]);
17830         if (!this.title.length) {
17831             this.el.select('.popover-title',true).hide();
17832         }
17833         
17834         var placement = typeof this.placement == 'function' ?
17835             this.placement.call(this, this.el, on_el) :
17836             this.placement;
17837             
17838         var autoToken = /\s?auto?\s?/i;
17839         var autoPlace = autoToken.test(placement);
17840         if (autoPlace) {
17841             placement = placement.replace(autoToken, '') || 'top';
17842         }
17843         
17844         //this.el.detach()
17845         //this.el.setXY([0,0]);
17846         this.el.show();
17847         this.el.dom.style.display='block';
17848         this.el.addClass(placement);
17849         
17850         //this.el.appendTo(on_el);
17851         
17852         var p = this.getPosition();
17853         var box = this.el.getBox();
17854         
17855         if (autoPlace) {
17856             // fixme..
17857         }
17858         var align = Roo.bootstrap.Popover.alignment[placement];
17859         
17860 //        Roo.log(align);
17861         this.el.alignTo(on_el, align[0],align[1]);
17862         //var arrow = this.el.select('.arrow',true).first();
17863         //arrow.set(align[2], 
17864         
17865         this.el.addClass('in');
17866         
17867         
17868         if (this.el.hasClass('fade')) {
17869             // fade it?
17870         }
17871         
17872         this.hoverState = 'in';
17873         
17874         this.fireEvent('show', this);
17875         
17876     },
17877     hide : function()
17878     {
17879         this.el.setXY([0,0]);
17880         this.el.removeClass('in');
17881         this.el.hide();
17882         this.hoverState = null;
17883         
17884         this.fireEvent('hide', this);
17885     }
17886     
17887 });
17888
17889 Roo.bootstrap.Popover.alignment = {
17890     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17891     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17892     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17893     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17894 };
17895
17896  /*
17897  * - LGPL
17898  *
17899  * Progress
17900  * 
17901  */
17902
17903 /**
17904  * @class Roo.bootstrap.Progress
17905  * @extends Roo.bootstrap.Component
17906  * Bootstrap Progress class
17907  * @cfg {Boolean} striped striped of the progress bar
17908  * @cfg {Boolean} active animated of the progress bar
17909  * 
17910  * 
17911  * @constructor
17912  * Create a new Progress
17913  * @param {Object} config The config object
17914  */
17915
17916 Roo.bootstrap.Progress = function(config){
17917     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17918 };
17919
17920 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17921     
17922     striped : false,
17923     active: false,
17924     
17925     getAutoCreate : function(){
17926         var cfg = {
17927             tag: 'div',
17928             cls: 'progress'
17929         };
17930         
17931         
17932         if(this.striped){
17933             cfg.cls += ' progress-striped';
17934         }
17935       
17936         if(this.active){
17937             cfg.cls += ' active';
17938         }
17939         
17940         
17941         return cfg;
17942     }
17943    
17944 });
17945
17946  
17947
17948  /*
17949  * - LGPL
17950  *
17951  * ProgressBar
17952  * 
17953  */
17954
17955 /**
17956  * @class Roo.bootstrap.ProgressBar
17957  * @extends Roo.bootstrap.Component
17958  * Bootstrap ProgressBar class
17959  * @cfg {Number} aria_valuenow aria-value now
17960  * @cfg {Number} aria_valuemin aria-value min
17961  * @cfg {Number} aria_valuemax aria-value max
17962  * @cfg {String} label label for the progress bar
17963  * @cfg {String} panel (success | info | warning | danger )
17964  * @cfg {String} role role of the progress bar
17965  * @cfg {String} sr_only text
17966  * 
17967  * 
17968  * @constructor
17969  * Create a new ProgressBar
17970  * @param {Object} config The config object
17971  */
17972
17973 Roo.bootstrap.ProgressBar = function(config){
17974     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17975 };
17976
17977 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17978     
17979     aria_valuenow : 0,
17980     aria_valuemin : 0,
17981     aria_valuemax : 100,
17982     label : false,
17983     panel : false,
17984     role : false,
17985     sr_only: false,
17986     
17987     getAutoCreate : function()
17988     {
17989         
17990         var cfg = {
17991             tag: 'div',
17992             cls: 'progress-bar',
17993             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17994         };
17995         
17996         if(this.sr_only){
17997             cfg.cn = {
17998                 tag: 'span',
17999                 cls: 'sr-only',
18000                 html: this.sr_only
18001             }
18002         }
18003         
18004         if(this.role){
18005             cfg.role = this.role;
18006         }
18007         
18008         if(this.aria_valuenow){
18009             cfg['aria-valuenow'] = this.aria_valuenow;
18010         }
18011         
18012         if(this.aria_valuemin){
18013             cfg['aria-valuemin'] = this.aria_valuemin;
18014         }
18015         
18016         if(this.aria_valuemax){
18017             cfg['aria-valuemax'] = this.aria_valuemax;
18018         }
18019         
18020         if(this.label && !this.sr_only){
18021             cfg.html = this.label;
18022         }
18023         
18024         if(this.panel){
18025             cfg.cls += ' progress-bar-' + this.panel;
18026         }
18027         
18028         return cfg;
18029     },
18030     
18031     update : function(aria_valuenow)
18032     {
18033         this.aria_valuenow = aria_valuenow;
18034         
18035         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18036     }
18037    
18038 });
18039
18040  
18041
18042  /*
18043  * - LGPL
18044  *
18045  * column
18046  * 
18047  */
18048
18049 /**
18050  * @class Roo.bootstrap.TabGroup
18051  * @extends Roo.bootstrap.Column
18052  * Bootstrap Column class
18053  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18054  * @cfg {Boolean} carousel true to make the group behave like a carousel
18055  * @cfg {Boolean} bullets show bullets for the panels
18056  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18057  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18058  * @cfg {Boolean} showarrow (true|false) show arrow default true
18059  * 
18060  * @constructor
18061  * Create a new TabGroup
18062  * @param {Object} config The config object
18063  */
18064
18065 Roo.bootstrap.TabGroup = function(config){
18066     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18067     if (!this.navId) {
18068         this.navId = Roo.id();
18069     }
18070     this.tabs = [];
18071     Roo.bootstrap.TabGroup.register(this);
18072     
18073 };
18074
18075 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18076     
18077     carousel : false,
18078     transition : false,
18079     bullets : 0,
18080     timer : 0,
18081     autoslide : false,
18082     slideFn : false,
18083     slideOnTouch : false,
18084     showarrow : true,
18085     
18086     getAutoCreate : function()
18087     {
18088         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18089         
18090         cfg.cls += ' tab-content';
18091         
18092         if (this.carousel) {
18093             cfg.cls += ' carousel slide';
18094             
18095             cfg.cn = [{
18096                cls : 'carousel-inner',
18097                cn : []
18098             }];
18099         
18100             if(this.bullets  && !Roo.isTouch){
18101                 
18102                 var bullets = {
18103                     cls : 'carousel-bullets',
18104                     cn : []
18105                 };
18106                
18107                 if(this.bullets_cls){
18108                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18109                 }
18110                 
18111                 bullets.cn.push({
18112                     cls : 'clear'
18113                 });
18114                 
18115                 cfg.cn[0].cn.push(bullets);
18116             }
18117             
18118             if(this.showarrow){
18119                 cfg.cn[0].cn.push({
18120                     tag : 'div',
18121                     class : 'carousel-arrow',
18122                     cn : [
18123                         {
18124                             tag : 'div',
18125                             class : 'carousel-prev',
18126                             cn : [
18127                                 {
18128                                     tag : 'i',
18129                                     class : 'fa fa-chevron-left'
18130                                 }
18131                             ]
18132                         },
18133                         {
18134                             tag : 'div',
18135                             class : 'carousel-next',
18136                             cn : [
18137                                 {
18138                                     tag : 'i',
18139                                     class : 'fa fa-chevron-right'
18140                                 }
18141                             ]
18142                         }
18143                     ]
18144                 });
18145             }
18146             
18147         }
18148         
18149         return cfg;
18150     },
18151     
18152     initEvents:  function()
18153     {
18154 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18155 //            this.el.on("touchstart", this.onTouchStart, this);
18156 //        }
18157         
18158         if(this.autoslide){
18159             var _this = this;
18160             
18161             this.slideFn = window.setInterval(function() {
18162                 _this.showPanelNext();
18163             }, this.timer);
18164         }
18165         
18166         if(this.showarrow){
18167             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18168             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18169         }
18170         
18171         
18172     },
18173     
18174 //    onTouchStart : function(e, el, o)
18175 //    {
18176 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18177 //            return;
18178 //        }
18179 //        
18180 //        this.showPanelNext();
18181 //    },
18182     
18183     
18184     getChildContainer : function()
18185     {
18186         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18187     },
18188     
18189     /**
18190     * register a Navigation item
18191     * @param {Roo.bootstrap.NavItem} the navitem to add
18192     */
18193     register : function(item)
18194     {
18195         this.tabs.push( item);
18196         item.navId = this.navId; // not really needed..
18197         this.addBullet();
18198     
18199     },
18200     
18201     getActivePanel : function()
18202     {
18203         var r = false;
18204         Roo.each(this.tabs, function(t) {
18205             if (t.active) {
18206                 r = t;
18207                 return false;
18208             }
18209             return null;
18210         });
18211         return r;
18212         
18213     },
18214     getPanelByName : function(n)
18215     {
18216         var r = false;
18217         Roo.each(this.tabs, function(t) {
18218             if (t.tabId == n) {
18219                 r = t;
18220                 return false;
18221             }
18222             return null;
18223         });
18224         return r;
18225     },
18226     indexOfPanel : function(p)
18227     {
18228         var r = false;
18229         Roo.each(this.tabs, function(t,i) {
18230             if (t.tabId == p.tabId) {
18231                 r = i;
18232                 return false;
18233             }
18234             return null;
18235         });
18236         return r;
18237     },
18238     /**
18239      * show a specific panel
18240      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18241      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18242      */
18243     showPanel : function (pan)
18244     {
18245         if(this.transition || typeof(pan) == 'undefined'){
18246             Roo.log("waiting for the transitionend");
18247             return;
18248         }
18249         
18250         if (typeof(pan) == 'number') {
18251             pan = this.tabs[pan];
18252         }
18253         
18254         if (typeof(pan) == 'string') {
18255             pan = this.getPanelByName(pan);
18256         }
18257         
18258         var cur = this.getActivePanel();
18259         
18260         if(!pan || !cur){
18261             Roo.log('pan or acitve pan is undefined');
18262             return false;
18263         }
18264         
18265         if (pan.tabId == this.getActivePanel().tabId) {
18266             return true;
18267         }
18268         
18269         if (false === cur.fireEvent('beforedeactivate')) {
18270             return false;
18271         }
18272         
18273         if(this.bullets > 0 && !Roo.isTouch){
18274             this.setActiveBullet(this.indexOfPanel(pan));
18275         }
18276         
18277         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18278             
18279             this.transition = true;
18280             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18281             var lr = dir == 'next' ? 'left' : 'right';
18282             pan.el.addClass(dir); // or prev
18283             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18284             cur.el.addClass(lr); // or right
18285             pan.el.addClass(lr);
18286             
18287             var _this = this;
18288             cur.el.on('transitionend', function() {
18289                 Roo.log("trans end?");
18290                 
18291                 pan.el.removeClass([lr,dir]);
18292                 pan.setActive(true);
18293                 
18294                 cur.el.removeClass([lr]);
18295                 cur.setActive(false);
18296                 
18297                 _this.transition = false;
18298                 
18299             }, this, { single:  true } );
18300             
18301             return true;
18302         }
18303         
18304         cur.setActive(false);
18305         pan.setActive(true);
18306         
18307         return true;
18308         
18309     },
18310     showPanelNext : function()
18311     {
18312         var i = this.indexOfPanel(this.getActivePanel());
18313         
18314         if (i >= this.tabs.length - 1 && !this.autoslide) {
18315             return;
18316         }
18317         
18318         if (i >= this.tabs.length - 1 && this.autoslide) {
18319             i = -1;
18320         }
18321         
18322         this.showPanel(this.tabs[i+1]);
18323     },
18324     
18325     showPanelPrev : function()
18326     {
18327         var i = this.indexOfPanel(this.getActivePanel());
18328         
18329         if (i  < 1 && !this.autoslide) {
18330             return;
18331         }
18332         
18333         if (i < 1 && this.autoslide) {
18334             i = this.tabs.length;
18335         }
18336         
18337         this.showPanel(this.tabs[i-1]);
18338     },
18339     
18340     
18341     addBullet: function()
18342     {
18343         if(!this.bullets || Roo.isTouch){
18344             return;
18345         }
18346         var ctr = this.el.select('.carousel-bullets',true).first();
18347         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18348         var bullet = ctr.createChild({
18349             cls : 'bullet bullet-' + i
18350         },ctr.dom.lastChild);
18351         
18352         
18353         var _this = this;
18354         
18355         bullet.on('click', (function(e, el, o, ii, t){
18356
18357             e.preventDefault();
18358
18359             this.showPanel(ii);
18360
18361             if(this.autoslide && this.slideFn){
18362                 clearInterval(this.slideFn);
18363                 this.slideFn = window.setInterval(function() {
18364                     _this.showPanelNext();
18365                 }, this.timer);
18366             }
18367
18368         }).createDelegate(this, [i, bullet], true));
18369                 
18370         
18371     },
18372      
18373     setActiveBullet : function(i)
18374     {
18375         if(Roo.isTouch){
18376             return;
18377         }
18378         
18379         Roo.each(this.el.select('.bullet', true).elements, function(el){
18380             el.removeClass('selected');
18381         });
18382
18383         var bullet = this.el.select('.bullet-' + i, true).first();
18384         
18385         if(!bullet){
18386             return;
18387         }
18388         
18389         bullet.addClass('selected');
18390     }
18391     
18392     
18393   
18394 });
18395
18396  
18397
18398  
18399  
18400 Roo.apply(Roo.bootstrap.TabGroup, {
18401     
18402     groups: {},
18403      /**
18404     * register a Navigation Group
18405     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18406     */
18407     register : function(navgrp)
18408     {
18409         this.groups[navgrp.navId] = navgrp;
18410         
18411     },
18412     /**
18413     * fetch a Navigation Group based on the navigation ID
18414     * if one does not exist , it will get created.
18415     * @param {string} the navgroup to add
18416     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18417     */
18418     get: function(navId) {
18419         if (typeof(this.groups[navId]) == 'undefined') {
18420             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18421         }
18422         return this.groups[navId] ;
18423     }
18424     
18425     
18426     
18427 });
18428
18429  /*
18430  * - LGPL
18431  *
18432  * TabPanel
18433  * 
18434  */
18435
18436 /**
18437  * @class Roo.bootstrap.TabPanel
18438  * @extends Roo.bootstrap.Component
18439  * Bootstrap TabPanel class
18440  * @cfg {Boolean} active panel active
18441  * @cfg {String} html panel content
18442  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18443  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18444  * @cfg {String} href click to link..
18445  * 
18446  * 
18447  * @constructor
18448  * Create a new TabPanel
18449  * @param {Object} config The config object
18450  */
18451
18452 Roo.bootstrap.TabPanel = function(config){
18453     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18454     this.addEvents({
18455         /**
18456              * @event changed
18457              * Fires when the active status changes
18458              * @param {Roo.bootstrap.TabPanel} this
18459              * @param {Boolean} state the new state
18460             
18461          */
18462         'changed': true,
18463         /**
18464              * @event beforedeactivate
18465              * Fires before a tab is de-activated - can be used to do validation on a form.
18466              * @param {Roo.bootstrap.TabPanel} this
18467              * @return {Boolean} false if there is an error
18468             
18469          */
18470         'beforedeactivate': true
18471      });
18472     
18473     this.tabId = this.tabId || Roo.id();
18474   
18475 };
18476
18477 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18478     
18479     active: false,
18480     html: false,
18481     tabId: false,
18482     navId : false,
18483     href : '',
18484     
18485     getAutoCreate : function(){
18486         var cfg = {
18487             tag: 'div',
18488             // item is needed for carousel - not sure if it has any effect otherwise
18489             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18490             html: this.html || ''
18491         };
18492         
18493         if(this.active){
18494             cfg.cls += ' active';
18495         }
18496         
18497         if(this.tabId){
18498             cfg.tabId = this.tabId;
18499         }
18500         
18501         
18502         return cfg;
18503     },
18504     
18505     initEvents:  function()
18506     {
18507         var p = this.parent();
18508         
18509         this.navId = this.navId || p.navId;
18510         
18511         if (typeof(this.navId) != 'undefined') {
18512             // not really needed.. but just in case.. parent should be a NavGroup.
18513             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18514             
18515             tg.register(this);
18516             
18517             var i = tg.tabs.length - 1;
18518             
18519             if(this.active && tg.bullets > 0 && i < tg.bullets){
18520                 tg.setActiveBullet(i);
18521             }
18522         }
18523         
18524         this.el.on('click', this.onClick, this);
18525         
18526         if(Roo.isTouch){
18527             this.el.on("touchstart", this.onTouchStart, this);
18528             this.el.on("touchmove", this.onTouchMove, this);
18529             this.el.on("touchend", this.onTouchEnd, this);
18530         }
18531         
18532     },
18533     
18534     onRender : function(ct, position)
18535     {
18536         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18537     },
18538     
18539     setActive : function(state)
18540     {
18541         Roo.log("panel - set active " + this.tabId + "=" + state);
18542         
18543         this.active = state;
18544         if (!state) {
18545             this.el.removeClass('active');
18546             
18547         } else  if (!this.el.hasClass('active')) {
18548             this.el.addClass('active');
18549         }
18550         
18551         this.fireEvent('changed', this, state);
18552     },
18553     
18554     onClick : function(e)
18555     {
18556         e.preventDefault();
18557         
18558         if(!this.href.length){
18559             return;
18560         }
18561         
18562         window.location.href = this.href;
18563     },
18564     
18565     startX : 0,
18566     startY : 0,
18567     endX : 0,
18568     endY : 0,
18569     swiping : false,
18570     
18571     onTouchStart : function(e)
18572     {
18573         this.swiping = false;
18574         
18575         this.startX = e.browserEvent.touches[0].clientX;
18576         this.startY = e.browserEvent.touches[0].clientY;
18577     },
18578     
18579     onTouchMove : function(e)
18580     {
18581         this.swiping = true;
18582         
18583         this.endX = e.browserEvent.touches[0].clientX;
18584         this.endY = e.browserEvent.touches[0].clientY;
18585     },
18586     
18587     onTouchEnd : function(e)
18588     {
18589         if(!this.swiping){
18590             this.onClick(e);
18591             return;
18592         }
18593         
18594         var tabGroup = this.parent();
18595         
18596         if(this.endX > this.startX){ // swiping right
18597             tabGroup.showPanelPrev();
18598             return;
18599         }
18600         
18601         if(this.startX > this.endX){ // swiping left
18602             tabGroup.showPanelNext();
18603             return;
18604         }
18605     }
18606     
18607     
18608 });
18609  
18610
18611  
18612
18613  /*
18614  * - LGPL
18615  *
18616  * DateField
18617  * 
18618  */
18619
18620 /**
18621  * @class Roo.bootstrap.DateField
18622  * @extends Roo.bootstrap.Input
18623  * Bootstrap DateField class
18624  * @cfg {Number} weekStart default 0
18625  * @cfg {String} viewMode default empty, (months|years)
18626  * @cfg {String} minViewMode default empty, (months|years)
18627  * @cfg {Number} startDate default -Infinity
18628  * @cfg {Number} endDate default Infinity
18629  * @cfg {Boolean} todayHighlight default false
18630  * @cfg {Boolean} todayBtn default false
18631  * @cfg {Boolean} calendarWeeks default false
18632  * @cfg {Object} daysOfWeekDisabled default empty
18633  * @cfg {Boolean} singleMode default false (true | false)
18634  * 
18635  * @cfg {Boolean} keyboardNavigation default true
18636  * @cfg {String} language default en
18637  * 
18638  * @constructor
18639  * Create a new DateField
18640  * @param {Object} config The config object
18641  */
18642
18643 Roo.bootstrap.DateField = function(config){
18644     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18645      this.addEvents({
18646             /**
18647              * @event show
18648              * Fires when this field show.
18649              * @param {Roo.bootstrap.DateField} this
18650              * @param {Mixed} date The date value
18651              */
18652             show : true,
18653             /**
18654              * @event show
18655              * Fires when this field hide.
18656              * @param {Roo.bootstrap.DateField} this
18657              * @param {Mixed} date The date value
18658              */
18659             hide : true,
18660             /**
18661              * @event select
18662              * Fires when select a date.
18663              * @param {Roo.bootstrap.DateField} this
18664              * @param {Mixed} date The date value
18665              */
18666             select : true,
18667             /**
18668              * @event beforeselect
18669              * Fires when before select a date.
18670              * @param {Roo.bootstrap.DateField} this
18671              * @param {Mixed} date The date value
18672              */
18673             beforeselect : true
18674         });
18675 };
18676
18677 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18678     
18679     /**
18680      * @cfg {String} format
18681      * The default date format string which can be overriden for localization support.  The format must be
18682      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18683      */
18684     format : "m/d/y",
18685     /**
18686      * @cfg {String} altFormats
18687      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18688      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18689      */
18690     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18691     
18692     weekStart : 0,
18693     
18694     viewMode : '',
18695     
18696     minViewMode : '',
18697     
18698     todayHighlight : false,
18699     
18700     todayBtn: false,
18701     
18702     language: 'en',
18703     
18704     keyboardNavigation: true,
18705     
18706     calendarWeeks: false,
18707     
18708     startDate: -Infinity,
18709     
18710     endDate: Infinity,
18711     
18712     daysOfWeekDisabled: [],
18713     
18714     _events: [],
18715     
18716     singleMode : false,
18717     
18718     UTCDate: function()
18719     {
18720         return new Date(Date.UTC.apply(Date, arguments));
18721     },
18722     
18723     UTCToday: function()
18724     {
18725         var today = new Date();
18726         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18727     },
18728     
18729     getDate: function() {
18730             var d = this.getUTCDate();
18731             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18732     },
18733     
18734     getUTCDate: function() {
18735             return this.date;
18736     },
18737     
18738     setDate: function(d) {
18739             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18740     },
18741     
18742     setUTCDate: function(d) {
18743             this.date = d;
18744             this.setValue(this.formatDate(this.date));
18745     },
18746         
18747     onRender: function(ct, position)
18748     {
18749         
18750         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18751         
18752         this.language = this.language || 'en';
18753         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18754         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18755         
18756         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18757         this.format = this.format || 'm/d/y';
18758         this.isInline = false;
18759         this.isInput = true;
18760         this.component = this.el.select('.add-on', true).first() || false;
18761         this.component = (this.component && this.component.length === 0) ? false : this.component;
18762         this.hasInput = this.component && this.inputEl().length;
18763         
18764         if (typeof(this.minViewMode === 'string')) {
18765             switch (this.minViewMode) {
18766                 case 'months':
18767                     this.minViewMode = 1;
18768                     break;
18769                 case 'years':
18770                     this.minViewMode = 2;
18771                     break;
18772                 default:
18773                     this.minViewMode = 0;
18774                     break;
18775             }
18776         }
18777         
18778         if (typeof(this.viewMode === 'string')) {
18779             switch (this.viewMode) {
18780                 case 'months':
18781                     this.viewMode = 1;
18782                     break;
18783                 case 'years':
18784                     this.viewMode = 2;
18785                     break;
18786                 default:
18787                     this.viewMode = 0;
18788                     break;
18789             }
18790         }
18791                 
18792         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18793         
18794 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18795         
18796         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18797         
18798         this.picker().on('mousedown', this.onMousedown, this);
18799         this.picker().on('click', this.onClick, this);
18800         
18801         this.picker().addClass('datepicker-dropdown');
18802         
18803         this.startViewMode = this.viewMode;
18804         
18805         if(this.singleMode){
18806             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18807                 v.setVisibilityMode(Roo.Element.DISPLAY);
18808                 v.hide();
18809             });
18810             
18811             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18812                 v.setStyle('width', '189px');
18813             });
18814         }
18815         
18816         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18817             if(!this.calendarWeeks){
18818                 v.remove();
18819                 return;
18820             }
18821             
18822             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18823             v.attr('colspan', function(i, val){
18824                 return parseInt(val) + 1;
18825             });
18826         });
18827                         
18828         
18829         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18830         
18831         this.setStartDate(this.startDate);
18832         this.setEndDate(this.endDate);
18833         
18834         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18835         
18836         this.fillDow();
18837         this.fillMonths();
18838         this.update();
18839         this.showMode();
18840         
18841         if(this.isInline) {
18842             this.showPopup();
18843         }
18844     },
18845     
18846     picker : function()
18847     {
18848         return this.pickerEl;
18849 //        return this.el.select('.datepicker', true).first();
18850     },
18851     
18852     fillDow: function()
18853     {
18854         var dowCnt = this.weekStart;
18855         
18856         var dow = {
18857             tag: 'tr',
18858             cn: [
18859                 
18860             ]
18861         };
18862         
18863         if(this.calendarWeeks){
18864             dow.cn.push({
18865                 tag: 'th',
18866                 cls: 'cw',
18867                 html: '&nbsp;'
18868             })
18869         }
18870         
18871         while (dowCnt < this.weekStart + 7) {
18872             dow.cn.push({
18873                 tag: 'th',
18874                 cls: 'dow',
18875                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18876             });
18877         }
18878         
18879         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18880     },
18881     
18882     fillMonths: function()
18883     {    
18884         var i = 0;
18885         var months = this.picker().select('>.datepicker-months td', true).first();
18886         
18887         months.dom.innerHTML = '';
18888         
18889         while (i < 12) {
18890             var month = {
18891                 tag: 'span',
18892                 cls: 'month',
18893                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18894             };
18895             
18896             months.createChild(month);
18897         }
18898         
18899     },
18900     
18901     update: function()
18902     {
18903         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;
18904         
18905         if (this.date < this.startDate) {
18906             this.viewDate = new Date(this.startDate);
18907         } else if (this.date > this.endDate) {
18908             this.viewDate = new Date(this.endDate);
18909         } else {
18910             this.viewDate = new Date(this.date);
18911         }
18912         
18913         this.fill();
18914     },
18915     
18916     fill: function() 
18917     {
18918         var d = new Date(this.viewDate),
18919                 year = d.getUTCFullYear(),
18920                 month = d.getUTCMonth(),
18921                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18922                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18923                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18924                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18925                 currentDate = this.date && this.date.valueOf(),
18926                 today = this.UTCToday();
18927         
18928         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18929         
18930 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18931         
18932 //        this.picker.select('>tfoot th.today').
18933 //                                              .text(dates[this.language].today)
18934 //                                              .toggle(this.todayBtn !== false);
18935     
18936         this.updateNavArrows();
18937         this.fillMonths();
18938                                                 
18939         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18940         
18941         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18942          
18943         prevMonth.setUTCDate(day);
18944         
18945         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18946         
18947         var nextMonth = new Date(prevMonth);
18948         
18949         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18950         
18951         nextMonth = nextMonth.valueOf();
18952         
18953         var fillMonths = false;
18954         
18955         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18956         
18957         while(prevMonth.valueOf() <= nextMonth) {
18958             var clsName = '';
18959             
18960             if (prevMonth.getUTCDay() === this.weekStart) {
18961                 if(fillMonths){
18962                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18963                 }
18964                     
18965                 fillMonths = {
18966                     tag: 'tr',
18967                     cn: []
18968                 };
18969                 
18970                 if(this.calendarWeeks){
18971                     // ISO 8601: First week contains first thursday.
18972                     // ISO also states week starts on Monday, but we can be more abstract here.
18973                     var
18974                     // Start of current week: based on weekstart/current date
18975                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18976                     // Thursday of this week
18977                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18978                     // First Thursday of year, year from thursday
18979                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18980                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18981                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18982                     
18983                     fillMonths.cn.push({
18984                         tag: 'td',
18985                         cls: 'cw',
18986                         html: calWeek
18987                     });
18988                 }
18989             }
18990             
18991             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18992                 clsName += ' old';
18993             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18994                 clsName += ' new';
18995             }
18996             if (this.todayHighlight &&
18997                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18998                 prevMonth.getUTCMonth() == today.getMonth() &&
18999                 prevMonth.getUTCDate() == today.getDate()) {
19000                 clsName += ' today';
19001             }
19002             
19003             if (currentDate && prevMonth.valueOf() === currentDate) {
19004                 clsName += ' active';
19005             }
19006             
19007             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19008                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19009                     clsName += ' disabled';
19010             }
19011             
19012             fillMonths.cn.push({
19013                 tag: 'td',
19014                 cls: 'day ' + clsName,
19015                 html: prevMonth.getDate()
19016             });
19017             
19018             prevMonth.setDate(prevMonth.getDate()+1);
19019         }
19020           
19021         var currentYear = this.date && this.date.getUTCFullYear();
19022         var currentMonth = this.date && this.date.getUTCMonth();
19023         
19024         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19025         
19026         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19027             v.removeClass('active');
19028             
19029             if(currentYear === year && k === currentMonth){
19030                 v.addClass('active');
19031             }
19032             
19033             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19034                 v.addClass('disabled');
19035             }
19036             
19037         });
19038         
19039         
19040         year = parseInt(year/10, 10) * 10;
19041         
19042         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19043         
19044         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19045         
19046         year -= 1;
19047         for (var i = -1; i < 11; i++) {
19048             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19049                 tag: 'span',
19050                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19051                 html: year
19052             });
19053             
19054             year += 1;
19055         }
19056     },
19057     
19058     showMode: function(dir) 
19059     {
19060         if (dir) {
19061             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19062         }
19063         
19064         Roo.each(this.picker().select('>div',true).elements, function(v){
19065             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19066             v.hide();
19067         });
19068         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19069     },
19070     
19071     place: function()
19072     {
19073         if(this.isInline) {
19074             return;
19075         }
19076         
19077         this.picker().removeClass(['bottom', 'top']);
19078         
19079         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19080             /*
19081              * place to the top of element!
19082              *
19083              */
19084             
19085             this.picker().addClass('top');
19086             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19087             
19088             return;
19089         }
19090         
19091         this.picker().addClass('bottom');
19092         
19093         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19094     },
19095     
19096     parseDate : function(value)
19097     {
19098         if(!value || value instanceof Date){
19099             return value;
19100         }
19101         var v = Date.parseDate(value, this.format);
19102         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19103             v = Date.parseDate(value, 'Y-m-d');
19104         }
19105         if(!v && this.altFormats){
19106             if(!this.altFormatsArray){
19107                 this.altFormatsArray = this.altFormats.split("|");
19108             }
19109             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19110                 v = Date.parseDate(value, this.altFormatsArray[i]);
19111             }
19112         }
19113         return v;
19114     },
19115     
19116     formatDate : function(date, fmt)
19117     {   
19118         return (!date || !(date instanceof Date)) ?
19119         date : date.dateFormat(fmt || this.format);
19120     },
19121     
19122     onFocus : function()
19123     {
19124         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19125         this.showPopup();
19126     },
19127     
19128     onBlur : function()
19129     {
19130         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19131         
19132         var d = this.inputEl().getValue();
19133         
19134         this.setValue(d);
19135                 
19136         this.hidePopup();
19137     },
19138     
19139     showPopup : function()
19140     {
19141         this.picker().show();
19142         this.update();
19143         this.place();
19144         
19145         this.fireEvent('showpopup', this, this.date);
19146     },
19147     
19148     hidePopup : function()
19149     {
19150         if(this.isInline) {
19151             return;
19152         }
19153         this.picker().hide();
19154         this.viewMode = this.startViewMode;
19155         this.showMode();
19156         
19157         this.fireEvent('hidepopup', this, this.date);
19158         
19159     },
19160     
19161     onMousedown: function(e)
19162     {
19163         e.stopPropagation();
19164         e.preventDefault();
19165     },
19166     
19167     keyup: function(e)
19168     {
19169         Roo.bootstrap.DateField.superclass.keyup.call(this);
19170         this.update();
19171     },
19172
19173     setValue: function(v)
19174     {
19175         if(this.fireEvent('beforeselect', this, v) !== false){
19176             var d = new Date(this.parseDate(v) ).clearTime();
19177         
19178             if(isNaN(d.getTime())){
19179                 this.date = this.viewDate = '';
19180                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19181                 return;
19182             }
19183
19184             v = this.formatDate(d);
19185
19186             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19187
19188             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19189
19190             this.update();
19191
19192             this.fireEvent('select', this, this.date);
19193         }
19194     },
19195     
19196     getValue: function()
19197     {
19198         return this.formatDate(this.date);
19199     },
19200     
19201     fireKey: function(e)
19202     {
19203         if (!this.picker().isVisible()){
19204             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19205                 this.showPopup();
19206             }
19207             return;
19208         }
19209         
19210         var dateChanged = false,
19211         dir, day, month,
19212         newDate, newViewDate;
19213         
19214         switch(e.keyCode){
19215             case 27: // escape
19216                 this.hidePopup();
19217                 e.preventDefault();
19218                 break;
19219             case 37: // left
19220             case 39: // right
19221                 if (!this.keyboardNavigation) {
19222                     break;
19223                 }
19224                 dir = e.keyCode == 37 ? -1 : 1;
19225                 
19226                 if (e.ctrlKey){
19227                     newDate = this.moveYear(this.date, dir);
19228                     newViewDate = this.moveYear(this.viewDate, dir);
19229                 } else if (e.shiftKey){
19230                     newDate = this.moveMonth(this.date, dir);
19231                     newViewDate = this.moveMonth(this.viewDate, dir);
19232                 } else {
19233                     newDate = new Date(this.date);
19234                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19235                     newViewDate = new Date(this.viewDate);
19236                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19237                 }
19238                 if (this.dateWithinRange(newDate)){
19239                     this.date = newDate;
19240                     this.viewDate = newViewDate;
19241                     this.setValue(this.formatDate(this.date));
19242 //                    this.update();
19243                     e.preventDefault();
19244                     dateChanged = true;
19245                 }
19246                 break;
19247             case 38: // up
19248             case 40: // down
19249                 if (!this.keyboardNavigation) {
19250                     break;
19251                 }
19252                 dir = e.keyCode == 38 ? -1 : 1;
19253                 if (e.ctrlKey){
19254                     newDate = this.moveYear(this.date, dir);
19255                     newViewDate = this.moveYear(this.viewDate, dir);
19256                 } else if (e.shiftKey){
19257                     newDate = this.moveMonth(this.date, dir);
19258                     newViewDate = this.moveMonth(this.viewDate, dir);
19259                 } else {
19260                     newDate = new Date(this.date);
19261                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19262                     newViewDate = new Date(this.viewDate);
19263                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19264                 }
19265                 if (this.dateWithinRange(newDate)){
19266                     this.date = newDate;
19267                     this.viewDate = newViewDate;
19268                     this.setValue(this.formatDate(this.date));
19269 //                    this.update();
19270                     e.preventDefault();
19271                     dateChanged = true;
19272                 }
19273                 break;
19274             case 13: // enter
19275                 this.setValue(this.formatDate(this.date));
19276                 this.hidePopup();
19277                 e.preventDefault();
19278                 break;
19279             case 9: // tab
19280                 this.setValue(this.formatDate(this.date));
19281                 this.hidePopup();
19282                 break;
19283             case 16: // shift
19284             case 17: // ctrl
19285             case 18: // alt
19286                 break;
19287             default :
19288                 this.hidePopup();
19289                 
19290         }
19291     },
19292     
19293     
19294     onClick: function(e) 
19295     {
19296         e.stopPropagation();
19297         e.preventDefault();
19298         
19299         var target = e.getTarget();
19300         
19301         if(target.nodeName.toLowerCase() === 'i'){
19302             target = Roo.get(target).dom.parentNode;
19303         }
19304         
19305         var nodeName = target.nodeName;
19306         var className = target.className;
19307         var html = target.innerHTML;
19308         //Roo.log(nodeName);
19309         
19310         switch(nodeName.toLowerCase()) {
19311             case 'th':
19312                 switch(className) {
19313                     case 'switch':
19314                         this.showMode(1);
19315                         break;
19316                     case 'prev':
19317                     case 'next':
19318                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19319                         switch(this.viewMode){
19320                                 case 0:
19321                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19322                                         break;
19323                                 case 1:
19324                                 case 2:
19325                                         this.viewDate = this.moveYear(this.viewDate, dir);
19326                                         break;
19327                         }
19328                         this.fill();
19329                         break;
19330                     case 'today':
19331                         var date = new Date();
19332                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19333 //                        this.fill()
19334                         this.setValue(this.formatDate(this.date));
19335                         
19336                         this.hidePopup();
19337                         break;
19338                 }
19339                 break;
19340             case 'span':
19341                 if (className.indexOf('disabled') < 0) {
19342                     this.viewDate.setUTCDate(1);
19343                     if (className.indexOf('month') > -1) {
19344                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19345                     } else {
19346                         var year = parseInt(html, 10) || 0;
19347                         this.viewDate.setUTCFullYear(year);
19348                         
19349                     }
19350                     
19351                     if(this.singleMode){
19352                         this.setValue(this.formatDate(this.viewDate));
19353                         this.hidePopup();
19354                         return;
19355                     }
19356                     
19357                     this.showMode(-1);
19358                     this.fill();
19359                 }
19360                 break;
19361                 
19362             case 'td':
19363                 //Roo.log(className);
19364                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19365                     var day = parseInt(html, 10) || 1;
19366                     var year = this.viewDate.getUTCFullYear(),
19367                         month = this.viewDate.getUTCMonth();
19368
19369                     if (className.indexOf('old') > -1) {
19370                         if(month === 0 ){
19371                             month = 11;
19372                             year -= 1;
19373                         }else{
19374                             month -= 1;
19375                         }
19376                     } else if (className.indexOf('new') > -1) {
19377                         if (month == 11) {
19378                             month = 0;
19379                             year += 1;
19380                         } else {
19381                             month += 1;
19382                         }
19383                     }
19384                     //Roo.log([year,month,day]);
19385                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19386                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19387 //                    this.fill();
19388                     //Roo.log(this.formatDate(this.date));
19389                     this.setValue(this.formatDate(this.date));
19390                     this.hidePopup();
19391                 }
19392                 break;
19393         }
19394     },
19395     
19396     setStartDate: function(startDate)
19397     {
19398         this.startDate = startDate || -Infinity;
19399         if (this.startDate !== -Infinity) {
19400             this.startDate = this.parseDate(this.startDate);
19401         }
19402         this.update();
19403         this.updateNavArrows();
19404     },
19405
19406     setEndDate: function(endDate)
19407     {
19408         this.endDate = endDate || Infinity;
19409         if (this.endDate !== Infinity) {
19410             this.endDate = this.parseDate(this.endDate);
19411         }
19412         this.update();
19413         this.updateNavArrows();
19414     },
19415     
19416     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19417     {
19418         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19419         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19420             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19421         }
19422         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19423             return parseInt(d, 10);
19424         });
19425         this.update();
19426         this.updateNavArrows();
19427     },
19428     
19429     updateNavArrows: function() 
19430     {
19431         if(this.singleMode){
19432             return;
19433         }
19434         
19435         var d = new Date(this.viewDate),
19436         year = d.getUTCFullYear(),
19437         month = d.getUTCMonth();
19438         
19439         Roo.each(this.picker().select('.prev', true).elements, function(v){
19440             v.show();
19441             switch (this.viewMode) {
19442                 case 0:
19443
19444                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19445                         v.hide();
19446                     }
19447                     break;
19448                 case 1:
19449                 case 2:
19450                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19451                         v.hide();
19452                     }
19453                     break;
19454             }
19455         });
19456         
19457         Roo.each(this.picker().select('.next', true).elements, function(v){
19458             v.show();
19459             switch (this.viewMode) {
19460                 case 0:
19461
19462                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19463                         v.hide();
19464                     }
19465                     break;
19466                 case 1:
19467                 case 2:
19468                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19469                         v.hide();
19470                     }
19471                     break;
19472             }
19473         })
19474     },
19475     
19476     moveMonth: function(date, dir)
19477     {
19478         if (!dir) {
19479             return date;
19480         }
19481         var new_date = new Date(date.valueOf()),
19482         day = new_date.getUTCDate(),
19483         month = new_date.getUTCMonth(),
19484         mag = Math.abs(dir),
19485         new_month, test;
19486         dir = dir > 0 ? 1 : -1;
19487         if (mag == 1){
19488             test = dir == -1
19489             // If going back one month, make sure month is not current month
19490             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19491             ? function(){
19492                 return new_date.getUTCMonth() == month;
19493             }
19494             // If going forward one month, make sure month is as expected
19495             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19496             : function(){
19497                 return new_date.getUTCMonth() != new_month;
19498             };
19499             new_month = month + dir;
19500             new_date.setUTCMonth(new_month);
19501             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19502             if (new_month < 0 || new_month > 11) {
19503                 new_month = (new_month + 12) % 12;
19504             }
19505         } else {
19506             // For magnitudes >1, move one month at a time...
19507             for (var i=0; i<mag; i++) {
19508                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19509                 new_date = this.moveMonth(new_date, dir);
19510             }
19511             // ...then reset the day, keeping it in the new month
19512             new_month = new_date.getUTCMonth();
19513             new_date.setUTCDate(day);
19514             test = function(){
19515                 return new_month != new_date.getUTCMonth();
19516             };
19517         }
19518         // Common date-resetting loop -- if date is beyond end of month, make it
19519         // end of month
19520         while (test()){
19521             new_date.setUTCDate(--day);
19522             new_date.setUTCMonth(new_month);
19523         }
19524         return new_date;
19525     },
19526
19527     moveYear: function(date, dir)
19528     {
19529         return this.moveMonth(date, dir*12);
19530     },
19531
19532     dateWithinRange: function(date)
19533     {
19534         return date >= this.startDate && date <= this.endDate;
19535     },
19536
19537     
19538     remove: function() 
19539     {
19540         this.picker().remove();
19541     },
19542     
19543     validateValue : function(value)
19544     {
19545         if(this.getVisibilityEl().hasClass('hidden')){
19546             return true;
19547         }
19548         
19549         if(value.length < 1)  {
19550             if(this.allowBlank){
19551                 return true;
19552             }
19553             return false;
19554         }
19555         
19556         if(value.length < this.minLength){
19557             return false;
19558         }
19559         if(value.length > this.maxLength){
19560             return false;
19561         }
19562         if(this.vtype){
19563             var vt = Roo.form.VTypes;
19564             if(!vt[this.vtype](value, this)){
19565                 return false;
19566             }
19567         }
19568         if(typeof this.validator == "function"){
19569             var msg = this.validator(value);
19570             if(msg !== true){
19571                 return false;
19572             }
19573         }
19574         
19575         if(this.regex && !this.regex.test(value)){
19576             return false;
19577         }
19578         
19579         if(typeof(this.parseDate(value)) == 'undefined'){
19580             return false;
19581         }
19582         
19583         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19584             return false;
19585         }      
19586         
19587         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19588             return false;
19589         } 
19590         
19591         
19592         return true;
19593     },
19594     
19595     reset : function()
19596     {
19597         this.date = this.viewDate = '';
19598         
19599         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19600     }
19601    
19602 });
19603
19604 Roo.apply(Roo.bootstrap.DateField,  {
19605     
19606     head : {
19607         tag: 'thead',
19608         cn: [
19609         {
19610             tag: 'tr',
19611             cn: [
19612             {
19613                 tag: 'th',
19614                 cls: 'prev',
19615                 html: '<i class="fa fa-arrow-left"/>'
19616             },
19617             {
19618                 tag: 'th',
19619                 cls: 'switch',
19620                 colspan: '5'
19621             },
19622             {
19623                 tag: 'th',
19624                 cls: 'next',
19625                 html: '<i class="fa fa-arrow-right"/>'
19626             }
19627
19628             ]
19629         }
19630         ]
19631     },
19632     
19633     content : {
19634         tag: 'tbody',
19635         cn: [
19636         {
19637             tag: 'tr',
19638             cn: [
19639             {
19640                 tag: 'td',
19641                 colspan: '7'
19642             }
19643             ]
19644         }
19645         ]
19646     },
19647     
19648     footer : {
19649         tag: 'tfoot',
19650         cn: [
19651         {
19652             tag: 'tr',
19653             cn: [
19654             {
19655                 tag: 'th',
19656                 colspan: '7',
19657                 cls: 'today'
19658             }
19659                     
19660             ]
19661         }
19662         ]
19663     },
19664     
19665     dates:{
19666         en: {
19667             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19668             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19669             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19670             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19671             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19672             today: "Today"
19673         }
19674     },
19675     
19676     modes: [
19677     {
19678         clsName: 'days',
19679         navFnc: 'Month',
19680         navStep: 1
19681     },
19682     {
19683         clsName: 'months',
19684         navFnc: 'FullYear',
19685         navStep: 1
19686     },
19687     {
19688         clsName: 'years',
19689         navFnc: 'FullYear',
19690         navStep: 10
19691     }]
19692 });
19693
19694 Roo.apply(Roo.bootstrap.DateField,  {
19695   
19696     template : {
19697         tag: 'div',
19698         cls: 'datepicker dropdown-menu roo-dynamic',
19699         cn: [
19700         {
19701             tag: 'div',
19702             cls: 'datepicker-days',
19703             cn: [
19704             {
19705                 tag: 'table',
19706                 cls: 'table-condensed',
19707                 cn:[
19708                 Roo.bootstrap.DateField.head,
19709                 {
19710                     tag: 'tbody'
19711                 },
19712                 Roo.bootstrap.DateField.footer
19713                 ]
19714             }
19715             ]
19716         },
19717         {
19718             tag: 'div',
19719             cls: 'datepicker-months',
19720             cn: [
19721             {
19722                 tag: 'table',
19723                 cls: 'table-condensed',
19724                 cn:[
19725                 Roo.bootstrap.DateField.head,
19726                 Roo.bootstrap.DateField.content,
19727                 Roo.bootstrap.DateField.footer
19728                 ]
19729             }
19730             ]
19731         },
19732         {
19733             tag: 'div',
19734             cls: 'datepicker-years',
19735             cn: [
19736             {
19737                 tag: 'table',
19738                 cls: 'table-condensed',
19739                 cn:[
19740                 Roo.bootstrap.DateField.head,
19741                 Roo.bootstrap.DateField.content,
19742                 Roo.bootstrap.DateField.footer
19743                 ]
19744             }
19745             ]
19746         }
19747         ]
19748     }
19749 });
19750
19751  
19752
19753  /*
19754  * - LGPL
19755  *
19756  * TimeField
19757  * 
19758  */
19759
19760 /**
19761  * @class Roo.bootstrap.TimeField
19762  * @extends Roo.bootstrap.Input
19763  * Bootstrap DateField class
19764  * 
19765  * 
19766  * @constructor
19767  * Create a new TimeField
19768  * @param {Object} config The config object
19769  */
19770
19771 Roo.bootstrap.TimeField = function(config){
19772     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19773     this.addEvents({
19774             /**
19775              * @event show
19776              * Fires when this field show.
19777              * @param {Roo.bootstrap.DateField} thisthis
19778              * @param {Mixed} date The date value
19779              */
19780             show : true,
19781             /**
19782              * @event show
19783              * Fires when this field hide.
19784              * @param {Roo.bootstrap.DateField} this
19785              * @param {Mixed} date The date value
19786              */
19787             hide : true,
19788             /**
19789              * @event select
19790              * Fires when select a date.
19791              * @param {Roo.bootstrap.DateField} this
19792              * @param {Mixed} date The date value
19793              */
19794             select : true
19795         });
19796 };
19797
19798 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19799     
19800     /**
19801      * @cfg {String} format
19802      * The default time format string which can be overriden for localization support.  The format must be
19803      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19804      */
19805     format : "H:i",
19806        
19807     onRender: function(ct, position)
19808     {
19809         
19810         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19811                 
19812         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19813         
19814         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19815         
19816         this.pop = this.picker().select('>.datepicker-time',true).first();
19817         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19818         
19819         this.picker().on('mousedown', this.onMousedown, this);
19820         this.picker().on('click', this.onClick, this);
19821         
19822         this.picker().addClass('datepicker-dropdown');
19823     
19824         this.fillTime();
19825         this.update();
19826             
19827         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19828         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19829         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19830         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19831         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19832         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19833
19834     },
19835     
19836     fireKey: function(e){
19837         if (!this.picker().isVisible()){
19838             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19839                 this.show();
19840             }
19841             return;
19842         }
19843
19844         e.preventDefault();
19845         
19846         switch(e.keyCode){
19847             case 27: // escape
19848                 this.hide();
19849                 break;
19850             case 37: // left
19851             case 39: // right
19852                 this.onTogglePeriod();
19853                 break;
19854             case 38: // up
19855                 this.onIncrementMinutes();
19856                 break;
19857             case 40: // down
19858                 this.onDecrementMinutes();
19859                 break;
19860             case 13: // enter
19861             case 9: // tab
19862                 this.setTime();
19863                 break;
19864         }
19865     },
19866     
19867     onClick: function(e) {
19868         e.stopPropagation();
19869         e.preventDefault();
19870     },
19871     
19872     picker : function()
19873     {
19874         return this.el.select('.datepicker', true).first();
19875     },
19876     
19877     fillTime: function()
19878     {    
19879         var time = this.pop.select('tbody', true).first();
19880         
19881         time.dom.innerHTML = '';
19882         
19883         time.createChild({
19884             tag: 'tr',
19885             cn: [
19886                 {
19887                     tag: 'td',
19888                     cn: [
19889                         {
19890                             tag: 'a',
19891                             href: '#',
19892                             cls: 'btn',
19893                             cn: [
19894                                 {
19895                                     tag: 'span',
19896                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19897                                 }
19898                             ]
19899                         } 
19900                     ]
19901                 },
19902                 {
19903                     tag: 'td',
19904                     cls: 'separator'
19905                 },
19906                 {
19907                     tag: 'td',
19908                     cn: [
19909                         {
19910                             tag: 'a',
19911                             href: '#',
19912                             cls: 'btn',
19913                             cn: [
19914                                 {
19915                                     tag: 'span',
19916                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19917                                 }
19918                             ]
19919                         }
19920                     ]
19921                 },
19922                 {
19923                     tag: 'td',
19924                     cls: 'separator'
19925                 }
19926             ]
19927         });
19928         
19929         time.createChild({
19930             tag: 'tr',
19931             cn: [
19932                 {
19933                     tag: 'td',
19934                     cn: [
19935                         {
19936                             tag: 'span',
19937                             cls: 'timepicker-hour',
19938                             html: '00'
19939                         }  
19940                     ]
19941                 },
19942                 {
19943                     tag: 'td',
19944                     cls: 'separator',
19945                     html: ':'
19946                 },
19947                 {
19948                     tag: 'td',
19949                     cn: [
19950                         {
19951                             tag: 'span',
19952                             cls: 'timepicker-minute',
19953                             html: '00'
19954                         }  
19955                     ]
19956                 },
19957                 {
19958                     tag: 'td',
19959                     cls: 'separator'
19960                 },
19961                 {
19962                     tag: 'td',
19963                     cn: [
19964                         {
19965                             tag: 'button',
19966                             type: 'button',
19967                             cls: 'btn btn-primary period',
19968                             html: 'AM'
19969                             
19970                         }
19971                     ]
19972                 }
19973             ]
19974         });
19975         
19976         time.createChild({
19977             tag: 'tr',
19978             cn: [
19979                 {
19980                     tag: 'td',
19981                     cn: [
19982                         {
19983                             tag: 'a',
19984                             href: '#',
19985                             cls: 'btn',
19986                             cn: [
19987                                 {
19988                                     tag: 'span',
19989                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19990                                 }
19991                             ]
19992                         }
19993                     ]
19994                 },
19995                 {
19996                     tag: 'td',
19997                     cls: 'separator'
19998                 },
19999                 {
20000                     tag: 'td',
20001                     cn: [
20002                         {
20003                             tag: 'a',
20004                             href: '#',
20005                             cls: 'btn',
20006                             cn: [
20007                                 {
20008                                     tag: 'span',
20009                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20010                                 }
20011                             ]
20012                         }
20013                     ]
20014                 },
20015                 {
20016                     tag: 'td',
20017                     cls: 'separator'
20018                 }
20019             ]
20020         });
20021         
20022     },
20023     
20024     update: function()
20025     {
20026         
20027         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20028         
20029         this.fill();
20030     },
20031     
20032     fill: function() 
20033     {
20034         var hours = this.time.getHours();
20035         var minutes = this.time.getMinutes();
20036         var period = 'AM';
20037         
20038         if(hours > 11){
20039             period = 'PM';
20040         }
20041         
20042         if(hours == 0){
20043             hours = 12;
20044         }
20045         
20046         
20047         if(hours > 12){
20048             hours = hours - 12;
20049         }
20050         
20051         if(hours < 10){
20052             hours = '0' + hours;
20053         }
20054         
20055         if(minutes < 10){
20056             minutes = '0' + minutes;
20057         }
20058         
20059         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20060         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20061         this.pop.select('button', true).first().dom.innerHTML = period;
20062         
20063     },
20064     
20065     place: function()
20066     {   
20067         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20068         
20069         var cls = ['bottom'];
20070         
20071         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20072             cls.pop();
20073             cls.push('top');
20074         }
20075         
20076         cls.push('right');
20077         
20078         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20079             cls.pop();
20080             cls.push('left');
20081         }
20082         
20083         this.picker().addClass(cls.join('-'));
20084         
20085         var _this = this;
20086         
20087         Roo.each(cls, function(c){
20088             if(c == 'bottom'){
20089                 _this.picker().setTop(_this.inputEl().getHeight());
20090                 return;
20091             }
20092             if(c == 'top'){
20093                 _this.picker().setTop(0 - _this.picker().getHeight());
20094                 return;
20095             }
20096             
20097             if(c == 'left'){
20098                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20099                 return;
20100             }
20101             if(c == 'right'){
20102                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20103                 return;
20104             }
20105         });
20106         
20107     },
20108   
20109     onFocus : function()
20110     {
20111         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20112         this.show();
20113     },
20114     
20115     onBlur : function()
20116     {
20117         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20118         this.hide();
20119     },
20120     
20121     show : function()
20122     {
20123         this.picker().show();
20124         this.pop.show();
20125         this.update();
20126         this.place();
20127         
20128         this.fireEvent('show', this, this.date);
20129     },
20130     
20131     hide : function()
20132     {
20133         this.picker().hide();
20134         this.pop.hide();
20135         
20136         this.fireEvent('hide', this, this.date);
20137     },
20138     
20139     setTime : function()
20140     {
20141         this.hide();
20142         this.setValue(this.time.format(this.format));
20143         
20144         this.fireEvent('select', this, this.date);
20145         
20146         
20147     },
20148     
20149     onMousedown: function(e){
20150         e.stopPropagation();
20151         e.preventDefault();
20152     },
20153     
20154     onIncrementHours: function()
20155     {
20156         Roo.log('onIncrementHours');
20157         this.time = this.time.add(Date.HOUR, 1);
20158         this.update();
20159         
20160     },
20161     
20162     onDecrementHours: function()
20163     {
20164         Roo.log('onDecrementHours');
20165         this.time = this.time.add(Date.HOUR, -1);
20166         this.update();
20167     },
20168     
20169     onIncrementMinutes: function()
20170     {
20171         Roo.log('onIncrementMinutes');
20172         this.time = this.time.add(Date.MINUTE, 1);
20173         this.update();
20174     },
20175     
20176     onDecrementMinutes: function()
20177     {
20178         Roo.log('onDecrementMinutes');
20179         this.time = this.time.add(Date.MINUTE, -1);
20180         this.update();
20181     },
20182     
20183     onTogglePeriod: function()
20184     {
20185         Roo.log('onTogglePeriod');
20186         this.time = this.time.add(Date.HOUR, 12);
20187         this.update();
20188     }
20189     
20190    
20191 });
20192
20193 Roo.apply(Roo.bootstrap.TimeField,  {
20194     
20195     content : {
20196         tag: 'tbody',
20197         cn: [
20198             {
20199                 tag: 'tr',
20200                 cn: [
20201                 {
20202                     tag: 'td',
20203                     colspan: '7'
20204                 }
20205                 ]
20206             }
20207         ]
20208     },
20209     
20210     footer : {
20211         tag: 'tfoot',
20212         cn: [
20213             {
20214                 tag: 'tr',
20215                 cn: [
20216                 {
20217                     tag: 'th',
20218                     colspan: '7',
20219                     cls: '',
20220                     cn: [
20221                         {
20222                             tag: 'button',
20223                             cls: 'btn btn-info ok',
20224                             html: 'OK'
20225                         }
20226                     ]
20227                 }
20228
20229                 ]
20230             }
20231         ]
20232     }
20233 });
20234
20235 Roo.apply(Roo.bootstrap.TimeField,  {
20236   
20237     template : {
20238         tag: 'div',
20239         cls: 'datepicker dropdown-menu',
20240         cn: [
20241             {
20242                 tag: 'div',
20243                 cls: 'datepicker-time',
20244                 cn: [
20245                 {
20246                     tag: 'table',
20247                     cls: 'table-condensed',
20248                     cn:[
20249                     Roo.bootstrap.TimeField.content,
20250                     Roo.bootstrap.TimeField.footer
20251                     ]
20252                 }
20253                 ]
20254             }
20255         ]
20256     }
20257 });
20258
20259  
20260
20261  /*
20262  * - LGPL
20263  *
20264  * MonthField
20265  * 
20266  */
20267
20268 /**
20269  * @class Roo.bootstrap.MonthField
20270  * @extends Roo.bootstrap.Input
20271  * Bootstrap MonthField class
20272  * 
20273  * @cfg {String} language default en
20274  * 
20275  * @constructor
20276  * Create a new MonthField
20277  * @param {Object} config The config object
20278  */
20279
20280 Roo.bootstrap.MonthField = function(config){
20281     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20282     
20283     this.addEvents({
20284         /**
20285          * @event show
20286          * Fires when this field show.
20287          * @param {Roo.bootstrap.MonthField} this
20288          * @param {Mixed} date The date value
20289          */
20290         show : true,
20291         /**
20292          * @event show
20293          * Fires when this field hide.
20294          * @param {Roo.bootstrap.MonthField} this
20295          * @param {Mixed} date The date value
20296          */
20297         hide : true,
20298         /**
20299          * @event select
20300          * Fires when select a date.
20301          * @param {Roo.bootstrap.MonthField} this
20302          * @param {String} oldvalue The old value
20303          * @param {String} newvalue The new value
20304          */
20305         select : true
20306     });
20307 };
20308
20309 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20310     
20311     onRender: function(ct, position)
20312     {
20313         
20314         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20315         
20316         this.language = this.language || 'en';
20317         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20318         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20319         
20320         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20321         this.isInline = false;
20322         this.isInput = true;
20323         this.component = this.el.select('.add-on', true).first() || false;
20324         this.component = (this.component && this.component.length === 0) ? false : this.component;
20325         this.hasInput = this.component && this.inputEL().length;
20326         
20327         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20328         
20329         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20330         
20331         this.picker().on('mousedown', this.onMousedown, this);
20332         this.picker().on('click', this.onClick, this);
20333         
20334         this.picker().addClass('datepicker-dropdown');
20335         
20336         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20337             v.setStyle('width', '189px');
20338         });
20339         
20340         this.fillMonths();
20341         
20342         this.update();
20343         
20344         if(this.isInline) {
20345             this.show();
20346         }
20347         
20348     },
20349     
20350     setValue: function(v, suppressEvent)
20351     {   
20352         var o = this.getValue();
20353         
20354         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20355         
20356         this.update();
20357
20358         if(suppressEvent !== true){
20359             this.fireEvent('select', this, o, v);
20360         }
20361         
20362     },
20363     
20364     getValue: function()
20365     {
20366         return this.value;
20367     },
20368     
20369     onClick: function(e) 
20370     {
20371         e.stopPropagation();
20372         e.preventDefault();
20373         
20374         var target = e.getTarget();
20375         
20376         if(target.nodeName.toLowerCase() === 'i'){
20377             target = Roo.get(target).dom.parentNode;
20378         }
20379         
20380         var nodeName = target.nodeName;
20381         var className = target.className;
20382         var html = target.innerHTML;
20383         
20384         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20385             return;
20386         }
20387         
20388         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20389         
20390         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20391         
20392         this.hide();
20393                         
20394     },
20395     
20396     picker : function()
20397     {
20398         return this.pickerEl;
20399     },
20400     
20401     fillMonths: function()
20402     {    
20403         var i = 0;
20404         var months = this.picker().select('>.datepicker-months td', true).first();
20405         
20406         months.dom.innerHTML = '';
20407         
20408         while (i < 12) {
20409             var month = {
20410                 tag: 'span',
20411                 cls: 'month',
20412                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20413             };
20414             
20415             months.createChild(month);
20416         }
20417         
20418     },
20419     
20420     update: function()
20421     {
20422         var _this = this;
20423         
20424         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20425             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20426         }
20427         
20428         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20429             e.removeClass('active');
20430             
20431             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20432                 e.addClass('active');
20433             }
20434         })
20435     },
20436     
20437     place: function()
20438     {
20439         if(this.isInline) {
20440             return;
20441         }
20442         
20443         this.picker().removeClass(['bottom', 'top']);
20444         
20445         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20446             /*
20447              * place to the top of element!
20448              *
20449              */
20450             
20451             this.picker().addClass('top');
20452             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20453             
20454             return;
20455         }
20456         
20457         this.picker().addClass('bottom');
20458         
20459         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20460     },
20461     
20462     onFocus : function()
20463     {
20464         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20465         this.show();
20466     },
20467     
20468     onBlur : function()
20469     {
20470         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20471         
20472         var d = this.inputEl().getValue();
20473         
20474         this.setValue(d);
20475                 
20476         this.hide();
20477     },
20478     
20479     show : function()
20480     {
20481         this.picker().show();
20482         this.picker().select('>.datepicker-months', true).first().show();
20483         this.update();
20484         this.place();
20485         
20486         this.fireEvent('show', this, this.date);
20487     },
20488     
20489     hide : function()
20490     {
20491         if(this.isInline) {
20492             return;
20493         }
20494         this.picker().hide();
20495         this.fireEvent('hide', this, this.date);
20496         
20497     },
20498     
20499     onMousedown: function(e)
20500     {
20501         e.stopPropagation();
20502         e.preventDefault();
20503     },
20504     
20505     keyup: function(e)
20506     {
20507         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20508         this.update();
20509     },
20510
20511     fireKey: function(e)
20512     {
20513         if (!this.picker().isVisible()){
20514             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20515                 this.show();
20516             }
20517             return;
20518         }
20519         
20520         var dir;
20521         
20522         switch(e.keyCode){
20523             case 27: // escape
20524                 this.hide();
20525                 e.preventDefault();
20526                 break;
20527             case 37: // left
20528             case 39: // right
20529                 dir = e.keyCode == 37 ? -1 : 1;
20530                 
20531                 this.vIndex = this.vIndex + dir;
20532                 
20533                 if(this.vIndex < 0){
20534                     this.vIndex = 0;
20535                 }
20536                 
20537                 if(this.vIndex > 11){
20538                     this.vIndex = 11;
20539                 }
20540                 
20541                 if(isNaN(this.vIndex)){
20542                     this.vIndex = 0;
20543                 }
20544                 
20545                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20546                 
20547                 break;
20548             case 38: // up
20549             case 40: // down
20550                 
20551                 dir = e.keyCode == 38 ? -1 : 1;
20552                 
20553                 this.vIndex = this.vIndex + dir * 4;
20554                 
20555                 if(this.vIndex < 0){
20556                     this.vIndex = 0;
20557                 }
20558                 
20559                 if(this.vIndex > 11){
20560                     this.vIndex = 11;
20561                 }
20562                 
20563                 if(isNaN(this.vIndex)){
20564                     this.vIndex = 0;
20565                 }
20566                 
20567                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20568                 break;
20569                 
20570             case 13: // enter
20571                 
20572                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20573                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20574                 }
20575                 
20576                 this.hide();
20577                 e.preventDefault();
20578                 break;
20579             case 9: // tab
20580                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20581                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20582                 }
20583                 this.hide();
20584                 break;
20585             case 16: // shift
20586             case 17: // ctrl
20587             case 18: // alt
20588                 break;
20589             default :
20590                 this.hide();
20591                 
20592         }
20593     },
20594     
20595     remove: function() 
20596     {
20597         this.picker().remove();
20598     }
20599    
20600 });
20601
20602 Roo.apply(Roo.bootstrap.MonthField,  {
20603     
20604     content : {
20605         tag: 'tbody',
20606         cn: [
20607         {
20608             tag: 'tr',
20609             cn: [
20610             {
20611                 tag: 'td',
20612                 colspan: '7'
20613             }
20614             ]
20615         }
20616         ]
20617     },
20618     
20619     dates:{
20620         en: {
20621             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20622             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20623         }
20624     }
20625 });
20626
20627 Roo.apply(Roo.bootstrap.MonthField,  {
20628   
20629     template : {
20630         tag: 'div',
20631         cls: 'datepicker dropdown-menu roo-dynamic',
20632         cn: [
20633             {
20634                 tag: 'div',
20635                 cls: 'datepicker-months',
20636                 cn: [
20637                 {
20638                     tag: 'table',
20639                     cls: 'table-condensed',
20640                     cn:[
20641                         Roo.bootstrap.DateField.content
20642                     ]
20643                 }
20644                 ]
20645             }
20646         ]
20647     }
20648 });
20649
20650  
20651
20652  
20653  /*
20654  * - LGPL
20655  *
20656  * CheckBox
20657  * 
20658  */
20659
20660 /**
20661  * @class Roo.bootstrap.CheckBox
20662  * @extends Roo.bootstrap.Input
20663  * Bootstrap CheckBox class
20664  * 
20665  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20666  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20667  * @cfg {String} boxLabel The text that appears beside the checkbox
20668  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20669  * @cfg {Boolean} checked initnal the element
20670  * @cfg {Boolean} inline inline the element (default false)
20671  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20672  * @cfg {String} tooltip label tooltip
20673  * 
20674  * @constructor
20675  * Create a new CheckBox
20676  * @param {Object} config The config object
20677  */
20678
20679 Roo.bootstrap.CheckBox = function(config){
20680     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20681    
20682     this.addEvents({
20683         /**
20684         * @event check
20685         * Fires when the element is checked or unchecked.
20686         * @param {Roo.bootstrap.CheckBox} this This input
20687         * @param {Boolean} checked The new checked value
20688         */
20689        check : true,
20690        /**
20691         * @event click
20692         * Fires when the element is click.
20693         * @param {Roo.bootstrap.CheckBox} this This input
20694         */
20695        click : true
20696     });
20697     
20698 };
20699
20700 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20701   
20702     inputType: 'checkbox',
20703     inputValue: 1,
20704     valueOff: 0,
20705     boxLabel: false,
20706     checked: false,
20707     weight : false,
20708     inline: false,
20709     tooltip : '',
20710     
20711     getAutoCreate : function()
20712     {
20713         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20714         
20715         var id = Roo.id();
20716         
20717         var cfg = {};
20718         
20719         cfg.cls = 'form-group ' + this.inputType; //input-group
20720         
20721         if(this.inline){
20722             cfg.cls += ' ' + this.inputType + '-inline';
20723         }
20724         
20725         var input =  {
20726             tag: 'input',
20727             id : id,
20728             type : this.inputType,
20729             value : this.inputValue,
20730             cls : 'roo-' + this.inputType, //'form-box',
20731             placeholder : this.placeholder || ''
20732             
20733         };
20734         
20735         if(this.inputType != 'radio'){
20736             var hidden =  {
20737                 tag: 'input',
20738                 type : 'hidden',
20739                 cls : 'roo-hidden-value',
20740                 value : this.checked ? this.inputValue : this.valueOff
20741             };
20742         }
20743         
20744             
20745         if (this.weight) { // Validity check?
20746             cfg.cls += " " + this.inputType + "-" + this.weight;
20747         }
20748         
20749         if (this.disabled) {
20750             input.disabled=true;
20751         }
20752         
20753         if(this.checked){
20754             input.checked = this.checked;
20755         }
20756         
20757         if (this.name) {
20758             
20759             input.name = this.name;
20760             
20761             if(this.inputType != 'radio'){
20762                 hidden.name = this.name;
20763                 input.name = '_hidden_' + this.name;
20764             }
20765         }
20766         
20767         if (this.size) {
20768             input.cls += ' input-' + this.size;
20769         }
20770         
20771         var settings=this;
20772         
20773         ['xs','sm','md','lg'].map(function(size){
20774             if (settings[size]) {
20775                 cfg.cls += ' col-' + size + '-' + settings[size];
20776             }
20777         });
20778         
20779         var inputblock = input;
20780          
20781         if (this.before || this.after) {
20782             
20783             inputblock = {
20784                 cls : 'input-group',
20785                 cn :  [] 
20786             };
20787             
20788             if (this.before) {
20789                 inputblock.cn.push({
20790                     tag :'span',
20791                     cls : 'input-group-addon',
20792                     html : this.before
20793                 });
20794             }
20795             
20796             inputblock.cn.push(input);
20797             
20798             if(this.inputType != 'radio'){
20799                 inputblock.cn.push(hidden);
20800             }
20801             
20802             if (this.after) {
20803                 inputblock.cn.push({
20804                     tag :'span',
20805                     cls : 'input-group-addon',
20806                     html : this.after
20807                 });
20808             }
20809             
20810         }
20811         
20812         if (align ==='left' && this.fieldLabel.length) {
20813 //                Roo.log("left and has label");
20814             cfg.cn = [
20815                 {
20816                     tag: 'label',
20817                     'for' :  id,
20818                     cls : 'control-label',
20819                     html : this.fieldLabel
20820                 },
20821                 {
20822                     cls : "", 
20823                     cn: [
20824                         inputblock
20825                     ]
20826                 }
20827             ];
20828             
20829             if(this.labelWidth > 12){
20830                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20831             }
20832             
20833             if(this.labelWidth < 13 && this.labelmd == 0){
20834                 this.labelmd = this.labelWidth;
20835             }
20836             
20837             if(this.labellg > 0){
20838                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20839                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20840             }
20841             
20842             if(this.labelmd > 0){
20843                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20844                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20845             }
20846             
20847             if(this.labelsm > 0){
20848                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20849                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20850             }
20851             
20852             if(this.labelxs > 0){
20853                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20854                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20855             }
20856             
20857         } else if ( this.fieldLabel.length) {
20858 //                Roo.log(" label");
20859                 cfg.cn = [
20860                    
20861                     {
20862                         tag: this.boxLabel ? 'span' : 'label',
20863                         'for': id,
20864                         cls: 'control-label box-input-label',
20865                         //cls : 'input-group-addon',
20866                         html : this.fieldLabel
20867                     },
20868                     
20869                     inputblock
20870                     
20871                 ];
20872
20873         } else {
20874             
20875 //                Roo.log(" no label && no align");
20876                 cfg.cn = [  inputblock ] ;
20877                 
20878                 
20879         }
20880         
20881         if(this.boxLabel){
20882              var boxLabelCfg = {
20883                 tag: 'label',
20884                 //'for': id, // box label is handled by onclick - so no for...
20885                 cls: 'box-label',
20886                 html: this.boxLabel
20887             };
20888             
20889             if(this.tooltip){
20890                 boxLabelCfg.tooltip = this.tooltip;
20891             }
20892              
20893             cfg.cn.push(boxLabelCfg);
20894         }
20895         
20896         if(this.inputType != 'radio'){
20897             cfg.cn.push(hidden);
20898         }
20899         
20900         return cfg;
20901         
20902     },
20903     
20904     /**
20905      * return the real input element.
20906      */
20907     inputEl: function ()
20908     {
20909         return this.el.select('input.roo-' + this.inputType,true).first();
20910     },
20911     hiddenEl: function ()
20912     {
20913         return this.el.select('input.roo-hidden-value',true).first();
20914     },
20915     
20916     labelEl: function()
20917     {
20918         return this.el.select('label.control-label',true).first();
20919     },
20920     /* depricated... */
20921     
20922     label: function()
20923     {
20924         return this.labelEl();
20925     },
20926     
20927     boxLabelEl: function()
20928     {
20929         return this.el.select('label.box-label',true).first();
20930     },
20931     
20932     initEvents : function()
20933     {
20934 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20935         
20936         this.inputEl().on('click', this.onClick,  this);
20937         
20938         if (this.boxLabel) { 
20939             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20940         }
20941         
20942         this.startValue = this.getValue();
20943         
20944         if(this.groupId){
20945             Roo.bootstrap.CheckBox.register(this);
20946         }
20947     },
20948     
20949     onClick : function(e)
20950     {   
20951         if(this.fireEvent('click', this, e) !== false){
20952             this.setChecked(!this.checked);
20953         }
20954         
20955     },
20956     
20957     setChecked : function(state,suppressEvent)
20958     {
20959         this.startValue = this.getValue();
20960
20961         if(this.inputType == 'radio'){
20962             
20963             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20964                 e.dom.checked = false;
20965             });
20966             
20967             this.inputEl().dom.checked = true;
20968             
20969             this.inputEl().dom.value = this.inputValue;
20970             
20971             if(suppressEvent !== true){
20972                 this.fireEvent('check', this, true);
20973             }
20974             
20975             this.validate();
20976             
20977             return;
20978         }
20979         
20980         this.checked = state;
20981         
20982         this.inputEl().dom.checked = state;
20983         
20984         
20985         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20986         
20987         if(suppressEvent !== true){
20988             this.fireEvent('check', this, state);
20989         }
20990         
20991         this.validate();
20992     },
20993     
20994     getValue : function()
20995     {
20996         if(this.inputType == 'radio'){
20997             return this.getGroupValue();
20998         }
20999         
21000         return this.hiddenEl().dom.value;
21001         
21002     },
21003     
21004     getGroupValue : function()
21005     {
21006         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21007             return '';
21008         }
21009         
21010         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21011     },
21012     
21013     setValue : function(v,suppressEvent)
21014     {
21015         if(this.inputType == 'radio'){
21016             this.setGroupValue(v, suppressEvent);
21017             return;
21018         }
21019         
21020         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21021         
21022         this.validate();
21023     },
21024     
21025     setGroupValue : function(v, suppressEvent)
21026     {
21027         this.startValue = this.getValue();
21028         
21029         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21030             e.dom.checked = false;
21031             
21032             if(e.dom.value == v){
21033                 e.dom.checked = true;
21034             }
21035         });
21036         
21037         if(suppressEvent !== true){
21038             this.fireEvent('check', this, true);
21039         }
21040
21041         this.validate();
21042         
21043         return;
21044     },
21045     
21046     validate : function()
21047     {
21048         if(this.getVisibilityEl().hasClass('hidden')){
21049             return true;
21050         }
21051         
21052         if(
21053                 this.disabled || 
21054                 (this.inputType == 'radio' && this.validateRadio()) ||
21055                 (this.inputType == 'checkbox' && this.validateCheckbox())
21056         ){
21057             this.markValid();
21058             return true;
21059         }
21060         
21061         this.markInvalid();
21062         return false;
21063     },
21064     
21065     validateRadio : function()
21066     {
21067         if(this.getVisibilityEl().hasClass('hidden')){
21068             return true;
21069         }
21070         
21071         if(this.allowBlank){
21072             return true;
21073         }
21074         
21075         var valid = false;
21076         
21077         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21078             if(!e.dom.checked){
21079                 return;
21080             }
21081             
21082             valid = true;
21083             
21084             return false;
21085         });
21086         
21087         return valid;
21088     },
21089     
21090     validateCheckbox : function()
21091     {
21092         if(!this.groupId){
21093             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21094             //return (this.getValue() == this.inputValue) ? true : false;
21095         }
21096         
21097         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21098         
21099         if(!group){
21100             return false;
21101         }
21102         
21103         var r = false;
21104         
21105         for(var i in group){
21106             if(group[i].el.isVisible(true)){
21107                 r = false;
21108                 break;
21109             }
21110             
21111             r = true;
21112         }
21113         
21114         for(var i in group){
21115             if(r){
21116                 break;
21117             }
21118             
21119             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21120         }
21121         
21122         return r;
21123     },
21124     
21125     /**
21126      * Mark this field as valid
21127      */
21128     markValid : function()
21129     {
21130         var _this = this;
21131         
21132         this.fireEvent('valid', this);
21133         
21134         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21135         
21136         if(this.groupId){
21137             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21138         }
21139         
21140         if(label){
21141             label.markValid();
21142         }
21143
21144         if(this.inputType == 'radio'){
21145             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21146                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21147                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21148             });
21149             
21150             return;
21151         }
21152
21153         if(!this.groupId){
21154             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21155             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21156             return;
21157         }
21158         
21159         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21160         
21161         if(!group){
21162             return;
21163         }
21164         
21165         for(var i in group){
21166             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21167             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21168         }
21169     },
21170     
21171      /**
21172      * Mark this field as invalid
21173      * @param {String} msg The validation message
21174      */
21175     markInvalid : function(msg)
21176     {
21177         if(this.allowBlank){
21178             return;
21179         }
21180         
21181         var _this = this;
21182         
21183         this.fireEvent('invalid', this, msg);
21184         
21185         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21186         
21187         if(this.groupId){
21188             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21189         }
21190         
21191         if(label){
21192             label.markInvalid();
21193         }
21194             
21195         if(this.inputType == 'radio'){
21196             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21197                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21198                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21199             });
21200             
21201             return;
21202         }
21203         
21204         if(!this.groupId){
21205             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21206             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21207             return;
21208         }
21209         
21210         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21211         
21212         if(!group){
21213             return;
21214         }
21215         
21216         for(var i in group){
21217             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21218             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21219         }
21220         
21221     },
21222     
21223     clearInvalid : function()
21224     {
21225         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21226         
21227         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21228         
21229         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21230         
21231         if (label && label.iconEl) {
21232             label.iconEl.removeClass(label.validClass);
21233             label.iconEl.removeClass(label.invalidClass);
21234         }
21235     },
21236     
21237     disable : function()
21238     {
21239         if(this.inputType != 'radio'){
21240             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21241             return;
21242         }
21243         
21244         var _this = this;
21245         
21246         if(this.rendered){
21247             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21248                 _this.getActionEl().addClass(this.disabledClass);
21249                 e.dom.disabled = true;
21250             });
21251         }
21252         
21253         this.disabled = true;
21254         this.fireEvent("disable", this);
21255         return this;
21256     },
21257
21258     enable : function()
21259     {
21260         if(this.inputType != 'radio'){
21261             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21262             return;
21263         }
21264         
21265         var _this = this;
21266         
21267         if(this.rendered){
21268             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21269                 _this.getActionEl().removeClass(this.disabledClass);
21270                 e.dom.disabled = false;
21271             });
21272         }
21273         
21274         this.disabled = false;
21275         this.fireEvent("enable", this);
21276         return this;
21277     },
21278     
21279     setBoxLabel : function(v)
21280     {
21281         this.boxLabel = v;
21282         
21283         if(this.rendered){
21284             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21285         }
21286     }
21287
21288 });
21289
21290 Roo.apply(Roo.bootstrap.CheckBox, {
21291     
21292     groups: {},
21293     
21294      /**
21295     * register a CheckBox Group
21296     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21297     */
21298     register : function(checkbox)
21299     {
21300         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21301             this.groups[checkbox.groupId] = {};
21302         }
21303         
21304         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21305             return;
21306         }
21307         
21308         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21309         
21310     },
21311     /**
21312     * fetch a CheckBox Group based on the group ID
21313     * @param {string} the group ID
21314     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21315     */
21316     get: function(groupId) {
21317         if (typeof(this.groups[groupId]) == 'undefined') {
21318             return false;
21319         }
21320         
21321         return this.groups[groupId] ;
21322     }
21323     
21324     
21325 });
21326 /*
21327  * - LGPL
21328  *
21329  * RadioItem
21330  * 
21331  */
21332
21333 /**
21334  * @class Roo.bootstrap.Radio
21335  * @extends Roo.bootstrap.Component
21336  * Bootstrap Radio class
21337  * @cfg {String} boxLabel - the label associated
21338  * @cfg {String} value - the value of radio
21339  * 
21340  * @constructor
21341  * Create a new Radio
21342  * @param {Object} config The config object
21343  */
21344 Roo.bootstrap.Radio = function(config){
21345     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21346     
21347 };
21348
21349 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21350     
21351     boxLabel : '',
21352     
21353     value : '',
21354     
21355     getAutoCreate : function()
21356     {
21357         var cfg = {
21358             tag : 'div',
21359             cls : 'form-group radio',
21360             cn : [
21361                 {
21362                     tag : 'label',
21363                     cls : 'box-label',
21364                     html : this.boxLabel
21365                 }
21366             ]
21367         };
21368         
21369         return cfg;
21370     },
21371     
21372     initEvents : function() 
21373     {
21374         this.parent().register(this);
21375         
21376         this.el.on('click', this.onClick, this);
21377         
21378     },
21379     
21380     onClick : function(e)
21381     {
21382         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21383             this.setChecked(true);
21384         }
21385     },
21386     
21387     setChecked : function(state, suppressEvent)
21388     {
21389         this.parent().setValue(this.value, suppressEvent);
21390         
21391     },
21392     
21393     setBoxLabel : function(v)
21394     {
21395         this.boxLabel = v;
21396         
21397         if(this.rendered){
21398             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21399         }
21400     }
21401     
21402 });
21403  
21404
21405  /*
21406  * - LGPL
21407  *
21408  * Input
21409  * 
21410  */
21411
21412 /**
21413  * @class Roo.bootstrap.SecurePass
21414  * @extends Roo.bootstrap.Input
21415  * Bootstrap SecurePass class
21416  *
21417  * 
21418  * @constructor
21419  * Create a new SecurePass
21420  * @param {Object} config The config object
21421  */
21422  
21423 Roo.bootstrap.SecurePass = function (config) {
21424     // these go here, so the translation tool can replace them..
21425     this.errors = {
21426         PwdEmpty: "Please type a password, and then retype it to confirm.",
21427         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21428         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21429         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21430         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21431         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21432         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21433         TooWeak: "Your password is Too Weak."
21434     },
21435     this.meterLabel = "Password strength:";
21436     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21437     this.meterClass = [
21438         "roo-password-meter-tooweak", 
21439         "roo-password-meter-weak", 
21440         "roo-password-meter-medium", 
21441         "roo-password-meter-strong", 
21442         "roo-password-meter-grey"
21443     ];
21444     
21445     this.errors = {};
21446     
21447     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21448 }
21449
21450 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21451     /**
21452      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21453      * {
21454      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21455      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21456      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21457      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21458      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21459      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21460      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21461      * })
21462      */
21463     // private
21464     
21465     meterWidth: 300,
21466     errorMsg :'',    
21467     errors: false,
21468     imageRoot: '/',
21469     /**
21470      * @cfg {String/Object} Label for the strength meter (defaults to
21471      * 'Password strength:')
21472      */
21473     // private
21474     meterLabel: '',
21475     /**
21476      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21477      * ['Weak', 'Medium', 'Strong'])
21478      */
21479     // private    
21480     pwdStrengths: false,    
21481     // private
21482     strength: 0,
21483     // private
21484     _lastPwd: null,
21485     // private
21486     kCapitalLetter: 0,
21487     kSmallLetter: 1,
21488     kDigit: 2,
21489     kPunctuation: 3,
21490     
21491     insecure: false,
21492     // private
21493     initEvents: function ()
21494     {
21495         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21496
21497         if (this.el.is('input[type=password]') && Roo.isSafari) {
21498             this.el.on('keydown', this.SafariOnKeyDown, this);
21499         }
21500
21501         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21502     },
21503     // private
21504     onRender: function (ct, position)
21505     {
21506         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21507         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21508         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21509
21510         this.trigger.createChild({
21511                    cn: [
21512                     {
21513                     //id: 'PwdMeter',
21514                     tag: 'div',
21515                     cls: 'roo-password-meter-grey col-xs-12',
21516                     style: {
21517                         //width: 0,
21518                         //width: this.meterWidth + 'px'                                                
21519                         }
21520                     },
21521                     {                            
21522                          cls: 'roo-password-meter-text'                          
21523                     }
21524                 ]            
21525         });
21526
21527          
21528         if (this.hideTrigger) {
21529             this.trigger.setDisplayed(false);
21530         }
21531         this.setSize(this.width || '', this.height || '');
21532     },
21533     // private
21534     onDestroy: function ()
21535     {
21536         if (this.trigger) {
21537             this.trigger.removeAllListeners();
21538             this.trigger.remove();
21539         }
21540         if (this.wrap) {
21541             this.wrap.remove();
21542         }
21543         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21544     },
21545     // private
21546     checkStrength: function ()
21547     {
21548         var pwd = this.inputEl().getValue();
21549         if (pwd == this._lastPwd) {
21550             return;
21551         }
21552
21553         var strength;
21554         if (this.ClientSideStrongPassword(pwd)) {
21555             strength = 3;
21556         } else if (this.ClientSideMediumPassword(pwd)) {
21557             strength = 2;
21558         } else if (this.ClientSideWeakPassword(pwd)) {
21559             strength = 1;
21560         } else {
21561             strength = 0;
21562         }
21563         
21564         Roo.log('strength1: ' + strength);
21565         
21566         //var pm = this.trigger.child('div/div/div').dom;
21567         var pm = this.trigger.child('div/div');
21568         pm.removeClass(this.meterClass);
21569         pm.addClass(this.meterClass[strength]);
21570                 
21571         
21572         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21573                 
21574         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21575         
21576         this._lastPwd = pwd;
21577     },
21578     reset: function ()
21579     {
21580         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21581         
21582         this._lastPwd = '';
21583         
21584         var pm = this.trigger.child('div/div');
21585         pm.removeClass(this.meterClass);
21586         pm.addClass('roo-password-meter-grey');        
21587         
21588         
21589         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21590         
21591         pt.innerHTML = '';
21592         this.inputEl().dom.type='password';
21593     },
21594     // private
21595     validateValue: function (value)
21596     {
21597         
21598         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21599             return false;
21600         }
21601         if (value.length == 0) {
21602             if (this.allowBlank) {
21603                 this.clearInvalid();
21604                 return true;
21605             }
21606
21607             this.markInvalid(this.errors.PwdEmpty);
21608             this.errorMsg = this.errors.PwdEmpty;
21609             return false;
21610         }
21611         
21612         if(this.insecure){
21613             return true;
21614         }
21615         
21616         if ('[\x21-\x7e]*'.match(value)) {
21617             this.markInvalid(this.errors.PwdBadChar);
21618             this.errorMsg = this.errors.PwdBadChar;
21619             return false;
21620         }
21621         if (value.length < 6) {
21622             this.markInvalid(this.errors.PwdShort);
21623             this.errorMsg = this.errors.PwdShort;
21624             return false;
21625         }
21626         if (value.length > 16) {
21627             this.markInvalid(this.errors.PwdLong);
21628             this.errorMsg = this.errors.PwdLong;
21629             return false;
21630         }
21631         var strength;
21632         if (this.ClientSideStrongPassword(value)) {
21633             strength = 3;
21634         } else if (this.ClientSideMediumPassword(value)) {
21635             strength = 2;
21636         } else if (this.ClientSideWeakPassword(value)) {
21637             strength = 1;
21638         } else {
21639             strength = 0;
21640         }
21641
21642         
21643         if (strength < 2) {
21644             //this.markInvalid(this.errors.TooWeak);
21645             this.errorMsg = this.errors.TooWeak;
21646             //return false;
21647         }
21648         
21649         
21650         console.log('strength2: ' + strength);
21651         
21652         //var pm = this.trigger.child('div/div/div').dom;
21653         
21654         var pm = this.trigger.child('div/div');
21655         pm.removeClass(this.meterClass);
21656         pm.addClass(this.meterClass[strength]);
21657                 
21658         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21659                 
21660         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21661         
21662         this.errorMsg = ''; 
21663         return true;
21664     },
21665     // private
21666     CharacterSetChecks: function (type)
21667     {
21668         this.type = type;
21669         this.fResult = false;
21670     },
21671     // private
21672     isctype: function (character, type)
21673     {
21674         switch (type) {  
21675             case this.kCapitalLetter:
21676                 if (character >= 'A' && character <= 'Z') {
21677                     return true;
21678                 }
21679                 break;
21680             
21681             case this.kSmallLetter:
21682                 if (character >= 'a' && character <= 'z') {
21683                     return true;
21684                 }
21685                 break;
21686             
21687             case this.kDigit:
21688                 if (character >= '0' && character <= '9') {
21689                     return true;
21690                 }
21691                 break;
21692             
21693             case this.kPunctuation:
21694                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21695                     return true;
21696                 }
21697                 break;
21698             
21699             default:
21700                 return false;
21701         }
21702
21703     },
21704     // private
21705     IsLongEnough: function (pwd, size)
21706     {
21707         return !(pwd == null || isNaN(size) || pwd.length < size);
21708     },
21709     // private
21710     SpansEnoughCharacterSets: function (word, nb)
21711     {
21712         if (!this.IsLongEnough(word, nb))
21713         {
21714             return false;
21715         }
21716
21717         var characterSetChecks = new Array(
21718             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21719             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21720         );
21721         
21722         for (var index = 0; index < word.length; ++index) {
21723             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21724                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21725                     characterSetChecks[nCharSet].fResult = true;
21726                     break;
21727                 }
21728             }
21729         }
21730
21731         var nCharSets = 0;
21732         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21733             if (characterSetChecks[nCharSet].fResult) {
21734                 ++nCharSets;
21735             }
21736         }
21737
21738         if (nCharSets < nb) {
21739             return false;
21740         }
21741         return true;
21742     },
21743     // private
21744     ClientSideStrongPassword: function (pwd)
21745     {
21746         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21747     },
21748     // private
21749     ClientSideMediumPassword: function (pwd)
21750     {
21751         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21752     },
21753     // private
21754     ClientSideWeakPassword: function (pwd)
21755     {
21756         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21757     }
21758           
21759 })//<script type="text/javascript">
21760
21761 /*
21762  * Based  Ext JS Library 1.1.1
21763  * Copyright(c) 2006-2007, Ext JS, LLC.
21764  * LGPL
21765  *
21766  */
21767  
21768 /**
21769  * @class Roo.HtmlEditorCore
21770  * @extends Roo.Component
21771  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21772  *
21773  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21774  */
21775
21776 Roo.HtmlEditorCore = function(config){
21777     
21778     
21779     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21780     
21781     
21782     this.addEvents({
21783         /**
21784          * @event initialize
21785          * Fires when the editor is fully initialized (including the iframe)
21786          * @param {Roo.HtmlEditorCore} this
21787          */
21788         initialize: true,
21789         /**
21790          * @event activate
21791          * Fires when the editor is first receives the focus. Any insertion must wait
21792          * until after this event.
21793          * @param {Roo.HtmlEditorCore} this
21794          */
21795         activate: true,
21796          /**
21797          * @event beforesync
21798          * Fires before the textarea is updated with content from the editor iframe. Return false
21799          * to cancel the sync.
21800          * @param {Roo.HtmlEditorCore} this
21801          * @param {String} html
21802          */
21803         beforesync: true,
21804          /**
21805          * @event beforepush
21806          * Fires before the iframe editor is updated with content from the textarea. Return false
21807          * to cancel the push.
21808          * @param {Roo.HtmlEditorCore} this
21809          * @param {String} html
21810          */
21811         beforepush: true,
21812          /**
21813          * @event sync
21814          * Fires when the textarea is updated with content from the editor iframe.
21815          * @param {Roo.HtmlEditorCore} this
21816          * @param {String} html
21817          */
21818         sync: true,
21819          /**
21820          * @event push
21821          * Fires when the iframe editor is updated with content from the textarea.
21822          * @param {Roo.HtmlEditorCore} this
21823          * @param {String} html
21824          */
21825         push: true,
21826         
21827         /**
21828          * @event editorevent
21829          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21830          * @param {Roo.HtmlEditorCore} this
21831          */
21832         editorevent: true
21833         
21834     });
21835     
21836     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21837     
21838     // defaults : white / black...
21839     this.applyBlacklists();
21840     
21841     
21842     
21843 };
21844
21845
21846 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21847
21848
21849      /**
21850      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21851      */
21852     
21853     owner : false,
21854     
21855      /**
21856      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21857      *                        Roo.resizable.
21858      */
21859     resizable : false,
21860      /**
21861      * @cfg {Number} height (in pixels)
21862      */   
21863     height: 300,
21864    /**
21865      * @cfg {Number} width (in pixels)
21866      */   
21867     width: 500,
21868     
21869     /**
21870      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21871      * 
21872      */
21873     stylesheets: false,
21874     
21875     // id of frame..
21876     frameId: false,
21877     
21878     // private properties
21879     validationEvent : false,
21880     deferHeight: true,
21881     initialized : false,
21882     activated : false,
21883     sourceEditMode : false,
21884     onFocus : Roo.emptyFn,
21885     iframePad:3,
21886     hideMode:'offsets',
21887     
21888     clearUp: true,
21889     
21890     // blacklist + whitelisted elements..
21891     black: false,
21892     white: false,
21893      
21894     bodyCls : '',
21895
21896     /**
21897      * Protected method that will not generally be called directly. It
21898      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21899      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21900      */
21901     getDocMarkup : function(){
21902         // body styles..
21903         var st = '';
21904         
21905         // inherit styels from page...?? 
21906         if (this.stylesheets === false) {
21907             
21908             Roo.get(document.head).select('style').each(function(node) {
21909                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21910             });
21911             
21912             Roo.get(document.head).select('link').each(function(node) { 
21913                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21914             });
21915             
21916         } else if (!this.stylesheets.length) {
21917                 // simple..
21918                 st = '<style type="text/css">' +
21919                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21920                    '</style>';
21921         } else { 
21922             st = '<style type="text/css">' +
21923                     this.stylesheets +
21924                 '</style>';
21925         }
21926         
21927         st +=  '<style type="text/css">' +
21928             'IMG { cursor: pointer } ' +
21929         '</style>';
21930
21931         var cls = 'roo-htmleditor-body';
21932         
21933         if(this.bodyCls.length){
21934             cls += ' ' + this.bodyCls;
21935         }
21936         
21937         return '<html><head>' + st  +
21938             //<style type="text/css">' +
21939             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21940             //'</style>' +
21941             ' </head><body class="' +  cls + '"></body></html>';
21942     },
21943
21944     // private
21945     onRender : function(ct, position)
21946     {
21947         var _t = this;
21948         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21949         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21950         
21951         
21952         this.el.dom.style.border = '0 none';
21953         this.el.dom.setAttribute('tabIndex', -1);
21954         this.el.addClass('x-hidden hide');
21955         
21956         
21957         
21958         if(Roo.isIE){ // fix IE 1px bogus margin
21959             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21960         }
21961        
21962         
21963         this.frameId = Roo.id();
21964         
21965          
21966         
21967         var iframe = this.owner.wrap.createChild({
21968             tag: 'iframe',
21969             cls: 'form-control', // bootstrap..
21970             id: this.frameId,
21971             name: this.frameId,
21972             frameBorder : 'no',
21973             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21974         }, this.el
21975         );
21976         
21977         
21978         this.iframe = iframe.dom;
21979
21980          this.assignDocWin();
21981         
21982         this.doc.designMode = 'on';
21983        
21984         this.doc.open();
21985         this.doc.write(this.getDocMarkup());
21986         this.doc.close();
21987
21988         
21989         var task = { // must defer to wait for browser to be ready
21990             run : function(){
21991                 //console.log("run task?" + this.doc.readyState);
21992                 this.assignDocWin();
21993                 if(this.doc.body || this.doc.readyState == 'complete'){
21994                     try {
21995                         this.doc.designMode="on";
21996                     } catch (e) {
21997                         return;
21998                     }
21999                     Roo.TaskMgr.stop(task);
22000                     this.initEditor.defer(10, this);
22001                 }
22002             },
22003             interval : 10,
22004             duration: 10000,
22005             scope: this
22006         };
22007         Roo.TaskMgr.start(task);
22008
22009     },
22010
22011     // private
22012     onResize : function(w, h)
22013     {
22014          Roo.log('resize: ' +w + ',' + h );
22015         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22016         if(!this.iframe){
22017             return;
22018         }
22019         if(typeof w == 'number'){
22020             
22021             this.iframe.style.width = w + 'px';
22022         }
22023         if(typeof h == 'number'){
22024             
22025             this.iframe.style.height = h + 'px';
22026             if(this.doc){
22027                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22028             }
22029         }
22030         
22031     },
22032
22033     /**
22034      * Toggles the editor between standard and source edit mode.
22035      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22036      */
22037     toggleSourceEdit : function(sourceEditMode){
22038         
22039         this.sourceEditMode = sourceEditMode === true;
22040         
22041         if(this.sourceEditMode){
22042  
22043             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22044             
22045         }else{
22046             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22047             //this.iframe.className = '';
22048             this.deferFocus();
22049         }
22050         //this.setSize(this.owner.wrap.getSize());
22051         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22052     },
22053
22054     
22055   
22056
22057     /**
22058      * Protected method that will not generally be called directly. If you need/want
22059      * custom HTML cleanup, this is the method you should override.
22060      * @param {String} html The HTML to be cleaned
22061      * return {String} The cleaned HTML
22062      */
22063     cleanHtml : function(html){
22064         html = String(html);
22065         if(html.length > 5){
22066             if(Roo.isSafari){ // strip safari nonsense
22067                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22068             }
22069         }
22070         if(html == '&nbsp;'){
22071             html = '';
22072         }
22073         return html;
22074     },
22075
22076     /**
22077      * HTML Editor -> Textarea
22078      * Protected method that will not generally be called directly. Syncs the contents
22079      * of the editor iframe with the textarea.
22080      */
22081     syncValue : function(){
22082         if(this.initialized){
22083             var bd = (this.doc.body || this.doc.documentElement);
22084             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22085             var html = bd.innerHTML;
22086             if(Roo.isSafari){
22087                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22088                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22089                 if(m && m[1]){
22090                     html = '<div style="'+m[0]+'">' + html + '</div>';
22091                 }
22092             }
22093             html = this.cleanHtml(html);
22094             // fix up the special chars.. normaly like back quotes in word...
22095             // however we do not want to do this with chinese..
22096             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22097                 var cc = b.charCodeAt();
22098                 if (
22099                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22100                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22101                     (cc >= 0xf900 && cc < 0xfb00 )
22102                 ) {
22103                         return b;
22104                 }
22105                 return "&#"+cc+";" 
22106             });
22107             if(this.owner.fireEvent('beforesync', this, html) !== false){
22108                 this.el.dom.value = html;
22109                 this.owner.fireEvent('sync', this, html);
22110             }
22111         }
22112     },
22113
22114     /**
22115      * Protected method that will not generally be called directly. Pushes the value of the textarea
22116      * into the iframe editor.
22117      */
22118     pushValue : function(){
22119         if(this.initialized){
22120             var v = this.el.dom.value.trim();
22121             
22122 //            if(v.length < 1){
22123 //                v = '&#160;';
22124 //            }
22125             
22126             if(this.owner.fireEvent('beforepush', this, v) !== false){
22127                 var d = (this.doc.body || this.doc.documentElement);
22128                 d.innerHTML = v;
22129                 this.cleanUpPaste();
22130                 this.el.dom.value = d.innerHTML;
22131                 this.owner.fireEvent('push', this, v);
22132             }
22133         }
22134     },
22135
22136     // private
22137     deferFocus : function(){
22138         this.focus.defer(10, this);
22139     },
22140
22141     // doc'ed in Field
22142     focus : function(){
22143         if(this.win && !this.sourceEditMode){
22144             this.win.focus();
22145         }else{
22146             this.el.focus();
22147         }
22148     },
22149     
22150     assignDocWin: function()
22151     {
22152         var iframe = this.iframe;
22153         
22154          if(Roo.isIE){
22155             this.doc = iframe.contentWindow.document;
22156             this.win = iframe.contentWindow;
22157         } else {
22158 //            if (!Roo.get(this.frameId)) {
22159 //                return;
22160 //            }
22161 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22162 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22163             
22164             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22165                 return;
22166             }
22167             
22168             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22169             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22170         }
22171     },
22172     
22173     // private
22174     initEditor : function(){
22175         //console.log("INIT EDITOR");
22176         this.assignDocWin();
22177         
22178         
22179         
22180         this.doc.designMode="on";
22181         this.doc.open();
22182         this.doc.write(this.getDocMarkup());
22183         this.doc.close();
22184         
22185         var dbody = (this.doc.body || this.doc.documentElement);
22186         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22187         // this copies styles from the containing element into thsi one..
22188         // not sure why we need all of this..
22189         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22190         
22191         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22192         //ss['background-attachment'] = 'fixed'; // w3c
22193         dbody.bgProperties = 'fixed'; // ie
22194         //Roo.DomHelper.applyStyles(dbody, ss);
22195         Roo.EventManager.on(this.doc, {
22196             //'mousedown': this.onEditorEvent,
22197             'mouseup': this.onEditorEvent,
22198             'dblclick': this.onEditorEvent,
22199             'click': this.onEditorEvent,
22200             'keyup': this.onEditorEvent,
22201             buffer:100,
22202             scope: this
22203         });
22204         if(Roo.isGecko){
22205             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22206         }
22207         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22208             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22209         }
22210         this.initialized = true;
22211
22212         this.owner.fireEvent('initialize', this);
22213         this.pushValue();
22214     },
22215
22216     // private
22217     onDestroy : function(){
22218         
22219         
22220         
22221         if(this.rendered){
22222             
22223             //for (var i =0; i < this.toolbars.length;i++) {
22224             //    // fixme - ask toolbars for heights?
22225             //    this.toolbars[i].onDestroy();
22226            // }
22227             
22228             //this.wrap.dom.innerHTML = '';
22229             //this.wrap.remove();
22230         }
22231     },
22232
22233     // private
22234     onFirstFocus : function(){
22235         
22236         this.assignDocWin();
22237         
22238         
22239         this.activated = true;
22240          
22241     
22242         if(Roo.isGecko){ // prevent silly gecko errors
22243             this.win.focus();
22244             var s = this.win.getSelection();
22245             if(!s.focusNode || s.focusNode.nodeType != 3){
22246                 var r = s.getRangeAt(0);
22247                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22248                 r.collapse(true);
22249                 this.deferFocus();
22250             }
22251             try{
22252                 this.execCmd('useCSS', true);
22253                 this.execCmd('styleWithCSS', false);
22254             }catch(e){}
22255         }
22256         this.owner.fireEvent('activate', this);
22257     },
22258
22259     // private
22260     adjustFont: function(btn){
22261         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22262         //if(Roo.isSafari){ // safari
22263         //    adjust *= 2;
22264        // }
22265         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22266         if(Roo.isSafari){ // safari
22267             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22268             v =  (v < 10) ? 10 : v;
22269             v =  (v > 48) ? 48 : v;
22270             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22271             
22272         }
22273         
22274         
22275         v = Math.max(1, v+adjust);
22276         
22277         this.execCmd('FontSize', v  );
22278     },
22279
22280     onEditorEvent : function(e)
22281     {
22282         this.owner.fireEvent('editorevent', this, e);
22283       //  this.updateToolbar();
22284         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22285     },
22286
22287     insertTag : function(tg)
22288     {
22289         // could be a bit smarter... -> wrap the current selected tRoo..
22290         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22291             
22292             range = this.createRange(this.getSelection());
22293             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22294             wrappingNode.appendChild(range.extractContents());
22295             range.insertNode(wrappingNode);
22296
22297             return;
22298             
22299             
22300             
22301         }
22302         this.execCmd("formatblock",   tg);
22303         
22304     },
22305     
22306     insertText : function(txt)
22307     {
22308         
22309         
22310         var range = this.createRange();
22311         range.deleteContents();
22312                //alert(Sender.getAttribute('label'));
22313                
22314         range.insertNode(this.doc.createTextNode(txt));
22315     } ,
22316     
22317      
22318
22319     /**
22320      * Executes a Midas editor command on the editor document and performs necessary focus and
22321      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22322      * @param {String} cmd The Midas command
22323      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22324      */
22325     relayCmd : function(cmd, value){
22326         this.win.focus();
22327         this.execCmd(cmd, value);
22328         this.owner.fireEvent('editorevent', this);
22329         //this.updateToolbar();
22330         this.owner.deferFocus();
22331     },
22332
22333     /**
22334      * Executes a Midas editor command directly on the editor document.
22335      * For visual commands, you should use {@link #relayCmd} instead.
22336      * <b>This should only be called after the editor is initialized.</b>
22337      * @param {String} cmd The Midas command
22338      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22339      */
22340     execCmd : function(cmd, value){
22341         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22342         this.syncValue();
22343     },
22344  
22345  
22346    
22347     /**
22348      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22349      * to insert tRoo.
22350      * @param {String} text | dom node.. 
22351      */
22352     insertAtCursor : function(text)
22353     {
22354         
22355         if(!this.activated){
22356             return;
22357         }
22358         /*
22359         if(Roo.isIE){
22360             this.win.focus();
22361             var r = this.doc.selection.createRange();
22362             if(r){
22363                 r.collapse(true);
22364                 r.pasteHTML(text);
22365                 this.syncValue();
22366                 this.deferFocus();
22367             
22368             }
22369             return;
22370         }
22371         */
22372         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22373             this.win.focus();
22374             
22375             
22376             // from jquery ui (MIT licenced)
22377             var range, node;
22378             var win = this.win;
22379             
22380             if (win.getSelection && win.getSelection().getRangeAt) {
22381                 range = win.getSelection().getRangeAt(0);
22382                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22383                 range.insertNode(node);
22384             } else if (win.document.selection && win.document.selection.createRange) {
22385                 // no firefox support
22386                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22387                 win.document.selection.createRange().pasteHTML(txt);
22388             } else {
22389                 // no firefox support
22390                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22391                 this.execCmd('InsertHTML', txt);
22392             } 
22393             
22394             this.syncValue();
22395             
22396             this.deferFocus();
22397         }
22398     },
22399  // private
22400     mozKeyPress : function(e){
22401         if(e.ctrlKey){
22402             var c = e.getCharCode(), cmd;
22403           
22404             if(c > 0){
22405                 c = String.fromCharCode(c).toLowerCase();
22406                 switch(c){
22407                     case 'b':
22408                         cmd = 'bold';
22409                         break;
22410                     case 'i':
22411                         cmd = 'italic';
22412                         break;
22413                     
22414                     case 'u':
22415                         cmd = 'underline';
22416                         break;
22417                     
22418                     case 'v':
22419                         this.cleanUpPaste.defer(100, this);
22420                         return;
22421                         
22422                 }
22423                 if(cmd){
22424                     this.win.focus();
22425                     this.execCmd(cmd);
22426                     this.deferFocus();
22427                     e.preventDefault();
22428                 }
22429                 
22430             }
22431         }
22432     },
22433
22434     // private
22435     fixKeys : function(){ // load time branching for fastest keydown performance
22436         if(Roo.isIE){
22437             return function(e){
22438                 var k = e.getKey(), r;
22439                 if(k == e.TAB){
22440                     e.stopEvent();
22441                     r = this.doc.selection.createRange();
22442                     if(r){
22443                         r.collapse(true);
22444                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22445                         this.deferFocus();
22446                     }
22447                     return;
22448                 }
22449                 
22450                 if(k == e.ENTER){
22451                     r = this.doc.selection.createRange();
22452                     if(r){
22453                         var target = r.parentElement();
22454                         if(!target || target.tagName.toLowerCase() != 'li'){
22455                             e.stopEvent();
22456                             r.pasteHTML('<br />');
22457                             r.collapse(false);
22458                             r.select();
22459                         }
22460                     }
22461                 }
22462                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22463                     this.cleanUpPaste.defer(100, this);
22464                     return;
22465                 }
22466                 
22467                 
22468             };
22469         }else if(Roo.isOpera){
22470             return function(e){
22471                 var k = e.getKey();
22472                 if(k == e.TAB){
22473                     e.stopEvent();
22474                     this.win.focus();
22475                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22476                     this.deferFocus();
22477                 }
22478                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22479                     this.cleanUpPaste.defer(100, this);
22480                     return;
22481                 }
22482                 
22483             };
22484         }else if(Roo.isSafari){
22485             return function(e){
22486                 var k = e.getKey();
22487                 
22488                 if(k == e.TAB){
22489                     e.stopEvent();
22490                     this.execCmd('InsertText','\t');
22491                     this.deferFocus();
22492                     return;
22493                 }
22494                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22495                     this.cleanUpPaste.defer(100, this);
22496                     return;
22497                 }
22498                 
22499              };
22500         }
22501     }(),
22502     
22503     getAllAncestors: function()
22504     {
22505         var p = this.getSelectedNode();
22506         var a = [];
22507         if (!p) {
22508             a.push(p); // push blank onto stack..
22509             p = this.getParentElement();
22510         }
22511         
22512         
22513         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22514             a.push(p);
22515             p = p.parentNode;
22516         }
22517         a.push(this.doc.body);
22518         return a;
22519     },
22520     lastSel : false,
22521     lastSelNode : false,
22522     
22523     
22524     getSelection : function() 
22525     {
22526         this.assignDocWin();
22527         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22528     },
22529     
22530     getSelectedNode: function() 
22531     {
22532         // this may only work on Gecko!!!
22533         
22534         // should we cache this!!!!
22535         
22536         
22537         
22538          
22539         var range = this.createRange(this.getSelection()).cloneRange();
22540         
22541         if (Roo.isIE) {
22542             var parent = range.parentElement();
22543             while (true) {
22544                 var testRange = range.duplicate();
22545                 testRange.moveToElementText(parent);
22546                 if (testRange.inRange(range)) {
22547                     break;
22548                 }
22549                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22550                     break;
22551                 }
22552                 parent = parent.parentElement;
22553             }
22554             return parent;
22555         }
22556         
22557         // is ancestor a text element.
22558         var ac =  range.commonAncestorContainer;
22559         if (ac.nodeType == 3) {
22560             ac = ac.parentNode;
22561         }
22562         
22563         var ar = ac.childNodes;
22564          
22565         var nodes = [];
22566         var other_nodes = [];
22567         var has_other_nodes = false;
22568         for (var i=0;i<ar.length;i++) {
22569             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22570                 continue;
22571             }
22572             // fullly contained node.
22573             
22574             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22575                 nodes.push(ar[i]);
22576                 continue;
22577             }
22578             
22579             // probably selected..
22580             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22581                 other_nodes.push(ar[i]);
22582                 continue;
22583             }
22584             // outer..
22585             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22586                 continue;
22587             }
22588             
22589             
22590             has_other_nodes = true;
22591         }
22592         if (!nodes.length && other_nodes.length) {
22593             nodes= other_nodes;
22594         }
22595         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22596             return false;
22597         }
22598         
22599         return nodes[0];
22600     },
22601     createRange: function(sel)
22602     {
22603         // this has strange effects when using with 
22604         // top toolbar - not sure if it's a great idea.
22605         //this.editor.contentWindow.focus();
22606         if (typeof sel != "undefined") {
22607             try {
22608                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22609             } catch(e) {
22610                 return this.doc.createRange();
22611             }
22612         } else {
22613             return this.doc.createRange();
22614         }
22615     },
22616     getParentElement: function()
22617     {
22618         
22619         this.assignDocWin();
22620         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22621         
22622         var range = this.createRange(sel);
22623          
22624         try {
22625             var p = range.commonAncestorContainer;
22626             while (p.nodeType == 3) { // text node
22627                 p = p.parentNode;
22628             }
22629             return p;
22630         } catch (e) {
22631             return null;
22632         }
22633     
22634     },
22635     /***
22636      *
22637      * Range intersection.. the hard stuff...
22638      *  '-1' = before
22639      *  '0' = hits..
22640      *  '1' = after.
22641      *         [ -- selected range --- ]
22642      *   [fail]                        [fail]
22643      *
22644      *    basically..
22645      *      if end is before start or  hits it. fail.
22646      *      if start is after end or hits it fail.
22647      *
22648      *   if either hits (but other is outside. - then it's not 
22649      *   
22650      *    
22651      **/
22652     
22653     
22654     // @see http://www.thismuchiknow.co.uk/?p=64.
22655     rangeIntersectsNode : function(range, node)
22656     {
22657         var nodeRange = node.ownerDocument.createRange();
22658         try {
22659             nodeRange.selectNode(node);
22660         } catch (e) {
22661             nodeRange.selectNodeContents(node);
22662         }
22663     
22664         var rangeStartRange = range.cloneRange();
22665         rangeStartRange.collapse(true);
22666     
22667         var rangeEndRange = range.cloneRange();
22668         rangeEndRange.collapse(false);
22669     
22670         var nodeStartRange = nodeRange.cloneRange();
22671         nodeStartRange.collapse(true);
22672     
22673         var nodeEndRange = nodeRange.cloneRange();
22674         nodeEndRange.collapse(false);
22675     
22676         return rangeStartRange.compareBoundaryPoints(
22677                  Range.START_TO_START, nodeEndRange) == -1 &&
22678                rangeEndRange.compareBoundaryPoints(
22679                  Range.START_TO_START, nodeStartRange) == 1;
22680         
22681          
22682     },
22683     rangeCompareNode : function(range, node)
22684     {
22685         var nodeRange = node.ownerDocument.createRange();
22686         try {
22687             nodeRange.selectNode(node);
22688         } catch (e) {
22689             nodeRange.selectNodeContents(node);
22690         }
22691         
22692         
22693         range.collapse(true);
22694     
22695         nodeRange.collapse(true);
22696      
22697         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22698         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22699          
22700         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22701         
22702         var nodeIsBefore   =  ss == 1;
22703         var nodeIsAfter    = ee == -1;
22704         
22705         if (nodeIsBefore && nodeIsAfter) {
22706             return 0; // outer
22707         }
22708         if (!nodeIsBefore && nodeIsAfter) {
22709             return 1; //right trailed.
22710         }
22711         
22712         if (nodeIsBefore && !nodeIsAfter) {
22713             return 2;  // left trailed.
22714         }
22715         // fully contined.
22716         return 3;
22717     },
22718
22719     // private? - in a new class?
22720     cleanUpPaste :  function()
22721     {
22722         // cleans up the whole document..
22723         Roo.log('cleanuppaste');
22724         
22725         this.cleanUpChildren(this.doc.body);
22726         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22727         if (clean != this.doc.body.innerHTML) {
22728             this.doc.body.innerHTML = clean;
22729         }
22730         
22731     },
22732     
22733     cleanWordChars : function(input) {// change the chars to hex code
22734         var he = Roo.HtmlEditorCore;
22735         
22736         var output = input;
22737         Roo.each(he.swapCodes, function(sw) { 
22738             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22739             
22740             output = output.replace(swapper, sw[1]);
22741         });
22742         
22743         return output;
22744     },
22745     
22746     
22747     cleanUpChildren : function (n)
22748     {
22749         if (!n.childNodes.length) {
22750             return;
22751         }
22752         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22753            this.cleanUpChild(n.childNodes[i]);
22754         }
22755     },
22756     
22757     
22758         
22759     
22760     cleanUpChild : function (node)
22761     {
22762         var ed = this;
22763         //console.log(node);
22764         if (node.nodeName == "#text") {
22765             // clean up silly Windows -- stuff?
22766             return; 
22767         }
22768         if (node.nodeName == "#comment") {
22769             node.parentNode.removeChild(node);
22770             // clean up silly Windows -- stuff?
22771             return; 
22772         }
22773         var lcname = node.tagName.toLowerCase();
22774         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22775         // whitelist of tags..
22776         
22777         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22778             // remove node.
22779             node.parentNode.removeChild(node);
22780             return;
22781             
22782         }
22783         
22784         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22785         
22786         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22787         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22788         
22789         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22790         //    remove_keep_children = true;
22791         //}
22792         
22793         if (remove_keep_children) {
22794             this.cleanUpChildren(node);
22795             // inserts everything just before this node...
22796             while (node.childNodes.length) {
22797                 var cn = node.childNodes[0];
22798                 node.removeChild(cn);
22799                 node.parentNode.insertBefore(cn, node);
22800             }
22801             node.parentNode.removeChild(node);
22802             return;
22803         }
22804         
22805         if (!node.attributes || !node.attributes.length) {
22806             this.cleanUpChildren(node);
22807             return;
22808         }
22809         
22810         function cleanAttr(n,v)
22811         {
22812             
22813             if (v.match(/^\./) || v.match(/^\//)) {
22814                 return;
22815             }
22816             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22817                 return;
22818             }
22819             if (v.match(/^#/)) {
22820                 return;
22821             }
22822 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22823             node.removeAttribute(n);
22824             
22825         }
22826         
22827         var cwhite = this.cwhite;
22828         var cblack = this.cblack;
22829             
22830         function cleanStyle(n,v)
22831         {
22832             if (v.match(/expression/)) { //XSS?? should we even bother..
22833                 node.removeAttribute(n);
22834                 return;
22835             }
22836             
22837             var parts = v.split(/;/);
22838             var clean = [];
22839             
22840             Roo.each(parts, function(p) {
22841                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22842                 if (!p.length) {
22843                     return true;
22844                 }
22845                 var l = p.split(':').shift().replace(/\s+/g,'');
22846                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22847                 
22848                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22849 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22850                     //node.removeAttribute(n);
22851                     return true;
22852                 }
22853                 //Roo.log()
22854                 // only allow 'c whitelisted system attributes'
22855                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22856 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22857                     //node.removeAttribute(n);
22858                     return true;
22859                 }
22860                 
22861                 
22862                  
22863                 
22864                 clean.push(p);
22865                 return true;
22866             });
22867             if (clean.length) { 
22868                 node.setAttribute(n, clean.join(';'));
22869             } else {
22870                 node.removeAttribute(n);
22871             }
22872             
22873         }
22874         
22875         
22876         for (var i = node.attributes.length-1; i > -1 ; i--) {
22877             var a = node.attributes[i];
22878             //console.log(a);
22879             
22880             if (a.name.toLowerCase().substr(0,2)=='on')  {
22881                 node.removeAttribute(a.name);
22882                 continue;
22883             }
22884             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22885                 node.removeAttribute(a.name);
22886                 continue;
22887             }
22888             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22889                 cleanAttr(a.name,a.value); // fixme..
22890                 continue;
22891             }
22892             if (a.name == 'style') {
22893                 cleanStyle(a.name,a.value);
22894                 continue;
22895             }
22896             /// clean up MS crap..
22897             // tecnically this should be a list of valid class'es..
22898             
22899             
22900             if (a.name == 'class') {
22901                 if (a.value.match(/^Mso/)) {
22902                     node.className = '';
22903                 }
22904                 
22905                 if (a.value.match(/^body$/)) {
22906                     node.className = '';
22907                 }
22908                 continue;
22909             }
22910             
22911             // style cleanup!?
22912             // class cleanup?
22913             
22914         }
22915         
22916         
22917         this.cleanUpChildren(node);
22918         
22919         
22920     },
22921     
22922     /**
22923      * Clean up MS wordisms...
22924      */
22925     cleanWord : function(node)
22926     {
22927         
22928         
22929         if (!node) {
22930             this.cleanWord(this.doc.body);
22931             return;
22932         }
22933         if (node.nodeName == "#text") {
22934             // clean up silly Windows -- stuff?
22935             return; 
22936         }
22937         if (node.nodeName == "#comment") {
22938             node.parentNode.removeChild(node);
22939             // clean up silly Windows -- stuff?
22940             return; 
22941         }
22942         
22943         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22944             node.parentNode.removeChild(node);
22945             return;
22946         }
22947         
22948         // remove - but keep children..
22949         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22950             while (node.childNodes.length) {
22951                 var cn = node.childNodes[0];
22952                 node.removeChild(cn);
22953                 node.parentNode.insertBefore(cn, node);
22954             }
22955             node.parentNode.removeChild(node);
22956             this.iterateChildren(node, this.cleanWord);
22957             return;
22958         }
22959         // clean styles
22960         if (node.className.length) {
22961             
22962             var cn = node.className.split(/\W+/);
22963             var cna = [];
22964             Roo.each(cn, function(cls) {
22965                 if (cls.match(/Mso[a-zA-Z]+/)) {
22966                     return;
22967                 }
22968                 cna.push(cls);
22969             });
22970             node.className = cna.length ? cna.join(' ') : '';
22971             if (!cna.length) {
22972                 node.removeAttribute("class");
22973             }
22974         }
22975         
22976         if (node.hasAttribute("lang")) {
22977             node.removeAttribute("lang");
22978         }
22979         
22980         if (node.hasAttribute("style")) {
22981             
22982             var styles = node.getAttribute("style").split(";");
22983             var nstyle = [];
22984             Roo.each(styles, function(s) {
22985                 if (!s.match(/:/)) {
22986                     return;
22987                 }
22988                 var kv = s.split(":");
22989                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22990                     return;
22991                 }
22992                 // what ever is left... we allow.
22993                 nstyle.push(s);
22994             });
22995             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22996             if (!nstyle.length) {
22997                 node.removeAttribute('style');
22998             }
22999         }
23000         this.iterateChildren(node, this.cleanWord);
23001         
23002         
23003         
23004     },
23005     /**
23006      * iterateChildren of a Node, calling fn each time, using this as the scole..
23007      * @param {DomNode} node node to iterate children of.
23008      * @param {Function} fn method of this class to call on each item.
23009      */
23010     iterateChildren : function(node, fn)
23011     {
23012         if (!node.childNodes.length) {
23013                 return;
23014         }
23015         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23016            fn.call(this, node.childNodes[i])
23017         }
23018     },
23019     
23020     
23021     /**
23022      * cleanTableWidths.
23023      *
23024      * Quite often pasting from word etc.. results in tables with column and widths.
23025      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23026      *
23027      */
23028     cleanTableWidths : function(node)
23029     {
23030          
23031          
23032         if (!node) {
23033             this.cleanTableWidths(this.doc.body);
23034             return;
23035         }
23036         
23037         // ignore list...
23038         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23039             return; 
23040         }
23041         Roo.log(node.tagName);
23042         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23043             this.iterateChildren(node, this.cleanTableWidths);
23044             return;
23045         }
23046         if (node.hasAttribute('width')) {
23047             node.removeAttribute('width');
23048         }
23049         
23050          
23051         if (node.hasAttribute("style")) {
23052             // pretty basic...
23053             
23054             var styles = node.getAttribute("style").split(";");
23055             var nstyle = [];
23056             Roo.each(styles, function(s) {
23057                 if (!s.match(/:/)) {
23058                     return;
23059                 }
23060                 var kv = s.split(":");
23061                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23062                     return;
23063                 }
23064                 // what ever is left... we allow.
23065                 nstyle.push(s);
23066             });
23067             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23068             if (!nstyle.length) {
23069                 node.removeAttribute('style');
23070             }
23071         }
23072         
23073         this.iterateChildren(node, this.cleanTableWidths);
23074         
23075         
23076     },
23077     
23078     
23079     
23080     
23081     domToHTML : function(currentElement, depth, nopadtext) {
23082         
23083         depth = depth || 0;
23084         nopadtext = nopadtext || false;
23085     
23086         if (!currentElement) {
23087             return this.domToHTML(this.doc.body);
23088         }
23089         
23090         //Roo.log(currentElement);
23091         var j;
23092         var allText = false;
23093         var nodeName = currentElement.nodeName;
23094         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23095         
23096         if  (nodeName == '#text') {
23097             
23098             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23099         }
23100         
23101         
23102         var ret = '';
23103         if (nodeName != 'BODY') {
23104              
23105             var i = 0;
23106             // Prints the node tagName, such as <A>, <IMG>, etc
23107             if (tagName) {
23108                 var attr = [];
23109                 for(i = 0; i < currentElement.attributes.length;i++) {
23110                     // quoting?
23111                     var aname = currentElement.attributes.item(i).name;
23112                     if (!currentElement.attributes.item(i).value.length) {
23113                         continue;
23114                     }
23115                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23116                 }
23117                 
23118                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23119             } 
23120             else {
23121                 
23122                 // eack
23123             }
23124         } else {
23125             tagName = false;
23126         }
23127         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23128             return ret;
23129         }
23130         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23131             nopadtext = true;
23132         }
23133         
23134         
23135         // Traverse the tree
23136         i = 0;
23137         var currentElementChild = currentElement.childNodes.item(i);
23138         var allText = true;
23139         var innerHTML  = '';
23140         lastnode = '';
23141         while (currentElementChild) {
23142             // Formatting code (indent the tree so it looks nice on the screen)
23143             var nopad = nopadtext;
23144             if (lastnode == 'SPAN') {
23145                 nopad  = true;
23146             }
23147             // text
23148             if  (currentElementChild.nodeName == '#text') {
23149                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23150                 toadd = nopadtext ? toadd : toadd.trim();
23151                 if (!nopad && toadd.length > 80) {
23152                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23153                 }
23154                 innerHTML  += toadd;
23155                 
23156                 i++;
23157                 currentElementChild = currentElement.childNodes.item(i);
23158                 lastNode = '';
23159                 continue;
23160             }
23161             allText = false;
23162             
23163             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23164                 
23165             // Recursively traverse the tree structure of the child node
23166             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23167             lastnode = currentElementChild.nodeName;
23168             i++;
23169             currentElementChild=currentElement.childNodes.item(i);
23170         }
23171         
23172         ret += innerHTML;
23173         
23174         if (!allText) {
23175                 // The remaining code is mostly for formatting the tree
23176             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23177         }
23178         
23179         
23180         if (tagName) {
23181             ret+= "</"+tagName+">";
23182         }
23183         return ret;
23184         
23185     },
23186         
23187     applyBlacklists : function()
23188     {
23189         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23190         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23191         
23192         this.white = [];
23193         this.black = [];
23194         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23195             if (b.indexOf(tag) > -1) {
23196                 return;
23197             }
23198             this.white.push(tag);
23199             
23200         }, this);
23201         
23202         Roo.each(w, function(tag) {
23203             if (b.indexOf(tag) > -1) {
23204                 return;
23205             }
23206             if (this.white.indexOf(tag) > -1) {
23207                 return;
23208             }
23209             this.white.push(tag);
23210             
23211         }, this);
23212         
23213         
23214         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23215             if (w.indexOf(tag) > -1) {
23216                 return;
23217             }
23218             this.black.push(tag);
23219             
23220         }, this);
23221         
23222         Roo.each(b, function(tag) {
23223             if (w.indexOf(tag) > -1) {
23224                 return;
23225             }
23226             if (this.black.indexOf(tag) > -1) {
23227                 return;
23228             }
23229             this.black.push(tag);
23230             
23231         }, this);
23232         
23233         
23234         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23235         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23236         
23237         this.cwhite = [];
23238         this.cblack = [];
23239         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23240             if (b.indexOf(tag) > -1) {
23241                 return;
23242             }
23243             this.cwhite.push(tag);
23244             
23245         }, this);
23246         
23247         Roo.each(w, function(tag) {
23248             if (b.indexOf(tag) > -1) {
23249                 return;
23250             }
23251             if (this.cwhite.indexOf(tag) > -1) {
23252                 return;
23253             }
23254             this.cwhite.push(tag);
23255             
23256         }, this);
23257         
23258         
23259         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23260             if (w.indexOf(tag) > -1) {
23261                 return;
23262             }
23263             this.cblack.push(tag);
23264             
23265         }, this);
23266         
23267         Roo.each(b, function(tag) {
23268             if (w.indexOf(tag) > -1) {
23269                 return;
23270             }
23271             if (this.cblack.indexOf(tag) > -1) {
23272                 return;
23273             }
23274             this.cblack.push(tag);
23275             
23276         }, this);
23277     },
23278     
23279     setStylesheets : function(stylesheets)
23280     {
23281         if(typeof(stylesheets) == 'string'){
23282             Roo.get(this.iframe.contentDocument.head).createChild({
23283                 tag : 'link',
23284                 rel : 'stylesheet',
23285                 type : 'text/css',
23286                 href : stylesheets
23287             });
23288             
23289             return;
23290         }
23291         var _this = this;
23292      
23293         Roo.each(stylesheets, function(s) {
23294             if(!s.length){
23295                 return;
23296             }
23297             
23298             Roo.get(_this.iframe.contentDocument.head).createChild({
23299                 tag : 'link',
23300                 rel : 'stylesheet',
23301                 type : 'text/css',
23302                 href : s
23303             });
23304         });
23305
23306         
23307     },
23308     
23309     removeStylesheets : function()
23310     {
23311         var _this = this;
23312         
23313         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23314             s.remove();
23315         });
23316     },
23317     
23318     setStyle : function(style)
23319     {
23320         Roo.get(this.iframe.contentDocument.head).createChild({
23321             tag : 'style',
23322             type : 'text/css',
23323             html : style
23324         });
23325
23326         return;
23327     }
23328     
23329     // hide stuff that is not compatible
23330     /**
23331      * @event blur
23332      * @hide
23333      */
23334     /**
23335      * @event change
23336      * @hide
23337      */
23338     /**
23339      * @event focus
23340      * @hide
23341      */
23342     /**
23343      * @event specialkey
23344      * @hide
23345      */
23346     /**
23347      * @cfg {String} fieldClass @hide
23348      */
23349     /**
23350      * @cfg {String} focusClass @hide
23351      */
23352     /**
23353      * @cfg {String} autoCreate @hide
23354      */
23355     /**
23356      * @cfg {String} inputType @hide
23357      */
23358     /**
23359      * @cfg {String} invalidClass @hide
23360      */
23361     /**
23362      * @cfg {String} invalidText @hide
23363      */
23364     /**
23365      * @cfg {String} msgFx @hide
23366      */
23367     /**
23368      * @cfg {String} validateOnBlur @hide
23369      */
23370 });
23371
23372 Roo.HtmlEditorCore.white = [
23373         'area', 'br', 'img', 'input', 'hr', 'wbr',
23374         
23375        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23376        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23377        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23378        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23379        'table',   'ul',         'xmp', 
23380        
23381        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23382       'thead',   'tr', 
23383      
23384       'dir', 'menu', 'ol', 'ul', 'dl',
23385        
23386       'embed',  'object'
23387 ];
23388
23389
23390 Roo.HtmlEditorCore.black = [
23391     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23392         'applet', // 
23393         'base',   'basefont', 'bgsound', 'blink',  'body', 
23394         'frame',  'frameset', 'head',    'html',   'ilayer', 
23395         'iframe', 'layer',  'link',     'meta',    'object',   
23396         'script', 'style' ,'title',  'xml' // clean later..
23397 ];
23398 Roo.HtmlEditorCore.clean = [
23399     'script', 'style', 'title', 'xml'
23400 ];
23401 Roo.HtmlEditorCore.remove = [
23402     'font'
23403 ];
23404 // attributes..
23405
23406 Roo.HtmlEditorCore.ablack = [
23407     'on'
23408 ];
23409     
23410 Roo.HtmlEditorCore.aclean = [ 
23411     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23412 ];
23413
23414 // protocols..
23415 Roo.HtmlEditorCore.pwhite= [
23416         'http',  'https',  'mailto'
23417 ];
23418
23419 // white listed style attributes.
23420 Roo.HtmlEditorCore.cwhite= [
23421       //  'text-align', /// default is to allow most things..
23422       
23423          
23424 //        'font-size'//??
23425 ];
23426
23427 // black listed style attributes.
23428 Roo.HtmlEditorCore.cblack= [
23429       //  'font-size' -- this can be set by the project 
23430 ];
23431
23432
23433 Roo.HtmlEditorCore.swapCodes   =[ 
23434     [    8211, "--" ], 
23435     [    8212, "--" ], 
23436     [    8216,  "'" ],  
23437     [    8217, "'" ],  
23438     [    8220, '"' ],  
23439     [    8221, '"' ],  
23440     [    8226, "*" ],  
23441     [    8230, "..." ]
23442 ]; 
23443
23444     /*
23445  * - LGPL
23446  *
23447  * HtmlEditor
23448  * 
23449  */
23450
23451 /**
23452  * @class Roo.bootstrap.HtmlEditor
23453  * @extends Roo.bootstrap.TextArea
23454  * Bootstrap HtmlEditor class
23455
23456  * @constructor
23457  * Create a new HtmlEditor
23458  * @param {Object} config The config object
23459  */
23460
23461 Roo.bootstrap.HtmlEditor = function(config){
23462     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23463     if (!this.toolbars) {
23464         this.toolbars = [];
23465     }
23466     
23467     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23468     this.addEvents({
23469             /**
23470              * @event initialize
23471              * Fires when the editor is fully initialized (including the iframe)
23472              * @param {HtmlEditor} this
23473              */
23474             initialize: true,
23475             /**
23476              * @event activate
23477              * Fires when the editor is first receives the focus. Any insertion must wait
23478              * until after this event.
23479              * @param {HtmlEditor} this
23480              */
23481             activate: true,
23482              /**
23483              * @event beforesync
23484              * Fires before the textarea is updated with content from the editor iframe. Return false
23485              * to cancel the sync.
23486              * @param {HtmlEditor} this
23487              * @param {String} html
23488              */
23489             beforesync: true,
23490              /**
23491              * @event beforepush
23492              * Fires before the iframe editor is updated with content from the textarea. Return false
23493              * to cancel the push.
23494              * @param {HtmlEditor} this
23495              * @param {String} html
23496              */
23497             beforepush: true,
23498              /**
23499              * @event sync
23500              * Fires when the textarea is updated with content from the editor iframe.
23501              * @param {HtmlEditor} this
23502              * @param {String} html
23503              */
23504             sync: true,
23505              /**
23506              * @event push
23507              * Fires when the iframe editor is updated with content from the textarea.
23508              * @param {HtmlEditor} this
23509              * @param {String} html
23510              */
23511             push: true,
23512              /**
23513              * @event editmodechange
23514              * Fires when the editor switches edit modes
23515              * @param {HtmlEditor} this
23516              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23517              */
23518             editmodechange: true,
23519             /**
23520              * @event editorevent
23521              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23522              * @param {HtmlEditor} this
23523              */
23524             editorevent: true,
23525             /**
23526              * @event firstfocus
23527              * Fires when on first focus - needed by toolbars..
23528              * @param {HtmlEditor} this
23529              */
23530             firstfocus: true,
23531             /**
23532              * @event autosave
23533              * Auto save the htmlEditor value as a file into Events
23534              * @param {HtmlEditor} this
23535              */
23536             autosave: true,
23537             /**
23538              * @event savedpreview
23539              * preview the saved version of htmlEditor
23540              * @param {HtmlEditor} this
23541              */
23542             savedpreview: true
23543         });
23544 };
23545
23546
23547 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23548     
23549     
23550       /**
23551      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23552      */
23553     toolbars : false,
23554     
23555      /**
23556     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23557     */
23558     btns : [],
23559    
23560      /**
23561      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23562      *                        Roo.resizable.
23563      */
23564     resizable : false,
23565      /**
23566      * @cfg {Number} height (in pixels)
23567      */   
23568     height: 300,
23569    /**
23570      * @cfg {Number} width (in pixels)
23571      */   
23572     width: false,
23573     
23574     /**
23575      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23576      * 
23577      */
23578     stylesheets: false,
23579     
23580     // id of frame..
23581     frameId: false,
23582     
23583     // private properties
23584     validationEvent : false,
23585     deferHeight: true,
23586     initialized : false,
23587     activated : false,
23588     
23589     onFocus : Roo.emptyFn,
23590     iframePad:3,
23591     hideMode:'offsets',
23592     
23593     tbContainer : false,
23594     
23595     bodyCls : '',
23596     
23597     toolbarContainer :function() {
23598         return this.wrap.select('.x-html-editor-tb',true).first();
23599     },
23600
23601     /**
23602      * Protected method that will not generally be called directly. It
23603      * is called when the editor creates its toolbar. Override this method if you need to
23604      * add custom toolbar buttons.
23605      * @param {HtmlEditor} editor
23606      */
23607     createToolbar : function(){
23608         Roo.log('renewing');
23609         Roo.log("create toolbars");
23610         
23611         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23612         this.toolbars[0].render(this.toolbarContainer());
23613         
23614         return;
23615         
23616 //        if (!editor.toolbars || !editor.toolbars.length) {
23617 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23618 //        }
23619 //        
23620 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23621 //            editor.toolbars[i] = Roo.factory(
23622 //                    typeof(editor.toolbars[i]) == 'string' ?
23623 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23624 //                Roo.bootstrap.HtmlEditor);
23625 //            editor.toolbars[i].init(editor);
23626 //        }
23627     },
23628
23629      
23630     // private
23631     onRender : function(ct, position)
23632     {
23633        // Roo.log("Call onRender: " + this.xtype);
23634         var _t = this;
23635         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23636       
23637         this.wrap = this.inputEl().wrap({
23638             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23639         });
23640         
23641         this.editorcore.onRender(ct, position);
23642          
23643         if (this.resizable) {
23644             this.resizeEl = new Roo.Resizable(this.wrap, {
23645                 pinned : true,
23646                 wrap: true,
23647                 dynamic : true,
23648                 minHeight : this.height,
23649                 height: this.height,
23650                 handles : this.resizable,
23651                 width: this.width,
23652                 listeners : {
23653                     resize : function(r, w, h) {
23654                         _t.onResize(w,h); // -something
23655                     }
23656                 }
23657             });
23658             
23659         }
23660         this.createToolbar(this);
23661        
23662         
23663         if(!this.width && this.resizable){
23664             this.setSize(this.wrap.getSize());
23665         }
23666         if (this.resizeEl) {
23667             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23668             // should trigger onReize..
23669         }
23670         
23671     },
23672
23673     // private
23674     onResize : function(w, h)
23675     {
23676         Roo.log('resize: ' +w + ',' + h );
23677         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23678         var ew = false;
23679         var eh = false;
23680         
23681         if(this.inputEl() ){
23682             if(typeof w == 'number'){
23683                 var aw = w - this.wrap.getFrameWidth('lr');
23684                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23685                 ew = aw;
23686             }
23687             if(typeof h == 'number'){
23688                  var tbh = -11;  // fixme it needs to tool bar size!
23689                 for (var i =0; i < this.toolbars.length;i++) {
23690                     // fixme - ask toolbars for heights?
23691                     tbh += this.toolbars[i].el.getHeight();
23692                     //if (this.toolbars[i].footer) {
23693                     //    tbh += this.toolbars[i].footer.el.getHeight();
23694                     //}
23695                 }
23696               
23697                 
23698                 
23699                 
23700                 
23701                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23702                 ah -= 5; // knock a few pixes off for look..
23703                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23704                 var eh = ah;
23705             }
23706         }
23707         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23708         this.editorcore.onResize(ew,eh);
23709         
23710     },
23711
23712     /**
23713      * Toggles the editor between standard and source edit mode.
23714      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23715      */
23716     toggleSourceEdit : function(sourceEditMode)
23717     {
23718         this.editorcore.toggleSourceEdit(sourceEditMode);
23719         
23720         if(this.editorcore.sourceEditMode){
23721             Roo.log('editor - showing textarea');
23722             
23723 //            Roo.log('in');
23724 //            Roo.log(this.syncValue());
23725             this.syncValue();
23726             this.inputEl().removeClass(['hide', 'x-hidden']);
23727             this.inputEl().dom.removeAttribute('tabIndex');
23728             this.inputEl().focus();
23729         }else{
23730             Roo.log('editor - hiding textarea');
23731 //            Roo.log('out')
23732 //            Roo.log(this.pushValue()); 
23733             this.pushValue();
23734             
23735             this.inputEl().addClass(['hide', 'x-hidden']);
23736             this.inputEl().dom.setAttribute('tabIndex', -1);
23737             //this.deferFocus();
23738         }
23739          
23740         if(this.resizable){
23741             this.setSize(this.wrap.getSize());
23742         }
23743         
23744         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23745     },
23746  
23747     // private (for BoxComponent)
23748     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23749
23750     // private (for BoxComponent)
23751     getResizeEl : function(){
23752         return this.wrap;
23753     },
23754
23755     // private (for BoxComponent)
23756     getPositionEl : function(){
23757         return this.wrap;
23758     },
23759
23760     // private
23761     initEvents : function(){
23762         this.originalValue = this.getValue();
23763     },
23764
23765 //    /**
23766 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23767 //     * @method
23768 //     */
23769 //    markInvalid : Roo.emptyFn,
23770 //    /**
23771 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23772 //     * @method
23773 //     */
23774 //    clearInvalid : Roo.emptyFn,
23775
23776     setValue : function(v){
23777         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23778         this.editorcore.pushValue();
23779     },
23780
23781      
23782     // private
23783     deferFocus : function(){
23784         this.focus.defer(10, this);
23785     },
23786
23787     // doc'ed in Field
23788     focus : function(){
23789         this.editorcore.focus();
23790         
23791     },
23792       
23793
23794     // private
23795     onDestroy : function(){
23796         
23797         
23798         
23799         if(this.rendered){
23800             
23801             for (var i =0; i < this.toolbars.length;i++) {
23802                 // fixme - ask toolbars for heights?
23803                 this.toolbars[i].onDestroy();
23804             }
23805             
23806             this.wrap.dom.innerHTML = '';
23807             this.wrap.remove();
23808         }
23809     },
23810
23811     // private
23812     onFirstFocus : function(){
23813         //Roo.log("onFirstFocus");
23814         this.editorcore.onFirstFocus();
23815          for (var i =0; i < this.toolbars.length;i++) {
23816             this.toolbars[i].onFirstFocus();
23817         }
23818         
23819     },
23820     
23821     // private
23822     syncValue : function()
23823     {   
23824         this.editorcore.syncValue();
23825     },
23826     
23827     pushValue : function()
23828     {   
23829         this.editorcore.pushValue();
23830     }
23831      
23832     
23833     // hide stuff that is not compatible
23834     /**
23835      * @event blur
23836      * @hide
23837      */
23838     /**
23839      * @event change
23840      * @hide
23841      */
23842     /**
23843      * @event focus
23844      * @hide
23845      */
23846     /**
23847      * @event specialkey
23848      * @hide
23849      */
23850     /**
23851      * @cfg {String} fieldClass @hide
23852      */
23853     /**
23854      * @cfg {String} focusClass @hide
23855      */
23856     /**
23857      * @cfg {String} autoCreate @hide
23858      */
23859     /**
23860      * @cfg {String} inputType @hide
23861      */
23862     /**
23863      * @cfg {String} invalidClass @hide
23864      */
23865     /**
23866      * @cfg {String} invalidText @hide
23867      */
23868     /**
23869      * @cfg {String} msgFx @hide
23870      */
23871     /**
23872      * @cfg {String} validateOnBlur @hide
23873      */
23874 });
23875  
23876     
23877    
23878    
23879    
23880       
23881 Roo.namespace('Roo.bootstrap.htmleditor');
23882 /**
23883  * @class Roo.bootstrap.HtmlEditorToolbar1
23884  * Basic Toolbar
23885  * 
23886  * Usage:
23887  *
23888  new Roo.bootstrap.HtmlEditor({
23889     ....
23890     toolbars : [
23891         new Roo.bootstrap.HtmlEditorToolbar1({
23892             disable : { fonts: 1 , format: 1, ..., ... , ...],
23893             btns : [ .... ]
23894         })
23895     }
23896      
23897  * 
23898  * @cfg {Object} disable List of elements to disable..
23899  * @cfg {Array} btns List of additional buttons.
23900  * 
23901  * 
23902  * NEEDS Extra CSS? 
23903  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23904  */
23905  
23906 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23907 {
23908     
23909     Roo.apply(this, config);
23910     
23911     // default disabled, based on 'good practice'..
23912     this.disable = this.disable || {};
23913     Roo.applyIf(this.disable, {
23914         fontSize : true,
23915         colors : true,
23916         specialElements : true
23917     });
23918     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23919     
23920     this.editor = config.editor;
23921     this.editorcore = config.editor.editorcore;
23922     
23923     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23924     
23925     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23926     // dont call parent... till later.
23927 }
23928 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23929      
23930     bar : true,
23931     
23932     editor : false,
23933     editorcore : false,
23934     
23935     
23936     formats : [
23937         "p" ,  
23938         "h1","h2","h3","h4","h5","h6", 
23939         "pre", "code", 
23940         "abbr", "acronym", "address", "cite", "samp", "var",
23941         'div','span'
23942     ],
23943     
23944     onRender : function(ct, position)
23945     {
23946        // Roo.log("Call onRender: " + this.xtype);
23947         
23948        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23949        Roo.log(this.el);
23950        this.el.dom.style.marginBottom = '0';
23951        var _this = this;
23952        var editorcore = this.editorcore;
23953        var editor= this.editor;
23954        
23955        var children = [];
23956        var btn = function(id,cmd , toggle, handler, html){
23957        
23958             var  event = toggle ? 'toggle' : 'click';
23959        
23960             var a = {
23961                 size : 'sm',
23962                 xtype: 'Button',
23963                 xns: Roo.bootstrap,
23964                 glyphicon : id,
23965                 cmd : id || cmd,
23966                 enableToggle:toggle !== false,
23967                 html : html || '',
23968                 pressed : toggle ? false : null,
23969                 listeners : {}
23970             };
23971             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23972                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23973             };
23974             children.push(a);
23975             return a;
23976        }
23977        
23978     //    var cb_box = function...
23979         
23980         var style = {
23981                 xtype: 'Button',
23982                 size : 'sm',
23983                 xns: Roo.bootstrap,
23984                 glyphicon : 'font',
23985                 //html : 'submit'
23986                 menu : {
23987                     xtype: 'Menu',
23988                     xns: Roo.bootstrap,
23989                     items:  []
23990                 }
23991         };
23992         Roo.each(this.formats, function(f) {
23993             style.menu.items.push({
23994                 xtype :'MenuItem',
23995                 xns: Roo.bootstrap,
23996                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23997                 tagname : f,
23998                 listeners : {
23999                     click : function()
24000                     {
24001                         editorcore.insertTag(this.tagname);
24002                         editor.focus();
24003                     }
24004                 }
24005                 
24006             });
24007         });
24008         children.push(style);   
24009         
24010         btn('bold',false,true);
24011         btn('italic',false,true);
24012         btn('align-left', 'justifyleft',true);
24013         btn('align-center', 'justifycenter',true);
24014         btn('align-right' , 'justifyright',true);
24015         btn('link', false, false, function(btn) {
24016             //Roo.log("create link?");
24017             var url = prompt(this.createLinkText, this.defaultLinkValue);
24018             if(url && url != 'http:/'+'/'){
24019                 this.editorcore.relayCmd('createlink', url);
24020             }
24021         }),
24022         btn('list','insertunorderedlist',true);
24023         btn('pencil', false,true, function(btn){
24024                 Roo.log(this);
24025                 this.toggleSourceEdit(btn.pressed);
24026         });
24027         
24028         if (this.editor.btns.length > 0) {
24029             for (var i = 0; i<this.editor.btns.length; i++) {
24030                 children.push(this.editor.btns[i]);
24031             }
24032         }
24033         
24034         /*
24035         var cog = {
24036                 xtype: 'Button',
24037                 size : 'sm',
24038                 xns: Roo.bootstrap,
24039                 glyphicon : 'cog',
24040                 //html : 'submit'
24041                 menu : {
24042                     xtype: 'Menu',
24043                     xns: Roo.bootstrap,
24044                     items:  []
24045                 }
24046         };
24047         
24048         cog.menu.items.push({
24049             xtype :'MenuItem',
24050             xns: Roo.bootstrap,
24051             html : Clean styles,
24052             tagname : f,
24053             listeners : {
24054                 click : function()
24055                 {
24056                     editorcore.insertTag(this.tagname);
24057                     editor.focus();
24058                 }
24059             }
24060             
24061         });
24062        */
24063         
24064          
24065        this.xtype = 'NavSimplebar';
24066         
24067         for(var i=0;i< children.length;i++) {
24068             
24069             this.buttons.add(this.addxtypeChild(children[i]));
24070             
24071         }
24072         
24073         editor.on('editorevent', this.updateToolbar, this);
24074     },
24075     onBtnClick : function(id)
24076     {
24077        this.editorcore.relayCmd(id);
24078        this.editorcore.focus();
24079     },
24080     
24081     /**
24082      * Protected method that will not generally be called directly. It triggers
24083      * a toolbar update by reading the markup state of the current selection in the editor.
24084      */
24085     updateToolbar: function(){
24086
24087         if(!this.editorcore.activated){
24088             this.editor.onFirstFocus(); // is this neeed?
24089             return;
24090         }
24091
24092         var btns = this.buttons; 
24093         var doc = this.editorcore.doc;
24094         btns.get('bold').setActive(doc.queryCommandState('bold'));
24095         btns.get('italic').setActive(doc.queryCommandState('italic'));
24096         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24097         
24098         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24099         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24100         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24101         
24102         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24103         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24104          /*
24105         
24106         var ans = this.editorcore.getAllAncestors();
24107         if (this.formatCombo) {
24108             
24109             
24110             var store = this.formatCombo.store;
24111             this.formatCombo.setValue("");
24112             for (var i =0; i < ans.length;i++) {
24113                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24114                     // select it..
24115                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24116                     break;
24117                 }
24118             }
24119         }
24120         
24121         
24122         
24123         // hides menus... - so this cant be on a menu...
24124         Roo.bootstrap.MenuMgr.hideAll();
24125         */
24126         Roo.bootstrap.MenuMgr.hideAll();
24127         //this.editorsyncValue();
24128     },
24129     onFirstFocus: function() {
24130         this.buttons.each(function(item){
24131            item.enable();
24132         });
24133     },
24134     toggleSourceEdit : function(sourceEditMode){
24135         
24136           
24137         if(sourceEditMode){
24138             Roo.log("disabling buttons");
24139            this.buttons.each( function(item){
24140                 if(item.cmd != 'pencil'){
24141                     item.disable();
24142                 }
24143             });
24144           
24145         }else{
24146             Roo.log("enabling buttons");
24147             if(this.editorcore.initialized){
24148                 this.buttons.each( function(item){
24149                     item.enable();
24150                 });
24151             }
24152             
24153         }
24154         Roo.log("calling toggole on editor");
24155         // tell the editor that it's been pressed..
24156         this.editor.toggleSourceEdit(sourceEditMode);
24157        
24158     }
24159 });
24160
24161
24162
24163
24164
24165 /**
24166  * @class Roo.bootstrap.Table.AbstractSelectionModel
24167  * @extends Roo.util.Observable
24168  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24169  * implemented by descendant classes.  This class should not be directly instantiated.
24170  * @constructor
24171  */
24172 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24173     this.locked = false;
24174     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24175 };
24176
24177
24178 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24179     /** @ignore Called by the grid automatically. Do not call directly. */
24180     init : function(grid){
24181         this.grid = grid;
24182         this.initEvents();
24183     },
24184
24185     /**
24186      * Locks the selections.
24187      */
24188     lock : function(){
24189         this.locked = true;
24190     },
24191
24192     /**
24193      * Unlocks the selections.
24194      */
24195     unlock : function(){
24196         this.locked = false;
24197     },
24198
24199     /**
24200      * Returns true if the selections are locked.
24201      * @return {Boolean}
24202      */
24203     isLocked : function(){
24204         return this.locked;
24205     }
24206 });
24207 /**
24208  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24209  * @class Roo.bootstrap.Table.RowSelectionModel
24210  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24211  * It supports multiple selections and keyboard selection/navigation. 
24212  * @constructor
24213  * @param {Object} config
24214  */
24215
24216 Roo.bootstrap.Table.RowSelectionModel = function(config){
24217     Roo.apply(this, config);
24218     this.selections = new Roo.util.MixedCollection(false, function(o){
24219         return o.id;
24220     });
24221
24222     this.last = false;
24223     this.lastActive = false;
24224
24225     this.addEvents({
24226         /**
24227              * @event selectionchange
24228              * Fires when the selection changes
24229              * @param {SelectionModel} this
24230              */
24231             "selectionchange" : true,
24232         /**
24233              * @event afterselectionchange
24234              * Fires after the selection changes (eg. by key press or clicking)
24235              * @param {SelectionModel} this
24236              */
24237             "afterselectionchange" : true,
24238         /**
24239              * @event beforerowselect
24240              * Fires when a row is selected being selected, return false to cancel.
24241              * @param {SelectionModel} this
24242              * @param {Number} rowIndex The selected index
24243              * @param {Boolean} keepExisting False if other selections will be cleared
24244              */
24245             "beforerowselect" : true,
24246         /**
24247              * @event rowselect
24248              * Fires when a row is selected.
24249              * @param {SelectionModel} this
24250              * @param {Number} rowIndex The selected index
24251              * @param {Roo.data.Record} r The record
24252              */
24253             "rowselect" : true,
24254         /**
24255              * @event rowdeselect
24256              * Fires when a row is deselected.
24257              * @param {SelectionModel} this
24258              * @param {Number} rowIndex The selected index
24259              */
24260         "rowdeselect" : true
24261     });
24262     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24263     this.locked = false;
24264  };
24265
24266 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24267     /**
24268      * @cfg {Boolean} singleSelect
24269      * True to allow selection of only one row at a time (defaults to false)
24270      */
24271     singleSelect : false,
24272
24273     // private
24274     initEvents : function()
24275     {
24276
24277         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24278         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24279         //}else{ // allow click to work like normal
24280          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24281         //}
24282         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24283         this.grid.on("rowclick", this.handleMouseDown, this);
24284         
24285         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24286             "up" : function(e){
24287                 if(!e.shiftKey){
24288                     this.selectPrevious(e.shiftKey);
24289                 }else if(this.last !== false && this.lastActive !== false){
24290                     var last = this.last;
24291                     this.selectRange(this.last,  this.lastActive-1);
24292                     this.grid.getView().focusRow(this.lastActive);
24293                     if(last !== false){
24294                         this.last = last;
24295                     }
24296                 }else{
24297                     this.selectFirstRow();
24298                 }
24299                 this.fireEvent("afterselectionchange", this);
24300             },
24301             "down" : function(e){
24302                 if(!e.shiftKey){
24303                     this.selectNext(e.shiftKey);
24304                 }else if(this.last !== false && this.lastActive !== false){
24305                     var last = this.last;
24306                     this.selectRange(this.last,  this.lastActive+1);
24307                     this.grid.getView().focusRow(this.lastActive);
24308                     if(last !== false){
24309                         this.last = last;
24310                     }
24311                 }else{
24312                     this.selectFirstRow();
24313                 }
24314                 this.fireEvent("afterselectionchange", this);
24315             },
24316             scope: this
24317         });
24318         this.grid.store.on('load', function(){
24319             this.selections.clear();
24320         },this);
24321         /*
24322         var view = this.grid.view;
24323         view.on("refresh", this.onRefresh, this);
24324         view.on("rowupdated", this.onRowUpdated, this);
24325         view.on("rowremoved", this.onRemove, this);
24326         */
24327     },
24328
24329     // private
24330     onRefresh : function()
24331     {
24332         var ds = this.grid.store, i, v = this.grid.view;
24333         var s = this.selections;
24334         s.each(function(r){
24335             if((i = ds.indexOfId(r.id)) != -1){
24336                 v.onRowSelect(i);
24337             }else{
24338                 s.remove(r);
24339             }
24340         });
24341     },
24342
24343     // private
24344     onRemove : function(v, index, r){
24345         this.selections.remove(r);
24346     },
24347
24348     // private
24349     onRowUpdated : function(v, index, r){
24350         if(this.isSelected(r)){
24351             v.onRowSelect(index);
24352         }
24353     },
24354
24355     /**
24356      * Select records.
24357      * @param {Array} records The records to select
24358      * @param {Boolean} keepExisting (optional) True to keep existing selections
24359      */
24360     selectRecords : function(records, keepExisting)
24361     {
24362         if(!keepExisting){
24363             this.clearSelections();
24364         }
24365             var ds = this.grid.store;
24366         for(var i = 0, len = records.length; i < len; i++){
24367             this.selectRow(ds.indexOf(records[i]), true);
24368         }
24369     },
24370
24371     /**
24372      * Gets the number of selected rows.
24373      * @return {Number}
24374      */
24375     getCount : function(){
24376         return this.selections.length;
24377     },
24378
24379     /**
24380      * Selects the first row in the grid.
24381      */
24382     selectFirstRow : function(){
24383         this.selectRow(0);
24384     },
24385
24386     /**
24387      * Select the last row.
24388      * @param {Boolean} keepExisting (optional) True to keep existing selections
24389      */
24390     selectLastRow : function(keepExisting){
24391         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24392         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24393     },
24394
24395     /**
24396      * Selects the row immediately following the last selected row.
24397      * @param {Boolean} keepExisting (optional) True to keep existing selections
24398      */
24399     selectNext : function(keepExisting)
24400     {
24401             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24402             this.selectRow(this.last+1, keepExisting);
24403             this.grid.getView().focusRow(this.last);
24404         }
24405     },
24406
24407     /**
24408      * Selects the row that precedes the last selected row.
24409      * @param {Boolean} keepExisting (optional) True to keep existing selections
24410      */
24411     selectPrevious : function(keepExisting){
24412         if(this.last){
24413             this.selectRow(this.last-1, keepExisting);
24414             this.grid.getView().focusRow(this.last);
24415         }
24416     },
24417
24418     /**
24419      * Returns the selected records
24420      * @return {Array} Array of selected records
24421      */
24422     getSelections : function(){
24423         return [].concat(this.selections.items);
24424     },
24425
24426     /**
24427      * Returns the first selected record.
24428      * @return {Record}
24429      */
24430     getSelected : function(){
24431         return this.selections.itemAt(0);
24432     },
24433
24434
24435     /**
24436      * Clears all selections.
24437      */
24438     clearSelections : function(fast)
24439     {
24440         if(this.locked) {
24441             return;
24442         }
24443         if(fast !== true){
24444                 var ds = this.grid.store;
24445             var s = this.selections;
24446             s.each(function(r){
24447                 this.deselectRow(ds.indexOfId(r.id));
24448             }, this);
24449             s.clear();
24450         }else{
24451             this.selections.clear();
24452         }
24453         this.last = false;
24454     },
24455
24456
24457     /**
24458      * Selects all rows.
24459      */
24460     selectAll : function(){
24461         if(this.locked) {
24462             return;
24463         }
24464         this.selections.clear();
24465         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24466             this.selectRow(i, true);
24467         }
24468     },
24469
24470     /**
24471      * Returns True if there is a selection.
24472      * @return {Boolean}
24473      */
24474     hasSelection : function(){
24475         return this.selections.length > 0;
24476     },
24477
24478     /**
24479      * Returns True if the specified row is selected.
24480      * @param {Number/Record} record The record or index of the record to check
24481      * @return {Boolean}
24482      */
24483     isSelected : function(index){
24484             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24485         return (r && this.selections.key(r.id) ? true : false);
24486     },
24487
24488     /**
24489      * Returns True if the specified record id is selected.
24490      * @param {String} id The id of record to check
24491      * @return {Boolean}
24492      */
24493     isIdSelected : function(id){
24494         return (this.selections.key(id) ? true : false);
24495     },
24496
24497
24498     // private
24499     handleMouseDBClick : function(e, t){
24500         
24501     },
24502     // private
24503     handleMouseDown : function(e, t)
24504     {
24505             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24506         if(this.isLocked() || rowIndex < 0 ){
24507             return;
24508         };
24509         if(e.shiftKey && this.last !== false){
24510             var last = this.last;
24511             this.selectRange(last, rowIndex, e.ctrlKey);
24512             this.last = last; // reset the last
24513             t.focus();
24514     
24515         }else{
24516             var isSelected = this.isSelected(rowIndex);
24517             //Roo.log("select row:" + rowIndex);
24518             if(isSelected){
24519                 this.deselectRow(rowIndex);
24520             } else {
24521                         this.selectRow(rowIndex, true);
24522             }
24523     
24524             /*
24525                 if(e.button !== 0 && isSelected){
24526                 alert('rowIndex 2: ' + rowIndex);
24527                     view.focusRow(rowIndex);
24528                 }else if(e.ctrlKey && isSelected){
24529                     this.deselectRow(rowIndex);
24530                 }else if(!isSelected){
24531                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24532                     view.focusRow(rowIndex);
24533                 }
24534             */
24535         }
24536         this.fireEvent("afterselectionchange", this);
24537     },
24538     // private
24539     handleDragableRowClick :  function(grid, rowIndex, e) 
24540     {
24541         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24542             this.selectRow(rowIndex, false);
24543             grid.view.focusRow(rowIndex);
24544              this.fireEvent("afterselectionchange", this);
24545         }
24546     },
24547     
24548     /**
24549      * Selects multiple rows.
24550      * @param {Array} rows Array of the indexes of the row to select
24551      * @param {Boolean} keepExisting (optional) True to keep existing selections
24552      */
24553     selectRows : function(rows, keepExisting){
24554         if(!keepExisting){
24555             this.clearSelections();
24556         }
24557         for(var i = 0, len = rows.length; i < len; i++){
24558             this.selectRow(rows[i], true);
24559         }
24560     },
24561
24562     /**
24563      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24564      * @param {Number} startRow The index of the first row in the range
24565      * @param {Number} endRow The index of the last row in the range
24566      * @param {Boolean} keepExisting (optional) True to retain existing selections
24567      */
24568     selectRange : function(startRow, endRow, keepExisting){
24569         if(this.locked) {
24570             return;
24571         }
24572         if(!keepExisting){
24573             this.clearSelections();
24574         }
24575         if(startRow <= endRow){
24576             for(var i = startRow; i <= endRow; i++){
24577                 this.selectRow(i, true);
24578             }
24579         }else{
24580             for(var i = startRow; i >= endRow; i--){
24581                 this.selectRow(i, true);
24582             }
24583         }
24584     },
24585
24586     /**
24587      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24588      * @param {Number} startRow The index of the first row in the range
24589      * @param {Number} endRow The index of the last row in the range
24590      */
24591     deselectRange : function(startRow, endRow, preventViewNotify){
24592         if(this.locked) {
24593             return;
24594         }
24595         for(var i = startRow; i <= endRow; i++){
24596             this.deselectRow(i, preventViewNotify);
24597         }
24598     },
24599
24600     /**
24601      * Selects a row.
24602      * @param {Number} row The index of the row to select
24603      * @param {Boolean} keepExisting (optional) True to keep existing selections
24604      */
24605     selectRow : function(index, keepExisting, preventViewNotify)
24606     {
24607             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24608             return;
24609         }
24610         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24611             if(!keepExisting || this.singleSelect){
24612                 this.clearSelections();
24613             }
24614             
24615             var r = this.grid.store.getAt(index);
24616             //console.log('selectRow - record id :' + r.id);
24617             
24618             this.selections.add(r);
24619             this.last = this.lastActive = index;
24620             if(!preventViewNotify){
24621                 var proxy = new Roo.Element(
24622                                 this.grid.getRowDom(index)
24623                 );
24624                 proxy.addClass('bg-info info');
24625             }
24626             this.fireEvent("rowselect", this, index, r);
24627             this.fireEvent("selectionchange", this);
24628         }
24629     },
24630
24631     /**
24632      * Deselects a row.
24633      * @param {Number} row The index of the row to deselect
24634      */
24635     deselectRow : function(index, preventViewNotify)
24636     {
24637         if(this.locked) {
24638             return;
24639         }
24640         if(this.last == index){
24641             this.last = false;
24642         }
24643         if(this.lastActive == index){
24644             this.lastActive = false;
24645         }
24646         
24647         var r = this.grid.store.getAt(index);
24648         if (!r) {
24649             return;
24650         }
24651         
24652         this.selections.remove(r);
24653         //.console.log('deselectRow - record id :' + r.id);
24654         if(!preventViewNotify){
24655         
24656             var proxy = new Roo.Element(
24657                 this.grid.getRowDom(index)
24658             );
24659             proxy.removeClass('bg-info info');
24660         }
24661         this.fireEvent("rowdeselect", this, index);
24662         this.fireEvent("selectionchange", this);
24663     },
24664
24665     // private
24666     restoreLast : function(){
24667         if(this._last){
24668             this.last = this._last;
24669         }
24670     },
24671
24672     // private
24673     acceptsNav : function(row, col, cm){
24674         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24675     },
24676
24677     // private
24678     onEditorKey : function(field, e){
24679         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24680         if(k == e.TAB){
24681             e.stopEvent();
24682             ed.completeEdit();
24683             if(e.shiftKey){
24684                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24685             }else{
24686                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24687             }
24688         }else if(k == e.ENTER && !e.ctrlKey){
24689             e.stopEvent();
24690             ed.completeEdit();
24691             if(e.shiftKey){
24692                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24693             }else{
24694                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24695             }
24696         }else if(k == e.ESC){
24697             ed.cancelEdit();
24698         }
24699         if(newCell){
24700             g.startEditing(newCell[0], newCell[1]);
24701         }
24702     }
24703 });
24704 /*
24705  * Based on:
24706  * Ext JS Library 1.1.1
24707  * Copyright(c) 2006-2007, Ext JS, LLC.
24708  *
24709  * Originally Released Under LGPL - original licence link has changed is not relivant.
24710  *
24711  * Fork - LGPL
24712  * <script type="text/javascript">
24713  */
24714  
24715 /**
24716  * @class Roo.bootstrap.PagingToolbar
24717  * @extends Roo.bootstrap.NavSimplebar
24718  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24719  * @constructor
24720  * Create a new PagingToolbar
24721  * @param {Object} config The config object
24722  * @param {Roo.data.Store} store
24723  */
24724 Roo.bootstrap.PagingToolbar = function(config)
24725 {
24726     // old args format still supported... - xtype is prefered..
24727         // created from xtype...
24728     
24729     this.ds = config.dataSource;
24730     
24731     if (config.store && !this.ds) {
24732         this.store= Roo.factory(config.store, Roo.data);
24733         this.ds = this.store;
24734         this.ds.xmodule = this.xmodule || false;
24735     }
24736     
24737     this.toolbarItems = [];
24738     if (config.items) {
24739         this.toolbarItems = config.items;
24740     }
24741     
24742     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24743     
24744     this.cursor = 0;
24745     
24746     if (this.ds) { 
24747         this.bind(this.ds);
24748     }
24749     
24750     if (Roo.bootstrap.version == 4) {
24751         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24752     } else {
24753         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24754     }
24755     
24756 };
24757
24758 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24759     /**
24760      * @cfg {Roo.data.Store} dataSource
24761      * The underlying data store providing the paged data
24762      */
24763     /**
24764      * @cfg {String/HTMLElement/Element} container
24765      * container The id or element that will contain the toolbar
24766      */
24767     /**
24768      * @cfg {Boolean} displayInfo
24769      * True to display the displayMsg (defaults to false)
24770      */
24771     /**
24772      * @cfg {Number} pageSize
24773      * The number of records to display per page (defaults to 20)
24774      */
24775     pageSize: 20,
24776     /**
24777      * @cfg {String} displayMsg
24778      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24779      */
24780     displayMsg : 'Displaying {0} - {1} of {2}',
24781     /**
24782      * @cfg {String} emptyMsg
24783      * The message to display when no records are found (defaults to "No data to display")
24784      */
24785     emptyMsg : 'No data to display',
24786     /**
24787      * Customizable piece of the default paging text (defaults to "Page")
24788      * @type String
24789      */
24790     beforePageText : "Page",
24791     /**
24792      * Customizable piece of the default paging text (defaults to "of %0")
24793      * @type String
24794      */
24795     afterPageText : "of {0}",
24796     /**
24797      * Customizable piece of the default paging text (defaults to "First Page")
24798      * @type String
24799      */
24800     firstText : "First Page",
24801     /**
24802      * Customizable piece of the default paging text (defaults to "Previous Page")
24803      * @type String
24804      */
24805     prevText : "Previous Page",
24806     /**
24807      * Customizable piece of the default paging text (defaults to "Next Page")
24808      * @type String
24809      */
24810     nextText : "Next Page",
24811     /**
24812      * Customizable piece of the default paging text (defaults to "Last Page")
24813      * @type String
24814      */
24815     lastText : "Last Page",
24816     /**
24817      * Customizable piece of the default paging text (defaults to "Refresh")
24818      * @type String
24819      */
24820     refreshText : "Refresh",
24821
24822     buttons : false,
24823     // private
24824     onRender : function(ct, position) 
24825     {
24826         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24827         this.navgroup.parentId = this.id;
24828         this.navgroup.onRender(this.el, null);
24829         // add the buttons to the navgroup
24830         
24831         if(this.displayInfo){
24832             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24833             this.displayEl = this.el.select('.x-paging-info', true).first();
24834 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24835 //            this.displayEl = navel.el.select('span',true).first();
24836         }
24837         
24838         var _this = this;
24839         
24840         if(this.buttons){
24841             Roo.each(_this.buttons, function(e){ // this might need to use render????
24842                Roo.factory(e).render(_this.el);
24843             });
24844         }
24845             
24846         Roo.each(_this.toolbarItems, function(e) {
24847             _this.navgroup.addItem(e);
24848         });
24849         
24850         
24851         this.first = this.navgroup.addItem({
24852             tooltip: this.firstText,
24853             cls: "prev btn-outline-secondary",
24854             html : ' <i class="fa fa-step-backward"></i>',
24855             disabled: true,
24856             preventDefault: true,
24857             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24858         });
24859         
24860         this.prev =  this.navgroup.addItem({
24861             tooltip: this.prevText,
24862             cls: "prev btn-outline-secondary",
24863             html : ' <i class="fa fa-backward"></i>',
24864             disabled: true,
24865             preventDefault: true,
24866             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24867         });
24868     //this.addSeparator();
24869         
24870         
24871         var field = this.navgroup.addItem( {
24872             tagtype : 'span',
24873             cls : 'x-paging-position  btn-outline-secondary',
24874              disabled: true,
24875             html : this.beforePageText  +
24876                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24877                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24878          } ); //?? escaped?
24879         
24880         this.field = field.el.select('input', true).first();
24881         this.field.on("keydown", this.onPagingKeydown, this);
24882         this.field.on("focus", function(){this.dom.select();});
24883     
24884     
24885         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24886         //this.field.setHeight(18);
24887         //this.addSeparator();
24888         this.next = this.navgroup.addItem({
24889             tooltip: this.nextText,
24890             cls: "next btn-outline-secondary",
24891             html : ' <i class="fa fa-forward"></i>',
24892             disabled: true,
24893             preventDefault: true,
24894             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24895         });
24896         this.last = this.navgroup.addItem({
24897             tooltip: this.lastText,
24898             html : ' <i class="fa fa-step-forward"></i>',
24899             cls: "next btn-outline-secondary",
24900             disabled: true,
24901             preventDefault: true,
24902             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24903         });
24904     //this.addSeparator();
24905         this.loading = this.navgroup.addItem({
24906             tooltip: this.refreshText,
24907             cls: "btn-outline-secondary",
24908             html : ' <i class="fa fa-refresh"></i>',
24909             preventDefault: true,
24910             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24911         });
24912         
24913     },
24914
24915     // private
24916     updateInfo : function(){
24917         if(this.displayEl){
24918             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24919             var msg = count == 0 ?
24920                 this.emptyMsg :
24921                 String.format(
24922                     this.displayMsg,
24923                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24924                 );
24925             this.displayEl.update(msg);
24926         }
24927     },
24928
24929     // private
24930     onLoad : function(ds, r, o)
24931     {
24932         this.cursor = o.params.start ? o.params.start : 0;
24933         
24934         var d = this.getPageData(),
24935             ap = d.activePage,
24936             ps = d.pages;
24937         
24938         
24939         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24940         this.field.dom.value = ap;
24941         this.first.setDisabled(ap == 1);
24942         this.prev.setDisabled(ap == 1);
24943         this.next.setDisabled(ap == ps);
24944         this.last.setDisabled(ap == ps);
24945         this.loading.enable();
24946         this.updateInfo();
24947     },
24948
24949     // private
24950     getPageData : function(){
24951         var total = this.ds.getTotalCount();
24952         return {
24953             total : total,
24954             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24955             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24956         };
24957     },
24958
24959     // private
24960     onLoadError : function(){
24961         this.loading.enable();
24962     },
24963
24964     // private
24965     onPagingKeydown : function(e){
24966         var k = e.getKey();
24967         var d = this.getPageData();
24968         if(k == e.RETURN){
24969             var v = this.field.dom.value, pageNum;
24970             if(!v || isNaN(pageNum = parseInt(v, 10))){
24971                 this.field.dom.value = d.activePage;
24972                 return;
24973             }
24974             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24975             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24976             e.stopEvent();
24977         }
24978         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))
24979         {
24980           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24981           this.field.dom.value = pageNum;
24982           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24983           e.stopEvent();
24984         }
24985         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24986         {
24987           var v = this.field.dom.value, pageNum; 
24988           var increment = (e.shiftKey) ? 10 : 1;
24989           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24990                 increment *= -1;
24991           }
24992           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24993             this.field.dom.value = d.activePage;
24994             return;
24995           }
24996           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24997           {
24998             this.field.dom.value = parseInt(v, 10) + increment;
24999             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25000             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25001           }
25002           e.stopEvent();
25003         }
25004     },
25005
25006     // private
25007     beforeLoad : function(){
25008         if(this.loading){
25009             this.loading.disable();
25010         }
25011     },
25012
25013     // private
25014     onClick : function(which){
25015         
25016         var ds = this.ds;
25017         if (!ds) {
25018             return;
25019         }
25020         
25021         switch(which){
25022             case "first":
25023                 ds.load({params:{start: 0, limit: this.pageSize}});
25024             break;
25025             case "prev":
25026                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25027             break;
25028             case "next":
25029                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25030             break;
25031             case "last":
25032                 var total = ds.getTotalCount();
25033                 var extra = total % this.pageSize;
25034                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25035                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25036             break;
25037             case "refresh":
25038                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25039             break;
25040         }
25041     },
25042
25043     /**
25044      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25045      * @param {Roo.data.Store} store The data store to unbind
25046      */
25047     unbind : function(ds){
25048         ds.un("beforeload", this.beforeLoad, this);
25049         ds.un("load", this.onLoad, this);
25050         ds.un("loadexception", this.onLoadError, this);
25051         ds.un("remove", this.updateInfo, this);
25052         ds.un("add", this.updateInfo, this);
25053         this.ds = undefined;
25054     },
25055
25056     /**
25057      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25058      * @param {Roo.data.Store} store The data store to bind
25059      */
25060     bind : function(ds){
25061         ds.on("beforeload", this.beforeLoad, this);
25062         ds.on("load", this.onLoad, this);
25063         ds.on("loadexception", this.onLoadError, this);
25064         ds.on("remove", this.updateInfo, this);
25065         ds.on("add", this.updateInfo, this);
25066         this.ds = ds;
25067     }
25068 });/*
25069  * - LGPL
25070  *
25071  * element
25072  * 
25073  */
25074
25075 /**
25076  * @class Roo.bootstrap.MessageBar
25077  * @extends Roo.bootstrap.Component
25078  * Bootstrap MessageBar class
25079  * @cfg {String} html contents of the MessageBar
25080  * @cfg {String} weight (info | success | warning | danger) default info
25081  * @cfg {String} beforeClass insert the bar before the given class
25082  * @cfg {Boolean} closable (true | false) default false
25083  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25084  * 
25085  * @constructor
25086  * Create a new Element
25087  * @param {Object} config The config object
25088  */
25089
25090 Roo.bootstrap.MessageBar = function(config){
25091     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25092 };
25093
25094 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25095     
25096     html: '',
25097     weight: 'info',
25098     closable: false,
25099     fixed: false,
25100     beforeClass: 'bootstrap-sticky-wrap',
25101     
25102     getAutoCreate : function(){
25103         
25104         var cfg = {
25105             tag: 'div',
25106             cls: 'alert alert-dismissable alert-' + this.weight,
25107             cn: [
25108                 {
25109                     tag: 'span',
25110                     cls: 'message',
25111                     html: this.html || ''
25112                 }
25113             ]
25114         };
25115         
25116         if(this.fixed){
25117             cfg.cls += ' alert-messages-fixed';
25118         }
25119         
25120         if(this.closable){
25121             cfg.cn.push({
25122                 tag: 'button',
25123                 cls: 'close',
25124                 html: 'x'
25125             });
25126         }
25127         
25128         return cfg;
25129     },
25130     
25131     onRender : function(ct, position)
25132     {
25133         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25134         
25135         if(!this.el){
25136             var cfg = Roo.apply({},  this.getAutoCreate());
25137             cfg.id = Roo.id();
25138             
25139             if (this.cls) {
25140                 cfg.cls += ' ' + this.cls;
25141             }
25142             if (this.style) {
25143                 cfg.style = this.style;
25144             }
25145             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25146             
25147             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25148         }
25149         
25150         this.el.select('>button.close').on('click', this.hide, this);
25151         
25152     },
25153     
25154     show : function()
25155     {
25156         if (!this.rendered) {
25157             this.render();
25158         }
25159         
25160         this.el.show();
25161         
25162         this.fireEvent('show', this);
25163         
25164     },
25165     
25166     hide : function()
25167     {
25168         if (!this.rendered) {
25169             this.render();
25170         }
25171         
25172         this.el.hide();
25173         
25174         this.fireEvent('hide', this);
25175     },
25176     
25177     update : function()
25178     {
25179 //        var e = this.el.dom.firstChild;
25180 //        
25181 //        if(this.closable){
25182 //            e = e.nextSibling;
25183 //        }
25184 //        
25185 //        e.data = this.html || '';
25186
25187         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25188     }
25189    
25190 });
25191
25192  
25193
25194      /*
25195  * - LGPL
25196  *
25197  * Graph
25198  * 
25199  */
25200
25201
25202 /**
25203  * @class Roo.bootstrap.Graph
25204  * @extends Roo.bootstrap.Component
25205  * Bootstrap Graph class
25206 > Prameters
25207  -sm {number} sm 4
25208  -md {number} md 5
25209  @cfg {String} graphtype  bar | vbar | pie
25210  @cfg {number} g_x coodinator | centre x (pie)
25211  @cfg {number} g_y coodinator | centre y (pie)
25212  @cfg {number} g_r radius (pie)
25213  @cfg {number} g_height height of the chart (respected by all elements in the set)
25214  @cfg {number} g_width width of the chart (respected by all elements in the set)
25215  @cfg {Object} title The title of the chart
25216     
25217  -{Array}  values
25218  -opts (object) options for the chart 
25219      o {
25220      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25221      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25222      o vgutter (number)
25223      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.
25224      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25225      o to
25226      o stretch (boolean)
25227      o }
25228  -opts (object) options for the pie
25229      o{
25230      o cut
25231      o startAngle (number)
25232      o endAngle (number)
25233      } 
25234  *
25235  * @constructor
25236  * Create a new Input
25237  * @param {Object} config The config object
25238  */
25239
25240 Roo.bootstrap.Graph = function(config){
25241     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25242     
25243     this.addEvents({
25244         // img events
25245         /**
25246          * @event click
25247          * The img click event for the img.
25248          * @param {Roo.EventObject} e
25249          */
25250         "click" : true
25251     });
25252 };
25253
25254 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25255     
25256     sm: 4,
25257     md: 5,
25258     graphtype: 'bar',
25259     g_height: 250,
25260     g_width: 400,
25261     g_x: 50,
25262     g_y: 50,
25263     g_r: 30,
25264     opts:{
25265         //g_colors: this.colors,
25266         g_type: 'soft',
25267         g_gutter: '20%'
25268
25269     },
25270     title : false,
25271
25272     getAutoCreate : function(){
25273         
25274         var cfg = {
25275             tag: 'div',
25276             html : null
25277         };
25278         
25279         
25280         return  cfg;
25281     },
25282
25283     onRender : function(ct,position){
25284         
25285         
25286         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25287         
25288         if (typeof(Raphael) == 'undefined') {
25289             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25290             return;
25291         }
25292         
25293         this.raphael = Raphael(this.el.dom);
25294         
25295                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25296                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25297                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25298                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25299                 /*
25300                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25301                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25302                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25303                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25304                 
25305                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25306                 r.barchart(330, 10, 300, 220, data1);
25307                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25308                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25309                 */
25310                 
25311                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25312                 // r.barchart(30, 30, 560, 250,  xdata, {
25313                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25314                 //     axis : "0 0 1 1",
25315                 //     axisxlabels :  xdata
25316                 //     //yvalues : cols,
25317                    
25318                 // });
25319 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25320 //        
25321 //        this.load(null,xdata,{
25322 //                axis : "0 0 1 1",
25323 //                axisxlabels :  xdata
25324 //                });
25325
25326     },
25327
25328     load : function(graphtype,xdata,opts)
25329     {
25330         this.raphael.clear();
25331         if(!graphtype) {
25332             graphtype = this.graphtype;
25333         }
25334         if(!opts){
25335             opts = this.opts;
25336         }
25337         var r = this.raphael,
25338             fin = function () {
25339                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25340             },
25341             fout = function () {
25342                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25343             },
25344             pfin = function() {
25345                 this.sector.stop();
25346                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25347
25348                 if (this.label) {
25349                     this.label[0].stop();
25350                     this.label[0].attr({ r: 7.5 });
25351                     this.label[1].attr({ "font-weight": 800 });
25352                 }
25353             },
25354             pfout = function() {
25355                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25356
25357                 if (this.label) {
25358                     this.label[0].animate({ r: 5 }, 500, "bounce");
25359                     this.label[1].attr({ "font-weight": 400 });
25360                 }
25361             };
25362
25363         switch(graphtype){
25364             case 'bar':
25365                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25366                 break;
25367             case 'hbar':
25368                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25369                 break;
25370             case 'pie':
25371 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25372 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25373 //            
25374                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25375                 
25376                 break;
25377
25378         }
25379         
25380         if(this.title){
25381             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25382         }
25383         
25384     },
25385     
25386     setTitle: function(o)
25387     {
25388         this.title = o;
25389     },
25390     
25391     initEvents: function() {
25392         
25393         if(!this.href){
25394             this.el.on('click', this.onClick, this);
25395         }
25396     },
25397     
25398     onClick : function(e)
25399     {
25400         Roo.log('img onclick');
25401         this.fireEvent('click', this, e);
25402     }
25403    
25404 });
25405
25406  
25407 /*
25408  * - LGPL
25409  *
25410  * numberBox
25411  * 
25412  */
25413 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25414
25415 /**
25416  * @class Roo.bootstrap.dash.NumberBox
25417  * @extends Roo.bootstrap.Component
25418  * Bootstrap NumberBox class
25419  * @cfg {String} headline Box headline
25420  * @cfg {String} content Box content
25421  * @cfg {String} icon Box icon
25422  * @cfg {String} footer Footer text
25423  * @cfg {String} fhref Footer href
25424  * 
25425  * @constructor
25426  * Create a new NumberBox
25427  * @param {Object} config The config object
25428  */
25429
25430
25431 Roo.bootstrap.dash.NumberBox = function(config){
25432     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25433     
25434 };
25435
25436 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25437     
25438     headline : '',
25439     content : '',
25440     icon : '',
25441     footer : '',
25442     fhref : '',
25443     ficon : '',
25444     
25445     getAutoCreate : function(){
25446         
25447         var cfg = {
25448             tag : 'div',
25449             cls : 'small-box ',
25450             cn : [
25451                 {
25452                     tag : 'div',
25453                     cls : 'inner',
25454                     cn :[
25455                         {
25456                             tag : 'h3',
25457                             cls : 'roo-headline',
25458                             html : this.headline
25459                         },
25460                         {
25461                             tag : 'p',
25462                             cls : 'roo-content',
25463                             html : this.content
25464                         }
25465                     ]
25466                 }
25467             ]
25468         };
25469         
25470         if(this.icon){
25471             cfg.cn.push({
25472                 tag : 'div',
25473                 cls : 'icon',
25474                 cn :[
25475                     {
25476                         tag : 'i',
25477                         cls : 'ion ' + this.icon
25478                     }
25479                 ]
25480             });
25481         }
25482         
25483         if(this.footer){
25484             var footer = {
25485                 tag : 'a',
25486                 cls : 'small-box-footer',
25487                 href : this.fhref || '#',
25488                 html : this.footer
25489             };
25490             
25491             cfg.cn.push(footer);
25492             
25493         }
25494         
25495         return  cfg;
25496     },
25497
25498     onRender : function(ct,position){
25499         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25500
25501
25502        
25503                 
25504     },
25505
25506     setHeadline: function (value)
25507     {
25508         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25509     },
25510     
25511     setFooter: function (value, href)
25512     {
25513         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25514         
25515         if(href){
25516             this.el.select('a.small-box-footer',true).first().attr('href', href);
25517         }
25518         
25519     },
25520
25521     setContent: function (value)
25522     {
25523         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25524     },
25525
25526     initEvents: function() 
25527     {   
25528         
25529     }
25530     
25531 });
25532
25533  
25534 /*
25535  * - LGPL
25536  *
25537  * TabBox
25538  * 
25539  */
25540 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25541
25542 /**
25543  * @class Roo.bootstrap.dash.TabBox
25544  * @extends Roo.bootstrap.Component
25545  * Bootstrap TabBox class
25546  * @cfg {String} title Title of the TabBox
25547  * @cfg {String} icon Icon of the TabBox
25548  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25549  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25550  * 
25551  * @constructor
25552  * Create a new TabBox
25553  * @param {Object} config The config object
25554  */
25555
25556
25557 Roo.bootstrap.dash.TabBox = function(config){
25558     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25559     this.addEvents({
25560         // raw events
25561         /**
25562          * @event addpane
25563          * When a pane is added
25564          * @param {Roo.bootstrap.dash.TabPane} pane
25565          */
25566         "addpane" : true,
25567         /**
25568          * @event activatepane
25569          * When a pane is activated
25570          * @param {Roo.bootstrap.dash.TabPane} pane
25571          */
25572         "activatepane" : true
25573         
25574          
25575     });
25576     
25577     this.panes = [];
25578 };
25579
25580 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25581
25582     title : '',
25583     icon : false,
25584     showtabs : true,
25585     tabScrollable : false,
25586     
25587     getChildContainer : function()
25588     {
25589         return this.el.select('.tab-content', true).first();
25590     },
25591     
25592     getAutoCreate : function(){
25593         
25594         var header = {
25595             tag: 'li',
25596             cls: 'pull-left header',
25597             html: this.title,
25598             cn : []
25599         };
25600         
25601         if(this.icon){
25602             header.cn.push({
25603                 tag: 'i',
25604                 cls: 'fa ' + this.icon
25605             });
25606         }
25607         
25608         var h = {
25609             tag: 'ul',
25610             cls: 'nav nav-tabs pull-right',
25611             cn: [
25612                 header
25613             ]
25614         };
25615         
25616         if(this.tabScrollable){
25617             h = {
25618                 tag: 'div',
25619                 cls: 'tab-header',
25620                 cn: [
25621                     {
25622                         tag: 'ul',
25623                         cls: 'nav nav-tabs pull-right',
25624                         cn: [
25625                             header
25626                         ]
25627                     }
25628                 ]
25629             };
25630         }
25631         
25632         var cfg = {
25633             tag: 'div',
25634             cls: 'nav-tabs-custom',
25635             cn: [
25636                 h,
25637                 {
25638                     tag: 'div',
25639                     cls: 'tab-content no-padding',
25640                     cn: []
25641                 }
25642             ]
25643         };
25644
25645         return  cfg;
25646     },
25647     initEvents : function()
25648     {
25649         //Roo.log('add add pane handler');
25650         this.on('addpane', this.onAddPane, this);
25651     },
25652      /**
25653      * Updates the box title
25654      * @param {String} html to set the title to.
25655      */
25656     setTitle : function(value)
25657     {
25658         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25659     },
25660     onAddPane : function(pane)
25661     {
25662         this.panes.push(pane);
25663         //Roo.log('addpane');
25664         //Roo.log(pane);
25665         // tabs are rendere left to right..
25666         if(!this.showtabs){
25667             return;
25668         }
25669         
25670         var ctr = this.el.select('.nav-tabs', true).first();
25671          
25672          
25673         var existing = ctr.select('.nav-tab',true);
25674         var qty = existing.getCount();;
25675         
25676         
25677         var tab = ctr.createChild({
25678             tag : 'li',
25679             cls : 'nav-tab' + (qty ? '' : ' active'),
25680             cn : [
25681                 {
25682                     tag : 'a',
25683                     href:'#',
25684                     html : pane.title
25685                 }
25686             ]
25687         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25688         pane.tab = tab;
25689         
25690         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25691         if (!qty) {
25692             pane.el.addClass('active');
25693         }
25694         
25695                 
25696     },
25697     onTabClick : function(ev,un,ob,pane)
25698     {
25699         //Roo.log('tab - prev default');
25700         ev.preventDefault();
25701         
25702         
25703         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25704         pane.tab.addClass('active');
25705         //Roo.log(pane.title);
25706         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25707         // technically we should have a deactivate event.. but maybe add later.
25708         // and it should not de-activate the selected tab...
25709         this.fireEvent('activatepane', pane);
25710         pane.el.addClass('active');
25711         pane.fireEvent('activate');
25712         
25713         
25714     },
25715     
25716     getActivePane : function()
25717     {
25718         var r = false;
25719         Roo.each(this.panes, function(p) {
25720             if(p.el.hasClass('active')){
25721                 r = p;
25722                 return false;
25723             }
25724             
25725             return;
25726         });
25727         
25728         return r;
25729     }
25730     
25731     
25732 });
25733
25734  
25735 /*
25736  * - LGPL
25737  *
25738  * Tab pane
25739  * 
25740  */
25741 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25742 /**
25743  * @class Roo.bootstrap.TabPane
25744  * @extends Roo.bootstrap.Component
25745  * Bootstrap TabPane class
25746  * @cfg {Boolean} active (false | true) Default false
25747  * @cfg {String} title title of panel
25748
25749  * 
25750  * @constructor
25751  * Create a new TabPane
25752  * @param {Object} config The config object
25753  */
25754
25755 Roo.bootstrap.dash.TabPane = function(config){
25756     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25757     
25758     this.addEvents({
25759         // raw events
25760         /**
25761          * @event activate
25762          * When a pane is activated
25763          * @param {Roo.bootstrap.dash.TabPane} pane
25764          */
25765         "activate" : true
25766          
25767     });
25768 };
25769
25770 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25771     
25772     active : false,
25773     title : '',
25774     
25775     // the tabBox that this is attached to.
25776     tab : false,
25777      
25778     getAutoCreate : function() 
25779     {
25780         var cfg = {
25781             tag: 'div',
25782             cls: 'tab-pane'
25783         };
25784         
25785         if(this.active){
25786             cfg.cls += ' active';
25787         }
25788         
25789         return cfg;
25790     },
25791     initEvents  : function()
25792     {
25793         //Roo.log('trigger add pane handler');
25794         this.parent().fireEvent('addpane', this)
25795     },
25796     
25797      /**
25798      * Updates the tab title 
25799      * @param {String} html to set the title to.
25800      */
25801     setTitle: function(str)
25802     {
25803         if (!this.tab) {
25804             return;
25805         }
25806         this.title = str;
25807         this.tab.select('a', true).first().dom.innerHTML = str;
25808         
25809     }
25810     
25811     
25812     
25813 });
25814
25815  
25816
25817
25818  /*
25819  * - LGPL
25820  *
25821  * menu
25822  * 
25823  */
25824 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25825
25826 /**
25827  * @class Roo.bootstrap.menu.Menu
25828  * @extends Roo.bootstrap.Component
25829  * Bootstrap Menu class - container for Menu
25830  * @cfg {String} html Text of the menu
25831  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25832  * @cfg {String} icon Font awesome icon
25833  * @cfg {String} pos Menu align to (top | bottom) default bottom
25834  * 
25835  * 
25836  * @constructor
25837  * Create a new Menu
25838  * @param {Object} config The config object
25839  */
25840
25841
25842 Roo.bootstrap.menu.Menu = function(config){
25843     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25844     
25845     this.addEvents({
25846         /**
25847          * @event beforeshow
25848          * Fires before this menu is displayed
25849          * @param {Roo.bootstrap.menu.Menu} this
25850          */
25851         beforeshow : true,
25852         /**
25853          * @event beforehide
25854          * Fires before this menu is hidden
25855          * @param {Roo.bootstrap.menu.Menu} this
25856          */
25857         beforehide : true,
25858         /**
25859          * @event show
25860          * Fires after this menu is displayed
25861          * @param {Roo.bootstrap.menu.Menu} this
25862          */
25863         show : true,
25864         /**
25865          * @event hide
25866          * Fires after this menu is hidden
25867          * @param {Roo.bootstrap.menu.Menu} this
25868          */
25869         hide : true,
25870         /**
25871          * @event click
25872          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25873          * @param {Roo.bootstrap.menu.Menu} this
25874          * @param {Roo.EventObject} e
25875          */
25876         click : true
25877     });
25878     
25879 };
25880
25881 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25882     
25883     submenu : false,
25884     html : '',
25885     weight : 'default',
25886     icon : false,
25887     pos : 'bottom',
25888     
25889     
25890     getChildContainer : function() {
25891         if(this.isSubMenu){
25892             return this.el;
25893         }
25894         
25895         return this.el.select('ul.dropdown-menu', true).first();  
25896     },
25897     
25898     getAutoCreate : function()
25899     {
25900         var text = [
25901             {
25902                 tag : 'span',
25903                 cls : 'roo-menu-text',
25904                 html : this.html
25905             }
25906         ];
25907         
25908         if(this.icon){
25909             text.unshift({
25910                 tag : 'i',
25911                 cls : 'fa ' + this.icon
25912             })
25913         }
25914         
25915         
25916         var cfg = {
25917             tag : 'div',
25918             cls : 'btn-group',
25919             cn : [
25920                 {
25921                     tag : 'button',
25922                     cls : 'dropdown-button btn btn-' + this.weight,
25923                     cn : text
25924                 },
25925                 {
25926                     tag : 'button',
25927                     cls : 'dropdown-toggle btn btn-' + this.weight,
25928                     cn : [
25929                         {
25930                             tag : 'span',
25931                             cls : 'caret'
25932                         }
25933                     ]
25934                 },
25935                 {
25936                     tag : 'ul',
25937                     cls : 'dropdown-menu'
25938                 }
25939             ]
25940             
25941         };
25942         
25943         if(this.pos == 'top'){
25944             cfg.cls += ' dropup';
25945         }
25946         
25947         if(this.isSubMenu){
25948             cfg = {
25949                 tag : 'ul',
25950                 cls : 'dropdown-menu'
25951             }
25952         }
25953         
25954         return cfg;
25955     },
25956     
25957     onRender : function(ct, position)
25958     {
25959         this.isSubMenu = ct.hasClass('dropdown-submenu');
25960         
25961         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25962     },
25963     
25964     initEvents : function() 
25965     {
25966         if(this.isSubMenu){
25967             return;
25968         }
25969         
25970         this.hidden = true;
25971         
25972         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25973         this.triggerEl.on('click', this.onTriggerPress, this);
25974         
25975         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25976         this.buttonEl.on('click', this.onClick, this);
25977         
25978     },
25979     
25980     list : function()
25981     {
25982         if(this.isSubMenu){
25983             return this.el;
25984         }
25985         
25986         return this.el.select('ul.dropdown-menu', true).first();
25987     },
25988     
25989     onClick : function(e)
25990     {
25991         this.fireEvent("click", this, e);
25992     },
25993     
25994     onTriggerPress  : function(e)
25995     {   
25996         if (this.isVisible()) {
25997             this.hide();
25998         } else {
25999             this.show();
26000         }
26001     },
26002     
26003     isVisible : function(){
26004         return !this.hidden;
26005     },
26006     
26007     show : function()
26008     {
26009         this.fireEvent("beforeshow", this);
26010         
26011         this.hidden = false;
26012         this.el.addClass('open');
26013         
26014         Roo.get(document).on("mouseup", this.onMouseUp, this);
26015         
26016         this.fireEvent("show", this);
26017         
26018         
26019     },
26020     
26021     hide : function()
26022     {
26023         this.fireEvent("beforehide", this);
26024         
26025         this.hidden = true;
26026         this.el.removeClass('open');
26027         
26028         Roo.get(document).un("mouseup", this.onMouseUp);
26029         
26030         this.fireEvent("hide", this);
26031     },
26032     
26033     onMouseUp : function()
26034     {
26035         this.hide();
26036     }
26037     
26038 });
26039
26040  
26041  /*
26042  * - LGPL
26043  *
26044  * menu item
26045  * 
26046  */
26047 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26048
26049 /**
26050  * @class Roo.bootstrap.menu.Item
26051  * @extends Roo.bootstrap.Component
26052  * Bootstrap MenuItem class
26053  * @cfg {Boolean} submenu (true | false) default false
26054  * @cfg {String} html text of the item
26055  * @cfg {String} href the link
26056  * @cfg {Boolean} disable (true | false) default false
26057  * @cfg {Boolean} preventDefault (true | false) default true
26058  * @cfg {String} icon Font awesome icon
26059  * @cfg {String} pos Submenu align to (left | right) default right 
26060  * 
26061  * 
26062  * @constructor
26063  * Create a new Item
26064  * @param {Object} config The config object
26065  */
26066
26067
26068 Roo.bootstrap.menu.Item = function(config){
26069     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26070     this.addEvents({
26071         /**
26072          * @event mouseover
26073          * Fires when the mouse is hovering over this menu
26074          * @param {Roo.bootstrap.menu.Item} this
26075          * @param {Roo.EventObject} e
26076          */
26077         mouseover : true,
26078         /**
26079          * @event mouseout
26080          * Fires when the mouse exits this menu
26081          * @param {Roo.bootstrap.menu.Item} this
26082          * @param {Roo.EventObject} e
26083          */
26084         mouseout : true,
26085         // raw events
26086         /**
26087          * @event click
26088          * The raw click event for the entire grid.
26089          * @param {Roo.EventObject} e
26090          */
26091         click : true
26092     });
26093 };
26094
26095 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26096     
26097     submenu : false,
26098     href : '',
26099     html : '',
26100     preventDefault: true,
26101     disable : false,
26102     icon : false,
26103     pos : 'right',
26104     
26105     getAutoCreate : function()
26106     {
26107         var text = [
26108             {
26109                 tag : 'span',
26110                 cls : 'roo-menu-item-text',
26111                 html : this.html
26112             }
26113         ];
26114         
26115         if(this.icon){
26116             text.unshift({
26117                 tag : 'i',
26118                 cls : 'fa ' + this.icon
26119             })
26120         }
26121         
26122         var cfg = {
26123             tag : 'li',
26124             cn : [
26125                 {
26126                     tag : 'a',
26127                     href : this.href || '#',
26128                     cn : text
26129                 }
26130             ]
26131         };
26132         
26133         if(this.disable){
26134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26135         }
26136         
26137         if(this.submenu){
26138             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26139             
26140             if(this.pos == 'left'){
26141                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26142             }
26143         }
26144         
26145         return cfg;
26146     },
26147     
26148     initEvents : function() 
26149     {
26150         this.el.on('mouseover', this.onMouseOver, this);
26151         this.el.on('mouseout', this.onMouseOut, this);
26152         
26153         this.el.select('a', true).first().on('click', this.onClick, this);
26154         
26155     },
26156     
26157     onClick : function(e)
26158     {
26159         if(this.preventDefault){
26160             e.preventDefault();
26161         }
26162         
26163         this.fireEvent("click", this, e);
26164     },
26165     
26166     onMouseOver : function(e)
26167     {
26168         if(this.submenu && this.pos == 'left'){
26169             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26170         }
26171         
26172         this.fireEvent("mouseover", this, e);
26173     },
26174     
26175     onMouseOut : function(e)
26176     {
26177         this.fireEvent("mouseout", this, e);
26178     }
26179 });
26180
26181  
26182
26183  /*
26184  * - LGPL
26185  *
26186  * menu separator
26187  * 
26188  */
26189 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26190
26191 /**
26192  * @class Roo.bootstrap.menu.Separator
26193  * @extends Roo.bootstrap.Component
26194  * Bootstrap Separator class
26195  * 
26196  * @constructor
26197  * Create a new Separator
26198  * @param {Object} config The config object
26199  */
26200
26201
26202 Roo.bootstrap.menu.Separator = function(config){
26203     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26204 };
26205
26206 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26207     
26208     getAutoCreate : function(){
26209         var cfg = {
26210             tag : 'li',
26211             cls: 'divider'
26212         };
26213         
26214         return cfg;
26215     }
26216    
26217 });
26218
26219  
26220
26221  /*
26222  * - LGPL
26223  *
26224  * Tooltip
26225  * 
26226  */
26227
26228 /**
26229  * @class Roo.bootstrap.Tooltip
26230  * Bootstrap Tooltip class
26231  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26232  * to determine which dom element triggers the tooltip.
26233  * 
26234  * It needs to add support for additional attributes like tooltip-position
26235  * 
26236  * @constructor
26237  * Create a new Toolti
26238  * @param {Object} config The config object
26239  */
26240
26241 Roo.bootstrap.Tooltip = function(config){
26242     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26243     
26244     this.alignment = Roo.bootstrap.Tooltip.alignment;
26245     
26246     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26247         this.alignment = config.alignment;
26248     }
26249     
26250 };
26251
26252 Roo.apply(Roo.bootstrap.Tooltip, {
26253     /**
26254      * @function init initialize tooltip monitoring.
26255      * @static
26256      */
26257     currentEl : false,
26258     currentTip : false,
26259     currentRegion : false,
26260     
26261     //  init : delay?
26262     
26263     init : function()
26264     {
26265         Roo.get(document).on('mouseover', this.enter ,this);
26266         Roo.get(document).on('mouseout', this.leave, this);
26267          
26268         
26269         this.currentTip = new Roo.bootstrap.Tooltip();
26270     },
26271     
26272     enter : function(ev)
26273     {
26274         var dom = ev.getTarget();
26275         
26276         //Roo.log(['enter',dom]);
26277         var el = Roo.fly(dom);
26278         if (this.currentEl) {
26279             //Roo.log(dom);
26280             //Roo.log(this.currentEl);
26281             //Roo.log(this.currentEl.contains(dom));
26282             if (this.currentEl == el) {
26283                 return;
26284             }
26285             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26286                 return;
26287             }
26288
26289         }
26290         
26291         if (this.currentTip.el) {
26292             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26293         }    
26294         //Roo.log(ev);
26295         
26296         if(!el || el.dom == document){
26297             return;
26298         }
26299         
26300         var bindEl = el;
26301         
26302         // you can not look for children, as if el is the body.. then everythign is the child..
26303         if (!el.attr('tooltip')) { //
26304             if (!el.select("[tooltip]").elements.length) {
26305                 return;
26306             }
26307             // is the mouse over this child...?
26308             bindEl = el.select("[tooltip]").first();
26309             var xy = ev.getXY();
26310             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26311                 //Roo.log("not in region.");
26312                 return;
26313             }
26314             //Roo.log("child element over..");
26315             
26316         }
26317         this.currentEl = bindEl;
26318         this.currentTip.bind(bindEl);
26319         this.currentRegion = Roo.lib.Region.getRegion(dom);
26320         this.currentTip.enter();
26321         
26322     },
26323     leave : function(ev)
26324     {
26325         var dom = ev.getTarget();
26326         //Roo.log(['leave',dom]);
26327         if (!this.currentEl) {
26328             return;
26329         }
26330         
26331         
26332         if (dom != this.currentEl.dom) {
26333             return;
26334         }
26335         var xy = ev.getXY();
26336         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26337             return;
26338         }
26339         // only activate leave if mouse cursor is outside... bounding box..
26340         
26341         
26342         
26343         
26344         if (this.currentTip) {
26345             this.currentTip.leave();
26346         }
26347         //Roo.log('clear currentEl');
26348         this.currentEl = false;
26349         
26350         
26351     },
26352     alignment : {
26353         'left' : ['r-l', [-2,0], 'right'],
26354         'right' : ['l-r', [2,0], 'left'],
26355         'bottom' : ['t-b', [0,2], 'top'],
26356         'top' : [ 'b-t', [0,-2], 'bottom']
26357     }
26358     
26359 });
26360
26361
26362 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26363     
26364     
26365     bindEl : false,
26366     
26367     delay : null, // can be { show : 300 , hide: 500}
26368     
26369     timeout : null,
26370     
26371     hoverState : null, //???
26372     
26373     placement : 'bottom', 
26374     
26375     alignment : false,
26376     
26377     getAutoCreate : function(){
26378     
26379         var cfg = {
26380            cls : 'tooltip',
26381            role : 'tooltip',
26382            cn : [
26383                 {
26384                     cls : 'tooltip-arrow'
26385                 },
26386                 {
26387                     cls : 'tooltip-inner'
26388                 }
26389            ]
26390         };
26391         
26392         return cfg;
26393     },
26394     bind : function(el)
26395     {
26396         this.bindEl = el;
26397     },
26398       
26399     
26400     enter : function () {
26401        
26402         if (this.timeout != null) {
26403             clearTimeout(this.timeout);
26404         }
26405         
26406         this.hoverState = 'in';
26407          //Roo.log("enter - show");
26408         if (!this.delay || !this.delay.show) {
26409             this.show();
26410             return;
26411         }
26412         var _t = this;
26413         this.timeout = setTimeout(function () {
26414             if (_t.hoverState == 'in') {
26415                 _t.show();
26416             }
26417         }, this.delay.show);
26418     },
26419     leave : function()
26420     {
26421         clearTimeout(this.timeout);
26422     
26423         this.hoverState = 'out';
26424          if (!this.delay || !this.delay.hide) {
26425             this.hide();
26426             return;
26427         }
26428        
26429         var _t = this;
26430         this.timeout = setTimeout(function () {
26431             //Roo.log("leave - timeout");
26432             
26433             if (_t.hoverState == 'out') {
26434                 _t.hide();
26435                 Roo.bootstrap.Tooltip.currentEl = false;
26436             }
26437         }, delay);
26438     },
26439     
26440     show : function (msg)
26441     {
26442         if (!this.el) {
26443             this.render(document.body);
26444         }
26445         // set content.
26446         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26447         
26448         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26449         
26450         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26451         
26452         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26453         
26454         var placement = typeof this.placement == 'function' ?
26455             this.placement.call(this, this.el, on_el) :
26456             this.placement;
26457             
26458         var autoToken = /\s?auto?\s?/i;
26459         var autoPlace = autoToken.test(placement);
26460         if (autoPlace) {
26461             placement = placement.replace(autoToken, '') || 'top';
26462         }
26463         
26464         //this.el.detach()
26465         //this.el.setXY([0,0]);
26466         this.el.show();
26467         //this.el.dom.style.display='block';
26468         
26469         //this.el.appendTo(on_el);
26470         
26471         var p = this.getPosition();
26472         var box = this.el.getBox();
26473         
26474         if (autoPlace) {
26475             // fixme..
26476         }
26477         
26478         var align = this.alignment[placement];
26479         
26480         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26481         
26482         if(placement == 'top' || placement == 'bottom'){
26483             if(xy[0] < 0){
26484                 placement = 'right';
26485             }
26486             
26487             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26488                 placement = 'left';
26489             }
26490             
26491             var scroll = Roo.select('body', true).first().getScroll();
26492             
26493             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26494                 placement = 'top';
26495             }
26496             
26497             align = this.alignment[placement];
26498         }
26499         
26500         this.el.alignTo(this.bindEl, align[0],align[1]);
26501         //var arrow = this.el.select('.arrow',true).first();
26502         //arrow.set(align[2], 
26503         
26504         this.el.addClass(placement);
26505         
26506         this.el.addClass('in fade');
26507         
26508         this.hoverState = null;
26509         
26510         if (this.el.hasClass('fade')) {
26511             // fade it?
26512         }
26513         
26514     },
26515     hide : function()
26516     {
26517          
26518         if (!this.el) {
26519             return;
26520         }
26521         //this.el.setXY([0,0]);
26522         this.el.removeClass('in');
26523         //this.el.hide();
26524         
26525     }
26526     
26527 });
26528  
26529
26530  /*
26531  * - LGPL
26532  *
26533  * Location Picker
26534  * 
26535  */
26536
26537 /**
26538  * @class Roo.bootstrap.LocationPicker
26539  * @extends Roo.bootstrap.Component
26540  * Bootstrap LocationPicker class
26541  * @cfg {Number} latitude Position when init default 0
26542  * @cfg {Number} longitude Position when init default 0
26543  * @cfg {Number} zoom default 15
26544  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26545  * @cfg {Boolean} mapTypeControl default false
26546  * @cfg {Boolean} disableDoubleClickZoom default false
26547  * @cfg {Boolean} scrollwheel default true
26548  * @cfg {Boolean} streetViewControl default false
26549  * @cfg {Number} radius default 0
26550  * @cfg {String} locationName
26551  * @cfg {Boolean} draggable default true
26552  * @cfg {Boolean} enableAutocomplete default false
26553  * @cfg {Boolean} enableReverseGeocode default true
26554  * @cfg {String} markerTitle
26555  * 
26556  * @constructor
26557  * Create a new LocationPicker
26558  * @param {Object} config The config object
26559  */
26560
26561
26562 Roo.bootstrap.LocationPicker = function(config){
26563     
26564     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26565     
26566     this.addEvents({
26567         /**
26568          * @event initial
26569          * Fires when the picker initialized.
26570          * @param {Roo.bootstrap.LocationPicker} this
26571          * @param {Google Location} location
26572          */
26573         initial : true,
26574         /**
26575          * @event positionchanged
26576          * Fires when the picker position changed.
26577          * @param {Roo.bootstrap.LocationPicker} this
26578          * @param {Google Location} location
26579          */
26580         positionchanged : true,
26581         /**
26582          * @event resize
26583          * Fires when the map resize.
26584          * @param {Roo.bootstrap.LocationPicker} this
26585          */
26586         resize : true,
26587         /**
26588          * @event show
26589          * Fires when the map show.
26590          * @param {Roo.bootstrap.LocationPicker} this
26591          */
26592         show : true,
26593         /**
26594          * @event hide
26595          * Fires when the map hide.
26596          * @param {Roo.bootstrap.LocationPicker} this
26597          */
26598         hide : true,
26599         /**
26600          * @event mapClick
26601          * Fires when click the map.
26602          * @param {Roo.bootstrap.LocationPicker} this
26603          * @param {Map event} e
26604          */
26605         mapClick : true,
26606         /**
26607          * @event mapRightClick
26608          * Fires when right click the map.
26609          * @param {Roo.bootstrap.LocationPicker} this
26610          * @param {Map event} e
26611          */
26612         mapRightClick : true,
26613         /**
26614          * @event markerClick
26615          * Fires when click the marker.
26616          * @param {Roo.bootstrap.LocationPicker} this
26617          * @param {Map event} e
26618          */
26619         markerClick : true,
26620         /**
26621          * @event markerRightClick
26622          * Fires when right click the marker.
26623          * @param {Roo.bootstrap.LocationPicker} this
26624          * @param {Map event} e
26625          */
26626         markerRightClick : true,
26627         /**
26628          * @event OverlayViewDraw
26629          * Fires when OverlayView Draw
26630          * @param {Roo.bootstrap.LocationPicker} this
26631          */
26632         OverlayViewDraw : true,
26633         /**
26634          * @event OverlayViewOnAdd
26635          * Fires when OverlayView Draw
26636          * @param {Roo.bootstrap.LocationPicker} this
26637          */
26638         OverlayViewOnAdd : true,
26639         /**
26640          * @event OverlayViewOnRemove
26641          * Fires when OverlayView Draw
26642          * @param {Roo.bootstrap.LocationPicker} this
26643          */
26644         OverlayViewOnRemove : true,
26645         /**
26646          * @event OverlayViewShow
26647          * Fires when OverlayView Draw
26648          * @param {Roo.bootstrap.LocationPicker} this
26649          * @param {Pixel} cpx
26650          */
26651         OverlayViewShow : true,
26652         /**
26653          * @event OverlayViewHide
26654          * Fires when OverlayView Draw
26655          * @param {Roo.bootstrap.LocationPicker} this
26656          */
26657         OverlayViewHide : true,
26658         /**
26659          * @event loadexception
26660          * Fires when load google lib failed.
26661          * @param {Roo.bootstrap.LocationPicker} this
26662          */
26663         loadexception : true
26664     });
26665         
26666 };
26667
26668 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26669     
26670     gMapContext: false,
26671     
26672     latitude: 0,
26673     longitude: 0,
26674     zoom: 15,
26675     mapTypeId: false,
26676     mapTypeControl: false,
26677     disableDoubleClickZoom: false,
26678     scrollwheel: true,
26679     streetViewControl: false,
26680     radius: 0,
26681     locationName: '',
26682     draggable: true,
26683     enableAutocomplete: false,
26684     enableReverseGeocode: true,
26685     markerTitle: '',
26686     
26687     getAutoCreate: function()
26688     {
26689
26690         var cfg = {
26691             tag: 'div',
26692             cls: 'roo-location-picker'
26693         };
26694         
26695         return cfg
26696     },
26697     
26698     initEvents: function(ct, position)
26699     {       
26700         if(!this.el.getWidth() || this.isApplied()){
26701             return;
26702         }
26703         
26704         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26705         
26706         this.initial();
26707     },
26708     
26709     initial: function()
26710     {
26711         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26712             this.fireEvent('loadexception', this);
26713             return;
26714         }
26715         
26716         if(!this.mapTypeId){
26717             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26718         }
26719         
26720         this.gMapContext = this.GMapContext();
26721         
26722         this.initOverlayView();
26723         
26724         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26725         
26726         var _this = this;
26727                 
26728         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26729             _this.setPosition(_this.gMapContext.marker.position);
26730         });
26731         
26732         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26733             _this.fireEvent('mapClick', this, event);
26734             
26735         });
26736
26737         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26738             _this.fireEvent('mapRightClick', this, event);
26739             
26740         });
26741         
26742         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26743             _this.fireEvent('markerClick', this, event);
26744             
26745         });
26746
26747         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26748             _this.fireEvent('markerRightClick', this, event);
26749             
26750         });
26751         
26752         this.setPosition(this.gMapContext.location);
26753         
26754         this.fireEvent('initial', this, this.gMapContext.location);
26755     },
26756     
26757     initOverlayView: function()
26758     {
26759         var _this = this;
26760         
26761         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26762             
26763             draw: function()
26764             {
26765                 _this.fireEvent('OverlayViewDraw', _this);
26766             },
26767             
26768             onAdd: function()
26769             {
26770                 _this.fireEvent('OverlayViewOnAdd', _this);
26771             },
26772             
26773             onRemove: function()
26774             {
26775                 _this.fireEvent('OverlayViewOnRemove', _this);
26776             },
26777             
26778             show: function(cpx)
26779             {
26780                 _this.fireEvent('OverlayViewShow', _this, cpx);
26781             },
26782             
26783             hide: function()
26784             {
26785                 _this.fireEvent('OverlayViewHide', _this);
26786             }
26787             
26788         });
26789     },
26790     
26791     fromLatLngToContainerPixel: function(event)
26792     {
26793         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26794     },
26795     
26796     isApplied: function() 
26797     {
26798         return this.getGmapContext() == false ? false : true;
26799     },
26800     
26801     getGmapContext: function() 
26802     {
26803         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26804     },
26805     
26806     GMapContext: function() 
26807     {
26808         var position = new google.maps.LatLng(this.latitude, this.longitude);
26809         
26810         var _map = new google.maps.Map(this.el.dom, {
26811             center: position,
26812             zoom: this.zoom,
26813             mapTypeId: this.mapTypeId,
26814             mapTypeControl: this.mapTypeControl,
26815             disableDoubleClickZoom: this.disableDoubleClickZoom,
26816             scrollwheel: this.scrollwheel,
26817             streetViewControl: this.streetViewControl,
26818             locationName: this.locationName,
26819             draggable: this.draggable,
26820             enableAutocomplete: this.enableAutocomplete,
26821             enableReverseGeocode: this.enableReverseGeocode
26822         });
26823         
26824         var _marker = new google.maps.Marker({
26825             position: position,
26826             map: _map,
26827             title: this.markerTitle,
26828             draggable: this.draggable
26829         });
26830         
26831         return {
26832             map: _map,
26833             marker: _marker,
26834             circle: null,
26835             location: position,
26836             radius: this.radius,
26837             locationName: this.locationName,
26838             addressComponents: {
26839                 formatted_address: null,
26840                 addressLine1: null,
26841                 addressLine2: null,
26842                 streetName: null,
26843                 streetNumber: null,
26844                 city: null,
26845                 district: null,
26846                 state: null,
26847                 stateOrProvince: null
26848             },
26849             settings: this,
26850             domContainer: this.el.dom,
26851             geodecoder: new google.maps.Geocoder()
26852         };
26853     },
26854     
26855     drawCircle: function(center, radius, options) 
26856     {
26857         if (this.gMapContext.circle != null) {
26858             this.gMapContext.circle.setMap(null);
26859         }
26860         if (radius > 0) {
26861             radius *= 1;
26862             options = Roo.apply({}, options, {
26863                 strokeColor: "#0000FF",
26864                 strokeOpacity: .35,
26865                 strokeWeight: 2,
26866                 fillColor: "#0000FF",
26867                 fillOpacity: .2
26868             });
26869             
26870             options.map = this.gMapContext.map;
26871             options.radius = radius;
26872             options.center = center;
26873             this.gMapContext.circle = new google.maps.Circle(options);
26874             return this.gMapContext.circle;
26875         }
26876         
26877         return null;
26878     },
26879     
26880     setPosition: function(location) 
26881     {
26882         this.gMapContext.location = location;
26883         this.gMapContext.marker.setPosition(location);
26884         this.gMapContext.map.panTo(location);
26885         this.drawCircle(location, this.gMapContext.radius, {});
26886         
26887         var _this = this;
26888         
26889         if (this.gMapContext.settings.enableReverseGeocode) {
26890             this.gMapContext.geodecoder.geocode({
26891                 latLng: this.gMapContext.location
26892             }, function(results, status) {
26893                 
26894                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26895                     _this.gMapContext.locationName = results[0].formatted_address;
26896                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26897                     
26898                     _this.fireEvent('positionchanged', this, location);
26899                 }
26900             });
26901             
26902             return;
26903         }
26904         
26905         this.fireEvent('positionchanged', this, location);
26906     },
26907     
26908     resize: function()
26909     {
26910         google.maps.event.trigger(this.gMapContext.map, "resize");
26911         
26912         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26913         
26914         this.fireEvent('resize', this);
26915     },
26916     
26917     setPositionByLatLng: function(latitude, longitude)
26918     {
26919         this.setPosition(new google.maps.LatLng(latitude, longitude));
26920     },
26921     
26922     getCurrentPosition: function() 
26923     {
26924         return {
26925             latitude: this.gMapContext.location.lat(),
26926             longitude: this.gMapContext.location.lng()
26927         };
26928     },
26929     
26930     getAddressName: function() 
26931     {
26932         return this.gMapContext.locationName;
26933     },
26934     
26935     getAddressComponents: function() 
26936     {
26937         return this.gMapContext.addressComponents;
26938     },
26939     
26940     address_component_from_google_geocode: function(address_components) 
26941     {
26942         var result = {};
26943         
26944         for (var i = 0; i < address_components.length; i++) {
26945             var component = address_components[i];
26946             if (component.types.indexOf("postal_code") >= 0) {
26947                 result.postalCode = component.short_name;
26948             } else if (component.types.indexOf("street_number") >= 0) {
26949                 result.streetNumber = component.short_name;
26950             } else if (component.types.indexOf("route") >= 0) {
26951                 result.streetName = component.short_name;
26952             } else if (component.types.indexOf("neighborhood") >= 0) {
26953                 result.city = component.short_name;
26954             } else if (component.types.indexOf("locality") >= 0) {
26955                 result.city = component.short_name;
26956             } else if (component.types.indexOf("sublocality") >= 0) {
26957                 result.district = component.short_name;
26958             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26959                 result.stateOrProvince = component.short_name;
26960             } else if (component.types.indexOf("country") >= 0) {
26961                 result.country = component.short_name;
26962             }
26963         }
26964         
26965         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26966         result.addressLine2 = "";
26967         return result;
26968     },
26969     
26970     setZoomLevel: function(zoom)
26971     {
26972         this.gMapContext.map.setZoom(zoom);
26973     },
26974     
26975     show: function()
26976     {
26977         if(!this.el){
26978             return;
26979         }
26980         
26981         this.el.show();
26982         
26983         this.resize();
26984         
26985         this.fireEvent('show', this);
26986     },
26987     
26988     hide: function()
26989     {
26990         if(!this.el){
26991             return;
26992         }
26993         
26994         this.el.hide();
26995         
26996         this.fireEvent('hide', this);
26997     }
26998     
26999 });
27000
27001 Roo.apply(Roo.bootstrap.LocationPicker, {
27002     
27003     OverlayView : function(map, options)
27004     {
27005         options = options || {};
27006         
27007         this.setMap(map);
27008     }
27009     
27010     
27011 });/*
27012  * - LGPL
27013  *
27014  * Alert
27015  * 
27016  */
27017
27018 /**
27019  * @class Roo.bootstrap.Alert
27020  * @extends Roo.bootstrap.Component
27021  * Bootstrap Alert class
27022  * @cfg {String} title The title of alert
27023  * @cfg {String} html The content of alert
27024  * @cfg {String} weight (  success | info | warning | danger )
27025  * @cfg {String} faicon font-awesomeicon
27026  * 
27027  * @constructor
27028  * Create a new alert
27029  * @param {Object} config The config object
27030  */
27031
27032
27033 Roo.bootstrap.Alert = function(config){
27034     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27035     
27036 };
27037
27038 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27039     
27040     title: '',
27041     html: '',
27042     weight: false,
27043     faicon: false,
27044     
27045     getAutoCreate : function()
27046     {
27047         
27048         var cfg = {
27049             tag : 'div',
27050             cls : 'alert',
27051             cn : [
27052                 {
27053                     tag : 'i',
27054                     cls : 'roo-alert-icon'
27055                     
27056                 },
27057                 {
27058                     tag : 'b',
27059                     cls : 'roo-alert-title',
27060                     html : this.title
27061                 },
27062                 {
27063                     tag : 'span',
27064                     cls : 'roo-alert-text',
27065                     html : this.html
27066                 }
27067             ]
27068         };
27069         
27070         if(this.faicon){
27071             cfg.cn[0].cls += ' fa ' + this.faicon;
27072         }
27073         
27074         if(this.weight){
27075             cfg.cls += ' alert-' + this.weight;
27076         }
27077         
27078         return cfg;
27079     },
27080     
27081     initEvents: function() 
27082     {
27083         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27084     },
27085     
27086     setTitle : function(str)
27087     {
27088         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27089     },
27090     
27091     setText : function(str)
27092     {
27093         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27094     },
27095     
27096     setWeight : function(weight)
27097     {
27098         if(this.weight){
27099             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27100         }
27101         
27102         this.weight = weight;
27103         
27104         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27105     },
27106     
27107     setIcon : function(icon)
27108     {
27109         if(this.faicon){
27110             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27111         }
27112         
27113         this.faicon = icon;
27114         
27115         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27116     },
27117     
27118     hide: function() 
27119     {
27120         this.el.hide();   
27121     },
27122     
27123     show: function() 
27124     {  
27125         this.el.show();   
27126     }
27127     
27128 });
27129
27130  
27131 /*
27132 * Licence: LGPL
27133 */
27134
27135 /**
27136  * @class Roo.bootstrap.UploadCropbox
27137  * @extends Roo.bootstrap.Component
27138  * Bootstrap UploadCropbox class
27139  * @cfg {String} emptyText show when image has been loaded
27140  * @cfg {String} rotateNotify show when image too small to rotate
27141  * @cfg {Number} errorTimeout default 3000
27142  * @cfg {Number} minWidth default 300
27143  * @cfg {Number} minHeight default 300
27144  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27145  * @cfg {Boolean} isDocument (true|false) default false
27146  * @cfg {String} url action url
27147  * @cfg {String} paramName default 'imageUpload'
27148  * @cfg {String} method default POST
27149  * @cfg {Boolean} loadMask (true|false) default true
27150  * @cfg {Boolean} loadingText default 'Loading...'
27151  * 
27152  * @constructor
27153  * Create a new UploadCropbox
27154  * @param {Object} config The config object
27155  */
27156
27157 Roo.bootstrap.UploadCropbox = function(config){
27158     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27159     
27160     this.addEvents({
27161         /**
27162          * @event beforeselectfile
27163          * Fire before select file
27164          * @param {Roo.bootstrap.UploadCropbox} this
27165          */
27166         "beforeselectfile" : true,
27167         /**
27168          * @event initial
27169          * Fire after initEvent
27170          * @param {Roo.bootstrap.UploadCropbox} this
27171          */
27172         "initial" : true,
27173         /**
27174          * @event crop
27175          * Fire after initEvent
27176          * @param {Roo.bootstrap.UploadCropbox} this
27177          * @param {String} data
27178          */
27179         "crop" : true,
27180         /**
27181          * @event prepare
27182          * Fire when preparing the file data
27183          * @param {Roo.bootstrap.UploadCropbox} this
27184          * @param {Object} file
27185          */
27186         "prepare" : true,
27187         /**
27188          * @event exception
27189          * Fire when get exception
27190          * @param {Roo.bootstrap.UploadCropbox} this
27191          * @param {XMLHttpRequest} xhr
27192          */
27193         "exception" : true,
27194         /**
27195          * @event beforeloadcanvas
27196          * Fire before load the canvas
27197          * @param {Roo.bootstrap.UploadCropbox} this
27198          * @param {String} src
27199          */
27200         "beforeloadcanvas" : true,
27201         /**
27202          * @event trash
27203          * Fire when trash image
27204          * @param {Roo.bootstrap.UploadCropbox} this
27205          */
27206         "trash" : true,
27207         /**
27208          * @event download
27209          * Fire when download the image
27210          * @param {Roo.bootstrap.UploadCropbox} this
27211          */
27212         "download" : true,
27213         /**
27214          * @event footerbuttonclick
27215          * Fire when footerbuttonclick
27216          * @param {Roo.bootstrap.UploadCropbox} this
27217          * @param {String} type
27218          */
27219         "footerbuttonclick" : true,
27220         /**
27221          * @event resize
27222          * Fire when resize
27223          * @param {Roo.bootstrap.UploadCropbox} this
27224          */
27225         "resize" : true,
27226         /**
27227          * @event rotate
27228          * Fire when rotate the image
27229          * @param {Roo.bootstrap.UploadCropbox} this
27230          * @param {String} pos
27231          */
27232         "rotate" : true,
27233         /**
27234          * @event inspect
27235          * Fire when inspect the file
27236          * @param {Roo.bootstrap.UploadCropbox} this
27237          * @param {Object} file
27238          */
27239         "inspect" : true,
27240         /**
27241          * @event upload
27242          * Fire when xhr upload the file
27243          * @param {Roo.bootstrap.UploadCropbox} this
27244          * @param {Object} data
27245          */
27246         "upload" : true,
27247         /**
27248          * @event arrange
27249          * Fire when arrange the file data
27250          * @param {Roo.bootstrap.UploadCropbox} this
27251          * @param {Object} formData
27252          */
27253         "arrange" : true
27254     });
27255     
27256     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27257 };
27258
27259 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27260     
27261     emptyText : 'Click to upload image',
27262     rotateNotify : 'Image is too small to rotate',
27263     errorTimeout : 3000,
27264     scale : 0,
27265     baseScale : 1,
27266     rotate : 0,
27267     dragable : false,
27268     pinching : false,
27269     mouseX : 0,
27270     mouseY : 0,
27271     cropData : false,
27272     minWidth : 300,
27273     minHeight : 300,
27274     file : false,
27275     exif : {},
27276     baseRotate : 1,
27277     cropType : 'image/jpeg',
27278     buttons : false,
27279     canvasLoaded : false,
27280     isDocument : false,
27281     method : 'POST',
27282     paramName : 'imageUpload',
27283     loadMask : true,
27284     loadingText : 'Loading...',
27285     maskEl : false,
27286     
27287     getAutoCreate : function()
27288     {
27289         var cfg = {
27290             tag : 'div',
27291             cls : 'roo-upload-cropbox',
27292             cn : [
27293                 {
27294                     tag : 'input',
27295                     cls : 'roo-upload-cropbox-selector',
27296                     type : 'file'
27297                 },
27298                 {
27299                     tag : 'div',
27300                     cls : 'roo-upload-cropbox-body',
27301                     style : 'cursor:pointer',
27302                     cn : [
27303                         {
27304                             tag : 'div',
27305                             cls : 'roo-upload-cropbox-preview'
27306                         },
27307                         {
27308                             tag : 'div',
27309                             cls : 'roo-upload-cropbox-thumb'
27310                         },
27311                         {
27312                             tag : 'div',
27313                             cls : 'roo-upload-cropbox-empty-notify',
27314                             html : this.emptyText
27315                         },
27316                         {
27317                             tag : 'div',
27318                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27319                             html : this.rotateNotify
27320                         }
27321                     ]
27322                 },
27323                 {
27324                     tag : 'div',
27325                     cls : 'roo-upload-cropbox-footer',
27326                     cn : {
27327                         tag : 'div',
27328                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27329                         cn : []
27330                     }
27331                 }
27332             ]
27333         };
27334         
27335         return cfg;
27336     },
27337     
27338     onRender : function(ct, position)
27339     {
27340         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27341         
27342         if (this.buttons.length) {
27343             
27344             Roo.each(this.buttons, function(bb) {
27345                 
27346                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27347                 
27348                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27349                 
27350             }, this);
27351         }
27352         
27353         if(this.loadMask){
27354             this.maskEl = this.el;
27355         }
27356     },
27357     
27358     initEvents : function()
27359     {
27360         this.urlAPI = (window.createObjectURL && window) || 
27361                                 (window.URL && URL.revokeObjectURL && URL) || 
27362                                 (window.webkitURL && webkitURL);
27363                         
27364         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27365         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27366         
27367         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27368         this.selectorEl.hide();
27369         
27370         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27371         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27372         
27373         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27374         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27375         this.thumbEl.hide();
27376         
27377         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27378         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27379         
27380         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27381         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27382         this.errorEl.hide();
27383         
27384         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27385         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27386         this.footerEl.hide();
27387         
27388         this.setThumbBoxSize();
27389         
27390         this.bind();
27391         
27392         this.resize();
27393         
27394         this.fireEvent('initial', this);
27395     },
27396
27397     bind : function()
27398     {
27399         var _this = this;
27400         
27401         window.addEventListener("resize", function() { _this.resize(); } );
27402         
27403         this.bodyEl.on('click', this.beforeSelectFile, this);
27404         
27405         if(Roo.isTouch){
27406             this.bodyEl.on('touchstart', this.onTouchStart, this);
27407             this.bodyEl.on('touchmove', this.onTouchMove, this);
27408             this.bodyEl.on('touchend', this.onTouchEnd, this);
27409         }
27410         
27411         if(!Roo.isTouch){
27412             this.bodyEl.on('mousedown', this.onMouseDown, this);
27413             this.bodyEl.on('mousemove', this.onMouseMove, this);
27414             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27415             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27416             Roo.get(document).on('mouseup', this.onMouseUp, this);
27417         }
27418         
27419         this.selectorEl.on('change', this.onFileSelected, this);
27420     },
27421     
27422     reset : function()
27423     {    
27424         this.scale = 0;
27425         this.baseScale = 1;
27426         this.rotate = 0;
27427         this.baseRotate = 1;
27428         this.dragable = false;
27429         this.pinching = false;
27430         this.mouseX = 0;
27431         this.mouseY = 0;
27432         this.cropData = false;
27433         this.notifyEl.dom.innerHTML = this.emptyText;
27434         
27435         this.selectorEl.dom.value = '';
27436         
27437     },
27438     
27439     resize : function()
27440     {
27441         if(this.fireEvent('resize', this) != false){
27442             this.setThumbBoxPosition();
27443             this.setCanvasPosition();
27444         }
27445     },
27446     
27447     onFooterButtonClick : function(e, el, o, type)
27448     {
27449         switch (type) {
27450             case 'rotate-left' :
27451                 this.onRotateLeft(e);
27452                 break;
27453             case 'rotate-right' :
27454                 this.onRotateRight(e);
27455                 break;
27456             case 'picture' :
27457                 this.beforeSelectFile(e);
27458                 break;
27459             case 'trash' :
27460                 this.trash(e);
27461                 break;
27462             case 'crop' :
27463                 this.crop(e);
27464                 break;
27465             case 'download' :
27466                 this.download(e);
27467                 break;
27468             default :
27469                 break;
27470         }
27471         
27472         this.fireEvent('footerbuttonclick', this, type);
27473     },
27474     
27475     beforeSelectFile : function(e)
27476     {
27477         e.preventDefault();
27478         
27479         if(this.fireEvent('beforeselectfile', this) != false){
27480             this.selectorEl.dom.click();
27481         }
27482     },
27483     
27484     onFileSelected : function(e)
27485     {
27486         e.preventDefault();
27487         
27488         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27489             return;
27490         }
27491         
27492         var file = this.selectorEl.dom.files[0];
27493         
27494         if(this.fireEvent('inspect', this, file) != false){
27495             this.prepare(file);
27496         }
27497         
27498     },
27499     
27500     trash : function(e)
27501     {
27502         this.fireEvent('trash', this);
27503     },
27504     
27505     download : function(e)
27506     {
27507         this.fireEvent('download', this);
27508     },
27509     
27510     loadCanvas : function(src)
27511     {   
27512         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27513             
27514             this.reset();
27515             
27516             this.imageEl = document.createElement('img');
27517             
27518             var _this = this;
27519             
27520             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27521             
27522             this.imageEl.src = src;
27523         }
27524     },
27525     
27526     onLoadCanvas : function()
27527     {   
27528         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27529         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27530         
27531         this.bodyEl.un('click', this.beforeSelectFile, this);
27532         
27533         this.notifyEl.hide();
27534         this.thumbEl.show();
27535         this.footerEl.show();
27536         
27537         this.baseRotateLevel();
27538         
27539         if(this.isDocument){
27540             this.setThumbBoxSize();
27541         }
27542         
27543         this.setThumbBoxPosition();
27544         
27545         this.baseScaleLevel();
27546         
27547         this.draw();
27548         
27549         this.resize();
27550         
27551         this.canvasLoaded = true;
27552         
27553         if(this.loadMask){
27554             this.maskEl.unmask();
27555         }
27556         
27557     },
27558     
27559     setCanvasPosition : function()
27560     {   
27561         if(!this.canvasEl){
27562             return;
27563         }
27564         
27565         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27566         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27567         
27568         this.previewEl.setLeft(pw);
27569         this.previewEl.setTop(ph);
27570         
27571     },
27572     
27573     onMouseDown : function(e)
27574     {   
27575         e.stopEvent();
27576         
27577         this.dragable = true;
27578         this.pinching = false;
27579         
27580         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27581             this.dragable = false;
27582             return;
27583         }
27584         
27585         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27586         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27587         
27588     },
27589     
27590     onMouseMove : function(e)
27591     {   
27592         e.stopEvent();
27593         
27594         if(!this.canvasLoaded){
27595             return;
27596         }
27597         
27598         if (!this.dragable){
27599             return;
27600         }
27601         
27602         var minX = Math.ceil(this.thumbEl.getLeft(true));
27603         var minY = Math.ceil(this.thumbEl.getTop(true));
27604         
27605         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27606         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27607         
27608         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27609         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27610         
27611         x = x - this.mouseX;
27612         y = y - this.mouseY;
27613         
27614         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27615         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27616         
27617         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27618         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27619         
27620         this.previewEl.setLeft(bgX);
27621         this.previewEl.setTop(bgY);
27622         
27623         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27624         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27625     },
27626     
27627     onMouseUp : function(e)
27628     {   
27629         e.stopEvent();
27630         
27631         this.dragable = false;
27632     },
27633     
27634     onMouseWheel : function(e)
27635     {   
27636         e.stopEvent();
27637         
27638         this.startScale = this.scale;
27639         
27640         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27641         
27642         if(!this.zoomable()){
27643             this.scale = this.startScale;
27644             return;
27645         }
27646         
27647         this.draw();
27648         
27649         return;
27650     },
27651     
27652     zoomable : function()
27653     {
27654         var minScale = this.thumbEl.getWidth() / this.minWidth;
27655         
27656         if(this.minWidth < this.minHeight){
27657             minScale = this.thumbEl.getHeight() / this.minHeight;
27658         }
27659         
27660         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27661         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27662         
27663         if(
27664                 this.isDocument &&
27665                 (this.rotate == 0 || this.rotate == 180) && 
27666                 (
27667                     width > this.imageEl.OriginWidth || 
27668                     height > this.imageEl.OriginHeight ||
27669                     (width < this.minWidth && height < this.minHeight)
27670                 )
27671         ){
27672             return false;
27673         }
27674         
27675         if(
27676                 this.isDocument &&
27677                 (this.rotate == 90 || this.rotate == 270) && 
27678                 (
27679                     width > this.imageEl.OriginWidth || 
27680                     height > this.imageEl.OriginHeight ||
27681                     (width < this.minHeight && height < this.minWidth)
27682                 )
27683         ){
27684             return false;
27685         }
27686         
27687         if(
27688                 !this.isDocument &&
27689                 (this.rotate == 0 || this.rotate == 180) && 
27690                 (
27691                     width < this.minWidth || 
27692                     width > this.imageEl.OriginWidth || 
27693                     height < this.minHeight || 
27694                     height > this.imageEl.OriginHeight
27695                 )
27696         ){
27697             return false;
27698         }
27699         
27700         if(
27701                 !this.isDocument &&
27702                 (this.rotate == 90 || this.rotate == 270) && 
27703                 (
27704                     width < this.minHeight || 
27705                     width > this.imageEl.OriginWidth || 
27706                     height < this.minWidth || 
27707                     height > this.imageEl.OriginHeight
27708                 )
27709         ){
27710             return false;
27711         }
27712         
27713         return true;
27714         
27715     },
27716     
27717     onRotateLeft : function(e)
27718     {   
27719         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27720             
27721             var minScale = this.thumbEl.getWidth() / this.minWidth;
27722             
27723             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27724             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27725             
27726             this.startScale = this.scale;
27727             
27728             while (this.getScaleLevel() < minScale){
27729             
27730                 this.scale = this.scale + 1;
27731                 
27732                 if(!this.zoomable()){
27733                     break;
27734                 }
27735                 
27736                 if(
27737                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27738                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27739                 ){
27740                     continue;
27741                 }
27742                 
27743                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27744
27745                 this.draw();
27746                 
27747                 return;
27748             }
27749             
27750             this.scale = this.startScale;
27751             
27752             this.onRotateFail();
27753             
27754             return false;
27755         }
27756         
27757         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27758
27759         if(this.isDocument){
27760             this.setThumbBoxSize();
27761             this.setThumbBoxPosition();
27762             this.setCanvasPosition();
27763         }
27764         
27765         this.draw();
27766         
27767         this.fireEvent('rotate', this, 'left');
27768         
27769     },
27770     
27771     onRotateRight : function(e)
27772     {
27773         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27774             
27775             var minScale = this.thumbEl.getWidth() / this.minWidth;
27776         
27777             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27778             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27779             
27780             this.startScale = this.scale;
27781             
27782             while (this.getScaleLevel() < minScale){
27783             
27784                 this.scale = this.scale + 1;
27785                 
27786                 if(!this.zoomable()){
27787                     break;
27788                 }
27789                 
27790                 if(
27791                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27792                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27793                 ){
27794                     continue;
27795                 }
27796                 
27797                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27798
27799                 this.draw();
27800                 
27801                 return;
27802             }
27803             
27804             this.scale = this.startScale;
27805             
27806             this.onRotateFail();
27807             
27808             return false;
27809         }
27810         
27811         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27812
27813         if(this.isDocument){
27814             this.setThumbBoxSize();
27815             this.setThumbBoxPosition();
27816             this.setCanvasPosition();
27817         }
27818         
27819         this.draw();
27820         
27821         this.fireEvent('rotate', this, 'right');
27822     },
27823     
27824     onRotateFail : function()
27825     {
27826         this.errorEl.show(true);
27827         
27828         var _this = this;
27829         
27830         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27831     },
27832     
27833     draw : function()
27834     {
27835         this.previewEl.dom.innerHTML = '';
27836         
27837         var canvasEl = document.createElement("canvas");
27838         
27839         var contextEl = canvasEl.getContext("2d");
27840         
27841         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27842         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27843         var center = this.imageEl.OriginWidth / 2;
27844         
27845         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27846             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27847             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27848             center = this.imageEl.OriginHeight / 2;
27849         }
27850         
27851         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27852         
27853         contextEl.translate(center, center);
27854         contextEl.rotate(this.rotate * Math.PI / 180);
27855
27856         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27857         
27858         this.canvasEl = document.createElement("canvas");
27859         
27860         this.contextEl = this.canvasEl.getContext("2d");
27861         
27862         switch (this.rotate) {
27863             case 0 :
27864                 
27865                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27866                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27867                 
27868                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27869                 
27870                 break;
27871             case 90 : 
27872                 
27873                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27874                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27875                 
27876                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27877                     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);
27878                     break;
27879                 }
27880                 
27881                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27882                 
27883                 break;
27884             case 180 :
27885                 
27886                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27887                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27888                 
27889                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27890                     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);
27891                     break;
27892                 }
27893                 
27894                 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);
27895                 
27896                 break;
27897             case 270 :
27898                 
27899                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27900                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27901         
27902                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27903                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27904                     break;
27905                 }
27906                 
27907                 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);
27908                 
27909                 break;
27910             default : 
27911                 break;
27912         }
27913         
27914         this.previewEl.appendChild(this.canvasEl);
27915         
27916         this.setCanvasPosition();
27917     },
27918     
27919     crop : function()
27920     {
27921         if(!this.canvasLoaded){
27922             return;
27923         }
27924         
27925         var imageCanvas = document.createElement("canvas");
27926         
27927         var imageContext = imageCanvas.getContext("2d");
27928         
27929         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27930         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27931         
27932         var center = imageCanvas.width / 2;
27933         
27934         imageContext.translate(center, center);
27935         
27936         imageContext.rotate(this.rotate * Math.PI / 180);
27937         
27938         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27939         
27940         var canvas = document.createElement("canvas");
27941         
27942         var context = canvas.getContext("2d");
27943                 
27944         canvas.width = this.minWidth;
27945         canvas.height = this.minHeight;
27946
27947         switch (this.rotate) {
27948             case 0 :
27949                 
27950                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27951                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27952                 
27953                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27954                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27955                 
27956                 var targetWidth = this.minWidth - 2 * x;
27957                 var targetHeight = this.minHeight - 2 * y;
27958                 
27959                 var scale = 1;
27960                 
27961                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27962                     scale = targetWidth / width;
27963                 }
27964                 
27965                 if(x > 0 && y == 0){
27966                     scale = targetHeight / height;
27967                 }
27968                 
27969                 if(x > 0 && y > 0){
27970                     scale = targetWidth / width;
27971                     
27972                     if(width < height){
27973                         scale = targetHeight / height;
27974                     }
27975                 }
27976                 
27977                 context.scale(scale, scale);
27978                 
27979                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27980                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27981
27982                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27983                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27984
27985                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27986                 
27987                 break;
27988             case 90 : 
27989                 
27990                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27991                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27992                 
27993                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27994                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27995                 
27996                 var targetWidth = this.minWidth - 2 * x;
27997                 var targetHeight = this.minHeight - 2 * y;
27998                 
27999                 var scale = 1;
28000                 
28001                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28002                     scale = targetWidth / width;
28003                 }
28004                 
28005                 if(x > 0 && y == 0){
28006                     scale = targetHeight / height;
28007                 }
28008                 
28009                 if(x > 0 && y > 0){
28010                     scale = targetWidth / width;
28011                     
28012                     if(width < height){
28013                         scale = targetHeight / height;
28014                     }
28015                 }
28016                 
28017                 context.scale(scale, scale);
28018                 
28019                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28020                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28021
28022                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28023                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28024                 
28025                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28026                 
28027                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28028                 
28029                 break;
28030             case 180 :
28031                 
28032                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28033                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28034                 
28035                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28036                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28037                 
28038                 var targetWidth = this.minWidth - 2 * x;
28039                 var targetHeight = this.minHeight - 2 * y;
28040                 
28041                 var scale = 1;
28042                 
28043                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28044                     scale = targetWidth / width;
28045                 }
28046                 
28047                 if(x > 0 && y == 0){
28048                     scale = targetHeight / height;
28049                 }
28050                 
28051                 if(x > 0 && y > 0){
28052                     scale = targetWidth / width;
28053                     
28054                     if(width < height){
28055                         scale = targetHeight / height;
28056                     }
28057                 }
28058                 
28059                 context.scale(scale, scale);
28060                 
28061                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28062                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28063
28064                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28065                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28066
28067                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28068                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28069                 
28070                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28071                 
28072                 break;
28073             case 270 :
28074                 
28075                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28076                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28077                 
28078                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28079                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28080                 
28081                 var targetWidth = this.minWidth - 2 * x;
28082                 var targetHeight = this.minHeight - 2 * y;
28083                 
28084                 var scale = 1;
28085                 
28086                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28087                     scale = targetWidth / width;
28088                 }
28089                 
28090                 if(x > 0 && y == 0){
28091                     scale = targetHeight / height;
28092                 }
28093                 
28094                 if(x > 0 && y > 0){
28095                     scale = targetWidth / width;
28096                     
28097                     if(width < height){
28098                         scale = targetHeight / height;
28099                     }
28100                 }
28101                 
28102                 context.scale(scale, scale);
28103                 
28104                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28105                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28106
28107                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28108                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28109                 
28110                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28111                 
28112                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28113                 
28114                 break;
28115             default : 
28116                 break;
28117         }
28118         
28119         this.cropData = canvas.toDataURL(this.cropType);
28120         
28121         if(this.fireEvent('crop', this, this.cropData) !== false){
28122             this.process(this.file, this.cropData);
28123         }
28124         
28125         return;
28126         
28127     },
28128     
28129     setThumbBoxSize : function()
28130     {
28131         var width, height;
28132         
28133         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28134             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28135             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28136             
28137             this.minWidth = width;
28138             this.minHeight = height;
28139             
28140             if(this.rotate == 90 || this.rotate == 270){
28141                 this.minWidth = height;
28142                 this.minHeight = width;
28143             }
28144         }
28145         
28146         height = 300;
28147         width = Math.ceil(this.minWidth * height / this.minHeight);
28148         
28149         if(this.minWidth > this.minHeight){
28150             width = 300;
28151             height = Math.ceil(this.minHeight * width / this.minWidth);
28152         }
28153         
28154         this.thumbEl.setStyle({
28155             width : width + 'px',
28156             height : height + 'px'
28157         });
28158
28159         return;
28160             
28161     },
28162     
28163     setThumbBoxPosition : function()
28164     {
28165         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28166         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28167         
28168         this.thumbEl.setLeft(x);
28169         this.thumbEl.setTop(y);
28170         
28171     },
28172     
28173     baseRotateLevel : function()
28174     {
28175         this.baseRotate = 1;
28176         
28177         if(
28178                 typeof(this.exif) != 'undefined' &&
28179                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28180                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28181         ){
28182             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28183         }
28184         
28185         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28186         
28187     },
28188     
28189     baseScaleLevel : function()
28190     {
28191         var width, height;
28192         
28193         if(this.isDocument){
28194             
28195             if(this.baseRotate == 6 || this.baseRotate == 8){
28196             
28197                 height = this.thumbEl.getHeight();
28198                 this.baseScale = height / this.imageEl.OriginWidth;
28199
28200                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28201                     width = this.thumbEl.getWidth();
28202                     this.baseScale = width / this.imageEl.OriginHeight;
28203                 }
28204
28205                 return;
28206             }
28207
28208             height = this.thumbEl.getHeight();
28209             this.baseScale = height / this.imageEl.OriginHeight;
28210
28211             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28212                 width = this.thumbEl.getWidth();
28213                 this.baseScale = width / this.imageEl.OriginWidth;
28214             }
28215
28216             return;
28217         }
28218         
28219         if(this.baseRotate == 6 || this.baseRotate == 8){
28220             
28221             width = this.thumbEl.getHeight();
28222             this.baseScale = width / this.imageEl.OriginHeight;
28223             
28224             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28225                 height = this.thumbEl.getWidth();
28226                 this.baseScale = height / this.imageEl.OriginHeight;
28227             }
28228             
28229             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28230                 height = this.thumbEl.getWidth();
28231                 this.baseScale = height / this.imageEl.OriginHeight;
28232                 
28233                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28234                     width = this.thumbEl.getHeight();
28235                     this.baseScale = width / this.imageEl.OriginWidth;
28236                 }
28237             }
28238             
28239             return;
28240         }
28241         
28242         width = this.thumbEl.getWidth();
28243         this.baseScale = width / this.imageEl.OriginWidth;
28244         
28245         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28246             height = this.thumbEl.getHeight();
28247             this.baseScale = height / this.imageEl.OriginHeight;
28248         }
28249         
28250         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28251             
28252             height = this.thumbEl.getHeight();
28253             this.baseScale = height / this.imageEl.OriginHeight;
28254             
28255             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28256                 width = this.thumbEl.getWidth();
28257                 this.baseScale = width / this.imageEl.OriginWidth;
28258             }
28259             
28260         }
28261         
28262         return;
28263     },
28264     
28265     getScaleLevel : function()
28266     {
28267         return this.baseScale * Math.pow(1.1, this.scale);
28268     },
28269     
28270     onTouchStart : function(e)
28271     {
28272         if(!this.canvasLoaded){
28273             this.beforeSelectFile(e);
28274             return;
28275         }
28276         
28277         var touches = e.browserEvent.touches;
28278         
28279         if(!touches){
28280             return;
28281         }
28282         
28283         if(touches.length == 1){
28284             this.onMouseDown(e);
28285             return;
28286         }
28287         
28288         if(touches.length != 2){
28289             return;
28290         }
28291         
28292         var coords = [];
28293         
28294         for(var i = 0, finger; finger = touches[i]; i++){
28295             coords.push(finger.pageX, finger.pageY);
28296         }
28297         
28298         var x = Math.pow(coords[0] - coords[2], 2);
28299         var y = Math.pow(coords[1] - coords[3], 2);
28300         
28301         this.startDistance = Math.sqrt(x + y);
28302         
28303         this.startScale = this.scale;
28304         
28305         this.pinching = true;
28306         this.dragable = false;
28307         
28308     },
28309     
28310     onTouchMove : function(e)
28311     {
28312         if(!this.pinching && !this.dragable){
28313             return;
28314         }
28315         
28316         var touches = e.browserEvent.touches;
28317         
28318         if(!touches){
28319             return;
28320         }
28321         
28322         if(this.dragable){
28323             this.onMouseMove(e);
28324             return;
28325         }
28326         
28327         var coords = [];
28328         
28329         for(var i = 0, finger; finger = touches[i]; i++){
28330             coords.push(finger.pageX, finger.pageY);
28331         }
28332         
28333         var x = Math.pow(coords[0] - coords[2], 2);
28334         var y = Math.pow(coords[1] - coords[3], 2);
28335         
28336         this.endDistance = Math.sqrt(x + y);
28337         
28338         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28339         
28340         if(!this.zoomable()){
28341             this.scale = this.startScale;
28342             return;
28343         }
28344         
28345         this.draw();
28346         
28347     },
28348     
28349     onTouchEnd : function(e)
28350     {
28351         this.pinching = false;
28352         this.dragable = false;
28353         
28354     },
28355     
28356     process : function(file, crop)
28357     {
28358         if(this.loadMask){
28359             this.maskEl.mask(this.loadingText);
28360         }
28361         
28362         this.xhr = new XMLHttpRequest();
28363         
28364         file.xhr = this.xhr;
28365
28366         this.xhr.open(this.method, this.url, true);
28367         
28368         var headers = {
28369             "Accept": "application/json",
28370             "Cache-Control": "no-cache",
28371             "X-Requested-With": "XMLHttpRequest"
28372         };
28373         
28374         for (var headerName in headers) {
28375             var headerValue = headers[headerName];
28376             if (headerValue) {
28377                 this.xhr.setRequestHeader(headerName, headerValue);
28378             }
28379         }
28380         
28381         var _this = this;
28382         
28383         this.xhr.onload = function()
28384         {
28385             _this.xhrOnLoad(_this.xhr);
28386         }
28387         
28388         this.xhr.onerror = function()
28389         {
28390             _this.xhrOnError(_this.xhr);
28391         }
28392         
28393         var formData = new FormData();
28394
28395         formData.append('returnHTML', 'NO');
28396         
28397         if(crop){
28398             formData.append('crop', crop);
28399         }
28400         
28401         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28402             formData.append(this.paramName, file, file.name);
28403         }
28404         
28405         if(typeof(file.filename) != 'undefined'){
28406             formData.append('filename', file.filename);
28407         }
28408         
28409         if(typeof(file.mimetype) != 'undefined'){
28410             formData.append('mimetype', file.mimetype);
28411         }
28412         
28413         if(this.fireEvent('arrange', this, formData) != false){
28414             this.xhr.send(formData);
28415         };
28416     },
28417     
28418     xhrOnLoad : function(xhr)
28419     {
28420         if(this.loadMask){
28421             this.maskEl.unmask();
28422         }
28423         
28424         if (xhr.readyState !== 4) {
28425             this.fireEvent('exception', this, xhr);
28426             return;
28427         }
28428
28429         var response = Roo.decode(xhr.responseText);
28430         
28431         if(!response.success){
28432             this.fireEvent('exception', this, xhr);
28433             return;
28434         }
28435         
28436         var response = Roo.decode(xhr.responseText);
28437         
28438         this.fireEvent('upload', this, response);
28439         
28440     },
28441     
28442     xhrOnError : function()
28443     {
28444         if(this.loadMask){
28445             this.maskEl.unmask();
28446         }
28447         
28448         Roo.log('xhr on error');
28449         
28450         var response = Roo.decode(xhr.responseText);
28451           
28452         Roo.log(response);
28453         
28454     },
28455     
28456     prepare : function(file)
28457     {   
28458         if(this.loadMask){
28459             this.maskEl.mask(this.loadingText);
28460         }
28461         
28462         this.file = false;
28463         this.exif = {};
28464         
28465         if(typeof(file) === 'string'){
28466             this.loadCanvas(file);
28467             return;
28468         }
28469         
28470         if(!file || !this.urlAPI){
28471             return;
28472         }
28473         
28474         this.file = file;
28475         this.cropType = file.type;
28476         
28477         var _this = this;
28478         
28479         if(this.fireEvent('prepare', this, this.file) != false){
28480             
28481             var reader = new FileReader();
28482             
28483             reader.onload = function (e) {
28484                 if (e.target.error) {
28485                     Roo.log(e.target.error);
28486                     return;
28487                 }
28488                 
28489                 var buffer = e.target.result,
28490                     dataView = new DataView(buffer),
28491                     offset = 2,
28492                     maxOffset = dataView.byteLength - 4,
28493                     markerBytes,
28494                     markerLength;
28495                 
28496                 if (dataView.getUint16(0) === 0xffd8) {
28497                     while (offset < maxOffset) {
28498                         markerBytes = dataView.getUint16(offset);
28499                         
28500                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28501                             markerLength = dataView.getUint16(offset + 2) + 2;
28502                             if (offset + markerLength > dataView.byteLength) {
28503                                 Roo.log('Invalid meta data: Invalid segment size.');
28504                                 break;
28505                             }
28506                             
28507                             if(markerBytes == 0xffe1){
28508                                 _this.parseExifData(
28509                                     dataView,
28510                                     offset,
28511                                     markerLength
28512                                 );
28513                             }
28514                             
28515                             offset += markerLength;
28516                             
28517                             continue;
28518                         }
28519                         
28520                         break;
28521                     }
28522                     
28523                 }
28524                 
28525                 var url = _this.urlAPI.createObjectURL(_this.file);
28526                 
28527                 _this.loadCanvas(url);
28528                 
28529                 return;
28530             }
28531             
28532             reader.readAsArrayBuffer(this.file);
28533             
28534         }
28535         
28536     },
28537     
28538     parseExifData : function(dataView, offset, length)
28539     {
28540         var tiffOffset = offset + 10,
28541             littleEndian,
28542             dirOffset;
28543     
28544         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28545             // No Exif data, might be XMP data instead
28546             return;
28547         }
28548         
28549         // Check for the ASCII code for "Exif" (0x45786966):
28550         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28551             // No Exif data, might be XMP data instead
28552             return;
28553         }
28554         if (tiffOffset + 8 > dataView.byteLength) {
28555             Roo.log('Invalid Exif data: Invalid segment size.');
28556             return;
28557         }
28558         // Check for the two null bytes:
28559         if (dataView.getUint16(offset + 8) !== 0x0000) {
28560             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28561             return;
28562         }
28563         // Check the byte alignment:
28564         switch (dataView.getUint16(tiffOffset)) {
28565         case 0x4949:
28566             littleEndian = true;
28567             break;
28568         case 0x4D4D:
28569             littleEndian = false;
28570             break;
28571         default:
28572             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28573             return;
28574         }
28575         // Check for the TIFF tag marker (0x002A):
28576         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28577             Roo.log('Invalid Exif data: Missing TIFF marker.');
28578             return;
28579         }
28580         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28581         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28582         
28583         this.parseExifTags(
28584             dataView,
28585             tiffOffset,
28586             tiffOffset + dirOffset,
28587             littleEndian
28588         );
28589     },
28590     
28591     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28592     {
28593         var tagsNumber,
28594             dirEndOffset,
28595             i;
28596         if (dirOffset + 6 > dataView.byteLength) {
28597             Roo.log('Invalid Exif data: Invalid directory offset.');
28598             return;
28599         }
28600         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28601         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28602         if (dirEndOffset + 4 > dataView.byteLength) {
28603             Roo.log('Invalid Exif data: Invalid directory size.');
28604             return;
28605         }
28606         for (i = 0; i < tagsNumber; i += 1) {
28607             this.parseExifTag(
28608                 dataView,
28609                 tiffOffset,
28610                 dirOffset + 2 + 12 * i, // tag offset
28611                 littleEndian
28612             );
28613         }
28614         // Return the offset to the next directory:
28615         return dataView.getUint32(dirEndOffset, littleEndian);
28616     },
28617     
28618     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28619     {
28620         var tag = dataView.getUint16(offset, littleEndian);
28621         
28622         this.exif[tag] = this.getExifValue(
28623             dataView,
28624             tiffOffset,
28625             offset,
28626             dataView.getUint16(offset + 2, littleEndian), // tag type
28627             dataView.getUint32(offset + 4, littleEndian), // tag length
28628             littleEndian
28629         );
28630     },
28631     
28632     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28633     {
28634         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28635             tagSize,
28636             dataOffset,
28637             values,
28638             i,
28639             str,
28640             c;
28641     
28642         if (!tagType) {
28643             Roo.log('Invalid Exif data: Invalid tag type.');
28644             return;
28645         }
28646         
28647         tagSize = tagType.size * length;
28648         // Determine if the value is contained in the dataOffset bytes,
28649         // or if the value at the dataOffset is a pointer to the actual data:
28650         dataOffset = tagSize > 4 ?
28651                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28652         if (dataOffset + tagSize > dataView.byteLength) {
28653             Roo.log('Invalid Exif data: Invalid data offset.');
28654             return;
28655         }
28656         if (length === 1) {
28657             return tagType.getValue(dataView, dataOffset, littleEndian);
28658         }
28659         values = [];
28660         for (i = 0; i < length; i += 1) {
28661             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28662         }
28663         
28664         if (tagType.ascii) {
28665             str = '';
28666             // Concatenate the chars:
28667             for (i = 0; i < values.length; i += 1) {
28668                 c = values[i];
28669                 // Ignore the terminating NULL byte(s):
28670                 if (c === '\u0000') {
28671                     break;
28672                 }
28673                 str += c;
28674             }
28675             return str;
28676         }
28677         return values;
28678     }
28679     
28680 });
28681
28682 Roo.apply(Roo.bootstrap.UploadCropbox, {
28683     tags : {
28684         'Orientation': 0x0112
28685     },
28686     
28687     Orientation: {
28688             1: 0, //'top-left',
28689 //            2: 'top-right',
28690             3: 180, //'bottom-right',
28691 //            4: 'bottom-left',
28692 //            5: 'left-top',
28693             6: 90, //'right-top',
28694 //            7: 'right-bottom',
28695             8: 270 //'left-bottom'
28696     },
28697     
28698     exifTagTypes : {
28699         // byte, 8-bit unsigned int:
28700         1: {
28701             getValue: function (dataView, dataOffset) {
28702                 return dataView.getUint8(dataOffset);
28703             },
28704             size: 1
28705         },
28706         // ascii, 8-bit byte:
28707         2: {
28708             getValue: function (dataView, dataOffset) {
28709                 return String.fromCharCode(dataView.getUint8(dataOffset));
28710             },
28711             size: 1,
28712             ascii: true
28713         },
28714         // short, 16 bit int:
28715         3: {
28716             getValue: function (dataView, dataOffset, littleEndian) {
28717                 return dataView.getUint16(dataOffset, littleEndian);
28718             },
28719             size: 2
28720         },
28721         // long, 32 bit int:
28722         4: {
28723             getValue: function (dataView, dataOffset, littleEndian) {
28724                 return dataView.getUint32(dataOffset, littleEndian);
28725             },
28726             size: 4
28727         },
28728         // rational = two long values, first is numerator, second is denominator:
28729         5: {
28730             getValue: function (dataView, dataOffset, littleEndian) {
28731                 return dataView.getUint32(dataOffset, littleEndian) /
28732                     dataView.getUint32(dataOffset + 4, littleEndian);
28733             },
28734             size: 8
28735         },
28736         // slong, 32 bit signed int:
28737         9: {
28738             getValue: function (dataView, dataOffset, littleEndian) {
28739                 return dataView.getInt32(dataOffset, littleEndian);
28740             },
28741             size: 4
28742         },
28743         // srational, two slongs, first is numerator, second is denominator:
28744         10: {
28745             getValue: function (dataView, dataOffset, littleEndian) {
28746                 return dataView.getInt32(dataOffset, littleEndian) /
28747                     dataView.getInt32(dataOffset + 4, littleEndian);
28748             },
28749             size: 8
28750         }
28751     },
28752     
28753     footer : {
28754         STANDARD : [
28755             {
28756                 tag : 'div',
28757                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28758                 action : 'rotate-left',
28759                 cn : [
28760                     {
28761                         tag : 'button',
28762                         cls : 'btn btn-default',
28763                         html : '<i class="fa fa-undo"></i>'
28764                     }
28765                 ]
28766             },
28767             {
28768                 tag : 'div',
28769                 cls : 'btn-group roo-upload-cropbox-picture',
28770                 action : 'picture',
28771                 cn : [
28772                     {
28773                         tag : 'button',
28774                         cls : 'btn btn-default',
28775                         html : '<i class="fa fa-picture-o"></i>'
28776                     }
28777                 ]
28778             },
28779             {
28780                 tag : 'div',
28781                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28782                 action : 'rotate-right',
28783                 cn : [
28784                     {
28785                         tag : 'button',
28786                         cls : 'btn btn-default',
28787                         html : '<i class="fa fa-repeat"></i>'
28788                     }
28789                 ]
28790             }
28791         ],
28792         DOCUMENT : [
28793             {
28794                 tag : 'div',
28795                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28796                 action : 'rotate-left',
28797                 cn : [
28798                     {
28799                         tag : 'button',
28800                         cls : 'btn btn-default',
28801                         html : '<i class="fa fa-undo"></i>'
28802                     }
28803                 ]
28804             },
28805             {
28806                 tag : 'div',
28807                 cls : 'btn-group roo-upload-cropbox-download',
28808                 action : 'download',
28809                 cn : [
28810                     {
28811                         tag : 'button',
28812                         cls : 'btn btn-default',
28813                         html : '<i class="fa fa-download"></i>'
28814                     }
28815                 ]
28816             },
28817             {
28818                 tag : 'div',
28819                 cls : 'btn-group roo-upload-cropbox-crop',
28820                 action : 'crop',
28821                 cn : [
28822                     {
28823                         tag : 'button',
28824                         cls : 'btn btn-default',
28825                         html : '<i class="fa fa-crop"></i>'
28826                     }
28827                 ]
28828             },
28829             {
28830                 tag : 'div',
28831                 cls : 'btn-group roo-upload-cropbox-trash',
28832                 action : 'trash',
28833                 cn : [
28834                     {
28835                         tag : 'button',
28836                         cls : 'btn btn-default',
28837                         html : '<i class="fa fa-trash"></i>'
28838                     }
28839                 ]
28840             },
28841             {
28842                 tag : 'div',
28843                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28844                 action : 'rotate-right',
28845                 cn : [
28846                     {
28847                         tag : 'button',
28848                         cls : 'btn btn-default',
28849                         html : '<i class="fa fa-repeat"></i>'
28850                     }
28851                 ]
28852             }
28853         ],
28854         ROTATOR : [
28855             {
28856                 tag : 'div',
28857                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28858                 action : 'rotate-left',
28859                 cn : [
28860                     {
28861                         tag : 'button',
28862                         cls : 'btn btn-default',
28863                         html : '<i class="fa fa-undo"></i>'
28864                     }
28865                 ]
28866             },
28867             {
28868                 tag : 'div',
28869                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28870                 action : 'rotate-right',
28871                 cn : [
28872                     {
28873                         tag : 'button',
28874                         cls : 'btn btn-default',
28875                         html : '<i class="fa fa-repeat"></i>'
28876                     }
28877                 ]
28878             }
28879         ]
28880     }
28881 });
28882
28883 /*
28884 * Licence: LGPL
28885 */
28886
28887 /**
28888  * @class Roo.bootstrap.DocumentManager
28889  * @extends Roo.bootstrap.Component
28890  * Bootstrap DocumentManager class
28891  * @cfg {String} paramName default 'imageUpload'
28892  * @cfg {String} toolTipName default 'filename'
28893  * @cfg {String} method default POST
28894  * @cfg {String} url action url
28895  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28896  * @cfg {Boolean} multiple multiple upload default true
28897  * @cfg {Number} thumbSize default 300
28898  * @cfg {String} fieldLabel
28899  * @cfg {Number} labelWidth default 4
28900  * @cfg {String} labelAlign (left|top) default left
28901  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28902 * @cfg {Number} labellg set the width of label (1-12)
28903  * @cfg {Number} labelmd set the width of label (1-12)
28904  * @cfg {Number} labelsm set the width of label (1-12)
28905  * @cfg {Number} labelxs set the width of label (1-12)
28906  * 
28907  * @constructor
28908  * Create a new DocumentManager
28909  * @param {Object} config The config object
28910  */
28911
28912 Roo.bootstrap.DocumentManager = function(config){
28913     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28914     
28915     this.files = [];
28916     this.delegates = [];
28917     
28918     this.addEvents({
28919         /**
28920          * @event initial
28921          * Fire when initial the DocumentManager
28922          * @param {Roo.bootstrap.DocumentManager} this
28923          */
28924         "initial" : true,
28925         /**
28926          * @event inspect
28927          * inspect selected file
28928          * @param {Roo.bootstrap.DocumentManager} this
28929          * @param {File} file
28930          */
28931         "inspect" : true,
28932         /**
28933          * @event exception
28934          * Fire when xhr load exception
28935          * @param {Roo.bootstrap.DocumentManager} this
28936          * @param {XMLHttpRequest} xhr
28937          */
28938         "exception" : true,
28939         /**
28940          * @event afterupload
28941          * Fire when xhr load exception
28942          * @param {Roo.bootstrap.DocumentManager} this
28943          * @param {XMLHttpRequest} xhr
28944          */
28945         "afterupload" : true,
28946         /**
28947          * @event prepare
28948          * prepare the form data
28949          * @param {Roo.bootstrap.DocumentManager} this
28950          * @param {Object} formData
28951          */
28952         "prepare" : true,
28953         /**
28954          * @event remove
28955          * Fire when remove the file
28956          * @param {Roo.bootstrap.DocumentManager} this
28957          * @param {Object} file
28958          */
28959         "remove" : true,
28960         /**
28961          * @event refresh
28962          * Fire after refresh the file
28963          * @param {Roo.bootstrap.DocumentManager} this
28964          */
28965         "refresh" : true,
28966         /**
28967          * @event click
28968          * Fire after click the image
28969          * @param {Roo.bootstrap.DocumentManager} this
28970          * @param {Object} file
28971          */
28972         "click" : true,
28973         /**
28974          * @event edit
28975          * Fire when upload a image and editable set to true
28976          * @param {Roo.bootstrap.DocumentManager} this
28977          * @param {Object} file
28978          */
28979         "edit" : true,
28980         /**
28981          * @event beforeselectfile
28982          * Fire before select file
28983          * @param {Roo.bootstrap.DocumentManager} this
28984          */
28985         "beforeselectfile" : true,
28986         /**
28987          * @event process
28988          * Fire before process file
28989          * @param {Roo.bootstrap.DocumentManager} this
28990          * @param {Object} file
28991          */
28992         "process" : true,
28993         /**
28994          * @event previewrendered
28995          * Fire when preview rendered
28996          * @param {Roo.bootstrap.DocumentManager} this
28997          * @param {Object} file
28998          */
28999         "previewrendered" : true,
29000         /**
29001          */
29002         "previewResize" : true
29003         
29004     });
29005 };
29006
29007 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29008     
29009     boxes : 0,
29010     inputName : '',
29011     thumbSize : 300,
29012     multiple : true,
29013     files : false,
29014     method : 'POST',
29015     url : '',
29016     paramName : 'imageUpload',
29017     toolTipName : 'filename',
29018     fieldLabel : '',
29019     labelWidth : 4,
29020     labelAlign : 'left',
29021     editable : true,
29022     delegates : false,
29023     xhr : false, 
29024     
29025     labellg : 0,
29026     labelmd : 0,
29027     labelsm : 0,
29028     labelxs : 0,
29029     
29030     getAutoCreate : function()
29031     {   
29032         var managerWidget = {
29033             tag : 'div',
29034             cls : 'roo-document-manager',
29035             cn : [
29036                 {
29037                     tag : 'input',
29038                     cls : 'roo-document-manager-selector',
29039                     type : 'file'
29040                 },
29041                 {
29042                     tag : 'div',
29043                     cls : 'roo-document-manager-uploader',
29044                     cn : [
29045                         {
29046                             tag : 'div',
29047                             cls : 'roo-document-manager-upload-btn',
29048                             html : '<i class="fa fa-plus"></i>'
29049                         }
29050                     ]
29051                     
29052                 }
29053             ]
29054         };
29055         
29056         var content = [
29057             {
29058                 tag : 'div',
29059                 cls : 'column col-md-12',
29060                 cn : managerWidget
29061             }
29062         ];
29063         
29064         if(this.fieldLabel.length){
29065             
29066             content = [
29067                 {
29068                     tag : 'div',
29069                     cls : 'column col-md-12',
29070                     html : this.fieldLabel
29071                 },
29072                 {
29073                     tag : 'div',
29074                     cls : 'column col-md-12',
29075                     cn : managerWidget
29076                 }
29077             ];
29078
29079             if(this.labelAlign == 'left'){
29080                 content = [
29081                     {
29082                         tag : 'div',
29083                         cls : 'column',
29084                         html : this.fieldLabel
29085                     },
29086                     {
29087                         tag : 'div',
29088                         cls : 'column',
29089                         cn : managerWidget
29090                     }
29091                 ];
29092                 
29093                 if(this.labelWidth > 12){
29094                     content[0].style = "width: " + this.labelWidth + 'px';
29095                 }
29096
29097                 if(this.labelWidth < 13 && this.labelmd == 0){
29098                     this.labelmd = this.labelWidth;
29099                 }
29100
29101                 if(this.labellg > 0){
29102                     content[0].cls += ' col-lg-' + this.labellg;
29103                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29104                 }
29105
29106                 if(this.labelmd > 0){
29107                     content[0].cls += ' col-md-' + this.labelmd;
29108                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29109                 }
29110
29111                 if(this.labelsm > 0){
29112                     content[0].cls += ' col-sm-' + this.labelsm;
29113                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29114                 }
29115
29116                 if(this.labelxs > 0){
29117                     content[0].cls += ' col-xs-' + this.labelxs;
29118                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29119                 }
29120                 
29121             }
29122         }
29123         
29124         var cfg = {
29125             tag : 'div',
29126             cls : 'row clearfix',
29127             cn : content
29128         };
29129         
29130         return cfg;
29131         
29132     },
29133     
29134     initEvents : function()
29135     {
29136         this.managerEl = this.el.select('.roo-document-manager', true).first();
29137         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29138         
29139         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29140         this.selectorEl.hide();
29141         
29142         if(this.multiple){
29143             this.selectorEl.attr('multiple', 'multiple');
29144         }
29145         
29146         this.selectorEl.on('change', this.onFileSelected, this);
29147         
29148         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29149         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29150         
29151         this.uploader.on('click', this.onUploaderClick, this);
29152         
29153         this.renderProgressDialog();
29154         
29155         var _this = this;
29156         
29157         window.addEventListener("resize", function() { _this.refresh(); } );
29158         
29159         this.fireEvent('initial', this);
29160     },
29161     
29162     renderProgressDialog : function()
29163     {
29164         var _this = this;
29165         
29166         this.progressDialog = new Roo.bootstrap.Modal({
29167             cls : 'roo-document-manager-progress-dialog',
29168             allow_close : false,
29169             title : '',
29170             buttons : [
29171                 {
29172                     name  :'cancel',
29173                     weight : 'danger',
29174                     html : 'Cancel'
29175                 }
29176             ], 
29177             listeners : { 
29178                 btnclick : function() {
29179                     _this.uploadCancel();
29180                     this.hide();
29181                 }
29182             }
29183         });
29184          
29185         this.progressDialog.render(Roo.get(document.body));
29186          
29187         this.progress = new Roo.bootstrap.Progress({
29188             cls : 'roo-document-manager-progress',
29189             active : true,
29190             striped : true
29191         });
29192         
29193         this.progress.render(this.progressDialog.getChildContainer());
29194         
29195         this.progressBar = new Roo.bootstrap.ProgressBar({
29196             cls : 'roo-document-manager-progress-bar',
29197             aria_valuenow : 0,
29198             aria_valuemin : 0,
29199             aria_valuemax : 12,
29200             panel : 'success'
29201         });
29202         
29203         this.progressBar.render(this.progress.getChildContainer());
29204     },
29205     
29206     onUploaderClick : function(e)
29207     {
29208         e.preventDefault();
29209      
29210         if(this.fireEvent('beforeselectfile', this) != false){
29211             this.selectorEl.dom.click();
29212         }
29213         
29214     },
29215     
29216     onFileSelected : function(e)
29217     {
29218         e.preventDefault();
29219         
29220         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29221             return;
29222         }
29223         
29224         Roo.each(this.selectorEl.dom.files, function(file){
29225             if(this.fireEvent('inspect', this, file) != false){
29226                 this.files.push(file);
29227             }
29228         }, this);
29229         
29230         this.queue();
29231         
29232     },
29233     
29234     queue : function()
29235     {
29236         this.selectorEl.dom.value = '';
29237         
29238         if(!this.files || !this.files.length){
29239             return;
29240         }
29241         
29242         if(this.boxes > 0 && this.files.length > this.boxes){
29243             this.files = this.files.slice(0, this.boxes);
29244         }
29245         
29246         this.uploader.show();
29247         
29248         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29249             this.uploader.hide();
29250         }
29251         
29252         var _this = this;
29253         
29254         var files = [];
29255         
29256         var docs = [];
29257         
29258         Roo.each(this.files, function(file){
29259             
29260             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29261                 var f = this.renderPreview(file);
29262                 files.push(f);
29263                 return;
29264             }
29265             
29266             if(file.type.indexOf('image') != -1){
29267                 this.delegates.push(
29268                     (function(){
29269                         _this.process(file);
29270                     }).createDelegate(this)
29271                 );
29272         
29273                 return;
29274             }
29275             
29276             docs.push(
29277                 (function(){
29278                     _this.process(file);
29279                 }).createDelegate(this)
29280             );
29281             
29282         }, this);
29283         
29284         this.files = files;
29285         
29286         this.delegates = this.delegates.concat(docs);
29287         
29288         if(!this.delegates.length){
29289             this.refresh();
29290             return;
29291         }
29292         
29293         this.progressBar.aria_valuemax = this.delegates.length;
29294         
29295         this.arrange();
29296         
29297         return;
29298     },
29299     
29300     arrange : function()
29301     {
29302         if(!this.delegates.length){
29303             this.progressDialog.hide();
29304             this.refresh();
29305             return;
29306         }
29307         
29308         var delegate = this.delegates.shift();
29309         
29310         this.progressDialog.show();
29311         
29312         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29313         
29314         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29315         
29316         delegate();
29317     },
29318     
29319     refresh : function()
29320     {
29321         this.uploader.show();
29322         
29323         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29324             this.uploader.hide();
29325         }
29326         
29327         Roo.isTouch ? this.closable(false) : this.closable(true);
29328         
29329         this.fireEvent('refresh', this);
29330     },
29331     
29332     onRemove : function(e, el, o)
29333     {
29334         e.preventDefault();
29335         
29336         this.fireEvent('remove', this, o);
29337         
29338     },
29339     
29340     remove : function(o)
29341     {
29342         var files = [];
29343         
29344         Roo.each(this.files, function(file){
29345             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29346                 files.push(file);
29347                 return;
29348             }
29349
29350             o.target.remove();
29351
29352         }, this);
29353         
29354         this.files = files;
29355         
29356         this.refresh();
29357     },
29358     
29359     clear : function()
29360     {
29361         Roo.each(this.files, function(file){
29362             if(!file.target){
29363                 return;
29364             }
29365             
29366             file.target.remove();
29367
29368         }, this);
29369         
29370         this.files = [];
29371         
29372         this.refresh();
29373     },
29374     
29375     onClick : function(e, el, o)
29376     {
29377         e.preventDefault();
29378         
29379         this.fireEvent('click', this, o);
29380         
29381     },
29382     
29383     closable : function(closable)
29384     {
29385         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29386             
29387             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29388             
29389             if(closable){
29390                 el.show();
29391                 return;
29392             }
29393             
29394             el.hide();
29395             
29396         }, this);
29397     },
29398     
29399     xhrOnLoad : function(xhr)
29400     {
29401         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29402             el.remove();
29403         }, this);
29404         
29405         if (xhr.readyState !== 4) {
29406             this.arrange();
29407             this.fireEvent('exception', this, xhr);
29408             return;
29409         }
29410
29411         var response = Roo.decode(xhr.responseText);
29412         
29413         if(!response.success){
29414             this.arrange();
29415             this.fireEvent('exception', this, xhr);
29416             return;
29417         }
29418         
29419         var file = this.renderPreview(response.data);
29420         
29421         this.files.push(file);
29422         
29423         this.arrange();
29424         
29425         this.fireEvent('afterupload', this, xhr);
29426         
29427     },
29428     
29429     xhrOnError : function(xhr)
29430     {
29431         Roo.log('xhr on error');
29432         
29433         var response = Roo.decode(xhr.responseText);
29434           
29435         Roo.log(response);
29436         
29437         this.arrange();
29438     },
29439     
29440     process : function(file)
29441     {
29442         if(this.fireEvent('process', this, file) !== false){
29443             if(this.editable && file.type.indexOf('image') != -1){
29444                 this.fireEvent('edit', this, file);
29445                 return;
29446             }
29447
29448             this.uploadStart(file, false);
29449
29450             return;
29451         }
29452         
29453     },
29454     
29455     uploadStart : function(file, crop)
29456     {
29457         this.xhr = new XMLHttpRequest();
29458         
29459         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29460             this.arrange();
29461             return;
29462         }
29463         
29464         file.xhr = this.xhr;
29465             
29466         this.managerEl.createChild({
29467             tag : 'div',
29468             cls : 'roo-document-manager-loading',
29469             cn : [
29470                 {
29471                     tag : 'div',
29472                     tooltip : file.name,
29473                     cls : 'roo-document-manager-thumb',
29474                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29475                 }
29476             ]
29477
29478         });
29479
29480         this.xhr.open(this.method, this.url, true);
29481         
29482         var headers = {
29483             "Accept": "application/json",
29484             "Cache-Control": "no-cache",
29485             "X-Requested-With": "XMLHttpRequest"
29486         };
29487         
29488         for (var headerName in headers) {
29489             var headerValue = headers[headerName];
29490             if (headerValue) {
29491                 this.xhr.setRequestHeader(headerName, headerValue);
29492             }
29493         }
29494         
29495         var _this = this;
29496         
29497         this.xhr.onload = function()
29498         {
29499             _this.xhrOnLoad(_this.xhr);
29500         }
29501         
29502         this.xhr.onerror = function()
29503         {
29504             _this.xhrOnError(_this.xhr);
29505         }
29506         
29507         var formData = new FormData();
29508
29509         formData.append('returnHTML', 'NO');
29510         
29511         if(crop){
29512             formData.append('crop', crop);
29513         }
29514         
29515         formData.append(this.paramName, file, file.name);
29516         
29517         var options = {
29518             file : file, 
29519             manually : false
29520         };
29521         
29522         if(this.fireEvent('prepare', this, formData, options) != false){
29523             
29524             if(options.manually){
29525                 return;
29526             }
29527             
29528             this.xhr.send(formData);
29529             return;
29530         };
29531         
29532         this.uploadCancel();
29533     },
29534     
29535     uploadCancel : function()
29536     {
29537         if (this.xhr) {
29538             this.xhr.abort();
29539         }
29540         
29541         this.delegates = [];
29542         
29543         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29544             el.remove();
29545         }, this);
29546         
29547         this.arrange();
29548     },
29549     
29550     renderPreview : function(file)
29551     {
29552         if(typeof(file.target) != 'undefined' && file.target){
29553             return file;
29554         }
29555         
29556         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29557         
29558         var previewEl = this.managerEl.createChild({
29559             tag : 'div',
29560             cls : 'roo-document-manager-preview',
29561             cn : [
29562                 {
29563                     tag : 'div',
29564                     tooltip : file[this.toolTipName],
29565                     cls : 'roo-document-manager-thumb',
29566                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29567                 },
29568                 {
29569                     tag : 'button',
29570                     cls : 'close',
29571                     html : '<i class="fa fa-times-circle"></i>'
29572                 }
29573             ]
29574         });
29575
29576         var close = previewEl.select('button.close', true).first();
29577
29578         close.on('click', this.onRemove, this, file);
29579
29580         file.target = previewEl;
29581
29582         var image = previewEl.select('img', true).first();
29583         
29584         var _this = this;
29585         
29586         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29587         
29588         image.on('click', this.onClick, this, file);
29589         
29590         this.fireEvent('previewrendered', this, file);
29591         
29592         return file;
29593         
29594     },
29595     
29596     onPreviewLoad : function(file, image)
29597     {
29598         if(typeof(file.target) == 'undefined' || !file.target){
29599             return;
29600         }
29601         
29602         var width = image.dom.naturalWidth || image.dom.width;
29603         var height = image.dom.naturalHeight || image.dom.height;
29604         
29605         if(!this.previewResize) {
29606             return;
29607         }
29608         
29609         if(width > height){
29610             file.target.addClass('wide');
29611             return;
29612         }
29613         
29614         file.target.addClass('tall');
29615         return;
29616         
29617     },
29618     
29619     uploadFromSource : function(file, crop)
29620     {
29621         this.xhr = new XMLHttpRequest();
29622         
29623         this.managerEl.createChild({
29624             tag : 'div',
29625             cls : 'roo-document-manager-loading',
29626             cn : [
29627                 {
29628                     tag : 'div',
29629                     tooltip : file.name,
29630                     cls : 'roo-document-manager-thumb',
29631                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29632                 }
29633             ]
29634
29635         });
29636
29637         this.xhr.open(this.method, this.url, true);
29638         
29639         var headers = {
29640             "Accept": "application/json",
29641             "Cache-Control": "no-cache",
29642             "X-Requested-With": "XMLHttpRequest"
29643         };
29644         
29645         for (var headerName in headers) {
29646             var headerValue = headers[headerName];
29647             if (headerValue) {
29648                 this.xhr.setRequestHeader(headerName, headerValue);
29649             }
29650         }
29651         
29652         var _this = this;
29653         
29654         this.xhr.onload = function()
29655         {
29656             _this.xhrOnLoad(_this.xhr);
29657         }
29658         
29659         this.xhr.onerror = function()
29660         {
29661             _this.xhrOnError(_this.xhr);
29662         }
29663         
29664         var formData = new FormData();
29665
29666         formData.append('returnHTML', 'NO');
29667         
29668         formData.append('crop', crop);
29669         
29670         if(typeof(file.filename) != 'undefined'){
29671             formData.append('filename', file.filename);
29672         }
29673         
29674         if(typeof(file.mimetype) != 'undefined'){
29675             formData.append('mimetype', file.mimetype);
29676         }
29677         
29678         Roo.log(formData);
29679         
29680         if(this.fireEvent('prepare', this, formData) != false){
29681             this.xhr.send(formData);
29682         };
29683     }
29684 });
29685
29686 /*
29687 * Licence: LGPL
29688 */
29689
29690 /**
29691  * @class Roo.bootstrap.DocumentViewer
29692  * @extends Roo.bootstrap.Component
29693  * Bootstrap DocumentViewer class
29694  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29695  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29696  * 
29697  * @constructor
29698  * Create a new DocumentViewer
29699  * @param {Object} config The config object
29700  */
29701
29702 Roo.bootstrap.DocumentViewer = function(config){
29703     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29704     
29705     this.addEvents({
29706         /**
29707          * @event initial
29708          * Fire after initEvent
29709          * @param {Roo.bootstrap.DocumentViewer} this
29710          */
29711         "initial" : true,
29712         /**
29713          * @event click
29714          * Fire after click
29715          * @param {Roo.bootstrap.DocumentViewer} this
29716          */
29717         "click" : true,
29718         /**
29719          * @event download
29720          * Fire after download button
29721          * @param {Roo.bootstrap.DocumentViewer} this
29722          */
29723         "download" : true,
29724         /**
29725          * @event trash
29726          * Fire after trash button
29727          * @param {Roo.bootstrap.DocumentViewer} this
29728          */
29729         "trash" : true
29730         
29731     });
29732 };
29733
29734 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29735     
29736     showDownload : true,
29737     
29738     showTrash : true,
29739     
29740     getAutoCreate : function()
29741     {
29742         var cfg = {
29743             tag : 'div',
29744             cls : 'roo-document-viewer',
29745             cn : [
29746                 {
29747                     tag : 'div',
29748                     cls : 'roo-document-viewer-body',
29749                     cn : [
29750                         {
29751                             tag : 'div',
29752                             cls : 'roo-document-viewer-thumb',
29753                             cn : [
29754                                 {
29755                                     tag : 'img',
29756                                     cls : 'roo-document-viewer-image'
29757                                 }
29758                             ]
29759                         }
29760                     ]
29761                 },
29762                 {
29763                     tag : 'div',
29764                     cls : 'roo-document-viewer-footer',
29765                     cn : {
29766                         tag : 'div',
29767                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29768                         cn : [
29769                             {
29770                                 tag : 'div',
29771                                 cls : 'btn-group roo-document-viewer-download',
29772                                 cn : [
29773                                     {
29774                                         tag : 'button',
29775                                         cls : 'btn btn-default',
29776                                         html : '<i class="fa fa-download"></i>'
29777                                     }
29778                                 ]
29779                             },
29780                             {
29781                                 tag : 'div',
29782                                 cls : 'btn-group roo-document-viewer-trash',
29783                                 cn : [
29784                                     {
29785                                         tag : 'button',
29786                                         cls : 'btn btn-default',
29787                                         html : '<i class="fa fa-trash"></i>'
29788                                     }
29789                                 ]
29790                             }
29791                         ]
29792                     }
29793                 }
29794             ]
29795         };
29796         
29797         return cfg;
29798     },
29799     
29800     initEvents : function()
29801     {
29802         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29803         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29804         
29805         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29806         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29807         
29808         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29809         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29810         
29811         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29812         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29813         
29814         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29815         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29816         
29817         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29818         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29819         
29820         this.bodyEl.on('click', this.onClick, this);
29821         this.downloadBtn.on('click', this.onDownload, this);
29822         this.trashBtn.on('click', this.onTrash, this);
29823         
29824         this.downloadBtn.hide();
29825         this.trashBtn.hide();
29826         
29827         if(this.showDownload){
29828             this.downloadBtn.show();
29829         }
29830         
29831         if(this.showTrash){
29832             this.trashBtn.show();
29833         }
29834         
29835         if(!this.showDownload && !this.showTrash) {
29836             this.footerEl.hide();
29837         }
29838         
29839     },
29840     
29841     initial : function()
29842     {
29843         this.fireEvent('initial', this);
29844         
29845     },
29846     
29847     onClick : function(e)
29848     {
29849         e.preventDefault();
29850         
29851         this.fireEvent('click', this);
29852     },
29853     
29854     onDownload : function(e)
29855     {
29856         e.preventDefault();
29857         
29858         this.fireEvent('download', this);
29859     },
29860     
29861     onTrash : function(e)
29862     {
29863         e.preventDefault();
29864         
29865         this.fireEvent('trash', this);
29866     }
29867     
29868 });
29869 /*
29870  * - LGPL
29871  *
29872  * nav progress bar
29873  * 
29874  */
29875
29876 /**
29877  * @class Roo.bootstrap.NavProgressBar
29878  * @extends Roo.bootstrap.Component
29879  * Bootstrap NavProgressBar class
29880  * 
29881  * @constructor
29882  * Create a new nav progress bar
29883  * @param {Object} config The config object
29884  */
29885
29886 Roo.bootstrap.NavProgressBar = function(config){
29887     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29888
29889     this.bullets = this.bullets || [];
29890    
29891 //    Roo.bootstrap.NavProgressBar.register(this);
29892      this.addEvents({
29893         /**
29894              * @event changed
29895              * Fires when the active item changes
29896              * @param {Roo.bootstrap.NavProgressBar} this
29897              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29898              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29899          */
29900         'changed': true
29901      });
29902     
29903 };
29904
29905 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29906     
29907     bullets : [],
29908     barItems : [],
29909     
29910     getAutoCreate : function()
29911     {
29912         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29913         
29914         cfg = {
29915             tag : 'div',
29916             cls : 'roo-navigation-bar-group',
29917             cn : [
29918                 {
29919                     tag : 'div',
29920                     cls : 'roo-navigation-top-bar'
29921                 },
29922                 {
29923                     tag : 'div',
29924                     cls : 'roo-navigation-bullets-bar',
29925                     cn : [
29926                         {
29927                             tag : 'ul',
29928                             cls : 'roo-navigation-bar'
29929                         }
29930                     ]
29931                 },
29932                 
29933                 {
29934                     tag : 'div',
29935                     cls : 'roo-navigation-bottom-bar'
29936                 }
29937             ]
29938             
29939         };
29940         
29941         return cfg;
29942         
29943     },
29944     
29945     initEvents: function() 
29946     {
29947         
29948     },
29949     
29950     onRender : function(ct, position) 
29951     {
29952         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29953         
29954         if(this.bullets.length){
29955             Roo.each(this.bullets, function(b){
29956                this.addItem(b);
29957             }, this);
29958         }
29959         
29960         this.format();
29961         
29962     },
29963     
29964     addItem : function(cfg)
29965     {
29966         var item = new Roo.bootstrap.NavProgressItem(cfg);
29967         
29968         item.parentId = this.id;
29969         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29970         
29971         if(cfg.html){
29972             var top = new Roo.bootstrap.Element({
29973                 tag : 'div',
29974                 cls : 'roo-navigation-bar-text'
29975             });
29976             
29977             var bottom = new Roo.bootstrap.Element({
29978                 tag : 'div',
29979                 cls : 'roo-navigation-bar-text'
29980             });
29981             
29982             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29983             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29984             
29985             var topText = new Roo.bootstrap.Element({
29986                 tag : 'span',
29987                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29988             });
29989             
29990             var bottomText = new Roo.bootstrap.Element({
29991                 tag : 'span',
29992                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29993             });
29994             
29995             topText.onRender(top.el, null);
29996             bottomText.onRender(bottom.el, null);
29997             
29998             item.topEl = top;
29999             item.bottomEl = bottom;
30000         }
30001         
30002         this.barItems.push(item);
30003         
30004         return item;
30005     },
30006     
30007     getActive : function()
30008     {
30009         var active = false;
30010         
30011         Roo.each(this.barItems, function(v){
30012             
30013             if (!v.isActive()) {
30014                 return;
30015             }
30016             
30017             active = v;
30018             return false;
30019             
30020         });
30021         
30022         return active;
30023     },
30024     
30025     setActiveItem : function(item)
30026     {
30027         var prev = false;
30028         
30029         Roo.each(this.barItems, function(v){
30030             if (v.rid == item.rid) {
30031                 return ;
30032             }
30033             
30034             if (v.isActive()) {
30035                 v.setActive(false);
30036                 prev = v;
30037             }
30038         });
30039
30040         item.setActive(true);
30041         
30042         this.fireEvent('changed', this, item, prev);
30043     },
30044     
30045     getBarItem: function(rid)
30046     {
30047         var ret = false;
30048         
30049         Roo.each(this.barItems, function(e) {
30050             if (e.rid != rid) {
30051                 return;
30052             }
30053             
30054             ret =  e;
30055             return false;
30056         });
30057         
30058         return ret;
30059     },
30060     
30061     indexOfItem : function(item)
30062     {
30063         var index = false;
30064         
30065         Roo.each(this.barItems, function(v, i){
30066             
30067             if (v.rid != item.rid) {
30068                 return;
30069             }
30070             
30071             index = i;
30072             return false
30073         });
30074         
30075         return index;
30076     },
30077     
30078     setActiveNext : function()
30079     {
30080         var i = this.indexOfItem(this.getActive());
30081         
30082         if (i > this.barItems.length) {
30083             return;
30084         }
30085         
30086         this.setActiveItem(this.barItems[i+1]);
30087     },
30088     
30089     setActivePrev : function()
30090     {
30091         var i = this.indexOfItem(this.getActive());
30092         
30093         if (i  < 1) {
30094             return;
30095         }
30096         
30097         this.setActiveItem(this.barItems[i-1]);
30098     },
30099     
30100     format : function()
30101     {
30102         if(!this.barItems.length){
30103             return;
30104         }
30105      
30106         var width = 100 / this.barItems.length;
30107         
30108         Roo.each(this.barItems, function(i){
30109             i.el.setStyle('width', width + '%');
30110             i.topEl.el.setStyle('width', width + '%');
30111             i.bottomEl.el.setStyle('width', width + '%');
30112         }, this);
30113         
30114     }
30115     
30116 });
30117 /*
30118  * - LGPL
30119  *
30120  * Nav Progress Item
30121  * 
30122  */
30123
30124 /**
30125  * @class Roo.bootstrap.NavProgressItem
30126  * @extends Roo.bootstrap.Component
30127  * Bootstrap NavProgressItem class
30128  * @cfg {String} rid the reference id
30129  * @cfg {Boolean} active (true|false) Is item active default false
30130  * @cfg {Boolean} disabled (true|false) Is item active default false
30131  * @cfg {String} html
30132  * @cfg {String} position (top|bottom) text position default bottom
30133  * @cfg {String} icon show icon instead of number
30134  * 
30135  * @constructor
30136  * Create a new NavProgressItem
30137  * @param {Object} config The config object
30138  */
30139 Roo.bootstrap.NavProgressItem = function(config){
30140     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30141     this.addEvents({
30142         // raw events
30143         /**
30144          * @event click
30145          * The raw click event for the entire grid.
30146          * @param {Roo.bootstrap.NavProgressItem} this
30147          * @param {Roo.EventObject} e
30148          */
30149         "click" : true
30150     });
30151    
30152 };
30153
30154 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30155     
30156     rid : '',
30157     active : false,
30158     disabled : false,
30159     html : '',
30160     position : 'bottom',
30161     icon : false,
30162     
30163     getAutoCreate : function()
30164     {
30165         var iconCls = 'roo-navigation-bar-item-icon';
30166         
30167         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30168         
30169         var cfg = {
30170             tag: 'li',
30171             cls: 'roo-navigation-bar-item',
30172             cn : [
30173                 {
30174                     tag : 'i',
30175                     cls : iconCls
30176                 }
30177             ]
30178         };
30179         
30180         if(this.active){
30181             cfg.cls += ' active';
30182         }
30183         if(this.disabled){
30184             cfg.cls += ' disabled';
30185         }
30186         
30187         return cfg;
30188     },
30189     
30190     disable : function()
30191     {
30192         this.setDisabled(true);
30193     },
30194     
30195     enable : function()
30196     {
30197         this.setDisabled(false);
30198     },
30199     
30200     initEvents: function() 
30201     {
30202         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30203         
30204         this.iconEl.on('click', this.onClick, this);
30205     },
30206     
30207     onClick : function(e)
30208     {
30209         e.preventDefault();
30210         
30211         if(this.disabled){
30212             return;
30213         }
30214         
30215         if(this.fireEvent('click', this, e) === false){
30216             return;
30217         };
30218         
30219         this.parent().setActiveItem(this);
30220     },
30221     
30222     isActive: function () 
30223     {
30224         return this.active;
30225     },
30226     
30227     setActive : function(state)
30228     {
30229         if(this.active == state){
30230             return;
30231         }
30232         
30233         this.active = state;
30234         
30235         if (state) {
30236             this.el.addClass('active');
30237             return;
30238         }
30239         
30240         this.el.removeClass('active');
30241         
30242         return;
30243     },
30244     
30245     setDisabled : function(state)
30246     {
30247         if(this.disabled == state){
30248             return;
30249         }
30250         
30251         this.disabled = state;
30252         
30253         if (state) {
30254             this.el.addClass('disabled');
30255             return;
30256         }
30257         
30258         this.el.removeClass('disabled');
30259     },
30260     
30261     tooltipEl : function()
30262     {
30263         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30264     }
30265 });
30266  
30267
30268  /*
30269  * - LGPL
30270  *
30271  * FieldLabel
30272  * 
30273  */
30274
30275 /**
30276  * @class Roo.bootstrap.FieldLabel
30277  * @extends Roo.bootstrap.Component
30278  * Bootstrap FieldLabel class
30279  * @cfg {String} html contents of the element
30280  * @cfg {String} tag tag of the element default label
30281  * @cfg {String} cls class of the element
30282  * @cfg {String} target label target 
30283  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30284  * @cfg {String} invalidClass default "text-warning"
30285  * @cfg {String} validClass default "text-success"
30286  * @cfg {String} iconTooltip default "This field is required"
30287  * @cfg {String} indicatorpos (left|right) default left
30288  * 
30289  * @constructor
30290  * Create a new FieldLabel
30291  * @param {Object} config The config object
30292  */
30293
30294 Roo.bootstrap.FieldLabel = function(config){
30295     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30296     
30297     this.addEvents({
30298             /**
30299              * @event invalid
30300              * Fires after the field has been marked as invalid.
30301              * @param {Roo.form.FieldLabel} this
30302              * @param {String} msg The validation message
30303              */
30304             invalid : true,
30305             /**
30306              * @event valid
30307              * Fires after the field has been validated with no errors.
30308              * @param {Roo.form.FieldLabel} this
30309              */
30310             valid : true
30311         });
30312 };
30313
30314 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30315     
30316     tag: 'label',
30317     cls: '',
30318     html: '',
30319     target: '',
30320     allowBlank : true,
30321     invalidClass : 'has-warning',
30322     validClass : 'has-success',
30323     iconTooltip : 'This field is required',
30324     indicatorpos : 'left',
30325     
30326     getAutoCreate : function(){
30327         
30328         var cls = "";
30329         if (!this.allowBlank) {
30330             cls  = "visible";
30331         }
30332         
30333         var cfg = {
30334             tag : this.tag,
30335             cls : 'roo-bootstrap-field-label ' + this.cls,
30336             for : this.target,
30337             cn : [
30338                 {
30339                     tag : 'i',
30340                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30341                     tooltip : this.iconTooltip
30342                 },
30343                 {
30344                     tag : 'span',
30345                     html : this.html
30346                 }
30347             ] 
30348         };
30349         
30350         if(this.indicatorpos == 'right'){
30351             var cfg = {
30352                 tag : this.tag,
30353                 cls : 'roo-bootstrap-field-label ' + this.cls,
30354                 for : this.target,
30355                 cn : [
30356                     {
30357                         tag : 'span',
30358                         html : this.html
30359                     },
30360                     {
30361                         tag : 'i',
30362                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30363                         tooltip : this.iconTooltip
30364                     }
30365                 ] 
30366             };
30367         }
30368         
30369         return cfg;
30370     },
30371     
30372     initEvents: function() 
30373     {
30374         Roo.bootstrap.Element.superclass.initEvents.call(this);
30375         
30376         this.indicator = this.indicatorEl();
30377         
30378         if(this.indicator){
30379             this.indicator.removeClass('visible');
30380             this.indicator.addClass('invisible');
30381         }
30382         
30383         Roo.bootstrap.FieldLabel.register(this);
30384     },
30385     
30386     indicatorEl : function()
30387     {
30388         var indicator = this.el.select('i.roo-required-indicator',true).first();
30389         
30390         if(!indicator){
30391             return false;
30392         }
30393         
30394         return indicator;
30395         
30396     },
30397     
30398     /**
30399      * Mark this field as valid
30400      */
30401     markValid : function()
30402     {
30403         if(this.indicator){
30404             this.indicator.removeClass('visible');
30405             this.indicator.addClass('invisible');
30406         }
30407         
30408         this.el.removeClass(this.invalidClass);
30409         
30410         this.el.addClass(this.validClass);
30411         
30412         this.fireEvent('valid', this);
30413     },
30414     
30415     /**
30416      * Mark this field as invalid
30417      * @param {String} msg The validation message
30418      */
30419     markInvalid : function(msg)
30420     {
30421         if(this.indicator){
30422             this.indicator.removeClass('invisible');
30423             this.indicator.addClass('visible');
30424         }
30425         
30426         this.el.removeClass(this.validClass);
30427         
30428         this.el.addClass(this.invalidClass);
30429         
30430         this.fireEvent('invalid', this, msg);
30431     }
30432     
30433    
30434 });
30435
30436 Roo.apply(Roo.bootstrap.FieldLabel, {
30437     
30438     groups: {},
30439     
30440      /**
30441     * register a FieldLabel Group
30442     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30443     */
30444     register : function(label)
30445     {
30446         if(this.groups.hasOwnProperty(label.target)){
30447             return;
30448         }
30449      
30450         this.groups[label.target] = label;
30451         
30452     },
30453     /**
30454     * fetch a FieldLabel Group based on the target
30455     * @param {string} target
30456     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30457     */
30458     get: function(target) {
30459         if (typeof(this.groups[target]) == 'undefined') {
30460             return false;
30461         }
30462         
30463         return this.groups[target] ;
30464     }
30465 });
30466
30467  
30468
30469  /*
30470  * - LGPL
30471  *
30472  * page DateSplitField.
30473  * 
30474  */
30475
30476
30477 /**
30478  * @class Roo.bootstrap.DateSplitField
30479  * @extends Roo.bootstrap.Component
30480  * Bootstrap DateSplitField class
30481  * @cfg {string} fieldLabel - the label associated
30482  * @cfg {Number} labelWidth set the width of label (0-12)
30483  * @cfg {String} labelAlign (top|left)
30484  * @cfg {Boolean} dayAllowBlank (true|false) default false
30485  * @cfg {Boolean} monthAllowBlank (true|false) default false
30486  * @cfg {Boolean} yearAllowBlank (true|false) default false
30487  * @cfg {string} dayPlaceholder 
30488  * @cfg {string} monthPlaceholder
30489  * @cfg {string} yearPlaceholder
30490  * @cfg {string} dayFormat default 'd'
30491  * @cfg {string} monthFormat default 'm'
30492  * @cfg {string} yearFormat default 'Y'
30493  * @cfg {Number} labellg set the width of label (1-12)
30494  * @cfg {Number} labelmd set the width of label (1-12)
30495  * @cfg {Number} labelsm set the width of label (1-12)
30496  * @cfg {Number} labelxs set the width of label (1-12)
30497
30498  *     
30499  * @constructor
30500  * Create a new DateSplitField
30501  * @param {Object} config The config object
30502  */
30503
30504 Roo.bootstrap.DateSplitField = function(config){
30505     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30506     
30507     this.addEvents({
30508         // raw events
30509          /**
30510          * @event years
30511          * getting the data of years
30512          * @param {Roo.bootstrap.DateSplitField} this
30513          * @param {Object} years
30514          */
30515         "years" : true,
30516         /**
30517          * @event days
30518          * getting the data of days
30519          * @param {Roo.bootstrap.DateSplitField} this
30520          * @param {Object} days
30521          */
30522         "days" : true,
30523         /**
30524          * @event invalid
30525          * Fires after the field has been marked as invalid.
30526          * @param {Roo.form.Field} this
30527          * @param {String} msg The validation message
30528          */
30529         invalid : true,
30530        /**
30531          * @event valid
30532          * Fires after the field has been validated with no errors.
30533          * @param {Roo.form.Field} this
30534          */
30535         valid : true
30536     });
30537 };
30538
30539 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30540     
30541     fieldLabel : '',
30542     labelAlign : 'top',
30543     labelWidth : 3,
30544     dayAllowBlank : false,
30545     monthAllowBlank : false,
30546     yearAllowBlank : false,
30547     dayPlaceholder : '',
30548     monthPlaceholder : '',
30549     yearPlaceholder : '',
30550     dayFormat : 'd',
30551     monthFormat : 'm',
30552     yearFormat : 'Y',
30553     isFormField : true,
30554     labellg : 0,
30555     labelmd : 0,
30556     labelsm : 0,
30557     labelxs : 0,
30558     
30559     getAutoCreate : function()
30560     {
30561         var cfg = {
30562             tag : 'div',
30563             cls : 'row roo-date-split-field-group',
30564             cn : [
30565                 {
30566                     tag : 'input',
30567                     type : 'hidden',
30568                     cls : 'form-hidden-field roo-date-split-field-group-value',
30569                     name : this.name
30570                 }
30571             ]
30572         };
30573         
30574         var labelCls = 'col-md-12';
30575         var contentCls = 'col-md-4';
30576         
30577         if(this.fieldLabel){
30578             
30579             var label = {
30580                 tag : 'div',
30581                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30582                 cn : [
30583                     {
30584                         tag : 'label',
30585                         html : this.fieldLabel
30586                     }
30587                 ]
30588             };
30589             
30590             if(this.labelAlign == 'left'){
30591             
30592                 if(this.labelWidth > 12){
30593                     label.style = "width: " + this.labelWidth + 'px';
30594                 }
30595
30596                 if(this.labelWidth < 13 && this.labelmd == 0){
30597                     this.labelmd = this.labelWidth;
30598                 }
30599
30600                 if(this.labellg > 0){
30601                     labelCls = ' col-lg-' + this.labellg;
30602                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30603                 }
30604
30605                 if(this.labelmd > 0){
30606                     labelCls = ' col-md-' + this.labelmd;
30607                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30608                 }
30609
30610                 if(this.labelsm > 0){
30611                     labelCls = ' col-sm-' + this.labelsm;
30612                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30613                 }
30614
30615                 if(this.labelxs > 0){
30616                     labelCls = ' col-xs-' + this.labelxs;
30617                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30618                 }
30619             }
30620             
30621             label.cls += ' ' + labelCls;
30622             
30623             cfg.cn.push(label);
30624         }
30625         
30626         Roo.each(['day', 'month', 'year'], function(t){
30627             cfg.cn.push({
30628                 tag : 'div',
30629                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30630             });
30631         }, this);
30632         
30633         return cfg;
30634     },
30635     
30636     inputEl: function ()
30637     {
30638         return this.el.select('.roo-date-split-field-group-value', true).first();
30639     },
30640     
30641     onRender : function(ct, position) 
30642     {
30643         var _this = this;
30644         
30645         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30646         
30647         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30648         
30649         this.dayField = new Roo.bootstrap.ComboBox({
30650             allowBlank : this.dayAllowBlank,
30651             alwaysQuery : true,
30652             displayField : 'value',
30653             editable : false,
30654             fieldLabel : '',
30655             forceSelection : true,
30656             mode : 'local',
30657             placeholder : this.dayPlaceholder,
30658             selectOnFocus : true,
30659             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30660             triggerAction : 'all',
30661             typeAhead : true,
30662             valueField : 'value',
30663             store : new Roo.data.SimpleStore({
30664                 data : (function() {    
30665                     var days = [];
30666                     _this.fireEvent('days', _this, days);
30667                     return days;
30668                 })(),
30669                 fields : [ 'value' ]
30670             }),
30671             listeners : {
30672                 select : function (_self, record, index)
30673                 {
30674                     _this.setValue(_this.getValue());
30675                 }
30676             }
30677         });
30678
30679         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30680         
30681         this.monthField = new Roo.bootstrap.MonthField({
30682             after : '<i class=\"fa fa-calendar\"></i>',
30683             allowBlank : this.monthAllowBlank,
30684             placeholder : this.monthPlaceholder,
30685             readOnly : true,
30686             listeners : {
30687                 render : function (_self)
30688                 {
30689                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30690                         e.preventDefault();
30691                         _self.focus();
30692                     });
30693                 },
30694                 select : function (_self, oldvalue, newvalue)
30695                 {
30696                     _this.setValue(_this.getValue());
30697                 }
30698             }
30699         });
30700         
30701         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30702         
30703         this.yearField = new Roo.bootstrap.ComboBox({
30704             allowBlank : this.yearAllowBlank,
30705             alwaysQuery : true,
30706             displayField : 'value',
30707             editable : false,
30708             fieldLabel : '',
30709             forceSelection : true,
30710             mode : 'local',
30711             placeholder : this.yearPlaceholder,
30712             selectOnFocus : true,
30713             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30714             triggerAction : 'all',
30715             typeAhead : true,
30716             valueField : 'value',
30717             store : new Roo.data.SimpleStore({
30718                 data : (function() {
30719                     var years = [];
30720                     _this.fireEvent('years', _this, years);
30721                     return years;
30722                 })(),
30723                 fields : [ 'value' ]
30724             }),
30725             listeners : {
30726                 select : function (_self, record, index)
30727                 {
30728                     _this.setValue(_this.getValue());
30729                 }
30730             }
30731         });
30732
30733         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30734     },
30735     
30736     setValue : function(v, format)
30737     {
30738         this.inputEl.dom.value = v;
30739         
30740         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30741         
30742         var d = Date.parseDate(v, f);
30743         
30744         if(!d){
30745             this.validate();
30746             return;
30747         }
30748         
30749         this.setDay(d.format(this.dayFormat));
30750         this.setMonth(d.format(this.monthFormat));
30751         this.setYear(d.format(this.yearFormat));
30752         
30753         this.validate();
30754         
30755         return;
30756     },
30757     
30758     setDay : function(v)
30759     {
30760         this.dayField.setValue(v);
30761         this.inputEl.dom.value = this.getValue();
30762         this.validate();
30763         return;
30764     },
30765     
30766     setMonth : function(v)
30767     {
30768         this.monthField.setValue(v, true);
30769         this.inputEl.dom.value = this.getValue();
30770         this.validate();
30771         return;
30772     },
30773     
30774     setYear : function(v)
30775     {
30776         this.yearField.setValue(v);
30777         this.inputEl.dom.value = this.getValue();
30778         this.validate();
30779         return;
30780     },
30781     
30782     getDay : function()
30783     {
30784         return this.dayField.getValue();
30785     },
30786     
30787     getMonth : function()
30788     {
30789         return this.monthField.getValue();
30790     },
30791     
30792     getYear : function()
30793     {
30794         return this.yearField.getValue();
30795     },
30796     
30797     getValue : function()
30798     {
30799         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30800         
30801         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30802         
30803         return date;
30804     },
30805     
30806     reset : function()
30807     {
30808         this.setDay('');
30809         this.setMonth('');
30810         this.setYear('');
30811         this.inputEl.dom.value = '';
30812         this.validate();
30813         return;
30814     },
30815     
30816     validate : function()
30817     {
30818         var d = this.dayField.validate();
30819         var m = this.monthField.validate();
30820         var y = this.yearField.validate();
30821         
30822         var valid = true;
30823         
30824         if(
30825                 (!this.dayAllowBlank && !d) ||
30826                 (!this.monthAllowBlank && !m) ||
30827                 (!this.yearAllowBlank && !y)
30828         ){
30829             valid = false;
30830         }
30831         
30832         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30833             return valid;
30834         }
30835         
30836         if(valid){
30837             this.markValid();
30838             return valid;
30839         }
30840         
30841         this.markInvalid();
30842         
30843         return valid;
30844     },
30845     
30846     markValid : function()
30847     {
30848         
30849         var label = this.el.select('label', true).first();
30850         var icon = this.el.select('i.fa-star', true).first();
30851
30852         if(label && icon){
30853             icon.remove();
30854         }
30855         
30856         this.fireEvent('valid', this);
30857     },
30858     
30859      /**
30860      * Mark this field as invalid
30861      * @param {String} msg The validation message
30862      */
30863     markInvalid : function(msg)
30864     {
30865         
30866         var label = this.el.select('label', true).first();
30867         var icon = this.el.select('i.fa-star', true).first();
30868
30869         if(label && !icon){
30870             this.el.select('.roo-date-split-field-label', true).createChild({
30871                 tag : 'i',
30872                 cls : 'text-danger fa fa-lg fa-star',
30873                 tooltip : 'This field is required',
30874                 style : 'margin-right:5px;'
30875             }, label, true);
30876         }
30877         
30878         this.fireEvent('invalid', this, msg);
30879     },
30880     
30881     clearInvalid : function()
30882     {
30883         var label = this.el.select('label', true).first();
30884         var icon = this.el.select('i.fa-star', true).first();
30885
30886         if(label && icon){
30887             icon.remove();
30888         }
30889         
30890         this.fireEvent('valid', this);
30891     },
30892     
30893     getName: function()
30894     {
30895         return this.name;
30896     }
30897     
30898 });
30899
30900  /**
30901  *
30902  * This is based on 
30903  * http://masonry.desandro.com
30904  *
30905  * The idea is to render all the bricks based on vertical width...
30906  *
30907  * The original code extends 'outlayer' - we might need to use that....
30908  * 
30909  */
30910
30911
30912 /**
30913  * @class Roo.bootstrap.LayoutMasonry
30914  * @extends Roo.bootstrap.Component
30915  * Bootstrap Layout Masonry class
30916  * 
30917  * @constructor
30918  * Create a new Element
30919  * @param {Object} config The config object
30920  */
30921
30922 Roo.bootstrap.LayoutMasonry = function(config){
30923     
30924     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30925     
30926     this.bricks = [];
30927     
30928     Roo.bootstrap.LayoutMasonry.register(this);
30929     
30930     this.addEvents({
30931         // raw events
30932         /**
30933          * @event layout
30934          * Fire after layout the items
30935          * @param {Roo.bootstrap.LayoutMasonry} this
30936          * @param {Roo.EventObject} e
30937          */
30938         "layout" : true
30939     });
30940     
30941 };
30942
30943 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30944     
30945     /**
30946      * @cfg {Boolean} isLayoutInstant = no animation?
30947      */   
30948     isLayoutInstant : false, // needed?
30949    
30950     /**
30951      * @cfg {Number} boxWidth  width of the columns
30952      */   
30953     boxWidth : 450,
30954     
30955       /**
30956      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30957      */   
30958     boxHeight : 0,
30959     
30960     /**
30961      * @cfg {Number} padWidth padding below box..
30962      */   
30963     padWidth : 10, 
30964     
30965     /**
30966      * @cfg {Number} gutter gutter width..
30967      */   
30968     gutter : 10,
30969     
30970      /**
30971      * @cfg {Number} maxCols maximum number of columns
30972      */   
30973     
30974     maxCols: 0,
30975     
30976     /**
30977      * @cfg {Boolean} isAutoInitial defalut true
30978      */   
30979     isAutoInitial : true, 
30980     
30981     containerWidth: 0,
30982     
30983     /**
30984      * @cfg {Boolean} isHorizontal defalut false
30985      */   
30986     isHorizontal : false, 
30987
30988     currentSize : null,
30989     
30990     tag: 'div',
30991     
30992     cls: '',
30993     
30994     bricks: null, //CompositeElement
30995     
30996     cols : 1,
30997     
30998     _isLayoutInited : false,
30999     
31000 //    isAlternative : false, // only use for vertical layout...
31001     
31002     /**
31003      * @cfg {Number} alternativePadWidth padding below box..
31004      */   
31005     alternativePadWidth : 50,
31006     
31007     selectedBrick : [],
31008     
31009     getAutoCreate : function(){
31010         
31011         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31012         
31013         var cfg = {
31014             tag: this.tag,
31015             cls: 'blog-masonary-wrapper ' + this.cls,
31016             cn : {
31017                 cls : 'mas-boxes masonary'
31018             }
31019         };
31020         
31021         return cfg;
31022     },
31023     
31024     getChildContainer: function( )
31025     {
31026         if (this.boxesEl) {
31027             return this.boxesEl;
31028         }
31029         
31030         this.boxesEl = this.el.select('.mas-boxes').first();
31031         
31032         return this.boxesEl;
31033     },
31034     
31035     
31036     initEvents : function()
31037     {
31038         var _this = this;
31039         
31040         if(this.isAutoInitial){
31041             Roo.log('hook children rendered');
31042             this.on('childrenrendered', function() {
31043                 Roo.log('children rendered');
31044                 _this.initial();
31045             } ,this);
31046         }
31047     },
31048     
31049     initial : function()
31050     {
31051         this.selectedBrick = [];
31052         
31053         this.currentSize = this.el.getBox(true);
31054         
31055         Roo.EventManager.onWindowResize(this.resize, this); 
31056
31057         if(!this.isAutoInitial){
31058             this.layout();
31059             return;
31060         }
31061         
31062         this.layout();
31063         
31064         return;
31065         //this.layout.defer(500,this);
31066         
31067     },
31068     
31069     resize : function()
31070     {
31071         var cs = this.el.getBox(true);
31072         
31073         if (
31074                 this.currentSize.width == cs.width && 
31075                 this.currentSize.x == cs.x && 
31076                 this.currentSize.height == cs.height && 
31077                 this.currentSize.y == cs.y 
31078         ) {
31079             Roo.log("no change in with or X or Y");
31080             return;
31081         }
31082         
31083         this.currentSize = cs;
31084         
31085         this.layout();
31086         
31087     },
31088     
31089     layout : function()
31090     {   
31091         this._resetLayout();
31092         
31093         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31094         
31095         this.layoutItems( isInstant );
31096       
31097         this._isLayoutInited = true;
31098         
31099         this.fireEvent('layout', this);
31100         
31101     },
31102     
31103     _resetLayout : function()
31104     {
31105         if(this.isHorizontal){
31106             this.horizontalMeasureColumns();
31107             return;
31108         }
31109         
31110         this.verticalMeasureColumns();
31111         
31112     },
31113     
31114     verticalMeasureColumns : function()
31115     {
31116         this.getContainerWidth();
31117         
31118 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31119 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31120 //            return;
31121 //        }
31122         
31123         var boxWidth = this.boxWidth + this.padWidth;
31124         
31125         if(this.containerWidth < this.boxWidth){
31126             boxWidth = this.containerWidth
31127         }
31128         
31129         var containerWidth = this.containerWidth;
31130         
31131         var cols = Math.floor(containerWidth / boxWidth);
31132         
31133         this.cols = Math.max( cols, 1 );
31134         
31135         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31136         
31137         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31138         
31139         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31140         
31141         this.colWidth = boxWidth + avail - this.padWidth;
31142         
31143         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31144         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31145     },
31146     
31147     horizontalMeasureColumns : function()
31148     {
31149         this.getContainerWidth();
31150         
31151         var boxWidth = this.boxWidth;
31152         
31153         if(this.containerWidth < boxWidth){
31154             boxWidth = this.containerWidth;
31155         }
31156         
31157         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31158         
31159         this.el.setHeight(boxWidth);
31160         
31161     },
31162     
31163     getContainerWidth : function()
31164     {
31165         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31166     },
31167     
31168     layoutItems : function( isInstant )
31169     {
31170         Roo.log(this.bricks);
31171         
31172         var items = Roo.apply([], this.bricks);
31173         
31174         if(this.isHorizontal){
31175             this._horizontalLayoutItems( items , isInstant );
31176             return;
31177         }
31178         
31179 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31180 //            this._verticalAlternativeLayoutItems( items , isInstant );
31181 //            return;
31182 //        }
31183         
31184         this._verticalLayoutItems( items , isInstant );
31185         
31186     },
31187     
31188     _verticalLayoutItems : function ( items , isInstant)
31189     {
31190         if ( !items || !items.length ) {
31191             return;
31192         }
31193         
31194         var standard = [
31195             ['xs', 'xs', 'xs', 'tall'],
31196             ['xs', 'xs', 'tall'],
31197             ['xs', 'xs', 'sm'],
31198             ['xs', 'xs', 'xs'],
31199             ['xs', 'tall'],
31200             ['xs', 'sm'],
31201             ['xs', 'xs'],
31202             ['xs'],
31203             
31204             ['sm', 'xs', 'xs'],
31205             ['sm', 'xs'],
31206             ['sm'],
31207             
31208             ['tall', 'xs', 'xs', 'xs'],
31209             ['tall', 'xs', 'xs'],
31210             ['tall', 'xs'],
31211             ['tall']
31212             
31213         ];
31214         
31215         var queue = [];
31216         
31217         var boxes = [];
31218         
31219         var box = [];
31220         
31221         Roo.each(items, function(item, k){
31222             
31223             switch (item.size) {
31224                 // these layouts take up a full box,
31225                 case 'md' :
31226                 case 'md-left' :
31227                 case 'md-right' :
31228                 case 'wide' :
31229                     
31230                     if(box.length){
31231                         boxes.push(box);
31232                         box = [];
31233                     }
31234                     
31235                     boxes.push([item]);
31236                     
31237                     break;
31238                     
31239                 case 'xs' :
31240                 case 'sm' :
31241                 case 'tall' :
31242                     
31243                     box.push(item);
31244                     
31245                     break;
31246                 default :
31247                     break;
31248                     
31249             }
31250             
31251         }, this);
31252         
31253         if(box.length){
31254             boxes.push(box);
31255             box = [];
31256         }
31257         
31258         var filterPattern = function(box, length)
31259         {
31260             if(!box.length){
31261                 return;
31262             }
31263             
31264             var match = false;
31265             
31266             var pattern = box.slice(0, length);
31267             
31268             var format = [];
31269             
31270             Roo.each(pattern, function(i){
31271                 format.push(i.size);
31272             }, this);
31273             
31274             Roo.each(standard, function(s){
31275                 
31276                 if(String(s) != String(format)){
31277                     return;
31278                 }
31279                 
31280                 match = true;
31281                 return false;
31282                 
31283             }, this);
31284             
31285             if(!match && length == 1){
31286                 return;
31287             }
31288             
31289             if(!match){
31290                 filterPattern(box, length - 1);
31291                 return;
31292             }
31293                 
31294             queue.push(pattern);
31295
31296             box = box.slice(length, box.length);
31297
31298             filterPattern(box, 4);
31299
31300             return;
31301             
31302         }
31303         
31304         Roo.each(boxes, function(box, k){
31305             
31306             if(!box.length){
31307                 return;
31308             }
31309             
31310             if(box.length == 1){
31311                 queue.push(box);
31312                 return;
31313             }
31314             
31315             filterPattern(box, 4);
31316             
31317         }, this);
31318         
31319         this._processVerticalLayoutQueue( queue, isInstant );
31320         
31321     },
31322     
31323 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31324 //    {
31325 //        if ( !items || !items.length ) {
31326 //            return;
31327 //        }
31328 //
31329 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31330 //        
31331 //    },
31332     
31333     _horizontalLayoutItems : function ( items , isInstant)
31334     {
31335         if ( !items || !items.length || items.length < 3) {
31336             return;
31337         }
31338         
31339         items.reverse();
31340         
31341         var eItems = items.slice(0, 3);
31342         
31343         items = items.slice(3, items.length);
31344         
31345         var standard = [
31346             ['xs', 'xs', 'xs', 'wide'],
31347             ['xs', 'xs', 'wide'],
31348             ['xs', 'xs', 'sm'],
31349             ['xs', 'xs', 'xs'],
31350             ['xs', 'wide'],
31351             ['xs', 'sm'],
31352             ['xs', 'xs'],
31353             ['xs'],
31354             
31355             ['sm', 'xs', 'xs'],
31356             ['sm', 'xs'],
31357             ['sm'],
31358             
31359             ['wide', 'xs', 'xs', 'xs'],
31360             ['wide', 'xs', 'xs'],
31361             ['wide', 'xs'],
31362             ['wide'],
31363             
31364             ['wide-thin']
31365         ];
31366         
31367         var queue = [];
31368         
31369         var boxes = [];
31370         
31371         var box = [];
31372         
31373         Roo.each(items, function(item, k){
31374             
31375             switch (item.size) {
31376                 case 'md' :
31377                 case 'md-left' :
31378                 case 'md-right' :
31379                 case 'tall' :
31380                     
31381                     if(box.length){
31382                         boxes.push(box);
31383                         box = [];
31384                     }
31385                     
31386                     boxes.push([item]);
31387                     
31388                     break;
31389                     
31390                 case 'xs' :
31391                 case 'sm' :
31392                 case 'wide' :
31393                 case 'wide-thin' :
31394                     
31395                     box.push(item);
31396                     
31397                     break;
31398                 default :
31399                     break;
31400                     
31401             }
31402             
31403         }, this);
31404         
31405         if(box.length){
31406             boxes.push(box);
31407             box = [];
31408         }
31409         
31410         var filterPattern = function(box, length)
31411         {
31412             if(!box.length){
31413                 return;
31414             }
31415             
31416             var match = false;
31417             
31418             var pattern = box.slice(0, length);
31419             
31420             var format = [];
31421             
31422             Roo.each(pattern, function(i){
31423                 format.push(i.size);
31424             }, this);
31425             
31426             Roo.each(standard, function(s){
31427                 
31428                 if(String(s) != String(format)){
31429                     return;
31430                 }
31431                 
31432                 match = true;
31433                 return false;
31434                 
31435             }, this);
31436             
31437             if(!match && length == 1){
31438                 return;
31439             }
31440             
31441             if(!match){
31442                 filterPattern(box, length - 1);
31443                 return;
31444             }
31445                 
31446             queue.push(pattern);
31447
31448             box = box.slice(length, box.length);
31449
31450             filterPattern(box, 4);
31451
31452             return;
31453             
31454         }
31455         
31456         Roo.each(boxes, function(box, k){
31457             
31458             if(!box.length){
31459                 return;
31460             }
31461             
31462             if(box.length == 1){
31463                 queue.push(box);
31464                 return;
31465             }
31466             
31467             filterPattern(box, 4);
31468             
31469         }, this);
31470         
31471         
31472         var prune = [];
31473         
31474         var pos = this.el.getBox(true);
31475         
31476         var minX = pos.x;
31477         
31478         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31479         
31480         var hit_end = false;
31481         
31482         Roo.each(queue, function(box){
31483             
31484             if(hit_end){
31485                 
31486                 Roo.each(box, function(b){
31487                 
31488                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31489                     b.el.hide();
31490
31491                 }, this);
31492
31493                 return;
31494             }
31495             
31496             var mx = 0;
31497             
31498             Roo.each(box, function(b){
31499                 
31500                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31501                 b.el.show();
31502
31503                 mx = Math.max(mx, b.x);
31504                 
31505             }, this);
31506             
31507             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31508             
31509             if(maxX < minX){
31510                 
31511                 Roo.each(box, function(b){
31512                 
31513                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31514                     b.el.hide();
31515                     
31516                 }, this);
31517                 
31518                 hit_end = true;
31519                 
31520                 return;
31521             }
31522             
31523             prune.push(box);
31524             
31525         }, this);
31526         
31527         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31528     },
31529     
31530     /** Sets position of item in DOM
31531     * @param {Element} item
31532     * @param {Number} x - horizontal position
31533     * @param {Number} y - vertical position
31534     * @param {Boolean} isInstant - disables transitions
31535     */
31536     _processVerticalLayoutQueue : function( queue, isInstant )
31537     {
31538         var pos = this.el.getBox(true);
31539         var x = pos.x;
31540         var y = pos.y;
31541         var maxY = [];
31542         
31543         for (var i = 0; i < this.cols; i++){
31544             maxY[i] = pos.y;
31545         }
31546         
31547         Roo.each(queue, function(box, k){
31548             
31549             var col = k % this.cols;
31550             
31551             Roo.each(box, function(b,kk){
31552                 
31553                 b.el.position('absolute');
31554                 
31555                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31556                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31557                 
31558                 if(b.size == 'md-left' || b.size == 'md-right'){
31559                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31560                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31561                 }
31562                 
31563                 b.el.setWidth(width);
31564                 b.el.setHeight(height);
31565                 // iframe?
31566                 b.el.select('iframe',true).setSize(width,height);
31567                 
31568             }, this);
31569             
31570             for (var i = 0; i < this.cols; i++){
31571                 
31572                 if(maxY[i] < maxY[col]){
31573                     col = i;
31574                     continue;
31575                 }
31576                 
31577                 col = Math.min(col, i);
31578                 
31579             }
31580             
31581             x = pos.x + col * (this.colWidth + this.padWidth);
31582             
31583             y = maxY[col];
31584             
31585             var positions = [];
31586             
31587             switch (box.length){
31588                 case 1 :
31589                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31590                     break;
31591                 case 2 :
31592                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31593                     break;
31594                 case 3 :
31595                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31596                     break;
31597                 case 4 :
31598                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31599                     break;
31600                 default :
31601                     break;
31602             }
31603             
31604             Roo.each(box, function(b,kk){
31605                 
31606                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31607                 
31608                 var sz = b.el.getSize();
31609                 
31610                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31611                 
31612             }, this);
31613             
31614         }, this);
31615         
31616         var mY = 0;
31617         
31618         for (var i = 0; i < this.cols; i++){
31619             mY = Math.max(mY, maxY[i]);
31620         }
31621         
31622         this.el.setHeight(mY - pos.y);
31623         
31624     },
31625     
31626 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31627 //    {
31628 //        var pos = this.el.getBox(true);
31629 //        var x = pos.x;
31630 //        var y = pos.y;
31631 //        var maxX = pos.right;
31632 //        
31633 //        var maxHeight = 0;
31634 //        
31635 //        Roo.each(items, function(item, k){
31636 //            
31637 //            var c = k % 2;
31638 //            
31639 //            item.el.position('absolute');
31640 //                
31641 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31642 //
31643 //            item.el.setWidth(width);
31644 //
31645 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31646 //
31647 //            item.el.setHeight(height);
31648 //            
31649 //            if(c == 0){
31650 //                item.el.setXY([x, y], isInstant ? false : true);
31651 //            } else {
31652 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31653 //            }
31654 //            
31655 //            y = y + height + this.alternativePadWidth;
31656 //            
31657 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31658 //            
31659 //        }, this);
31660 //        
31661 //        this.el.setHeight(maxHeight);
31662 //        
31663 //    },
31664     
31665     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31666     {
31667         var pos = this.el.getBox(true);
31668         
31669         var minX = pos.x;
31670         var minY = pos.y;
31671         
31672         var maxX = pos.right;
31673         
31674         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31675         
31676         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31677         
31678         Roo.each(queue, function(box, k){
31679             
31680             Roo.each(box, function(b, kk){
31681                 
31682                 b.el.position('absolute');
31683                 
31684                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31685                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31686                 
31687                 if(b.size == 'md-left' || b.size == 'md-right'){
31688                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31689                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31690                 }
31691                 
31692                 b.el.setWidth(width);
31693                 b.el.setHeight(height);
31694                 
31695             }, this);
31696             
31697             if(!box.length){
31698                 return;
31699             }
31700             
31701             var positions = [];
31702             
31703             switch (box.length){
31704                 case 1 :
31705                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31706                     break;
31707                 case 2 :
31708                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31709                     break;
31710                 case 3 :
31711                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31712                     break;
31713                 case 4 :
31714                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31715                     break;
31716                 default :
31717                     break;
31718             }
31719             
31720             Roo.each(box, function(b,kk){
31721                 
31722                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31723                 
31724                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31725                 
31726             }, this);
31727             
31728         }, this);
31729         
31730     },
31731     
31732     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31733     {
31734         Roo.each(eItems, function(b,k){
31735             
31736             b.size = (k == 0) ? 'sm' : 'xs';
31737             b.x = (k == 0) ? 2 : 1;
31738             b.y = (k == 0) ? 2 : 1;
31739             
31740             b.el.position('absolute');
31741             
31742             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31743                 
31744             b.el.setWidth(width);
31745             
31746             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31747             
31748             b.el.setHeight(height);
31749             
31750         }, this);
31751
31752         var positions = [];
31753         
31754         positions.push({
31755             x : maxX - this.unitWidth * 2 - this.gutter,
31756             y : minY
31757         });
31758         
31759         positions.push({
31760             x : maxX - this.unitWidth,
31761             y : minY + (this.unitWidth + this.gutter) * 2
31762         });
31763         
31764         positions.push({
31765             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31766             y : minY
31767         });
31768         
31769         Roo.each(eItems, function(b,k){
31770             
31771             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31772
31773         }, this);
31774         
31775     },
31776     
31777     getVerticalOneBoxColPositions : function(x, y, box)
31778     {
31779         var pos = [];
31780         
31781         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31782         
31783         if(box[0].size == 'md-left'){
31784             rand = 0;
31785         }
31786         
31787         if(box[0].size == 'md-right'){
31788             rand = 1;
31789         }
31790         
31791         pos.push({
31792             x : x + (this.unitWidth + this.gutter) * rand,
31793             y : y
31794         });
31795         
31796         return pos;
31797     },
31798     
31799     getVerticalTwoBoxColPositions : function(x, y, box)
31800     {
31801         var pos = [];
31802         
31803         if(box[0].size == 'xs'){
31804             
31805             pos.push({
31806                 x : x,
31807                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31808             });
31809
31810             pos.push({
31811                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31812                 y : y
31813             });
31814             
31815             return pos;
31816             
31817         }
31818         
31819         pos.push({
31820             x : x,
31821             y : y
31822         });
31823
31824         pos.push({
31825             x : x + (this.unitWidth + this.gutter) * 2,
31826             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31827         });
31828         
31829         return pos;
31830         
31831     },
31832     
31833     getVerticalThreeBoxColPositions : function(x, y, box)
31834     {
31835         var pos = [];
31836         
31837         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31838             
31839             pos.push({
31840                 x : x,
31841                 y : y
31842             });
31843
31844             pos.push({
31845                 x : x + (this.unitWidth + this.gutter) * 1,
31846                 y : y
31847             });
31848             
31849             pos.push({
31850                 x : x + (this.unitWidth + this.gutter) * 2,
31851                 y : y
31852             });
31853             
31854             return pos;
31855             
31856         }
31857         
31858         if(box[0].size == 'xs' && box[1].size == 'xs'){
31859             
31860             pos.push({
31861                 x : x,
31862                 y : y
31863             });
31864
31865             pos.push({
31866                 x : x,
31867                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31868             });
31869             
31870             pos.push({
31871                 x : x + (this.unitWidth + this.gutter) * 1,
31872                 y : y
31873             });
31874             
31875             return pos;
31876             
31877         }
31878         
31879         pos.push({
31880             x : x,
31881             y : y
31882         });
31883
31884         pos.push({
31885             x : x + (this.unitWidth + this.gutter) * 2,
31886             y : y
31887         });
31888
31889         pos.push({
31890             x : x + (this.unitWidth + this.gutter) * 2,
31891             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31892         });
31893             
31894         return pos;
31895         
31896     },
31897     
31898     getVerticalFourBoxColPositions : function(x, y, box)
31899     {
31900         var pos = [];
31901         
31902         if(box[0].size == 'xs'){
31903             
31904             pos.push({
31905                 x : x,
31906                 y : y
31907             });
31908
31909             pos.push({
31910                 x : x,
31911                 y : y + (this.unitHeight + this.gutter) * 1
31912             });
31913             
31914             pos.push({
31915                 x : x,
31916                 y : y + (this.unitHeight + this.gutter) * 2
31917             });
31918             
31919             pos.push({
31920                 x : x + (this.unitWidth + this.gutter) * 1,
31921                 y : y
31922             });
31923             
31924             return pos;
31925             
31926         }
31927         
31928         pos.push({
31929             x : x,
31930             y : y
31931         });
31932
31933         pos.push({
31934             x : x + (this.unitWidth + this.gutter) * 2,
31935             y : y
31936         });
31937
31938         pos.push({
31939             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31940             y : y + (this.unitHeight + this.gutter) * 1
31941         });
31942
31943         pos.push({
31944             x : x + (this.unitWidth + this.gutter) * 2,
31945             y : y + (this.unitWidth + this.gutter) * 2
31946         });
31947
31948         return pos;
31949         
31950     },
31951     
31952     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31953     {
31954         var pos = [];
31955         
31956         if(box[0].size == 'md-left'){
31957             pos.push({
31958                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31959                 y : minY
31960             });
31961             
31962             return pos;
31963         }
31964         
31965         if(box[0].size == 'md-right'){
31966             pos.push({
31967                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31968                 y : minY + (this.unitWidth + this.gutter) * 1
31969             });
31970             
31971             return pos;
31972         }
31973         
31974         var rand = Math.floor(Math.random() * (4 - box[0].y));
31975         
31976         pos.push({
31977             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31978             y : minY + (this.unitWidth + this.gutter) * rand
31979         });
31980         
31981         return pos;
31982         
31983     },
31984     
31985     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31986     {
31987         var pos = [];
31988         
31989         if(box[0].size == 'xs'){
31990             
31991             pos.push({
31992                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31993                 y : minY
31994             });
31995
31996             pos.push({
31997                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31998                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31999             });
32000             
32001             return pos;
32002             
32003         }
32004         
32005         pos.push({
32006             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32007             y : minY
32008         });
32009
32010         pos.push({
32011             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32012             y : minY + (this.unitWidth + this.gutter) * 2
32013         });
32014         
32015         return pos;
32016         
32017     },
32018     
32019     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32020     {
32021         var pos = [];
32022         
32023         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32024             
32025             pos.push({
32026                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32027                 y : minY
32028             });
32029
32030             pos.push({
32031                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32032                 y : minY + (this.unitWidth + this.gutter) * 1
32033             });
32034             
32035             pos.push({
32036                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32037                 y : minY + (this.unitWidth + this.gutter) * 2
32038             });
32039             
32040             return pos;
32041             
32042         }
32043         
32044         if(box[0].size == 'xs' && box[1].size == 'xs'){
32045             
32046             pos.push({
32047                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32048                 y : minY
32049             });
32050
32051             pos.push({
32052                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32053                 y : minY
32054             });
32055             
32056             pos.push({
32057                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32058                 y : minY + (this.unitWidth + this.gutter) * 1
32059             });
32060             
32061             return pos;
32062             
32063         }
32064         
32065         pos.push({
32066             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32067             y : minY
32068         });
32069
32070         pos.push({
32071             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32072             y : minY + (this.unitWidth + this.gutter) * 2
32073         });
32074
32075         pos.push({
32076             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32077             y : minY + (this.unitWidth + this.gutter) * 2
32078         });
32079             
32080         return pos;
32081         
32082     },
32083     
32084     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32085     {
32086         var pos = [];
32087         
32088         if(box[0].size == 'xs'){
32089             
32090             pos.push({
32091                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32092                 y : minY
32093             });
32094
32095             pos.push({
32096                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32097                 y : minY
32098             });
32099             
32100             pos.push({
32101                 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),
32102                 y : minY
32103             });
32104             
32105             pos.push({
32106                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32107                 y : minY + (this.unitWidth + this.gutter) * 1
32108             });
32109             
32110             return pos;
32111             
32112         }
32113         
32114         pos.push({
32115             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32116             y : minY
32117         });
32118         
32119         pos.push({
32120             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32121             y : minY + (this.unitWidth + this.gutter) * 2
32122         });
32123         
32124         pos.push({
32125             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32126             y : minY + (this.unitWidth + this.gutter) * 2
32127         });
32128         
32129         pos.push({
32130             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),
32131             y : minY + (this.unitWidth + this.gutter) * 2
32132         });
32133
32134         return pos;
32135         
32136     },
32137     
32138     /**
32139     * remove a Masonry Brick
32140     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32141     */
32142     removeBrick : function(brick_id)
32143     {
32144         if (!brick_id) {
32145             return;
32146         }
32147         
32148         for (var i = 0; i<this.bricks.length; i++) {
32149             if (this.bricks[i].id == brick_id) {
32150                 this.bricks.splice(i,1);
32151                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32152                 this.initial();
32153             }
32154         }
32155     },
32156     
32157     /**
32158     * adds a Masonry Brick
32159     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32160     */
32161     addBrick : function(cfg)
32162     {
32163         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32164         //this.register(cn);
32165         cn.parentId = this.id;
32166         cn.render(this.el);
32167         return cn;
32168     },
32169     
32170     /**
32171     * register a Masonry Brick
32172     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32173     */
32174     
32175     register : function(brick)
32176     {
32177         this.bricks.push(brick);
32178         brick.masonryId = this.id;
32179     },
32180     
32181     /**
32182     * clear all the Masonry Brick
32183     */
32184     clearAll : function()
32185     {
32186         this.bricks = [];
32187         //this.getChildContainer().dom.innerHTML = "";
32188         this.el.dom.innerHTML = '';
32189     },
32190     
32191     getSelected : function()
32192     {
32193         if (!this.selectedBrick) {
32194             return false;
32195         }
32196         
32197         return this.selectedBrick;
32198     }
32199 });
32200
32201 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32202     
32203     groups: {},
32204      /**
32205     * register a Masonry Layout
32206     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32207     */
32208     
32209     register : function(layout)
32210     {
32211         this.groups[layout.id] = layout;
32212     },
32213     /**
32214     * fetch a  Masonry Layout based on the masonry layout ID
32215     * @param {string} the masonry layout to add
32216     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32217     */
32218     
32219     get: function(layout_id) {
32220         if (typeof(this.groups[layout_id]) == 'undefined') {
32221             return false;
32222         }
32223         return this.groups[layout_id] ;
32224     }
32225     
32226     
32227     
32228 });
32229
32230  
32231
32232  /**
32233  *
32234  * This is based on 
32235  * http://masonry.desandro.com
32236  *
32237  * The idea is to render all the bricks based on vertical width...
32238  *
32239  * The original code extends 'outlayer' - we might need to use that....
32240  * 
32241  */
32242
32243
32244 /**
32245  * @class Roo.bootstrap.LayoutMasonryAuto
32246  * @extends Roo.bootstrap.Component
32247  * Bootstrap Layout Masonry class
32248  * 
32249  * @constructor
32250  * Create a new Element
32251  * @param {Object} config The config object
32252  */
32253
32254 Roo.bootstrap.LayoutMasonryAuto = function(config){
32255     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32256 };
32257
32258 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32259     
32260       /**
32261      * @cfg {Boolean} isFitWidth  - resize the width..
32262      */   
32263     isFitWidth : false,  // options..
32264     /**
32265      * @cfg {Boolean} isOriginLeft = left align?
32266      */   
32267     isOriginLeft : true,
32268     /**
32269      * @cfg {Boolean} isOriginTop = top align?
32270      */   
32271     isOriginTop : false,
32272     /**
32273      * @cfg {Boolean} isLayoutInstant = no animation?
32274      */   
32275     isLayoutInstant : false, // needed?
32276     /**
32277      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32278      */   
32279     isResizingContainer : true,
32280     /**
32281      * @cfg {Number} columnWidth  width of the columns 
32282      */   
32283     
32284     columnWidth : 0,
32285     
32286     /**
32287      * @cfg {Number} maxCols maximum number of columns
32288      */   
32289     
32290     maxCols: 0,
32291     /**
32292      * @cfg {Number} padHeight padding below box..
32293      */   
32294     
32295     padHeight : 10, 
32296     
32297     /**
32298      * @cfg {Boolean} isAutoInitial defalut true
32299      */   
32300     
32301     isAutoInitial : true, 
32302     
32303     // private?
32304     gutter : 0,
32305     
32306     containerWidth: 0,
32307     initialColumnWidth : 0,
32308     currentSize : null,
32309     
32310     colYs : null, // array.
32311     maxY : 0,
32312     padWidth: 10,
32313     
32314     
32315     tag: 'div',
32316     cls: '',
32317     bricks: null, //CompositeElement
32318     cols : 0, // array?
32319     // element : null, // wrapped now this.el
32320     _isLayoutInited : null, 
32321     
32322     
32323     getAutoCreate : function(){
32324         
32325         var cfg = {
32326             tag: this.tag,
32327             cls: 'blog-masonary-wrapper ' + this.cls,
32328             cn : {
32329                 cls : 'mas-boxes masonary'
32330             }
32331         };
32332         
32333         return cfg;
32334     },
32335     
32336     getChildContainer: function( )
32337     {
32338         if (this.boxesEl) {
32339             return this.boxesEl;
32340         }
32341         
32342         this.boxesEl = this.el.select('.mas-boxes').first();
32343         
32344         return this.boxesEl;
32345     },
32346     
32347     
32348     initEvents : function()
32349     {
32350         var _this = this;
32351         
32352         if(this.isAutoInitial){
32353             Roo.log('hook children rendered');
32354             this.on('childrenrendered', function() {
32355                 Roo.log('children rendered');
32356                 _this.initial();
32357             } ,this);
32358         }
32359         
32360     },
32361     
32362     initial : function()
32363     {
32364         this.reloadItems();
32365
32366         this.currentSize = this.el.getBox(true);
32367
32368         /// was window resize... - let's see if this works..
32369         Roo.EventManager.onWindowResize(this.resize, this); 
32370
32371         if(!this.isAutoInitial){
32372             this.layout();
32373             return;
32374         }
32375         
32376         this.layout.defer(500,this);
32377     },
32378     
32379     reloadItems: function()
32380     {
32381         this.bricks = this.el.select('.masonry-brick', true);
32382         
32383         this.bricks.each(function(b) {
32384             //Roo.log(b.getSize());
32385             if (!b.attr('originalwidth')) {
32386                 b.attr('originalwidth',  b.getSize().width);
32387             }
32388             
32389         });
32390         
32391         Roo.log(this.bricks.elements.length);
32392     },
32393     
32394     resize : function()
32395     {
32396         Roo.log('resize');
32397         var cs = this.el.getBox(true);
32398         
32399         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32400             Roo.log("no change in with or X");
32401             return;
32402         }
32403         this.currentSize = cs;
32404         this.layout();
32405     },
32406     
32407     layout : function()
32408     {
32409          Roo.log('layout');
32410         this._resetLayout();
32411         //this._manageStamps();
32412       
32413         // don't animate first layout
32414         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32415         this.layoutItems( isInstant );
32416       
32417         // flag for initalized
32418         this._isLayoutInited = true;
32419     },
32420     
32421     layoutItems : function( isInstant )
32422     {
32423         //var items = this._getItemsForLayout( this.items );
32424         // original code supports filtering layout items.. we just ignore it..
32425         
32426         this._layoutItems( this.bricks , isInstant );
32427       
32428         this._postLayout();
32429     },
32430     _layoutItems : function ( items , isInstant)
32431     {
32432        //this.fireEvent( 'layout', this, items );
32433     
32434
32435         if ( !items || !items.elements.length ) {
32436           // no items, emit event with empty array
32437             return;
32438         }
32439
32440         var queue = [];
32441         items.each(function(item) {
32442             Roo.log("layout item");
32443             Roo.log(item);
32444             // get x/y object from method
32445             var position = this._getItemLayoutPosition( item );
32446             // enqueue
32447             position.item = item;
32448             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32449             queue.push( position );
32450         }, this);
32451       
32452         this._processLayoutQueue( queue );
32453     },
32454     /** Sets position of item in DOM
32455     * @param {Element} item
32456     * @param {Number} x - horizontal position
32457     * @param {Number} y - vertical position
32458     * @param {Boolean} isInstant - disables transitions
32459     */
32460     _processLayoutQueue : function( queue )
32461     {
32462         for ( var i=0, len = queue.length; i < len; i++ ) {
32463             var obj = queue[i];
32464             obj.item.position('absolute');
32465             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32466         }
32467     },
32468       
32469     
32470     /**
32471     * Any logic you want to do after each layout,
32472     * i.e. size the container
32473     */
32474     _postLayout : function()
32475     {
32476         this.resizeContainer();
32477     },
32478     
32479     resizeContainer : function()
32480     {
32481         if ( !this.isResizingContainer ) {
32482             return;
32483         }
32484         var size = this._getContainerSize();
32485         if ( size ) {
32486             this.el.setSize(size.width,size.height);
32487             this.boxesEl.setSize(size.width,size.height);
32488         }
32489     },
32490     
32491     
32492     
32493     _resetLayout : function()
32494     {
32495         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32496         this.colWidth = this.el.getWidth();
32497         //this.gutter = this.el.getWidth(); 
32498         
32499         this.measureColumns();
32500
32501         // reset column Y
32502         var i = this.cols;
32503         this.colYs = [];
32504         while (i--) {
32505             this.colYs.push( 0 );
32506         }
32507     
32508         this.maxY = 0;
32509     },
32510
32511     measureColumns : function()
32512     {
32513         this.getContainerWidth();
32514       // if columnWidth is 0, default to outerWidth of first item
32515         if ( !this.columnWidth ) {
32516             var firstItem = this.bricks.first();
32517             Roo.log(firstItem);
32518             this.columnWidth  = this.containerWidth;
32519             if (firstItem && firstItem.attr('originalwidth') ) {
32520                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32521             }
32522             // columnWidth fall back to item of first element
32523             Roo.log("set column width?");
32524                         this.initialColumnWidth = this.columnWidth  ;
32525
32526             // if first elem has no width, default to size of container
32527             
32528         }
32529         
32530         
32531         if (this.initialColumnWidth) {
32532             this.columnWidth = this.initialColumnWidth;
32533         }
32534         
32535         
32536             
32537         // column width is fixed at the top - however if container width get's smaller we should
32538         // reduce it...
32539         
32540         // this bit calcs how man columns..
32541             
32542         var columnWidth = this.columnWidth += this.gutter;
32543       
32544         // calculate columns
32545         var containerWidth = this.containerWidth + this.gutter;
32546         
32547         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32548         // fix rounding errors, typically with gutters
32549         var excess = columnWidth - containerWidth % columnWidth;
32550         
32551         
32552         // if overshoot is less than a pixel, round up, otherwise floor it
32553         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32554         cols = Math[ mathMethod ]( cols );
32555         this.cols = Math.max( cols, 1 );
32556         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32557         
32558          // padding positioning..
32559         var totalColWidth = this.cols * this.columnWidth;
32560         var padavail = this.containerWidth - totalColWidth;
32561         // so for 2 columns - we need 3 'pads'
32562         
32563         var padNeeded = (1+this.cols) * this.padWidth;
32564         
32565         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32566         
32567         this.columnWidth += padExtra
32568         //this.padWidth = Math.floor(padavail /  ( this.cols));
32569         
32570         // adjust colum width so that padding is fixed??
32571         
32572         // we have 3 columns ... total = width * 3
32573         // we have X left over... that should be used by 
32574         
32575         //if (this.expandC) {
32576             
32577         //}
32578         
32579         
32580         
32581     },
32582     
32583     getContainerWidth : function()
32584     {
32585        /* // container is parent if fit width
32586         var container = this.isFitWidth ? this.element.parentNode : this.element;
32587         // check that this.size and size are there
32588         // IE8 triggers resize on body size change, so they might not be
32589         
32590         var size = getSize( container );  //FIXME
32591         this.containerWidth = size && size.innerWidth; //FIXME
32592         */
32593          
32594         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32595         
32596     },
32597     
32598     _getItemLayoutPosition : function( item )  // what is item?
32599     {
32600         // we resize the item to our columnWidth..
32601       
32602         item.setWidth(this.columnWidth);
32603         item.autoBoxAdjust  = false;
32604         
32605         var sz = item.getSize();
32606  
32607         // how many columns does this brick span
32608         var remainder = this.containerWidth % this.columnWidth;
32609         
32610         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32611         // round if off by 1 pixel, otherwise use ceil
32612         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32613         colSpan = Math.min( colSpan, this.cols );
32614         
32615         // normally this should be '1' as we dont' currently allow multi width columns..
32616         
32617         var colGroup = this._getColGroup( colSpan );
32618         // get the minimum Y value from the columns
32619         var minimumY = Math.min.apply( Math, colGroup );
32620         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32621         
32622         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32623          
32624         // position the brick
32625         var position = {
32626             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32627             y: this.currentSize.y + minimumY + this.padHeight
32628         };
32629         
32630         Roo.log(position);
32631         // apply setHeight to necessary columns
32632         var setHeight = minimumY + sz.height + this.padHeight;
32633         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32634         
32635         var setSpan = this.cols + 1 - colGroup.length;
32636         for ( var i = 0; i < setSpan; i++ ) {
32637           this.colYs[ shortColIndex + i ] = setHeight ;
32638         }
32639       
32640         return position;
32641     },
32642     
32643     /**
32644      * @param {Number} colSpan - number of columns the element spans
32645      * @returns {Array} colGroup
32646      */
32647     _getColGroup : function( colSpan )
32648     {
32649         if ( colSpan < 2 ) {
32650           // if brick spans only one column, use all the column Ys
32651           return this.colYs;
32652         }
32653       
32654         var colGroup = [];
32655         // how many different places could this brick fit horizontally
32656         var groupCount = this.cols + 1 - colSpan;
32657         // for each group potential horizontal position
32658         for ( var i = 0; i < groupCount; i++ ) {
32659           // make an array of colY values for that one group
32660           var groupColYs = this.colYs.slice( i, i + colSpan );
32661           // and get the max value of the array
32662           colGroup[i] = Math.max.apply( Math, groupColYs );
32663         }
32664         return colGroup;
32665     },
32666     /*
32667     _manageStamp : function( stamp )
32668     {
32669         var stampSize =  stamp.getSize();
32670         var offset = stamp.getBox();
32671         // get the columns that this stamp affects
32672         var firstX = this.isOriginLeft ? offset.x : offset.right;
32673         var lastX = firstX + stampSize.width;
32674         var firstCol = Math.floor( firstX / this.columnWidth );
32675         firstCol = Math.max( 0, firstCol );
32676         
32677         var lastCol = Math.floor( lastX / this.columnWidth );
32678         // lastCol should not go over if multiple of columnWidth #425
32679         lastCol -= lastX % this.columnWidth ? 0 : 1;
32680         lastCol = Math.min( this.cols - 1, lastCol );
32681         
32682         // set colYs to bottom of the stamp
32683         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32684             stampSize.height;
32685             
32686         for ( var i = firstCol; i <= lastCol; i++ ) {
32687           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32688         }
32689     },
32690     */
32691     
32692     _getContainerSize : function()
32693     {
32694         this.maxY = Math.max.apply( Math, this.colYs );
32695         var size = {
32696             height: this.maxY
32697         };
32698       
32699         if ( this.isFitWidth ) {
32700             size.width = this._getContainerFitWidth();
32701         }
32702       
32703         return size;
32704     },
32705     
32706     _getContainerFitWidth : function()
32707     {
32708         var unusedCols = 0;
32709         // count unused columns
32710         var i = this.cols;
32711         while ( --i ) {
32712           if ( this.colYs[i] !== 0 ) {
32713             break;
32714           }
32715           unusedCols++;
32716         }
32717         // fit container to columns that have been used
32718         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32719     },
32720     
32721     needsResizeLayout : function()
32722     {
32723         var previousWidth = this.containerWidth;
32724         this.getContainerWidth();
32725         return previousWidth !== this.containerWidth;
32726     }
32727  
32728 });
32729
32730  
32731
32732  /*
32733  * - LGPL
32734  *
32735  * element
32736  * 
32737  */
32738
32739 /**
32740  * @class Roo.bootstrap.MasonryBrick
32741  * @extends Roo.bootstrap.Component
32742  * Bootstrap MasonryBrick class
32743  * 
32744  * @constructor
32745  * Create a new MasonryBrick
32746  * @param {Object} config The config object
32747  */
32748
32749 Roo.bootstrap.MasonryBrick = function(config){
32750     
32751     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32752     
32753     Roo.bootstrap.MasonryBrick.register(this);
32754     
32755     this.addEvents({
32756         // raw events
32757         /**
32758          * @event click
32759          * When a MasonryBrick is clcik
32760          * @param {Roo.bootstrap.MasonryBrick} this
32761          * @param {Roo.EventObject} e
32762          */
32763         "click" : true
32764     });
32765 };
32766
32767 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32768     
32769     /**
32770      * @cfg {String} title
32771      */   
32772     title : '',
32773     /**
32774      * @cfg {String} html
32775      */   
32776     html : '',
32777     /**
32778      * @cfg {String} bgimage
32779      */   
32780     bgimage : '',
32781     /**
32782      * @cfg {String} videourl
32783      */   
32784     videourl : '',
32785     /**
32786      * @cfg {String} cls
32787      */   
32788     cls : '',
32789     /**
32790      * @cfg {String} href
32791      */   
32792     href : '',
32793     /**
32794      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32795      */   
32796     size : 'xs',
32797     
32798     /**
32799      * @cfg {String} placetitle (center|bottom)
32800      */   
32801     placetitle : '',
32802     
32803     /**
32804      * @cfg {Boolean} isFitContainer defalut true
32805      */   
32806     isFitContainer : true, 
32807     
32808     /**
32809      * @cfg {Boolean} preventDefault defalut false
32810      */   
32811     preventDefault : false, 
32812     
32813     /**
32814      * @cfg {Boolean} inverse defalut false
32815      */   
32816     maskInverse : false, 
32817     
32818     getAutoCreate : function()
32819     {
32820         if(!this.isFitContainer){
32821             return this.getSplitAutoCreate();
32822         }
32823         
32824         var cls = 'masonry-brick masonry-brick-full';
32825         
32826         if(this.href.length){
32827             cls += ' masonry-brick-link';
32828         }
32829         
32830         if(this.bgimage.length){
32831             cls += ' masonry-brick-image';
32832         }
32833         
32834         if(this.maskInverse){
32835             cls += ' mask-inverse';
32836         }
32837         
32838         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32839             cls += ' enable-mask';
32840         }
32841         
32842         if(this.size){
32843             cls += ' masonry-' + this.size + '-brick';
32844         }
32845         
32846         if(this.placetitle.length){
32847             
32848             switch (this.placetitle) {
32849                 case 'center' :
32850                     cls += ' masonry-center-title';
32851                     break;
32852                 case 'bottom' :
32853                     cls += ' masonry-bottom-title';
32854                     break;
32855                 default:
32856                     break;
32857             }
32858             
32859         } else {
32860             if(!this.html.length && !this.bgimage.length){
32861                 cls += ' masonry-center-title';
32862             }
32863
32864             if(!this.html.length && this.bgimage.length){
32865                 cls += ' masonry-bottom-title';
32866             }
32867         }
32868         
32869         if(this.cls){
32870             cls += ' ' + this.cls;
32871         }
32872         
32873         var cfg = {
32874             tag: (this.href.length) ? 'a' : 'div',
32875             cls: cls,
32876             cn: [
32877                 {
32878                     tag: 'div',
32879                     cls: 'masonry-brick-mask'
32880                 },
32881                 {
32882                     tag: 'div',
32883                     cls: 'masonry-brick-paragraph',
32884                     cn: []
32885                 }
32886             ]
32887         };
32888         
32889         if(this.href.length){
32890             cfg.href = this.href;
32891         }
32892         
32893         var cn = cfg.cn[1].cn;
32894         
32895         if(this.title.length){
32896             cn.push({
32897                 tag: 'h4',
32898                 cls: 'masonry-brick-title',
32899                 html: this.title
32900             });
32901         }
32902         
32903         if(this.html.length){
32904             cn.push({
32905                 tag: 'p',
32906                 cls: 'masonry-brick-text',
32907                 html: this.html
32908             });
32909         }
32910         
32911         if (!this.title.length && !this.html.length) {
32912             cfg.cn[1].cls += ' hide';
32913         }
32914         
32915         if(this.bgimage.length){
32916             cfg.cn.push({
32917                 tag: 'img',
32918                 cls: 'masonry-brick-image-view',
32919                 src: this.bgimage
32920             });
32921         }
32922         
32923         if(this.videourl.length){
32924             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32925             // youtube support only?
32926             cfg.cn.push({
32927                 tag: 'iframe',
32928                 cls: 'masonry-brick-image-view',
32929                 src: vurl,
32930                 frameborder : 0,
32931                 allowfullscreen : true
32932             });
32933         }
32934         
32935         return cfg;
32936         
32937     },
32938     
32939     getSplitAutoCreate : function()
32940     {
32941         var cls = 'masonry-brick masonry-brick-split';
32942         
32943         if(this.href.length){
32944             cls += ' masonry-brick-link';
32945         }
32946         
32947         if(this.bgimage.length){
32948             cls += ' masonry-brick-image';
32949         }
32950         
32951         if(this.size){
32952             cls += ' masonry-' + this.size + '-brick';
32953         }
32954         
32955         switch (this.placetitle) {
32956             case 'center' :
32957                 cls += ' masonry-center-title';
32958                 break;
32959             case 'bottom' :
32960                 cls += ' masonry-bottom-title';
32961                 break;
32962             default:
32963                 if(!this.bgimage.length){
32964                     cls += ' masonry-center-title';
32965                 }
32966
32967                 if(this.bgimage.length){
32968                     cls += ' masonry-bottom-title';
32969                 }
32970                 break;
32971         }
32972         
32973         if(this.cls){
32974             cls += ' ' + this.cls;
32975         }
32976         
32977         var cfg = {
32978             tag: (this.href.length) ? 'a' : 'div',
32979             cls: cls,
32980             cn: [
32981                 {
32982                     tag: 'div',
32983                     cls: 'masonry-brick-split-head',
32984                     cn: [
32985                         {
32986                             tag: 'div',
32987                             cls: 'masonry-brick-paragraph',
32988                             cn: []
32989                         }
32990                     ]
32991                 },
32992                 {
32993                     tag: 'div',
32994                     cls: 'masonry-brick-split-body',
32995                     cn: []
32996                 }
32997             ]
32998         };
32999         
33000         if(this.href.length){
33001             cfg.href = this.href;
33002         }
33003         
33004         if(this.title.length){
33005             cfg.cn[0].cn[0].cn.push({
33006                 tag: 'h4',
33007                 cls: 'masonry-brick-title',
33008                 html: this.title
33009             });
33010         }
33011         
33012         if(this.html.length){
33013             cfg.cn[1].cn.push({
33014                 tag: 'p',
33015                 cls: 'masonry-brick-text',
33016                 html: this.html
33017             });
33018         }
33019
33020         if(this.bgimage.length){
33021             cfg.cn[0].cn.push({
33022                 tag: 'img',
33023                 cls: 'masonry-brick-image-view',
33024                 src: this.bgimage
33025             });
33026         }
33027         
33028         if(this.videourl.length){
33029             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33030             // youtube support only?
33031             cfg.cn[0].cn.cn.push({
33032                 tag: 'iframe',
33033                 cls: 'masonry-brick-image-view',
33034                 src: vurl,
33035                 frameborder : 0,
33036                 allowfullscreen : true
33037             });
33038         }
33039         
33040         return cfg;
33041     },
33042     
33043     initEvents: function() 
33044     {
33045         switch (this.size) {
33046             case 'xs' :
33047                 this.x = 1;
33048                 this.y = 1;
33049                 break;
33050             case 'sm' :
33051                 this.x = 2;
33052                 this.y = 2;
33053                 break;
33054             case 'md' :
33055             case 'md-left' :
33056             case 'md-right' :
33057                 this.x = 3;
33058                 this.y = 3;
33059                 break;
33060             case 'tall' :
33061                 this.x = 2;
33062                 this.y = 3;
33063                 break;
33064             case 'wide' :
33065                 this.x = 3;
33066                 this.y = 2;
33067                 break;
33068             case 'wide-thin' :
33069                 this.x = 3;
33070                 this.y = 1;
33071                 break;
33072                         
33073             default :
33074                 break;
33075         }
33076         
33077         if(Roo.isTouch){
33078             this.el.on('touchstart', this.onTouchStart, this);
33079             this.el.on('touchmove', this.onTouchMove, this);
33080             this.el.on('touchend', this.onTouchEnd, this);
33081             this.el.on('contextmenu', this.onContextMenu, this);
33082         } else {
33083             this.el.on('mouseenter'  ,this.enter, this);
33084             this.el.on('mouseleave', this.leave, this);
33085             this.el.on('click', this.onClick, this);
33086         }
33087         
33088         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33089             this.parent().bricks.push(this);   
33090         }
33091         
33092     },
33093     
33094     onClick: function(e, el)
33095     {
33096         var time = this.endTimer - this.startTimer;
33097         // Roo.log(e.preventDefault());
33098         if(Roo.isTouch){
33099             if(time > 1000){
33100                 e.preventDefault();
33101                 return;
33102             }
33103         }
33104         
33105         if(!this.preventDefault){
33106             return;
33107         }
33108         
33109         e.preventDefault();
33110         
33111         if (this.activeClass != '') {
33112             this.selectBrick();
33113         }
33114         
33115         this.fireEvent('click', this, e);
33116     },
33117     
33118     enter: function(e, el)
33119     {
33120         e.preventDefault();
33121         
33122         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33123             return;
33124         }
33125         
33126         if(this.bgimage.length && this.html.length){
33127             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33128         }
33129     },
33130     
33131     leave: function(e, el)
33132     {
33133         e.preventDefault();
33134         
33135         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33136             return;
33137         }
33138         
33139         if(this.bgimage.length && this.html.length){
33140             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33141         }
33142     },
33143     
33144     onTouchStart: function(e, el)
33145     {
33146 //        e.preventDefault();
33147         
33148         this.touchmoved = false;
33149         
33150         if(!this.isFitContainer){
33151             return;
33152         }
33153         
33154         if(!this.bgimage.length || !this.html.length){
33155             return;
33156         }
33157         
33158         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33159         
33160         this.timer = new Date().getTime();
33161         
33162     },
33163     
33164     onTouchMove: function(e, el)
33165     {
33166         this.touchmoved = true;
33167     },
33168     
33169     onContextMenu : function(e,el)
33170     {
33171         e.preventDefault();
33172         e.stopPropagation();
33173         return false;
33174     },
33175     
33176     onTouchEnd: function(e, el)
33177     {
33178 //        e.preventDefault();
33179         
33180         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33181         
33182             this.leave(e,el);
33183             
33184             return;
33185         }
33186         
33187         if(!this.bgimage.length || !this.html.length){
33188             
33189             if(this.href.length){
33190                 window.location.href = this.href;
33191             }
33192             
33193             return;
33194         }
33195         
33196         if(!this.isFitContainer){
33197             return;
33198         }
33199         
33200         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33201         
33202         window.location.href = this.href;
33203     },
33204     
33205     //selection on single brick only
33206     selectBrick : function() {
33207         
33208         if (!this.parentId) {
33209             return;
33210         }
33211         
33212         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33213         var index = m.selectedBrick.indexOf(this.id);
33214         
33215         if ( index > -1) {
33216             m.selectedBrick.splice(index,1);
33217             this.el.removeClass(this.activeClass);
33218             return;
33219         }
33220         
33221         for(var i = 0; i < m.selectedBrick.length; i++) {
33222             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33223             b.el.removeClass(b.activeClass);
33224         }
33225         
33226         m.selectedBrick = [];
33227         
33228         m.selectedBrick.push(this.id);
33229         this.el.addClass(this.activeClass);
33230         return;
33231     },
33232     
33233     isSelected : function(){
33234         return this.el.hasClass(this.activeClass);
33235         
33236     }
33237 });
33238
33239 Roo.apply(Roo.bootstrap.MasonryBrick, {
33240     
33241     //groups: {},
33242     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33243      /**
33244     * register a Masonry Brick
33245     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33246     */
33247     
33248     register : function(brick)
33249     {
33250         //this.groups[brick.id] = brick;
33251         this.groups.add(brick.id, brick);
33252     },
33253     /**
33254     * fetch a  masonry brick based on the masonry brick ID
33255     * @param {string} the masonry brick to add
33256     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33257     */
33258     
33259     get: function(brick_id) 
33260     {
33261         // if (typeof(this.groups[brick_id]) == 'undefined') {
33262         //     return false;
33263         // }
33264         // return this.groups[brick_id] ;
33265         
33266         if(this.groups.key(brick_id)) {
33267             return this.groups.key(brick_id);
33268         }
33269         
33270         return false;
33271     }
33272     
33273     
33274     
33275 });
33276
33277  /*
33278  * - LGPL
33279  *
33280  * element
33281  * 
33282  */
33283
33284 /**
33285  * @class Roo.bootstrap.Brick
33286  * @extends Roo.bootstrap.Component
33287  * Bootstrap Brick class
33288  * 
33289  * @constructor
33290  * Create a new Brick
33291  * @param {Object} config The config object
33292  */
33293
33294 Roo.bootstrap.Brick = function(config){
33295     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33296     
33297     this.addEvents({
33298         // raw events
33299         /**
33300          * @event click
33301          * When a Brick is click
33302          * @param {Roo.bootstrap.Brick} this
33303          * @param {Roo.EventObject} e
33304          */
33305         "click" : true
33306     });
33307 };
33308
33309 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33310     
33311     /**
33312      * @cfg {String} title
33313      */   
33314     title : '',
33315     /**
33316      * @cfg {String} html
33317      */   
33318     html : '',
33319     /**
33320      * @cfg {String} bgimage
33321      */   
33322     bgimage : '',
33323     /**
33324      * @cfg {String} cls
33325      */   
33326     cls : '',
33327     /**
33328      * @cfg {String} href
33329      */   
33330     href : '',
33331     /**
33332      * @cfg {String} video
33333      */   
33334     video : '',
33335     /**
33336      * @cfg {Boolean} square
33337      */   
33338     square : true,
33339     
33340     getAutoCreate : function()
33341     {
33342         var cls = 'roo-brick';
33343         
33344         if(this.href.length){
33345             cls += ' roo-brick-link';
33346         }
33347         
33348         if(this.bgimage.length){
33349             cls += ' roo-brick-image';
33350         }
33351         
33352         if(!this.html.length && !this.bgimage.length){
33353             cls += ' roo-brick-center-title';
33354         }
33355         
33356         if(!this.html.length && this.bgimage.length){
33357             cls += ' roo-brick-bottom-title';
33358         }
33359         
33360         if(this.cls){
33361             cls += ' ' + this.cls;
33362         }
33363         
33364         var cfg = {
33365             tag: (this.href.length) ? 'a' : 'div',
33366             cls: cls,
33367             cn: [
33368                 {
33369                     tag: 'div',
33370                     cls: 'roo-brick-paragraph',
33371                     cn: []
33372                 }
33373             ]
33374         };
33375         
33376         if(this.href.length){
33377             cfg.href = this.href;
33378         }
33379         
33380         var cn = cfg.cn[0].cn;
33381         
33382         if(this.title.length){
33383             cn.push({
33384                 tag: 'h4',
33385                 cls: 'roo-brick-title',
33386                 html: this.title
33387             });
33388         }
33389         
33390         if(this.html.length){
33391             cn.push({
33392                 tag: 'p',
33393                 cls: 'roo-brick-text',
33394                 html: this.html
33395             });
33396         } else {
33397             cn.cls += ' hide';
33398         }
33399         
33400         if(this.bgimage.length){
33401             cfg.cn.push({
33402                 tag: 'img',
33403                 cls: 'roo-brick-image-view',
33404                 src: this.bgimage
33405             });
33406         }
33407         
33408         return cfg;
33409     },
33410     
33411     initEvents: function() 
33412     {
33413         if(this.title.length || this.html.length){
33414             this.el.on('mouseenter'  ,this.enter, this);
33415             this.el.on('mouseleave', this.leave, this);
33416         }
33417         
33418         Roo.EventManager.onWindowResize(this.resize, this); 
33419         
33420         if(this.bgimage.length){
33421             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33422             this.imageEl.on('load', this.onImageLoad, this);
33423             return;
33424         }
33425         
33426         this.resize();
33427     },
33428     
33429     onImageLoad : function()
33430     {
33431         this.resize();
33432     },
33433     
33434     resize : function()
33435     {
33436         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33437         
33438         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33439         
33440         if(this.bgimage.length){
33441             var image = this.el.select('.roo-brick-image-view', true).first();
33442             
33443             image.setWidth(paragraph.getWidth());
33444             
33445             if(this.square){
33446                 image.setHeight(paragraph.getWidth());
33447             }
33448             
33449             this.el.setHeight(image.getHeight());
33450             paragraph.setHeight(image.getHeight());
33451             
33452         }
33453         
33454     },
33455     
33456     enter: function(e, el)
33457     {
33458         e.preventDefault();
33459         
33460         if(this.bgimage.length){
33461             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33462             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33463         }
33464     },
33465     
33466     leave: function(e, el)
33467     {
33468         e.preventDefault();
33469         
33470         if(this.bgimage.length){
33471             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33472             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33473         }
33474     }
33475     
33476 });
33477
33478  
33479
33480  /*
33481  * - LGPL
33482  *
33483  * Number field 
33484  */
33485
33486 /**
33487  * @class Roo.bootstrap.NumberField
33488  * @extends Roo.bootstrap.Input
33489  * Bootstrap NumberField class
33490  * 
33491  * 
33492  * 
33493  * 
33494  * @constructor
33495  * Create a new NumberField
33496  * @param {Object} config The config object
33497  */
33498
33499 Roo.bootstrap.NumberField = function(config){
33500     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33501 };
33502
33503 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33504     
33505     /**
33506      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33507      */
33508     allowDecimals : true,
33509     /**
33510      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33511      */
33512     decimalSeparator : ".",
33513     /**
33514      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33515      */
33516     decimalPrecision : 2,
33517     /**
33518      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33519      */
33520     allowNegative : true,
33521     
33522     /**
33523      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33524      */
33525     allowZero: true,
33526     /**
33527      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33528      */
33529     minValue : Number.NEGATIVE_INFINITY,
33530     /**
33531      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33532      */
33533     maxValue : Number.MAX_VALUE,
33534     /**
33535      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33536      */
33537     minText : "The minimum value for this field is {0}",
33538     /**
33539      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33540      */
33541     maxText : "The maximum value for this field is {0}",
33542     /**
33543      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33544      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33545      */
33546     nanText : "{0} is not a valid number",
33547     /**
33548      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33549      */
33550     thousandsDelimiter : false,
33551     /**
33552      * @cfg {String} valueAlign alignment of value
33553      */
33554     valueAlign : "left",
33555
33556     getAutoCreate : function()
33557     {
33558         var hiddenInput = {
33559             tag: 'input',
33560             type: 'hidden',
33561             id: Roo.id(),
33562             cls: 'hidden-number-input'
33563         };
33564         
33565         if (this.name) {
33566             hiddenInput.name = this.name;
33567         }
33568         
33569         this.name = '';
33570         
33571         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33572         
33573         this.name = hiddenInput.name;
33574         
33575         if(cfg.cn.length > 0) {
33576             cfg.cn.push(hiddenInput);
33577         }
33578         
33579         return cfg;
33580     },
33581
33582     // private
33583     initEvents : function()
33584     {   
33585         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33586         
33587         var allowed = "0123456789";
33588         
33589         if(this.allowDecimals){
33590             allowed += this.decimalSeparator;
33591         }
33592         
33593         if(this.allowNegative){
33594             allowed += "-";
33595         }
33596         
33597         if(this.thousandsDelimiter) {
33598             allowed += ",";
33599         }
33600         
33601         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33602         
33603         var keyPress = function(e){
33604             
33605             var k = e.getKey();
33606             
33607             var c = e.getCharCode();
33608             
33609             if(
33610                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33611                     allowed.indexOf(String.fromCharCode(c)) === -1
33612             ){
33613                 e.stopEvent();
33614                 return;
33615             }
33616             
33617             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33618                 return;
33619             }
33620             
33621             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33622                 e.stopEvent();
33623             }
33624         };
33625         
33626         this.el.on("keypress", keyPress, this);
33627     },
33628     
33629     validateValue : function(value)
33630     {
33631         
33632         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33633             return false;
33634         }
33635         
33636         var num = this.parseValue(value);
33637         
33638         if(isNaN(num)){
33639             this.markInvalid(String.format(this.nanText, value));
33640             return false;
33641         }
33642         
33643         if(num < this.minValue){
33644             this.markInvalid(String.format(this.minText, this.minValue));
33645             return false;
33646         }
33647         
33648         if(num > this.maxValue){
33649             this.markInvalid(String.format(this.maxText, this.maxValue));
33650             return false;
33651         }
33652         
33653         return true;
33654     },
33655
33656     getValue : function()
33657     {
33658         var v = this.hiddenEl().getValue();
33659         
33660         return this.fixPrecision(this.parseValue(v));
33661     },
33662
33663     parseValue : function(value)
33664     {
33665         if(this.thousandsDelimiter) {
33666             value += "";
33667             r = new RegExp(",", "g");
33668             value = value.replace(r, "");
33669         }
33670         
33671         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33672         return isNaN(value) ? '' : value;
33673     },
33674
33675     fixPrecision : function(value)
33676     {
33677         if(this.thousandsDelimiter) {
33678             value += "";
33679             r = new RegExp(",", "g");
33680             value = value.replace(r, "");
33681         }
33682         
33683         var nan = isNaN(value);
33684         
33685         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33686             return nan ? '' : value;
33687         }
33688         return parseFloat(value).toFixed(this.decimalPrecision);
33689     },
33690
33691     setValue : function(v)
33692     {
33693         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33694         
33695         this.value = v;
33696         
33697         if(this.rendered){
33698             
33699             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33700             
33701             this.inputEl().dom.value = (v == '') ? '' :
33702                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33703             
33704             if(!this.allowZero && v === '0') {
33705                 this.hiddenEl().dom.value = '';
33706                 this.inputEl().dom.value = '';
33707             }
33708             
33709             this.validate();
33710         }
33711     },
33712
33713     decimalPrecisionFcn : function(v)
33714     {
33715         return Math.floor(v);
33716     },
33717
33718     beforeBlur : function()
33719     {
33720         var v = this.parseValue(this.getRawValue());
33721         
33722         if(v || v === 0 || v === ''){
33723             this.setValue(v);
33724         }
33725     },
33726     
33727     hiddenEl : function()
33728     {
33729         return this.el.select('input.hidden-number-input',true).first();
33730     }
33731     
33732 });
33733
33734  
33735
33736 /*
33737 * Licence: LGPL
33738 */
33739
33740 /**
33741  * @class Roo.bootstrap.DocumentSlider
33742  * @extends Roo.bootstrap.Component
33743  * Bootstrap DocumentSlider class
33744  * 
33745  * @constructor
33746  * Create a new DocumentViewer
33747  * @param {Object} config The config object
33748  */
33749
33750 Roo.bootstrap.DocumentSlider = function(config){
33751     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33752     
33753     this.files = [];
33754     
33755     this.addEvents({
33756         /**
33757          * @event initial
33758          * Fire after initEvent
33759          * @param {Roo.bootstrap.DocumentSlider} this
33760          */
33761         "initial" : true,
33762         /**
33763          * @event update
33764          * Fire after update
33765          * @param {Roo.bootstrap.DocumentSlider} this
33766          */
33767         "update" : true,
33768         /**
33769          * @event click
33770          * Fire after click
33771          * @param {Roo.bootstrap.DocumentSlider} this
33772          */
33773         "click" : true
33774     });
33775 };
33776
33777 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33778     
33779     files : false,
33780     
33781     indicator : 0,
33782     
33783     getAutoCreate : function()
33784     {
33785         var cfg = {
33786             tag : 'div',
33787             cls : 'roo-document-slider',
33788             cn : [
33789                 {
33790                     tag : 'div',
33791                     cls : 'roo-document-slider-header',
33792                     cn : [
33793                         {
33794                             tag : 'div',
33795                             cls : 'roo-document-slider-header-title'
33796                         }
33797                     ]
33798                 },
33799                 {
33800                     tag : 'div',
33801                     cls : 'roo-document-slider-body',
33802                     cn : [
33803                         {
33804                             tag : 'div',
33805                             cls : 'roo-document-slider-prev',
33806                             cn : [
33807                                 {
33808                                     tag : 'i',
33809                                     cls : 'fa fa-chevron-left'
33810                                 }
33811                             ]
33812                         },
33813                         {
33814                             tag : 'div',
33815                             cls : 'roo-document-slider-thumb',
33816                             cn : [
33817                                 {
33818                                     tag : 'img',
33819                                     cls : 'roo-document-slider-image'
33820                                 }
33821                             ]
33822                         },
33823                         {
33824                             tag : 'div',
33825                             cls : 'roo-document-slider-next',
33826                             cn : [
33827                                 {
33828                                     tag : 'i',
33829                                     cls : 'fa fa-chevron-right'
33830                                 }
33831                             ]
33832                         }
33833                     ]
33834                 }
33835             ]
33836         };
33837         
33838         return cfg;
33839     },
33840     
33841     initEvents : function()
33842     {
33843         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33844         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33845         
33846         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33847         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33848         
33849         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33850         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33851         
33852         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33853         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33854         
33855         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33856         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33857         
33858         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33859         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33860         
33861         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33862         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33863         
33864         this.thumbEl.on('click', this.onClick, this);
33865         
33866         this.prevIndicator.on('click', this.prev, this);
33867         
33868         this.nextIndicator.on('click', this.next, this);
33869         
33870     },
33871     
33872     initial : function()
33873     {
33874         if(this.files.length){
33875             this.indicator = 1;
33876             this.update()
33877         }
33878         
33879         this.fireEvent('initial', this);
33880     },
33881     
33882     update : function()
33883     {
33884         this.imageEl.attr('src', this.files[this.indicator - 1]);
33885         
33886         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33887         
33888         this.prevIndicator.show();
33889         
33890         if(this.indicator == 1){
33891             this.prevIndicator.hide();
33892         }
33893         
33894         this.nextIndicator.show();
33895         
33896         if(this.indicator == this.files.length){
33897             this.nextIndicator.hide();
33898         }
33899         
33900         this.thumbEl.scrollTo('top');
33901         
33902         this.fireEvent('update', this);
33903     },
33904     
33905     onClick : function(e)
33906     {
33907         e.preventDefault();
33908         
33909         this.fireEvent('click', this);
33910     },
33911     
33912     prev : function(e)
33913     {
33914         e.preventDefault();
33915         
33916         this.indicator = Math.max(1, this.indicator - 1);
33917         
33918         this.update();
33919     },
33920     
33921     next : function(e)
33922     {
33923         e.preventDefault();
33924         
33925         this.indicator = Math.min(this.files.length, this.indicator + 1);
33926         
33927         this.update();
33928     }
33929 });
33930 /*
33931  * - LGPL
33932  *
33933  * RadioSet
33934  *
33935  *
33936  */
33937
33938 /**
33939  * @class Roo.bootstrap.RadioSet
33940  * @extends Roo.bootstrap.Input
33941  * Bootstrap RadioSet class
33942  * @cfg {String} indicatorpos (left|right) default left
33943  * @cfg {Boolean} inline (true|false) inline the element (default true)
33944  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33945  * @constructor
33946  * Create a new RadioSet
33947  * @param {Object} config The config object
33948  */
33949
33950 Roo.bootstrap.RadioSet = function(config){
33951     
33952     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33953     
33954     this.radioes = [];
33955     
33956     Roo.bootstrap.RadioSet.register(this);
33957     
33958     this.addEvents({
33959         /**
33960         * @event check
33961         * Fires when the element is checked or unchecked.
33962         * @param {Roo.bootstrap.RadioSet} this This radio
33963         * @param {Roo.bootstrap.Radio} item The checked item
33964         */
33965        check : true,
33966        /**
33967         * @event click
33968         * Fires when the element is click.
33969         * @param {Roo.bootstrap.RadioSet} this This radio set
33970         * @param {Roo.bootstrap.Radio} item The checked item
33971         * @param {Roo.EventObject} e The event object
33972         */
33973        click : true
33974     });
33975     
33976 };
33977
33978 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33979
33980     radioes : false,
33981     
33982     inline : true,
33983     
33984     weight : '',
33985     
33986     indicatorpos : 'left',
33987     
33988     getAutoCreate : function()
33989     {
33990         var label = {
33991             tag : 'label',
33992             cls : 'roo-radio-set-label',
33993             cn : [
33994                 {
33995                     tag : 'span',
33996                     html : this.fieldLabel
33997                 }
33998             ]
33999         };
34000         
34001         if(this.indicatorpos == 'left'){
34002             label.cn.unshift({
34003                 tag : 'i',
34004                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34005                 tooltip : 'This field is required'
34006             });
34007         } else {
34008             label.cn.push({
34009                 tag : 'i',
34010                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34011                 tooltip : 'This field is required'
34012             });
34013         }
34014         
34015         var items = {
34016             tag : 'div',
34017             cls : 'roo-radio-set-items'
34018         };
34019         
34020         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34021         
34022         if (align === 'left' && this.fieldLabel.length) {
34023             
34024             items = {
34025                 cls : "roo-radio-set-right", 
34026                 cn: [
34027                     items
34028                 ]
34029             };
34030             
34031             if(this.labelWidth > 12){
34032                 label.style = "width: " + this.labelWidth + 'px';
34033             }
34034             
34035             if(this.labelWidth < 13 && this.labelmd == 0){
34036                 this.labelmd = this.labelWidth;
34037             }
34038             
34039             if(this.labellg > 0){
34040                 label.cls += ' col-lg-' + this.labellg;
34041                 items.cls += ' col-lg-' + (12 - this.labellg);
34042             }
34043             
34044             if(this.labelmd > 0){
34045                 label.cls += ' col-md-' + this.labelmd;
34046                 items.cls += ' col-md-' + (12 - this.labelmd);
34047             }
34048             
34049             if(this.labelsm > 0){
34050                 label.cls += ' col-sm-' + this.labelsm;
34051                 items.cls += ' col-sm-' + (12 - this.labelsm);
34052             }
34053             
34054             if(this.labelxs > 0){
34055                 label.cls += ' col-xs-' + this.labelxs;
34056                 items.cls += ' col-xs-' + (12 - this.labelxs);
34057             }
34058         }
34059         
34060         var cfg = {
34061             tag : 'div',
34062             cls : 'roo-radio-set',
34063             cn : [
34064                 {
34065                     tag : 'input',
34066                     cls : 'roo-radio-set-input',
34067                     type : 'hidden',
34068                     name : this.name,
34069                     value : this.value ? this.value :  ''
34070                 },
34071                 label,
34072                 items
34073             ]
34074         };
34075         
34076         if(this.weight.length){
34077             cfg.cls += ' roo-radio-' + this.weight;
34078         }
34079         
34080         if(this.inline) {
34081             cfg.cls += ' roo-radio-set-inline';
34082         }
34083         
34084         var settings=this;
34085         ['xs','sm','md','lg'].map(function(size){
34086             if (settings[size]) {
34087                 cfg.cls += ' col-' + size + '-' + settings[size];
34088             }
34089         });
34090         
34091         return cfg;
34092         
34093     },
34094
34095     initEvents : function()
34096     {
34097         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34098         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34099         
34100         if(!this.fieldLabel.length){
34101             this.labelEl.hide();
34102         }
34103         
34104         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34105         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34106         
34107         this.indicator = this.indicatorEl();
34108         
34109         if(this.indicator){
34110             this.indicator.addClass('invisible');
34111         }
34112         
34113         this.originalValue = this.getValue();
34114         
34115     },
34116     
34117     inputEl: function ()
34118     {
34119         return this.el.select('.roo-radio-set-input', true).first();
34120     },
34121     
34122     getChildContainer : function()
34123     {
34124         return this.itemsEl;
34125     },
34126     
34127     register : function(item)
34128     {
34129         this.radioes.push(item);
34130         
34131     },
34132     
34133     validate : function()
34134     {   
34135         if(this.getVisibilityEl().hasClass('hidden')){
34136             return true;
34137         }
34138         
34139         var valid = false;
34140         
34141         Roo.each(this.radioes, function(i){
34142             if(!i.checked){
34143                 return;
34144             }
34145             
34146             valid = true;
34147             return false;
34148         });
34149         
34150         if(this.allowBlank) {
34151             return true;
34152         }
34153         
34154         if(this.disabled || valid){
34155             this.markValid();
34156             return true;
34157         }
34158         
34159         this.markInvalid();
34160         return false;
34161         
34162     },
34163     
34164     markValid : function()
34165     {
34166         if(this.labelEl.isVisible(true)){
34167             this.indicatorEl().removeClass('visible');
34168             this.indicatorEl().addClass('invisible');
34169         }
34170         
34171         this.el.removeClass([this.invalidClass, this.validClass]);
34172         this.el.addClass(this.validClass);
34173         
34174         this.fireEvent('valid', this);
34175     },
34176     
34177     markInvalid : function(msg)
34178     {
34179         if(this.allowBlank || this.disabled){
34180             return;
34181         }
34182         
34183         if(this.labelEl.isVisible(true)){
34184             this.indicatorEl().removeClass('invisible');
34185             this.indicatorEl().addClass('visible');
34186         }
34187         
34188         this.el.removeClass([this.invalidClass, this.validClass]);
34189         this.el.addClass(this.invalidClass);
34190         
34191         this.fireEvent('invalid', this, msg);
34192         
34193     },
34194     
34195     setValue : function(v, suppressEvent)
34196     {   
34197         if(this.value === v){
34198             return;
34199         }
34200         
34201         this.value = v;
34202         
34203         if(this.rendered){
34204             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34205         }
34206         
34207         Roo.each(this.radioes, function(i){
34208             i.checked = false;
34209             i.el.removeClass('checked');
34210         });
34211         
34212         Roo.each(this.radioes, function(i){
34213             
34214             if(i.value === v || i.value.toString() === v.toString()){
34215                 i.checked = true;
34216                 i.el.addClass('checked');
34217                 
34218                 if(suppressEvent !== true){
34219                     this.fireEvent('check', this, i);
34220                 }
34221                 
34222                 return false;
34223             }
34224             
34225         }, this);
34226         
34227         this.validate();
34228     },
34229     
34230     clearInvalid : function(){
34231         
34232         if(!this.el || this.preventMark){
34233             return;
34234         }
34235         
34236         this.el.removeClass([this.invalidClass]);
34237         
34238         this.fireEvent('valid', this);
34239     }
34240     
34241 });
34242
34243 Roo.apply(Roo.bootstrap.RadioSet, {
34244     
34245     groups: {},
34246     
34247     register : function(set)
34248     {
34249         this.groups[set.name] = set;
34250     },
34251     
34252     get: function(name) 
34253     {
34254         if (typeof(this.groups[name]) == 'undefined') {
34255             return false;
34256         }
34257         
34258         return this.groups[name] ;
34259     }
34260     
34261 });
34262 /*
34263  * Based on:
34264  * Ext JS Library 1.1.1
34265  * Copyright(c) 2006-2007, Ext JS, LLC.
34266  *
34267  * Originally Released Under LGPL - original licence link has changed is not relivant.
34268  *
34269  * Fork - LGPL
34270  * <script type="text/javascript">
34271  */
34272
34273
34274 /**
34275  * @class Roo.bootstrap.SplitBar
34276  * @extends Roo.util.Observable
34277  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34278  * <br><br>
34279  * Usage:
34280  * <pre><code>
34281 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34282                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34283 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34284 split.minSize = 100;
34285 split.maxSize = 600;
34286 split.animate = true;
34287 split.on('moved', splitterMoved);
34288 </code></pre>
34289  * @constructor
34290  * Create a new SplitBar
34291  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34292  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34293  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34294  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34295                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34296                         position of the SplitBar).
34297  */
34298 Roo.bootstrap.SplitBar = function(cfg){
34299     
34300     /** @private */
34301     
34302     //{
34303     //  dragElement : elm
34304     //  resizingElement: el,
34305         // optional..
34306     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34307     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34308         // existingProxy ???
34309     //}
34310     
34311     this.el = Roo.get(cfg.dragElement, true);
34312     this.el.dom.unselectable = "on";
34313     /** @private */
34314     this.resizingEl = Roo.get(cfg.resizingElement, true);
34315
34316     /**
34317      * @private
34318      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34319      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34320      * @type Number
34321      */
34322     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34323     
34324     /**
34325      * The minimum size of the resizing element. (Defaults to 0)
34326      * @type Number
34327      */
34328     this.minSize = 0;
34329     
34330     /**
34331      * The maximum size of the resizing element. (Defaults to 2000)
34332      * @type Number
34333      */
34334     this.maxSize = 2000;
34335     
34336     /**
34337      * Whether to animate the transition to the new size
34338      * @type Boolean
34339      */
34340     this.animate = false;
34341     
34342     /**
34343      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34344      * @type Boolean
34345      */
34346     this.useShim = false;
34347     
34348     /** @private */
34349     this.shim = null;
34350     
34351     if(!cfg.existingProxy){
34352         /** @private */
34353         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34354     }else{
34355         this.proxy = Roo.get(cfg.existingProxy).dom;
34356     }
34357     /** @private */
34358     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34359     
34360     /** @private */
34361     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34362     
34363     /** @private */
34364     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34365     
34366     /** @private */
34367     this.dragSpecs = {};
34368     
34369     /**
34370      * @private The adapter to use to positon and resize elements
34371      */
34372     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34373     this.adapter.init(this);
34374     
34375     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34376         /** @private */
34377         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34378         this.el.addClass("roo-splitbar-h");
34379     }else{
34380         /** @private */
34381         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34382         this.el.addClass("roo-splitbar-v");
34383     }
34384     
34385     this.addEvents({
34386         /**
34387          * @event resize
34388          * Fires when the splitter is moved (alias for {@link #event-moved})
34389          * @param {Roo.bootstrap.SplitBar} this
34390          * @param {Number} newSize the new width or height
34391          */
34392         "resize" : true,
34393         /**
34394          * @event moved
34395          * Fires when the splitter is moved
34396          * @param {Roo.bootstrap.SplitBar} this
34397          * @param {Number} newSize the new width or height
34398          */
34399         "moved" : true,
34400         /**
34401          * @event beforeresize
34402          * Fires before the splitter is dragged
34403          * @param {Roo.bootstrap.SplitBar} this
34404          */
34405         "beforeresize" : true,
34406
34407         "beforeapply" : true
34408     });
34409
34410     Roo.util.Observable.call(this);
34411 };
34412
34413 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34414     onStartProxyDrag : function(x, y){
34415         this.fireEvent("beforeresize", this);
34416         if(!this.overlay){
34417             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34418             o.unselectable();
34419             o.enableDisplayMode("block");
34420             // all splitbars share the same overlay
34421             Roo.bootstrap.SplitBar.prototype.overlay = o;
34422         }
34423         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34424         this.overlay.show();
34425         Roo.get(this.proxy).setDisplayed("block");
34426         var size = this.adapter.getElementSize(this);
34427         this.activeMinSize = this.getMinimumSize();;
34428         this.activeMaxSize = this.getMaximumSize();;
34429         var c1 = size - this.activeMinSize;
34430         var c2 = Math.max(this.activeMaxSize - size, 0);
34431         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34432             this.dd.resetConstraints();
34433             this.dd.setXConstraint(
34434                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34435                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34436             );
34437             this.dd.setYConstraint(0, 0);
34438         }else{
34439             this.dd.resetConstraints();
34440             this.dd.setXConstraint(0, 0);
34441             this.dd.setYConstraint(
34442                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34443                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34444             );
34445          }
34446         this.dragSpecs.startSize = size;
34447         this.dragSpecs.startPoint = [x, y];
34448         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34449     },
34450     
34451     /** 
34452      * @private Called after the drag operation by the DDProxy
34453      */
34454     onEndProxyDrag : function(e){
34455         Roo.get(this.proxy).setDisplayed(false);
34456         var endPoint = Roo.lib.Event.getXY(e);
34457         if(this.overlay){
34458             this.overlay.hide();
34459         }
34460         var newSize;
34461         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34462             newSize = this.dragSpecs.startSize + 
34463                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34464                     endPoint[0] - this.dragSpecs.startPoint[0] :
34465                     this.dragSpecs.startPoint[0] - endPoint[0]
34466                 );
34467         }else{
34468             newSize = this.dragSpecs.startSize + 
34469                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34470                     endPoint[1] - this.dragSpecs.startPoint[1] :
34471                     this.dragSpecs.startPoint[1] - endPoint[1]
34472                 );
34473         }
34474         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34475         if(newSize != this.dragSpecs.startSize){
34476             if(this.fireEvent('beforeapply', this, newSize) !== false){
34477                 this.adapter.setElementSize(this, newSize);
34478                 this.fireEvent("moved", this, newSize);
34479                 this.fireEvent("resize", this, newSize);
34480             }
34481         }
34482     },
34483     
34484     /**
34485      * Get the adapter this SplitBar uses
34486      * @return The adapter object
34487      */
34488     getAdapter : function(){
34489         return this.adapter;
34490     },
34491     
34492     /**
34493      * Set the adapter this SplitBar uses
34494      * @param {Object} adapter A SplitBar adapter object
34495      */
34496     setAdapter : function(adapter){
34497         this.adapter = adapter;
34498         this.adapter.init(this);
34499     },
34500     
34501     /**
34502      * Gets the minimum size for the resizing element
34503      * @return {Number} The minimum size
34504      */
34505     getMinimumSize : function(){
34506         return this.minSize;
34507     },
34508     
34509     /**
34510      * Sets the minimum size for the resizing element
34511      * @param {Number} minSize The minimum size
34512      */
34513     setMinimumSize : function(minSize){
34514         this.minSize = minSize;
34515     },
34516     
34517     /**
34518      * Gets the maximum size for the resizing element
34519      * @return {Number} The maximum size
34520      */
34521     getMaximumSize : function(){
34522         return this.maxSize;
34523     },
34524     
34525     /**
34526      * Sets the maximum size for the resizing element
34527      * @param {Number} maxSize The maximum size
34528      */
34529     setMaximumSize : function(maxSize){
34530         this.maxSize = maxSize;
34531     },
34532     
34533     /**
34534      * Sets the initialize size for the resizing element
34535      * @param {Number} size The initial size
34536      */
34537     setCurrentSize : function(size){
34538         var oldAnimate = this.animate;
34539         this.animate = false;
34540         this.adapter.setElementSize(this, size);
34541         this.animate = oldAnimate;
34542     },
34543     
34544     /**
34545      * Destroy this splitbar. 
34546      * @param {Boolean} removeEl True to remove the element
34547      */
34548     destroy : function(removeEl){
34549         if(this.shim){
34550             this.shim.remove();
34551         }
34552         this.dd.unreg();
34553         this.proxy.parentNode.removeChild(this.proxy);
34554         if(removeEl){
34555             this.el.remove();
34556         }
34557     }
34558 });
34559
34560 /**
34561  * @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.
34562  */
34563 Roo.bootstrap.SplitBar.createProxy = function(dir){
34564     var proxy = new Roo.Element(document.createElement("div"));
34565     proxy.unselectable();
34566     var cls = 'roo-splitbar-proxy';
34567     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34568     document.body.appendChild(proxy.dom);
34569     return proxy.dom;
34570 };
34571
34572 /** 
34573  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34574  * Default Adapter. It assumes the splitter and resizing element are not positioned
34575  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34576  */
34577 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34578 };
34579
34580 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34581     // do nothing for now
34582     init : function(s){
34583     
34584     },
34585     /**
34586      * Called before drag operations to get the current size of the resizing element. 
34587      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34588      */
34589      getElementSize : function(s){
34590         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34591             return s.resizingEl.getWidth();
34592         }else{
34593             return s.resizingEl.getHeight();
34594         }
34595     },
34596     
34597     /**
34598      * Called after drag operations to set the size of the resizing element.
34599      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34600      * @param {Number} newSize The new size to set
34601      * @param {Function} onComplete A function to be invoked when resizing is complete
34602      */
34603     setElementSize : function(s, newSize, onComplete){
34604         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34605             if(!s.animate){
34606                 s.resizingEl.setWidth(newSize);
34607                 if(onComplete){
34608                     onComplete(s, newSize);
34609                 }
34610             }else{
34611                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34612             }
34613         }else{
34614             
34615             if(!s.animate){
34616                 s.resizingEl.setHeight(newSize);
34617                 if(onComplete){
34618                     onComplete(s, newSize);
34619                 }
34620             }else{
34621                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34622             }
34623         }
34624     }
34625 };
34626
34627 /** 
34628  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34629  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34630  * Adapter that  moves the splitter element to align with the resized sizing element. 
34631  * Used with an absolute positioned SplitBar.
34632  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34633  * document.body, make sure you assign an id to the body element.
34634  */
34635 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34636     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34637     this.container = Roo.get(container);
34638 };
34639
34640 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34641     init : function(s){
34642         this.basic.init(s);
34643     },
34644     
34645     getElementSize : function(s){
34646         return this.basic.getElementSize(s);
34647     },
34648     
34649     setElementSize : function(s, newSize, onComplete){
34650         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34651     },
34652     
34653     moveSplitter : function(s){
34654         var yes = Roo.bootstrap.SplitBar;
34655         switch(s.placement){
34656             case yes.LEFT:
34657                 s.el.setX(s.resizingEl.getRight());
34658                 break;
34659             case yes.RIGHT:
34660                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34661                 break;
34662             case yes.TOP:
34663                 s.el.setY(s.resizingEl.getBottom());
34664                 break;
34665             case yes.BOTTOM:
34666                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34667                 break;
34668         }
34669     }
34670 };
34671
34672 /**
34673  * Orientation constant - Create a vertical SplitBar
34674  * @static
34675  * @type Number
34676  */
34677 Roo.bootstrap.SplitBar.VERTICAL = 1;
34678
34679 /**
34680  * Orientation constant - Create a horizontal SplitBar
34681  * @static
34682  * @type Number
34683  */
34684 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34685
34686 /**
34687  * Placement constant - The resizing element is to the left of the splitter element
34688  * @static
34689  * @type Number
34690  */
34691 Roo.bootstrap.SplitBar.LEFT = 1;
34692
34693 /**
34694  * Placement constant - The resizing element is to the right of the splitter element
34695  * @static
34696  * @type Number
34697  */
34698 Roo.bootstrap.SplitBar.RIGHT = 2;
34699
34700 /**
34701  * Placement constant - The resizing element is positioned above the splitter element
34702  * @static
34703  * @type Number
34704  */
34705 Roo.bootstrap.SplitBar.TOP = 3;
34706
34707 /**
34708  * Placement constant - The resizing element is positioned under splitter element
34709  * @static
34710  * @type Number
34711  */
34712 Roo.bootstrap.SplitBar.BOTTOM = 4;
34713 Roo.namespace("Roo.bootstrap.layout");/*
34714  * Based on:
34715  * Ext JS Library 1.1.1
34716  * Copyright(c) 2006-2007, Ext JS, LLC.
34717  *
34718  * Originally Released Under LGPL - original licence link has changed is not relivant.
34719  *
34720  * Fork - LGPL
34721  * <script type="text/javascript">
34722  */
34723
34724 /**
34725  * @class Roo.bootstrap.layout.Manager
34726  * @extends Roo.bootstrap.Component
34727  * Base class for layout managers.
34728  */
34729 Roo.bootstrap.layout.Manager = function(config)
34730 {
34731     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34732
34733
34734
34735
34736
34737     /** false to disable window resize monitoring @type Boolean */
34738     this.monitorWindowResize = true;
34739     this.regions = {};
34740     this.addEvents({
34741         /**
34742          * @event layout
34743          * Fires when a layout is performed.
34744          * @param {Roo.LayoutManager} this
34745          */
34746         "layout" : true,
34747         /**
34748          * @event regionresized
34749          * Fires when the user resizes a region.
34750          * @param {Roo.LayoutRegion} region The resized region
34751          * @param {Number} newSize The new size (width for east/west, height for north/south)
34752          */
34753         "regionresized" : true,
34754         /**
34755          * @event regioncollapsed
34756          * Fires when a region is collapsed.
34757          * @param {Roo.LayoutRegion} region The collapsed region
34758          */
34759         "regioncollapsed" : true,
34760         /**
34761          * @event regionexpanded
34762          * Fires when a region is expanded.
34763          * @param {Roo.LayoutRegion} region The expanded region
34764          */
34765         "regionexpanded" : true
34766     });
34767     this.updating = false;
34768
34769     if (config.el) {
34770         this.el = Roo.get(config.el);
34771         this.initEvents();
34772     }
34773
34774 };
34775
34776 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34777
34778
34779     regions : null,
34780
34781     monitorWindowResize : true,
34782
34783
34784     updating : false,
34785
34786
34787     onRender : function(ct, position)
34788     {
34789         if(!this.el){
34790             this.el = Roo.get(ct);
34791             this.initEvents();
34792         }
34793         //this.fireEvent('render',this);
34794     },
34795
34796
34797     initEvents: function()
34798     {
34799
34800
34801         // ie scrollbar fix
34802         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34803             document.body.scroll = "no";
34804         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34805             this.el.position('relative');
34806         }
34807         this.id = this.el.id;
34808         this.el.addClass("roo-layout-container");
34809         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34810         if(this.el.dom != document.body ) {
34811             this.el.on('resize', this.layout,this);
34812             this.el.on('show', this.layout,this);
34813         }
34814
34815     },
34816
34817     /**
34818      * Returns true if this layout is currently being updated
34819      * @return {Boolean}
34820      */
34821     isUpdating : function(){
34822         return this.updating;
34823     },
34824
34825     /**
34826      * Suspend the LayoutManager from doing auto-layouts while
34827      * making multiple add or remove calls
34828      */
34829     beginUpdate : function(){
34830         this.updating = true;
34831     },
34832
34833     /**
34834      * Restore auto-layouts and optionally disable the manager from performing a layout
34835      * @param {Boolean} noLayout true to disable a layout update
34836      */
34837     endUpdate : function(noLayout){
34838         this.updating = false;
34839         if(!noLayout){
34840             this.layout();
34841         }
34842     },
34843
34844     layout: function(){
34845         // abstract...
34846     },
34847
34848     onRegionResized : function(region, newSize){
34849         this.fireEvent("regionresized", region, newSize);
34850         this.layout();
34851     },
34852
34853     onRegionCollapsed : function(region){
34854         this.fireEvent("regioncollapsed", region);
34855     },
34856
34857     onRegionExpanded : function(region){
34858         this.fireEvent("regionexpanded", region);
34859     },
34860
34861     /**
34862      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34863      * performs box-model adjustments.
34864      * @return {Object} The size as an object {width: (the width), height: (the height)}
34865      */
34866     getViewSize : function()
34867     {
34868         var size;
34869         if(this.el.dom != document.body){
34870             size = this.el.getSize();
34871         }else{
34872             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34873         }
34874         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34875         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34876         return size;
34877     },
34878
34879     /**
34880      * Returns the Element this layout is bound to.
34881      * @return {Roo.Element}
34882      */
34883     getEl : function(){
34884         return this.el;
34885     },
34886
34887     /**
34888      * Returns the specified region.
34889      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34890      * @return {Roo.LayoutRegion}
34891      */
34892     getRegion : function(target){
34893         return this.regions[target.toLowerCase()];
34894     },
34895
34896     onWindowResize : function(){
34897         if(this.monitorWindowResize){
34898             this.layout();
34899         }
34900     }
34901 });
34902 /*
34903  * Based on:
34904  * Ext JS Library 1.1.1
34905  * Copyright(c) 2006-2007, Ext JS, LLC.
34906  *
34907  * Originally Released Under LGPL - original licence link has changed is not relivant.
34908  *
34909  * Fork - LGPL
34910  * <script type="text/javascript">
34911  */
34912 /**
34913  * @class Roo.bootstrap.layout.Border
34914  * @extends Roo.bootstrap.layout.Manager
34915  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34916  * please see: examples/bootstrap/nested.html<br><br>
34917  
34918 <b>The container the layout is rendered into can be either the body element or any other element.
34919 If it is not the body element, the container needs to either be an absolute positioned element,
34920 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34921 the container size if it is not the body element.</b>
34922
34923 * @constructor
34924 * Create a new Border
34925 * @param {Object} config Configuration options
34926  */
34927 Roo.bootstrap.layout.Border = function(config){
34928     config = config || {};
34929     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34930     
34931     
34932     
34933     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34934         if(config[region]){
34935             config[region].region = region;
34936             this.addRegion(config[region]);
34937         }
34938     },this);
34939     
34940 };
34941
34942 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34943
34944 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34945     /**
34946      * Creates and adds a new region if it doesn't already exist.
34947      * @param {String} target The target region key (north, south, east, west or center).
34948      * @param {Object} config The regions config object
34949      * @return {BorderLayoutRegion} The new region
34950      */
34951     addRegion : function(config)
34952     {
34953         if(!this.regions[config.region]){
34954             var r = this.factory(config);
34955             this.bindRegion(r);
34956         }
34957         return this.regions[config.region];
34958     },
34959
34960     // private (kinda)
34961     bindRegion : function(r){
34962         this.regions[r.config.region] = r;
34963         
34964         r.on("visibilitychange",    this.layout, this);
34965         r.on("paneladded",          this.layout, this);
34966         r.on("panelremoved",        this.layout, this);
34967         r.on("invalidated",         this.layout, this);
34968         r.on("resized",             this.onRegionResized, this);
34969         r.on("collapsed",           this.onRegionCollapsed, this);
34970         r.on("expanded",            this.onRegionExpanded, this);
34971     },
34972
34973     /**
34974      * Performs a layout update.
34975      */
34976     layout : function()
34977     {
34978         if(this.updating) {
34979             return;
34980         }
34981         
34982         // render all the rebions if they have not been done alreayd?
34983         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34984             if(this.regions[region] && !this.regions[region].bodyEl){
34985                 this.regions[region].onRender(this.el)
34986             }
34987         },this);
34988         
34989         var size = this.getViewSize();
34990         var w = size.width;
34991         var h = size.height;
34992         var centerW = w;
34993         var centerH = h;
34994         var centerY = 0;
34995         var centerX = 0;
34996         //var x = 0, y = 0;
34997
34998         var rs = this.regions;
34999         var north = rs["north"];
35000         var south = rs["south"]; 
35001         var west = rs["west"];
35002         var east = rs["east"];
35003         var center = rs["center"];
35004         //if(this.hideOnLayout){ // not supported anymore
35005             //c.el.setStyle("display", "none");
35006         //}
35007         if(north && north.isVisible()){
35008             var b = north.getBox();
35009             var m = north.getMargins();
35010             b.width = w - (m.left+m.right);
35011             b.x = m.left;
35012             b.y = m.top;
35013             centerY = b.height + b.y + m.bottom;
35014             centerH -= centerY;
35015             north.updateBox(this.safeBox(b));
35016         }
35017         if(south && south.isVisible()){
35018             var b = south.getBox();
35019             var m = south.getMargins();
35020             b.width = w - (m.left+m.right);
35021             b.x = m.left;
35022             var totalHeight = (b.height + m.top + m.bottom);
35023             b.y = h - totalHeight + m.top;
35024             centerH -= totalHeight;
35025             south.updateBox(this.safeBox(b));
35026         }
35027         if(west && west.isVisible()){
35028             var b = west.getBox();
35029             var m = west.getMargins();
35030             b.height = centerH - (m.top+m.bottom);
35031             b.x = m.left;
35032             b.y = centerY + m.top;
35033             var totalWidth = (b.width + m.left + m.right);
35034             centerX += totalWidth;
35035             centerW -= totalWidth;
35036             west.updateBox(this.safeBox(b));
35037         }
35038         if(east && east.isVisible()){
35039             var b = east.getBox();
35040             var m = east.getMargins();
35041             b.height = centerH - (m.top+m.bottom);
35042             var totalWidth = (b.width + m.left + m.right);
35043             b.x = w - totalWidth + m.left;
35044             b.y = centerY + m.top;
35045             centerW -= totalWidth;
35046             east.updateBox(this.safeBox(b));
35047         }
35048         if(center){
35049             var m = center.getMargins();
35050             var centerBox = {
35051                 x: centerX + m.left,
35052                 y: centerY + m.top,
35053                 width: centerW - (m.left+m.right),
35054                 height: centerH - (m.top+m.bottom)
35055             };
35056             //if(this.hideOnLayout){
35057                 //center.el.setStyle("display", "block");
35058             //}
35059             center.updateBox(this.safeBox(centerBox));
35060         }
35061         this.el.repaint();
35062         this.fireEvent("layout", this);
35063     },
35064
35065     // private
35066     safeBox : function(box){
35067         box.width = Math.max(0, box.width);
35068         box.height = Math.max(0, box.height);
35069         return box;
35070     },
35071
35072     /**
35073      * Adds a ContentPanel (or subclass) to this layout.
35074      * @param {String} target The target region key (north, south, east, west or center).
35075      * @param {Roo.ContentPanel} panel The panel to add
35076      * @return {Roo.ContentPanel} The added panel
35077      */
35078     add : function(target, panel){
35079          
35080         target = target.toLowerCase();
35081         return this.regions[target].add(panel);
35082     },
35083
35084     /**
35085      * Remove a ContentPanel (or subclass) to this layout.
35086      * @param {String} target The target region key (north, south, east, west or center).
35087      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35088      * @return {Roo.ContentPanel} The removed panel
35089      */
35090     remove : function(target, panel){
35091         target = target.toLowerCase();
35092         return this.regions[target].remove(panel);
35093     },
35094
35095     /**
35096      * Searches all regions for a panel with the specified id
35097      * @param {String} panelId
35098      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35099      */
35100     findPanel : function(panelId){
35101         var rs = this.regions;
35102         for(var target in rs){
35103             if(typeof rs[target] != "function"){
35104                 var p = rs[target].getPanel(panelId);
35105                 if(p){
35106                     return p;
35107                 }
35108             }
35109         }
35110         return null;
35111     },
35112
35113     /**
35114      * Searches all regions for a panel with the specified id and activates (shows) it.
35115      * @param {String/ContentPanel} panelId The panels id or the panel itself
35116      * @return {Roo.ContentPanel} The shown panel or null
35117      */
35118     showPanel : function(panelId) {
35119       var rs = this.regions;
35120       for(var target in rs){
35121          var r = rs[target];
35122          if(typeof r != "function"){
35123             if(r.hasPanel(panelId)){
35124                return r.showPanel(panelId);
35125             }
35126          }
35127       }
35128       return null;
35129    },
35130
35131    /**
35132      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35133      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35134      */
35135    /*
35136     restoreState : function(provider){
35137         if(!provider){
35138             provider = Roo.state.Manager;
35139         }
35140         var sm = new Roo.LayoutStateManager();
35141         sm.init(this, provider);
35142     },
35143 */
35144  
35145  
35146     /**
35147      * Adds a xtype elements to the layout.
35148      * <pre><code>
35149
35150 layout.addxtype({
35151        xtype : 'ContentPanel',
35152        region: 'west',
35153        items: [ .... ]
35154    }
35155 );
35156
35157 layout.addxtype({
35158         xtype : 'NestedLayoutPanel',
35159         region: 'west',
35160         layout: {
35161            center: { },
35162            west: { }   
35163         },
35164         items : [ ... list of content panels or nested layout panels.. ]
35165    }
35166 );
35167 </code></pre>
35168      * @param {Object} cfg Xtype definition of item to add.
35169      */
35170     addxtype : function(cfg)
35171     {
35172         // basically accepts a pannel...
35173         // can accept a layout region..!?!?
35174         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35175         
35176         
35177         // theory?  children can only be panels??
35178         
35179         //if (!cfg.xtype.match(/Panel$/)) {
35180         //    return false;
35181         //}
35182         var ret = false;
35183         
35184         if (typeof(cfg.region) == 'undefined') {
35185             Roo.log("Failed to add Panel, region was not set");
35186             Roo.log(cfg);
35187             return false;
35188         }
35189         var region = cfg.region;
35190         delete cfg.region;
35191         
35192           
35193         var xitems = [];
35194         if (cfg.items) {
35195             xitems = cfg.items;
35196             delete cfg.items;
35197         }
35198         var nb = false;
35199         
35200         switch(cfg.xtype) 
35201         {
35202             case 'Content':  // ContentPanel (el, cfg)
35203             case 'Scroll':  // ContentPanel (el, cfg)
35204             case 'View': 
35205                 cfg.autoCreate = true;
35206                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35207                 //} else {
35208                 //    var el = this.el.createChild();
35209                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35210                 //}
35211                 
35212                 this.add(region, ret);
35213                 break;
35214             
35215             /*
35216             case 'TreePanel': // our new panel!
35217                 cfg.el = this.el.createChild();
35218                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35219                 this.add(region, ret);
35220                 break;
35221             */
35222             
35223             case 'Nest': 
35224                 // create a new Layout (which is  a Border Layout...
35225                 
35226                 var clayout = cfg.layout;
35227                 clayout.el  = this.el.createChild();
35228                 clayout.items   = clayout.items  || [];
35229                 
35230                 delete cfg.layout;
35231                 
35232                 // replace this exitems with the clayout ones..
35233                 xitems = clayout.items;
35234                  
35235                 // force background off if it's in center...
35236                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35237                     cfg.background = false;
35238                 }
35239                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35240                 
35241                 
35242                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35243                 //console.log('adding nested layout panel '  + cfg.toSource());
35244                 this.add(region, ret);
35245                 nb = {}; /// find first...
35246                 break;
35247             
35248             case 'Grid':
35249                 
35250                 // needs grid and region
35251                 
35252                 //var el = this.getRegion(region).el.createChild();
35253                 /*
35254                  *var el = this.el.createChild();
35255                 // create the grid first...
35256                 cfg.grid.container = el;
35257                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35258                 */
35259                 
35260                 if (region == 'center' && this.active ) {
35261                     cfg.background = false;
35262                 }
35263                 
35264                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35265                 
35266                 this.add(region, ret);
35267                 /*
35268                 if (cfg.background) {
35269                     // render grid on panel activation (if panel background)
35270                     ret.on('activate', function(gp) {
35271                         if (!gp.grid.rendered) {
35272                     //        gp.grid.render(el);
35273                         }
35274                     });
35275                 } else {
35276                   //  cfg.grid.render(el);
35277                 }
35278                 */
35279                 break;
35280            
35281            
35282             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35283                 // it was the old xcomponent building that caused this before.
35284                 // espeically if border is the top element in the tree.
35285                 ret = this;
35286                 break; 
35287                 
35288                     
35289                 
35290                 
35291                 
35292             default:
35293                 /*
35294                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35295                     
35296                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35297                     this.add(region, ret);
35298                 } else {
35299                 */
35300                     Roo.log(cfg);
35301                     throw "Can not add '" + cfg.xtype + "' to Border";
35302                     return null;
35303              
35304                                 
35305              
35306         }
35307         this.beginUpdate();
35308         // add children..
35309         var region = '';
35310         var abn = {};
35311         Roo.each(xitems, function(i)  {
35312             region = nb && i.region ? i.region : false;
35313             
35314             var add = ret.addxtype(i);
35315            
35316             if (region) {
35317                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35318                 if (!i.background) {
35319                     abn[region] = nb[region] ;
35320                 }
35321             }
35322             
35323         });
35324         this.endUpdate();
35325
35326         // make the last non-background panel active..
35327         //if (nb) { Roo.log(abn); }
35328         if (nb) {
35329             
35330             for(var r in abn) {
35331                 region = this.getRegion(r);
35332                 if (region) {
35333                     // tried using nb[r], but it does not work..
35334                      
35335                     region.showPanel(abn[r]);
35336                    
35337                 }
35338             }
35339         }
35340         return ret;
35341         
35342     },
35343     
35344     
35345 // private
35346     factory : function(cfg)
35347     {
35348         
35349         var validRegions = Roo.bootstrap.layout.Border.regions;
35350
35351         var target = cfg.region;
35352         cfg.mgr = this;
35353         
35354         var r = Roo.bootstrap.layout;
35355         Roo.log(target);
35356         switch(target){
35357             case "north":
35358                 return new r.North(cfg);
35359             case "south":
35360                 return new r.South(cfg);
35361             case "east":
35362                 return new r.East(cfg);
35363             case "west":
35364                 return new r.West(cfg);
35365             case "center":
35366                 return new r.Center(cfg);
35367         }
35368         throw 'Layout region "'+target+'" not supported.';
35369     }
35370     
35371     
35372 });
35373  /*
35374  * Based on:
35375  * Ext JS Library 1.1.1
35376  * Copyright(c) 2006-2007, Ext JS, LLC.
35377  *
35378  * Originally Released Under LGPL - original licence link has changed is not relivant.
35379  *
35380  * Fork - LGPL
35381  * <script type="text/javascript">
35382  */
35383  
35384 /**
35385  * @class Roo.bootstrap.layout.Basic
35386  * @extends Roo.util.Observable
35387  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35388  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35389  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35390  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35391  * @cfg {string}   region  the region that it inhabits..
35392  * @cfg {bool}   skipConfig skip config?
35393  * 
35394
35395  */
35396 Roo.bootstrap.layout.Basic = function(config){
35397     
35398     this.mgr = config.mgr;
35399     
35400     this.position = config.region;
35401     
35402     var skipConfig = config.skipConfig;
35403     
35404     this.events = {
35405         /**
35406          * @scope Roo.BasicLayoutRegion
35407          */
35408         
35409         /**
35410          * @event beforeremove
35411          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35412          * @param {Roo.LayoutRegion} this
35413          * @param {Roo.ContentPanel} panel The panel
35414          * @param {Object} e The cancel event object
35415          */
35416         "beforeremove" : true,
35417         /**
35418          * @event invalidated
35419          * Fires when the layout for this region is changed.
35420          * @param {Roo.LayoutRegion} this
35421          */
35422         "invalidated" : true,
35423         /**
35424          * @event visibilitychange
35425          * Fires when this region is shown or hidden 
35426          * @param {Roo.LayoutRegion} this
35427          * @param {Boolean} visibility true or false
35428          */
35429         "visibilitychange" : true,
35430         /**
35431          * @event paneladded
35432          * Fires when a panel is added. 
35433          * @param {Roo.LayoutRegion} this
35434          * @param {Roo.ContentPanel} panel The panel
35435          */
35436         "paneladded" : true,
35437         /**
35438          * @event panelremoved
35439          * Fires when a panel is removed. 
35440          * @param {Roo.LayoutRegion} this
35441          * @param {Roo.ContentPanel} panel The panel
35442          */
35443         "panelremoved" : true,
35444         /**
35445          * @event beforecollapse
35446          * Fires when this region before collapse.
35447          * @param {Roo.LayoutRegion} this
35448          */
35449         "beforecollapse" : true,
35450         /**
35451          * @event collapsed
35452          * Fires when this region is collapsed.
35453          * @param {Roo.LayoutRegion} this
35454          */
35455         "collapsed" : true,
35456         /**
35457          * @event expanded
35458          * Fires when this region is expanded.
35459          * @param {Roo.LayoutRegion} this
35460          */
35461         "expanded" : true,
35462         /**
35463          * @event slideshow
35464          * Fires when this region is slid into view.
35465          * @param {Roo.LayoutRegion} this
35466          */
35467         "slideshow" : true,
35468         /**
35469          * @event slidehide
35470          * Fires when this region slides out of view. 
35471          * @param {Roo.LayoutRegion} this
35472          */
35473         "slidehide" : true,
35474         /**
35475          * @event panelactivated
35476          * Fires when a panel is activated. 
35477          * @param {Roo.LayoutRegion} this
35478          * @param {Roo.ContentPanel} panel The activated panel
35479          */
35480         "panelactivated" : true,
35481         /**
35482          * @event resized
35483          * Fires when the user resizes this region. 
35484          * @param {Roo.LayoutRegion} this
35485          * @param {Number} newSize The new size (width for east/west, height for north/south)
35486          */
35487         "resized" : true
35488     };
35489     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35490     this.panels = new Roo.util.MixedCollection();
35491     this.panels.getKey = this.getPanelId.createDelegate(this);
35492     this.box = null;
35493     this.activePanel = null;
35494     // ensure listeners are added...
35495     
35496     if (config.listeners || config.events) {
35497         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35498             listeners : config.listeners || {},
35499             events : config.events || {}
35500         });
35501     }
35502     
35503     if(skipConfig !== true){
35504         this.applyConfig(config);
35505     }
35506 };
35507
35508 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35509 {
35510     getPanelId : function(p){
35511         return p.getId();
35512     },
35513     
35514     applyConfig : function(config){
35515         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35516         this.config = config;
35517         
35518     },
35519     
35520     /**
35521      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35522      * the width, for horizontal (north, south) the height.
35523      * @param {Number} newSize The new width or height
35524      */
35525     resizeTo : function(newSize){
35526         var el = this.el ? this.el :
35527                  (this.activePanel ? this.activePanel.getEl() : null);
35528         if(el){
35529             switch(this.position){
35530                 case "east":
35531                 case "west":
35532                     el.setWidth(newSize);
35533                     this.fireEvent("resized", this, newSize);
35534                 break;
35535                 case "north":
35536                 case "south":
35537                     el.setHeight(newSize);
35538                     this.fireEvent("resized", this, newSize);
35539                 break;                
35540             }
35541         }
35542     },
35543     
35544     getBox : function(){
35545         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35546     },
35547     
35548     getMargins : function(){
35549         return this.margins;
35550     },
35551     
35552     updateBox : function(box){
35553         this.box = box;
35554         var el = this.activePanel.getEl();
35555         el.dom.style.left = box.x + "px";
35556         el.dom.style.top = box.y + "px";
35557         this.activePanel.setSize(box.width, box.height);
35558     },
35559     
35560     /**
35561      * Returns the container element for this region.
35562      * @return {Roo.Element}
35563      */
35564     getEl : function(){
35565         return this.activePanel;
35566     },
35567     
35568     /**
35569      * Returns true if this region is currently visible.
35570      * @return {Boolean}
35571      */
35572     isVisible : function(){
35573         return this.activePanel ? true : false;
35574     },
35575     
35576     setActivePanel : function(panel){
35577         panel = this.getPanel(panel);
35578         if(this.activePanel && this.activePanel != panel){
35579             this.activePanel.setActiveState(false);
35580             this.activePanel.getEl().setLeftTop(-10000,-10000);
35581         }
35582         this.activePanel = panel;
35583         panel.setActiveState(true);
35584         if(this.box){
35585             panel.setSize(this.box.width, this.box.height);
35586         }
35587         this.fireEvent("panelactivated", this, panel);
35588         this.fireEvent("invalidated");
35589     },
35590     
35591     /**
35592      * Show the specified panel.
35593      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35594      * @return {Roo.ContentPanel} The shown panel or null
35595      */
35596     showPanel : function(panel){
35597         panel = this.getPanel(panel);
35598         if(panel){
35599             this.setActivePanel(panel);
35600         }
35601         return panel;
35602     },
35603     
35604     /**
35605      * Get the active panel for this region.
35606      * @return {Roo.ContentPanel} The active panel or null
35607      */
35608     getActivePanel : function(){
35609         return this.activePanel;
35610     },
35611     
35612     /**
35613      * Add the passed ContentPanel(s)
35614      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35615      * @return {Roo.ContentPanel} The panel added (if only one was added)
35616      */
35617     add : function(panel){
35618         if(arguments.length > 1){
35619             for(var i = 0, len = arguments.length; i < len; i++) {
35620                 this.add(arguments[i]);
35621             }
35622             return null;
35623         }
35624         if(this.hasPanel(panel)){
35625             this.showPanel(panel);
35626             return panel;
35627         }
35628         var el = panel.getEl();
35629         if(el.dom.parentNode != this.mgr.el.dom){
35630             this.mgr.el.dom.appendChild(el.dom);
35631         }
35632         if(panel.setRegion){
35633             panel.setRegion(this);
35634         }
35635         this.panels.add(panel);
35636         el.setStyle("position", "absolute");
35637         if(!panel.background){
35638             this.setActivePanel(panel);
35639             if(this.config.initialSize && this.panels.getCount()==1){
35640                 this.resizeTo(this.config.initialSize);
35641             }
35642         }
35643         this.fireEvent("paneladded", this, panel);
35644         return panel;
35645     },
35646     
35647     /**
35648      * Returns true if the panel is in this region.
35649      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35650      * @return {Boolean}
35651      */
35652     hasPanel : function(panel){
35653         if(typeof panel == "object"){ // must be panel obj
35654             panel = panel.getId();
35655         }
35656         return this.getPanel(panel) ? true : false;
35657     },
35658     
35659     /**
35660      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35661      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35662      * @param {Boolean} preservePanel Overrides the config preservePanel option
35663      * @return {Roo.ContentPanel} The panel that was removed
35664      */
35665     remove : function(panel, preservePanel){
35666         panel = this.getPanel(panel);
35667         if(!panel){
35668             return null;
35669         }
35670         var e = {};
35671         this.fireEvent("beforeremove", this, panel, e);
35672         if(e.cancel === true){
35673             return null;
35674         }
35675         var panelId = panel.getId();
35676         this.panels.removeKey(panelId);
35677         return panel;
35678     },
35679     
35680     /**
35681      * Returns the panel specified or null if it's not in this region.
35682      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35683      * @return {Roo.ContentPanel}
35684      */
35685     getPanel : function(id){
35686         if(typeof id == "object"){ // must be panel obj
35687             return id;
35688         }
35689         return this.panels.get(id);
35690     },
35691     
35692     /**
35693      * Returns this regions position (north/south/east/west/center).
35694      * @return {String} 
35695      */
35696     getPosition: function(){
35697         return this.position;    
35698     }
35699 });/*
35700  * Based on:
35701  * Ext JS Library 1.1.1
35702  * Copyright(c) 2006-2007, Ext JS, LLC.
35703  *
35704  * Originally Released Under LGPL - original licence link has changed is not relivant.
35705  *
35706  * Fork - LGPL
35707  * <script type="text/javascript">
35708  */
35709  
35710 /**
35711  * @class Roo.bootstrap.layout.Region
35712  * @extends Roo.bootstrap.layout.Basic
35713  * This class represents a region in a layout manager.
35714  
35715  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35716  * @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})
35717  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35718  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35719  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35720  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35721  * @cfg {String}    title           The title for the region (overrides panel titles)
35722  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35723  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35724  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35725  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35726  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35727  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35728  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35729  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35730  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35731  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35732
35733  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35734  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35735  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35736  * @cfg {Number}    width           For East/West panels
35737  * @cfg {Number}    height          For North/South panels
35738  * @cfg {Boolean}   split           To show the splitter
35739  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35740  * 
35741  * @cfg {string}   cls             Extra CSS classes to add to region
35742  * 
35743  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35744  * @cfg {string}   region  the region that it inhabits..
35745  *
35746
35747  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35748  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35749
35750  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35751  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35752  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35753  */
35754 Roo.bootstrap.layout.Region = function(config)
35755 {
35756     this.applyConfig(config);
35757
35758     var mgr = config.mgr;
35759     var pos = config.region;
35760     config.skipConfig = true;
35761     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35762     
35763     if (mgr.el) {
35764         this.onRender(mgr.el);   
35765     }
35766      
35767     this.visible = true;
35768     this.collapsed = false;
35769     this.unrendered_panels = [];
35770 };
35771
35772 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35773
35774     position: '', // set by wrapper (eg. north/south etc..)
35775     unrendered_panels : null,  // unrendered panels.
35776     createBody : function(){
35777         /** This region's body element 
35778         * @type Roo.Element */
35779         this.bodyEl = this.el.createChild({
35780                 tag: "div",
35781                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35782         });
35783     },
35784
35785     onRender: function(ctr, pos)
35786     {
35787         var dh = Roo.DomHelper;
35788         /** This region's container element 
35789         * @type Roo.Element */
35790         this.el = dh.append(ctr.dom, {
35791                 tag: "div",
35792                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35793             }, true);
35794         /** This region's title element 
35795         * @type Roo.Element */
35796     
35797         this.titleEl = dh.append(this.el.dom,
35798             {
35799                     tag: "div",
35800                     unselectable: "on",
35801                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35802                     children:[
35803                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35804                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35805                     ]}, true);
35806         
35807         this.titleEl.enableDisplayMode();
35808         /** This region's title text element 
35809         * @type HTMLElement */
35810         this.titleTextEl = this.titleEl.dom.firstChild;
35811         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35812         /*
35813         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35814         this.closeBtn.enableDisplayMode();
35815         this.closeBtn.on("click", this.closeClicked, this);
35816         this.closeBtn.hide();
35817     */
35818         this.createBody(this.config);
35819         if(this.config.hideWhenEmpty){
35820             this.hide();
35821             this.on("paneladded", this.validateVisibility, this);
35822             this.on("panelremoved", this.validateVisibility, this);
35823         }
35824         if(this.autoScroll){
35825             this.bodyEl.setStyle("overflow", "auto");
35826         }else{
35827             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35828         }
35829         //if(c.titlebar !== false){
35830             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35831                 this.titleEl.hide();
35832             }else{
35833                 this.titleEl.show();
35834                 if(this.config.title){
35835                     this.titleTextEl.innerHTML = this.config.title;
35836                 }
35837             }
35838         //}
35839         if(this.config.collapsed){
35840             this.collapse(true);
35841         }
35842         if(this.config.hidden){
35843             this.hide();
35844         }
35845         
35846         if (this.unrendered_panels && this.unrendered_panels.length) {
35847             for (var i =0;i< this.unrendered_panels.length; i++) {
35848                 this.add(this.unrendered_panels[i]);
35849             }
35850             this.unrendered_panels = null;
35851             
35852         }
35853         
35854     },
35855     
35856     applyConfig : function(c)
35857     {
35858         /*
35859          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35860             var dh = Roo.DomHelper;
35861             if(c.titlebar !== false){
35862                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35863                 this.collapseBtn.on("click", this.collapse, this);
35864                 this.collapseBtn.enableDisplayMode();
35865                 /*
35866                 if(c.showPin === true || this.showPin){
35867                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35868                     this.stickBtn.enableDisplayMode();
35869                     this.stickBtn.on("click", this.expand, this);
35870                     this.stickBtn.hide();
35871                 }
35872                 
35873             }
35874             */
35875             /** This region's collapsed element
35876             * @type Roo.Element */
35877             /*
35878              *
35879             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35880                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35881             ]}, true);
35882             
35883             if(c.floatable !== false){
35884                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35885                this.collapsedEl.on("click", this.collapseClick, this);
35886             }
35887
35888             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35889                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35890                    id: "message", unselectable: "on", style:{"float":"left"}});
35891                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35892              }
35893             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35894             this.expandBtn.on("click", this.expand, this);
35895             
35896         }
35897         
35898         if(this.collapseBtn){
35899             this.collapseBtn.setVisible(c.collapsible == true);
35900         }
35901         
35902         this.cmargins = c.cmargins || this.cmargins ||
35903                          (this.position == "west" || this.position == "east" ?
35904                              {top: 0, left: 2, right:2, bottom: 0} :
35905                              {top: 2, left: 0, right:0, bottom: 2});
35906         */
35907         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35908         
35909         
35910         this.bottomTabs = c.tabPosition != "top";
35911         
35912         this.autoScroll = c.autoScroll || false;
35913         
35914         
35915        
35916         
35917         this.duration = c.duration || .30;
35918         this.slideDuration = c.slideDuration || .45;
35919         this.config = c;
35920        
35921     },
35922     /**
35923      * Returns true if this region is currently visible.
35924      * @return {Boolean}
35925      */
35926     isVisible : function(){
35927         return this.visible;
35928     },
35929
35930     /**
35931      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35932      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35933      */
35934     //setCollapsedTitle : function(title){
35935     //    title = title || "&#160;";
35936      //   if(this.collapsedTitleTextEl){
35937       //      this.collapsedTitleTextEl.innerHTML = title;
35938        // }
35939     //},
35940
35941     getBox : function(){
35942         var b;
35943       //  if(!this.collapsed){
35944             b = this.el.getBox(false, true);
35945        // }else{
35946           //  b = this.collapsedEl.getBox(false, true);
35947         //}
35948         return b;
35949     },
35950
35951     getMargins : function(){
35952         return this.margins;
35953         //return this.collapsed ? this.cmargins : this.margins;
35954     },
35955 /*
35956     highlight : function(){
35957         this.el.addClass("x-layout-panel-dragover");
35958     },
35959
35960     unhighlight : function(){
35961         this.el.removeClass("x-layout-panel-dragover");
35962     },
35963 */
35964     updateBox : function(box)
35965     {
35966         if (!this.bodyEl) {
35967             return; // not rendered yet..
35968         }
35969         
35970         this.box = box;
35971         if(!this.collapsed){
35972             this.el.dom.style.left = box.x + "px";
35973             this.el.dom.style.top = box.y + "px";
35974             this.updateBody(box.width, box.height);
35975         }else{
35976             this.collapsedEl.dom.style.left = box.x + "px";
35977             this.collapsedEl.dom.style.top = box.y + "px";
35978             this.collapsedEl.setSize(box.width, box.height);
35979         }
35980         if(this.tabs){
35981             this.tabs.autoSizeTabs();
35982         }
35983     },
35984
35985     updateBody : function(w, h)
35986     {
35987         if(w !== null){
35988             this.el.setWidth(w);
35989             w -= this.el.getBorderWidth("rl");
35990             if(this.config.adjustments){
35991                 w += this.config.adjustments[0];
35992             }
35993         }
35994         if(h !== null && h > 0){
35995             this.el.setHeight(h);
35996             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35997             h -= this.el.getBorderWidth("tb");
35998             if(this.config.adjustments){
35999                 h += this.config.adjustments[1];
36000             }
36001             this.bodyEl.setHeight(h);
36002             if(this.tabs){
36003                 h = this.tabs.syncHeight(h);
36004             }
36005         }
36006         if(this.panelSize){
36007             w = w !== null ? w : this.panelSize.width;
36008             h = h !== null ? h : this.panelSize.height;
36009         }
36010         if(this.activePanel){
36011             var el = this.activePanel.getEl();
36012             w = w !== null ? w : el.getWidth();
36013             h = h !== null ? h : el.getHeight();
36014             this.panelSize = {width: w, height: h};
36015             this.activePanel.setSize(w, h);
36016         }
36017         if(Roo.isIE && this.tabs){
36018             this.tabs.el.repaint();
36019         }
36020     },
36021
36022     /**
36023      * Returns the container element for this region.
36024      * @return {Roo.Element}
36025      */
36026     getEl : function(){
36027         return this.el;
36028     },
36029
36030     /**
36031      * Hides this region.
36032      */
36033     hide : function(){
36034         //if(!this.collapsed){
36035             this.el.dom.style.left = "-2000px";
36036             this.el.hide();
36037         //}else{
36038          //   this.collapsedEl.dom.style.left = "-2000px";
36039          //   this.collapsedEl.hide();
36040        // }
36041         this.visible = false;
36042         this.fireEvent("visibilitychange", this, false);
36043     },
36044
36045     /**
36046      * Shows this region if it was previously hidden.
36047      */
36048     show : function(){
36049         //if(!this.collapsed){
36050             this.el.show();
36051         //}else{
36052         //    this.collapsedEl.show();
36053        // }
36054         this.visible = true;
36055         this.fireEvent("visibilitychange", this, true);
36056     },
36057 /*
36058     closeClicked : function(){
36059         if(this.activePanel){
36060             this.remove(this.activePanel);
36061         }
36062     },
36063
36064     collapseClick : function(e){
36065         if(this.isSlid){
36066            e.stopPropagation();
36067            this.slideIn();
36068         }else{
36069            e.stopPropagation();
36070            this.slideOut();
36071         }
36072     },
36073 */
36074     /**
36075      * Collapses this region.
36076      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36077      */
36078     /*
36079     collapse : function(skipAnim, skipCheck = false){
36080         if(this.collapsed) {
36081             return;
36082         }
36083         
36084         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36085             
36086             this.collapsed = true;
36087             if(this.split){
36088                 this.split.el.hide();
36089             }
36090             if(this.config.animate && skipAnim !== true){
36091                 this.fireEvent("invalidated", this);
36092                 this.animateCollapse();
36093             }else{
36094                 this.el.setLocation(-20000,-20000);
36095                 this.el.hide();
36096                 this.collapsedEl.show();
36097                 this.fireEvent("collapsed", this);
36098                 this.fireEvent("invalidated", this);
36099             }
36100         }
36101         
36102     },
36103 */
36104     animateCollapse : function(){
36105         // overridden
36106     },
36107
36108     /**
36109      * Expands this region if it was previously collapsed.
36110      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36111      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36112      */
36113     /*
36114     expand : function(e, skipAnim){
36115         if(e) {
36116             e.stopPropagation();
36117         }
36118         if(!this.collapsed || this.el.hasActiveFx()) {
36119             return;
36120         }
36121         if(this.isSlid){
36122             this.afterSlideIn();
36123             skipAnim = true;
36124         }
36125         this.collapsed = false;
36126         if(this.config.animate && skipAnim !== true){
36127             this.animateExpand();
36128         }else{
36129             this.el.show();
36130             if(this.split){
36131                 this.split.el.show();
36132             }
36133             this.collapsedEl.setLocation(-2000,-2000);
36134             this.collapsedEl.hide();
36135             this.fireEvent("invalidated", this);
36136             this.fireEvent("expanded", this);
36137         }
36138     },
36139 */
36140     animateExpand : function(){
36141         // overridden
36142     },
36143
36144     initTabs : function()
36145     {
36146         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36147         
36148         var ts = new Roo.bootstrap.panel.Tabs({
36149                 el: this.bodyEl.dom,
36150                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36151                 disableTooltips: this.config.disableTabTips,
36152                 toolbar : this.config.toolbar
36153             });
36154         
36155         if(this.config.hideTabs){
36156             ts.stripWrap.setDisplayed(false);
36157         }
36158         this.tabs = ts;
36159         ts.resizeTabs = this.config.resizeTabs === true;
36160         ts.minTabWidth = this.config.minTabWidth || 40;
36161         ts.maxTabWidth = this.config.maxTabWidth || 250;
36162         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36163         ts.monitorResize = false;
36164         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36165         ts.bodyEl.addClass('roo-layout-tabs-body');
36166         this.panels.each(this.initPanelAsTab, this);
36167     },
36168
36169     initPanelAsTab : function(panel){
36170         var ti = this.tabs.addTab(
36171             panel.getEl().id,
36172             panel.getTitle(),
36173             null,
36174             this.config.closeOnTab && panel.isClosable(),
36175             panel.tpl
36176         );
36177         if(panel.tabTip !== undefined){
36178             ti.setTooltip(panel.tabTip);
36179         }
36180         ti.on("activate", function(){
36181               this.setActivePanel(panel);
36182         }, this);
36183         
36184         if(this.config.closeOnTab){
36185             ti.on("beforeclose", function(t, e){
36186                 e.cancel = true;
36187                 this.remove(panel);
36188             }, this);
36189         }
36190         
36191         panel.tabItem = ti;
36192         
36193         return ti;
36194     },
36195
36196     updatePanelTitle : function(panel, title)
36197     {
36198         if(this.activePanel == panel){
36199             this.updateTitle(title);
36200         }
36201         if(this.tabs){
36202             var ti = this.tabs.getTab(panel.getEl().id);
36203             ti.setText(title);
36204             if(panel.tabTip !== undefined){
36205                 ti.setTooltip(panel.tabTip);
36206             }
36207         }
36208     },
36209
36210     updateTitle : function(title){
36211         if(this.titleTextEl && !this.config.title){
36212             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36213         }
36214     },
36215
36216     setActivePanel : function(panel)
36217     {
36218         panel = this.getPanel(panel);
36219         if(this.activePanel && this.activePanel != panel){
36220             if(this.activePanel.setActiveState(false) === false){
36221                 return;
36222             }
36223         }
36224         this.activePanel = panel;
36225         panel.setActiveState(true);
36226         if(this.panelSize){
36227             panel.setSize(this.panelSize.width, this.panelSize.height);
36228         }
36229         if(this.closeBtn){
36230             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36231         }
36232         this.updateTitle(panel.getTitle());
36233         if(this.tabs){
36234             this.fireEvent("invalidated", this);
36235         }
36236         this.fireEvent("panelactivated", this, panel);
36237     },
36238
36239     /**
36240      * Shows the specified panel.
36241      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36242      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36243      */
36244     showPanel : function(panel)
36245     {
36246         panel = this.getPanel(panel);
36247         if(panel){
36248             if(this.tabs){
36249                 var tab = this.tabs.getTab(panel.getEl().id);
36250                 if(tab.isHidden()){
36251                     this.tabs.unhideTab(tab.id);
36252                 }
36253                 tab.activate();
36254             }else{
36255                 this.setActivePanel(panel);
36256             }
36257         }
36258         return panel;
36259     },
36260
36261     /**
36262      * Get the active panel for this region.
36263      * @return {Roo.ContentPanel} The active panel or null
36264      */
36265     getActivePanel : function(){
36266         return this.activePanel;
36267     },
36268
36269     validateVisibility : function(){
36270         if(this.panels.getCount() < 1){
36271             this.updateTitle("&#160;");
36272             this.closeBtn.hide();
36273             this.hide();
36274         }else{
36275             if(!this.isVisible()){
36276                 this.show();
36277             }
36278         }
36279     },
36280
36281     /**
36282      * Adds the passed ContentPanel(s) to this region.
36283      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36284      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36285      */
36286     add : function(panel)
36287     {
36288         if(arguments.length > 1){
36289             for(var i = 0, len = arguments.length; i < len; i++) {
36290                 this.add(arguments[i]);
36291             }
36292             return null;
36293         }
36294         
36295         // if we have not been rendered yet, then we can not really do much of this..
36296         if (!this.bodyEl) {
36297             this.unrendered_panels.push(panel);
36298             return panel;
36299         }
36300         
36301         
36302         
36303         
36304         if(this.hasPanel(panel)){
36305             this.showPanel(panel);
36306             return panel;
36307         }
36308         panel.setRegion(this);
36309         this.panels.add(panel);
36310        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36311             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36312             // and hide them... ???
36313             this.bodyEl.dom.appendChild(panel.getEl().dom);
36314             if(panel.background !== true){
36315                 this.setActivePanel(panel);
36316             }
36317             this.fireEvent("paneladded", this, panel);
36318             return panel;
36319         }
36320         */
36321         if(!this.tabs){
36322             this.initTabs();
36323         }else{
36324             this.initPanelAsTab(panel);
36325         }
36326         
36327         
36328         if(panel.background !== true){
36329             this.tabs.activate(panel.getEl().id);
36330         }
36331         this.fireEvent("paneladded", this, panel);
36332         return panel;
36333     },
36334
36335     /**
36336      * Hides the tab for the specified panel.
36337      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36338      */
36339     hidePanel : function(panel){
36340         if(this.tabs && (panel = this.getPanel(panel))){
36341             this.tabs.hideTab(panel.getEl().id);
36342         }
36343     },
36344
36345     /**
36346      * Unhides the tab for a previously hidden panel.
36347      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36348      */
36349     unhidePanel : function(panel){
36350         if(this.tabs && (panel = this.getPanel(panel))){
36351             this.tabs.unhideTab(panel.getEl().id);
36352         }
36353     },
36354
36355     clearPanels : function(){
36356         while(this.panels.getCount() > 0){
36357              this.remove(this.panels.first());
36358         }
36359     },
36360
36361     /**
36362      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36363      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36364      * @param {Boolean} preservePanel Overrides the config preservePanel option
36365      * @return {Roo.ContentPanel} The panel that was removed
36366      */
36367     remove : function(panel, preservePanel)
36368     {
36369         panel = this.getPanel(panel);
36370         if(!panel){
36371             return null;
36372         }
36373         var e = {};
36374         this.fireEvent("beforeremove", this, panel, e);
36375         if(e.cancel === true){
36376             return null;
36377         }
36378         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36379         var panelId = panel.getId();
36380         this.panels.removeKey(panelId);
36381         if(preservePanel){
36382             document.body.appendChild(panel.getEl().dom);
36383         }
36384         if(this.tabs){
36385             this.tabs.removeTab(panel.getEl().id);
36386         }else if (!preservePanel){
36387             this.bodyEl.dom.removeChild(panel.getEl().dom);
36388         }
36389         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36390             var p = this.panels.first();
36391             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36392             tempEl.appendChild(p.getEl().dom);
36393             this.bodyEl.update("");
36394             this.bodyEl.dom.appendChild(p.getEl().dom);
36395             tempEl = null;
36396             this.updateTitle(p.getTitle());
36397             this.tabs = null;
36398             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36399             this.setActivePanel(p);
36400         }
36401         panel.setRegion(null);
36402         if(this.activePanel == panel){
36403             this.activePanel = null;
36404         }
36405         if(this.config.autoDestroy !== false && preservePanel !== true){
36406             try{panel.destroy();}catch(e){}
36407         }
36408         this.fireEvent("panelremoved", this, panel);
36409         return panel;
36410     },
36411
36412     /**
36413      * Returns the TabPanel component used by this region
36414      * @return {Roo.TabPanel}
36415      */
36416     getTabs : function(){
36417         return this.tabs;
36418     },
36419
36420     createTool : function(parentEl, className){
36421         var btn = Roo.DomHelper.append(parentEl, {
36422             tag: "div",
36423             cls: "x-layout-tools-button",
36424             children: [ {
36425                 tag: "div",
36426                 cls: "roo-layout-tools-button-inner " + className,
36427                 html: "&#160;"
36428             }]
36429         }, true);
36430         btn.addClassOnOver("roo-layout-tools-button-over");
36431         return btn;
36432     }
36433 });/*
36434  * Based on:
36435  * Ext JS Library 1.1.1
36436  * Copyright(c) 2006-2007, Ext JS, LLC.
36437  *
36438  * Originally Released Under LGPL - original licence link has changed is not relivant.
36439  *
36440  * Fork - LGPL
36441  * <script type="text/javascript">
36442  */
36443  
36444
36445
36446 /**
36447  * @class Roo.SplitLayoutRegion
36448  * @extends Roo.LayoutRegion
36449  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36450  */
36451 Roo.bootstrap.layout.Split = function(config){
36452     this.cursor = config.cursor;
36453     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36454 };
36455
36456 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36457 {
36458     splitTip : "Drag to resize.",
36459     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36460     useSplitTips : false,
36461
36462     applyConfig : function(config){
36463         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36464     },
36465     
36466     onRender : function(ctr,pos) {
36467         
36468         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36469         if(!this.config.split){
36470             return;
36471         }
36472         if(!this.split){
36473             
36474             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36475                             tag: "div",
36476                             id: this.el.id + "-split",
36477                             cls: "roo-layout-split roo-layout-split-"+this.position,
36478                             html: "&#160;"
36479             });
36480             /** The SplitBar for this region 
36481             * @type Roo.SplitBar */
36482             // does not exist yet...
36483             Roo.log([this.position, this.orientation]);
36484             
36485             this.split = new Roo.bootstrap.SplitBar({
36486                 dragElement : splitEl,
36487                 resizingElement: this.el,
36488                 orientation : this.orientation
36489             });
36490             
36491             this.split.on("moved", this.onSplitMove, this);
36492             this.split.useShim = this.config.useShim === true;
36493             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36494             if(this.useSplitTips){
36495                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36496             }
36497             //if(config.collapsible){
36498             //    this.split.el.on("dblclick", this.collapse,  this);
36499             //}
36500         }
36501         if(typeof this.config.minSize != "undefined"){
36502             this.split.minSize = this.config.minSize;
36503         }
36504         if(typeof this.config.maxSize != "undefined"){
36505             this.split.maxSize = this.config.maxSize;
36506         }
36507         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36508             this.hideSplitter();
36509         }
36510         
36511     },
36512
36513     getHMaxSize : function(){
36514          var cmax = this.config.maxSize || 10000;
36515          var center = this.mgr.getRegion("center");
36516          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36517     },
36518
36519     getVMaxSize : function(){
36520          var cmax = this.config.maxSize || 10000;
36521          var center = this.mgr.getRegion("center");
36522          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36523     },
36524
36525     onSplitMove : function(split, newSize){
36526         this.fireEvent("resized", this, newSize);
36527     },
36528     
36529     /** 
36530      * Returns the {@link Roo.SplitBar} for this region.
36531      * @return {Roo.SplitBar}
36532      */
36533     getSplitBar : function(){
36534         return this.split;
36535     },
36536     
36537     hide : function(){
36538         this.hideSplitter();
36539         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36540     },
36541
36542     hideSplitter : function(){
36543         if(this.split){
36544             this.split.el.setLocation(-2000,-2000);
36545             this.split.el.hide();
36546         }
36547     },
36548
36549     show : function(){
36550         if(this.split){
36551             this.split.el.show();
36552         }
36553         Roo.bootstrap.layout.Split.superclass.show.call(this);
36554     },
36555     
36556     beforeSlide: function(){
36557         if(Roo.isGecko){// firefox overflow auto bug workaround
36558             this.bodyEl.clip();
36559             if(this.tabs) {
36560                 this.tabs.bodyEl.clip();
36561             }
36562             if(this.activePanel){
36563                 this.activePanel.getEl().clip();
36564                 
36565                 if(this.activePanel.beforeSlide){
36566                     this.activePanel.beforeSlide();
36567                 }
36568             }
36569         }
36570     },
36571     
36572     afterSlide : function(){
36573         if(Roo.isGecko){// firefox overflow auto bug workaround
36574             this.bodyEl.unclip();
36575             if(this.tabs) {
36576                 this.tabs.bodyEl.unclip();
36577             }
36578             if(this.activePanel){
36579                 this.activePanel.getEl().unclip();
36580                 if(this.activePanel.afterSlide){
36581                     this.activePanel.afterSlide();
36582                 }
36583             }
36584         }
36585     },
36586
36587     initAutoHide : function(){
36588         if(this.autoHide !== false){
36589             if(!this.autoHideHd){
36590                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36591                 this.autoHideHd = {
36592                     "mouseout": function(e){
36593                         if(!e.within(this.el, true)){
36594                             st.delay(500);
36595                         }
36596                     },
36597                     "mouseover" : function(e){
36598                         st.cancel();
36599                     },
36600                     scope : this
36601                 };
36602             }
36603             this.el.on(this.autoHideHd);
36604         }
36605     },
36606
36607     clearAutoHide : function(){
36608         if(this.autoHide !== false){
36609             this.el.un("mouseout", this.autoHideHd.mouseout);
36610             this.el.un("mouseover", this.autoHideHd.mouseover);
36611         }
36612     },
36613
36614     clearMonitor : function(){
36615         Roo.get(document).un("click", this.slideInIf, this);
36616     },
36617
36618     // these names are backwards but not changed for compat
36619     slideOut : function(){
36620         if(this.isSlid || this.el.hasActiveFx()){
36621             return;
36622         }
36623         this.isSlid = true;
36624         if(this.collapseBtn){
36625             this.collapseBtn.hide();
36626         }
36627         this.closeBtnState = this.closeBtn.getStyle('display');
36628         this.closeBtn.hide();
36629         if(this.stickBtn){
36630             this.stickBtn.show();
36631         }
36632         this.el.show();
36633         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36634         this.beforeSlide();
36635         this.el.setStyle("z-index", 10001);
36636         this.el.slideIn(this.getSlideAnchor(), {
36637             callback: function(){
36638                 this.afterSlide();
36639                 this.initAutoHide();
36640                 Roo.get(document).on("click", this.slideInIf, this);
36641                 this.fireEvent("slideshow", this);
36642             },
36643             scope: this,
36644             block: true
36645         });
36646     },
36647
36648     afterSlideIn : function(){
36649         this.clearAutoHide();
36650         this.isSlid = false;
36651         this.clearMonitor();
36652         this.el.setStyle("z-index", "");
36653         if(this.collapseBtn){
36654             this.collapseBtn.show();
36655         }
36656         this.closeBtn.setStyle('display', this.closeBtnState);
36657         if(this.stickBtn){
36658             this.stickBtn.hide();
36659         }
36660         this.fireEvent("slidehide", this);
36661     },
36662
36663     slideIn : function(cb){
36664         if(!this.isSlid || this.el.hasActiveFx()){
36665             Roo.callback(cb);
36666             return;
36667         }
36668         this.isSlid = false;
36669         this.beforeSlide();
36670         this.el.slideOut(this.getSlideAnchor(), {
36671             callback: function(){
36672                 this.el.setLeftTop(-10000, -10000);
36673                 this.afterSlide();
36674                 this.afterSlideIn();
36675                 Roo.callback(cb);
36676             },
36677             scope: this,
36678             block: true
36679         });
36680     },
36681     
36682     slideInIf : function(e){
36683         if(!e.within(this.el)){
36684             this.slideIn();
36685         }
36686     },
36687
36688     animateCollapse : function(){
36689         this.beforeSlide();
36690         this.el.setStyle("z-index", 20000);
36691         var anchor = this.getSlideAnchor();
36692         this.el.slideOut(anchor, {
36693             callback : function(){
36694                 this.el.setStyle("z-index", "");
36695                 this.collapsedEl.slideIn(anchor, {duration:.3});
36696                 this.afterSlide();
36697                 this.el.setLocation(-10000,-10000);
36698                 this.el.hide();
36699                 this.fireEvent("collapsed", this);
36700             },
36701             scope: this,
36702             block: true
36703         });
36704     },
36705
36706     animateExpand : function(){
36707         this.beforeSlide();
36708         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36709         this.el.setStyle("z-index", 20000);
36710         this.collapsedEl.hide({
36711             duration:.1
36712         });
36713         this.el.slideIn(this.getSlideAnchor(), {
36714             callback : function(){
36715                 this.el.setStyle("z-index", "");
36716                 this.afterSlide();
36717                 if(this.split){
36718                     this.split.el.show();
36719                 }
36720                 this.fireEvent("invalidated", this);
36721                 this.fireEvent("expanded", this);
36722             },
36723             scope: this,
36724             block: true
36725         });
36726     },
36727
36728     anchors : {
36729         "west" : "left",
36730         "east" : "right",
36731         "north" : "top",
36732         "south" : "bottom"
36733     },
36734
36735     sanchors : {
36736         "west" : "l",
36737         "east" : "r",
36738         "north" : "t",
36739         "south" : "b"
36740     },
36741
36742     canchors : {
36743         "west" : "tl-tr",
36744         "east" : "tr-tl",
36745         "north" : "tl-bl",
36746         "south" : "bl-tl"
36747     },
36748
36749     getAnchor : function(){
36750         return this.anchors[this.position];
36751     },
36752
36753     getCollapseAnchor : function(){
36754         return this.canchors[this.position];
36755     },
36756
36757     getSlideAnchor : function(){
36758         return this.sanchors[this.position];
36759     },
36760
36761     getAlignAdj : function(){
36762         var cm = this.cmargins;
36763         switch(this.position){
36764             case "west":
36765                 return [0, 0];
36766             break;
36767             case "east":
36768                 return [0, 0];
36769             break;
36770             case "north":
36771                 return [0, 0];
36772             break;
36773             case "south":
36774                 return [0, 0];
36775             break;
36776         }
36777     },
36778
36779     getExpandAdj : function(){
36780         var c = this.collapsedEl, cm = this.cmargins;
36781         switch(this.position){
36782             case "west":
36783                 return [-(cm.right+c.getWidth()+cm.left), 0];
36784             break;
36785             case "east":
36786                 return [cm.right+c.getWidth()+cm.left, 0];
36787             break;
36788             case "north":
36789                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36790             break;
36791             case "south":
36792                 return [0, cm.top+cm.bottom+c.getHeight()];
36793             break;
36794         }
36795     }
36796 });/*
36797  * Based on:
36798  * Ext JS Library 1.1.1
36799  * Copyright(c) 2006-2007, Ext JS, LLC.
36800  *
36801  * Originally Released Under LGPL - original licence link has changed is not relivant.
36802  *
36803  * Fork - LGPL
36804  * <script type="text/javascript">
36805  */
36806 /*
36807  * These classes are private internal classes
36808  */
36809 Roo.bootstrap.layout.Center = function(config){
36810     config.region = "center";
36811     Roo.bootstrap.layout.Region.call(this, config);
36812     this.visible = true;
36813     this.minWidth = config.minWidth || 20;
36814     this.minHeight = config.minHeight || 20;
36815 };
36816
36817 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36818     hide : function(){
36819         // center panel can't be hidden
36820     },
36821     
36822     show : function(){
36823         // center panel can't be hidden
36824     },
36825     
36826     getMinWidth: function(){
36827         return this.minWidth;
36828     },
36829     
36830     getMinHeight: function(){
36831         return this.minHeight;
36832     }
36833 });
36834
36835
36836
36837
36838  
36839
36840
36841
36842
36843
36844 Roo.bootstrap.layout.North = function(config)
36845 {
36846     config.region = 'north';
36847     config.cursor = 'n-resize';
36848     
36849     Roo.bootstrap.layout.Split.call(this, config);
36850     
36851     
36852     if(this.split){
36853         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36854         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36855         this.split.el.addClass("roo-layout-split-v");
36856     }
36857     var size = config.initialSize || config.height;
36858     if(typeof size != "undefined"){
36859         this.el.setHeight(size);
36860     }
36861 };
36862 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36863 {
36864     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36865     
36866     
36867     
36868     getBox : function(){
36869         if(this.collapsed){
36870             return this.collapsedEl.getBox();
36871         }
36872         var box = this.el.getBox();
36873         if(this.split){
36874             box.height += this.split.el.getHeight();
36875         }
36876         return box;
36877     },
36878     
36879     updateBox : function(box){
36880         if(this.split && !this.collapsed){
36881             box.height -= this.split.el.getHeight();
36882             this.split.el.setLeft(box.x);
36883             this.split.el.setTop(box.y+box.height);
36884             this.split.el.setWidth(box.width);
36885         }
36886         if(this.collapsed){
36887             this.updateBody(box.width, null);
36888         }
36889         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36890     }
36891 });
36892
36893
36894
36895
36896
36897 Roo.bootstrap.layout.South = function(config){
36898     config.region = 'south';
36899     config.cursor = 's-resize';
36900     Roo.bootstrap.layout.Split.call(this, config);
36901     if(this.split){
36902         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36903         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36904         this.split.el.addClass("roo-layout-split-v");
36905     }
36906     var size = config.initialSize || config.height;
36907     if(typeof size != "undefined"){
36908         this.el.setHeight(size);
36909     }
36910 };
36911
36912 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36913     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36914     getBox : function(){
36915         if(this.collapsed){
36916             return this.collapsedEl.getBox();
36917         }
36918         var box = this.el.getBox();
36919         if(this.split){
36920             var sh = this.split.el.getHeight();
36921             box.height += sh;
36922             box.y -= sh;
36923         }
36924         return box;
36925     },
36926     
36927     updateBox : function(box){
36928         if(this.split && !this.collapsed){
36929             var sh = this.split.el.getHeight();
36930             box.height -= sh;
36931             box.y += sh;
36932             this.split.el.setLeft(box.x);
36933             this.split.el.setTop(box.y-sh);
36934             this.split.el.setWidth(box.width);
36935         }
36936         if(this.collapsed){
36937             this.updateBody(box.width, null);
36938         }
36939         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36940     }
36941 });
36942
36943 Roo.bootstrap.layout.East = function(config){
36944     config.region = "east";
36945     config.cursor = "e-resize";
36946     Roo.bootstrap.layout.Split.call(this, config);
36947     if(this.split){
36948         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36949         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36950         this.split.el.addClass("roo-layout-split-h");
36951     }
36952     var size = config.initialSize || config.width;
36953     if(typeof size != "undefined"){
36954         this.el.setWidth(size);
36955     }
36956 };
36957 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36958     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36959     getBox : function(){
36960         if(this.collapsed){
36961             return this.collapsedEl.getBox();
36962         }
36963         var box = this.el.getBox();
36964         if(this.split){
36965             var sw = this.split.el.getWidth();
36966             box.width += sw;
36967             box.x -= sw;
36968         }
36969         return box;
36970     },
36971
36972     updateBox : function(box){
36973         if(this.split && !this.collapsed){
36974             var sw = this.split.el.getWidth();
36975             box.width -= sw;
36976             this.split.el.setLeft(box.x);
36977             this.split.el.setTop(box.y);
36978             this.split.el.setHeight(box.height);
36979             box.x += sw;
36980         }
36981         if(this.collapsed){
36982             this.updateBody(null, box.height);
36983         }
36984         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36985     }
36986 });
36987
36988 Roo.bootstrap.layout.West = function(config){
36989     config.region = "west";
36990     config.cursor = "w-resize";
36991     
36992     Roo.bootstrap.layout.Split.call(this, config);
36993     if(this.split){
36994         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36995         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36996         this.split.el.addClass("roo-layout-split-h");
36997     }
36998     
36999 };
37000 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37001     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37002     
37003     onRender: function(ctr, pos)
37004     {
37005         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37006         var size = this.config.initialSize || this.config.width;
37007         if(typeof size != "undefined"){
37008             this.el.setWidth(size);
37009         }
37010     },
37011     
37012     getBox : function(){
37013         if(this.collapsed){
37014             return this.collapsedEl.getBox();
37015         }
37016         var box = this.el.getBox();
37017         if(this.split){
37018             box.width += this.split.el.getWidth();
37019         }
37020         return box;
37021     },
37022     
37023     updateBox : function(box){
37024         if(this.split && !this.collapsed){
37025             var sw = this.split.el.getWidth();
37026             box.width -= sw;
37027             this.split.el.setLeft(box.x+box.width);
37028             this.split.el.setTop(box.y);
37029             this.split.el.setHeight(box.height);
37030         }
37031         if(this.collapsed){
37032             this.updateBody(null, box.height);
37033         }
37034         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37035     }
37036 });
37037 Roo.namespace("Roo.bootstrap.panel");/*
37038  * Based on:
37039  * Ext JS Library 1.1.1
37040  * Copyright(c) 2006-2007, Ext JS, LLC.
37041  *
37042  * Originally Released Under LGPL - original licence link has changed is not relivant.
37043  *
37044  * Fork - LGPL
37045  * <script type="text/javascript">
37046  */
37047 /**
37048  * @class Roo.ContentPanel
37049  * @extends Roo.util.Observable
37050  * A basic ContentPanel element.
37051  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37052  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37053  * @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
37054  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37055  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37056  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37057  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37058  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37059  * @cfg {String} title          The title for this panel
37060  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37061  * @cfg {String} url            Calls {@link #setUrl} with this value
37062  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37063  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37064  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37065  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37066  * @cfg {Boolean} badges render the badges
37067
37068  * @constructor
37069  * Create a new ContentPanel.
37070  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37071  * @param {String/Object} config A string to set only the title or a config object
37072  * @param {String} content (optional) Set the HTML content for this panel
37073  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37074  */
37075 Roo.bootstrap.panel.Content = function( config){
37076     
37077     this.tpl = config.tpl || false;
37078     
37079     var el = config.el;
37080     var content = config.content;
37081
37082     if(config.autoCreate){ // xtype is available if this is called from factory
37083         el = Roo.id();
37084     }
37085     this.el = Roo.get(el);
37086     if(!this.el && config && config.autoCreate){
37087         if(typeof config.autoCreate == "object"){
37088             if(!config.autoCreate.id){
37089                 config.autoCreate.id = config.id||el;
37090             }
37091             this.el = Roo.DomHelper.append(document.body,
37092                         config.autoCreate, true);
37093         }else{
37094             var elcfg =  {   tag: "div",
37095                             cls: "roo-layout-inactive-content",
37096                             id: config.id||el
37097                             };
37098             if (config.html) {
37099                 elcfg.html = config.html;
37100                 
37101             }
37102                         
37103             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37104         }
37105     } 
37106     this.closable = false;
37107     this.loaded = false;
37108     this.active = false;
37109    
37110       
37111     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37112         
37113         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37114         
37115         this.wrapEl = this.el; //this.el.wrap();
37116         var ti = [];
37117         if (config.toolbar.items) {
37118             ti = config.toolbar.items ;
37119             delete config.toolbar.items ;
37120         }
37121         
37122         var nitems = [];
37123         this.toolbar.render(this.wrapEl, 'before');
37124         for(var i =0;i < ti.length;i++) {
37125           //  Roo.log(['add child', items[i]]);
37126             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37127         }
37128         this.toolbar.items = nitems;
37129         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37130         delete config.toolbar;
37131         
37132     }
37133     /*
37134     // xtype created footer. - not sure if will work as we normally have to render first..
37135     if (this.footer && !this.footer.el && this.footer.xtype) {
37136         if (!this.wrapEl) {
37137             this.wrapEl = this.el.wrap();
37138         }
37139     
37140         this.footer.container = this.wrapEl.createChild();
37141          
37142         this.footer = Roo.factory(this.footer, Roo);
37143         
37144     }
37145     */
37146     
37147      if(typeof config == "string"){
37148         this.title = config;
37149     }else{
37150         Roo.apply(this, config);
37151     }
37152     
37153     if(this.resizeEl){
37154         this.resizeEl = Roo.get(this.resizeEl, true);
37155     }else{
37156         this.resizeEl = this.el;
37157     }
37158     // handle view.xtype
37159     
37160  
37161     
37162     
37163     this.addEvents({
37164         /**
37165          * @event activate
37166          * Fires when this panel is activated. 
37167          * @param {Roo.ContentPanel} this
37168          */
37169         "activate" : true,
37170         /**
37171          * @event deactivate
37172          * Fires when this panel is activated. 
37173          * @param {Roo.ContentPanel} this
37174          */
37175         "deactivate" : true,
37176
37177         /**
37178          * @event resize
37179          * Fires when this panel is resized if fitToFrame is true.
37180          * @param {Roo.ContentPanel} this
37181          * @param {Number} width The width after any component adjustments
37182          * @param {Number} height The height after any component adjustments
37183          */
37184         "resize" : true,
37185         
37186          /**
37187          * @event render
37188          * Fires when this tab is created
37189          * @param {Roo.ContentPanel} this
37190          */
37191         "render" : true
37192         
37193         
37194         
37195     });
37196     
37197
37198     
37199     
37200     if(this.autoScroll){
37201         this.resizeEl.setStyle("overflow", "auto");
37202     } else {
37203         // fix randome scrolling
37204         //this.el.on('scroll', function() {
37205         //    Roo.log('fix random scolling');
37206         //    this.scrollTo('top',0); 
37207         //});
37208     }
37209     content = content || this.content;
37210     if(content){
37211         this.setContent(content);
37212     }
37213     if(config && config.url){
37214         this.setUrl(this.url, this.params, this.loadOnce);
37215     }
37216     
37217     
37218     
37219     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37220     
37221     if (this.view && typeof(this.view.xtype) != 'undefined') {
37222         this.view.el = this.el.appendChild(document.createElement("div"));
37223         this.view = Roo.factory(this.view); 
37224         this.view.render  &&  this.view.render(false, '');  
37225     }
37226     
37227     
37228     this.fireEvent('render', this);
37229 };
37230
37231 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37232     
37233     tabTip : '',
37234     
37235     setRegion : function(region){
37236         this.region = region;
37237         this.setActiveClass(region && !this.background);
37238     },
37239     
37240     
37241     setActiveClass: function(state)
37242     {
37243         if(state){
37244            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37245            this.el.setStyle('position','relative');
37246         }else{
37247            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37248            this.el.setStyle('position', 'absolute');
37249         } 
37250     },
37251     
37252     /**
37253      * Returns the toolbar for this Panel if one was configured. 
37254      * @return {Roo.Toolbar} 
37255      */
37256     getToolbar : function(){
37257         return this.toolbar;
37258     },
37259     
37260     setActiveState : function(active)
37261     {
37262         this.active = active;
37263         this.setActiveClass(active);
37264         if(!active){
37265             if(this.fireEvent("deactivate", this) === false){
37266                 return false;
37267             }
37268             return true;
37269         }
37270         this.fireEvent("activate", this);
37271         return true;
37272     },
37273     /**
37274      * Updates this panel's element
37275      * @param {String} content The new content
37276      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37277     */
37278     setContent : function(content, loadScripts){
37279         this.el.update(content, loadScripts);
37280     },
37281
37282     ignoreResize : function(w, h){
37283         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37284             return true;
37285         }else{
37286             this.lastSize = {width: w, height: h};
37287             return false;
37288         }
37289     },
37290     /**
37291      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37292      * @return {Roo.UpdateManager} The UpdateManager
37293      */
37294     getUpdateManager : function(){
37295         return this.el.getUpdateManager();
37296     },
37297      /**
37298      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37299      * @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:
37300 <pre><code>
37301 panel.load({
37302     url: "your-url.php",
37303     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37304     callback: yourFunction,
37305     scope: yourObject, //(optional scope)
37306     discardUrl: false,
37307     nocache: false,
37308     text: "Loading...",
37309     timeout: 30,
37310     scripts: false
37311 });
37312 </code></pre>
37313      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37314      * 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.
37315      * @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}
37316      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37317      * @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.
37318      * @return {Roo.ContentPanel} this
37319      */
37320     load : function(){
37321         var um = this.el.getUpdateManager();
37322         um.update.apply(um, arguments);
37323         return this;
37324     },
37325
37326
37327     /**
37328      * 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.
37329      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37330      * @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)
37331      * @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)
37332      * @return {Roo.UpdateManager} The UpdateManager
37333      */
37334     setUrl : function(url, params, loadOnce){
37335         if(this.refreshDelegate){
37336             this.removeListener("activate", this.refreshDelegate);
37337         }
37338         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37339         this.on("activate", this.refreshDelegate);
37340         return this.el.getUpdateManager();
37341     },
37342     
37343     _handleRefresh : function(url, params, loadOnce){
37344         if(!loadOnce || !this.loaded){
37345             var updater = this.el.getUpdateManager();
37346             updater.update(url, params, this._setLoaded.createDelegate(this));
37347         }
37348     },
37349     
37350     _setLoaded : function(){
37351         this.loaded = true;
37352     }, 
37353     
37354     /**
37355      * Returns this panel's id
37356      * @return {String} 
37357      */
37358     getId : function(){
37359         return this.el.id;
37360     },
37361     
37362     /** 
37363      * Returns this panel's element - used by regiosn to add.
37364      * @return {Roo.Element} 
37365      */
37366     getEl : function(){
37367         return this.wrapEl || this.el;
37368     },
37369     
37370    
37371     
37372     adjustForComponents : function(width, height)
37373     {
37374         //Roo.log('adjustForComponents ');
37375         if(this.resizeEl != this.el){
37376             width -= this.el.getFrameWidth('lr');
37377             height -= this.el.getFrameWidth('tb');
37378         }
37379         if(this.toolbar){
37380             var te = this.toolbar.getEl();
37381             te.setWidth(width);
37382             height -= te.getHeight();
37383         }
37384         if(this.footer){
37385             var te = this.footer.getEl();
37386             te.setWidth(width);
37387             height -= te.getHeight();
37388         }
37389         
37390         
37391         if(this.adjustments){
37392             width += this.adjustments[0];
37393             height += this.adjustments[1];
37394         }
37395         return {"width": width, "height": height};
37396     },
37397     
37398     setSize : function(width, height){
37399         if(this.fitToFrame && !this.ignoreResize(width, height)){
37400             if(this.fitContainer && this.resizeEl != this.el){
37401                 this.el.setSize(width, height);
37402             }
37403             var size = this.adjustForComponents(width, height);
37404             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37405             this.fireEvent('resize', this, size.width, size.height);
37406         }
37407     },
37408     
37409     /**
37410      * Returns this panel's title
37411      * @return {String} 
37412      */
37413     getTitle : function(){
37414         
37415         if (typeof(this.title) != 'object') {
37416             return this.title;
37417         }
37418         
37419         var t = '';
37420         for (var k in this.title) {
37421             if (!this.title.hasOwnProperty(k)) {
37422                 continue;
37423             }
37424             
37425             if (k.indexOf('-') >= 0) {
37426                 var s = k.split('-');
37427                 for (var i = 0; i<s.length; i++) {
37428                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37429                 }
37430             } else {
37431                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37432             }
37433         }
37434         return t;
37435     },
37436     
37437     /**
37438      * Set this panel's title
37439      * @param {String} title
37440      */
37441     setTitle : function(title){
37442         this.title = title;
37443         if(this.region){
37444             this.region.updatePanelTitle(this, title);
37445         }
37446     },
37447     
37448     /**
37449      * Returns true is this panel was configured to be closable
37450      * @return {Boolean} 
37451      */
37452     isClosable : function(){
37453         return this.closable;
37454     },
37455     
37456     beforeSlide : function(){
37457         this.el.clip();
37458         this.resizeEl.clip();
37459     },
37460     
37461     afterSlide : function(){
37462         this.el.unclip();
37463         this.resizeEl.unclip();
37464     },
37465     
37466     /**
37467      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37468      *   Will fail silently if the {@link #setUrl} method has not been called.
37469      *   This does not activate the panel, just updates its content.
37470      */
37471     refresh : function(){
37472         if(this.refreshDelegate){
37473            this.loaded = false;
37474            this.refreshDelegate();
37475         }
37476     },
37477     
37478     /**
37479      * Destroys this panel
37480      */
37481     destroy : function(){
37482         this.el.removeAllListeners();
37483         var tempEl = document.createElement("span");
37484         tempEl.appendChild(this.el.dom);
37485         tempEl.innerHTML = "";
37486         this.el.remove();
37487         this.el = null;
37488     },
37489     
37490     /**
37491      * form - if the content panel contains a form - this is a reference to it.
37492      * @type {Roo.form.Form}
37493      */
37494     form : false,
37495     /**
37496      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37497      *    This contains a reference to it.
37498      * @type {Roo.View}
37499      */
37500     view : false,
37501     
37502       /**
37503      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37504      * <pre><code>
37505
37506 layout.addxtype({
37507        xtype : 'Form',
37508        items: [ .... ]
37509    }
37510 );
37511
37512 </code></pre>
37513      * @param {Object} cfg Xtype definition of item to add.
37514      */
37515     
37516     
37517     getChildContainer: function () {
37518         return this.getEl();
37519     }
37520     
37521     
37522     /*
37523         var  ret = new Roo.factory(cfg);
37524         return ret;
37525         
37526         
37527         // add form..
37528         if (cfg.xtype.match(/^Form$/)) {
37529             
37530             var el;
37531             //if (this.footer) {
37532             //    el = this.footer.container.insertSibling(false, 'before');
37533             //} else {
37534                 el = this.el.createChild();
37535             //}
37536
37537             this.form = new  Roo.form.Form(cfg);
37538             
37539             
37540             if ( this.form.allItems.length) {
37541                 this.form.render(el.dom);
37542             }
37543             return this.form;
37544         }
37545         // should only have one of theses..
37546         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37547             // views.. should not be just added - used named prop 'view''
37548             
37549             cfg.el = this.el.appendChild(document.createElement("div"));
37550             // factory?
37551             
37552             var ret = new Roo.factory(cfg);
37553              
37554              ret.render && ret.render(false, ''); // render blank..
37555             this.view = ret;
37556             return ret;
37557         }
37558         return false;
37559     }
37560     \*/
37561 });
37562  
37563 /**
37564  * @class Roo.bootstrap.panel.Grid
37565  * @extends Roo.bootstrap.panel.Content
37566  * @constructor
37567  * Create a new GridPanel.
37568  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37569  * @param {Object} config A the config object
37570   
37571  */
37572
37573
37574
37575 Roo.bootstrap.panel.Grid = function(config)
37576 {
37577     
37578       
37579     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37580         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37581
37582     config.el = this.wrapper;
37583     //this.el = this.wrapper;
37584     
37585       if (config.container) {
37586         // ctor'ed from a Border/panel.grid
37587         
37588         
37589         this.wrapper.setStyle("overflow", "hidden");
37590         this.wrapper.addClass('roo-grid-container');
37591
37592     }
37593     
37594     
37595     if(config.toolbar){
37596         var tool_el = this.wrapper.createChild();    
37597         this.toolbar = Roo.factory(config.toolbar);
37598         var ti = [];
37599         if (config.toolbar.items) {
37600             ti = config.toolbar.items ;
37601             delete config.toolbar.items ;
37602         }
37603         
37604         var nitems = [];
37605         this.toolbar.render(tool_el);
37606         for(var i =0;i < ti.length;i++) {
37607           //  Roo.log(['add child', items[i]]);
37608             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37609         }
37610         this.toolbar.items = nitems;
37611         
37612         delete config.toolbar;
37613     }
37614     
37615     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37616     config.grid.scrollBody = true;;
37617     config.grid.monitorWindowResize = false; // turn off autosizing
37618     config.grid.autoHeight = false;
37619     config.grid.autoWidth = false;
37620     
37621     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37622     
37623     if (config.background) {
37624         // render grid on panel activation (if panel background)
37625         this.on('activate', function(gp) {
37626             if (!gp.grid.rendered) {
37627                 gp.grid.render(this.wrapper);
37628                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37629             }
37630         });
37631             
37632     } else {
37633         this.grid.render(this.wrapper);
37634         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37635
37636     }
37637     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37638     // ??? needed ??? config.el = this.wrapper;
37639     
37640     
37641     
37642   
37643     // xtype created footer. - not sure if will work as we normally have to render first..
37644     if (this.footer && !this.footer.el && this.footer.xtype) {
37645         
37646         var ctr = this.grid.getView().getFooterPanel(true);
37647         this.footer.dataSource = this.grid.dataSource;
37648         this.footer = Roo.factory(this.footer, Roo);
37649         this.footer.render(ctr);
37650         
37651     }
37652     
37653     
37654     
37655     
37656      
37657 };
37658
37659 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37660     getId : function(){
37661         return this.grid.id;
37662     },
37663     
37664     /**
37665      * Returns the grid for this panel
37666      * @return {Roo.bootstrap.Table} 
37667      */
37668     getGrid : function(){
37669         return this.grid;    
37670     },
37671     
37672     setSize : function(width, height){
37673         if(!this.ignoreResize(width, height)){
37674             var grid = this.grid;
37675             var size = this.adjustForComponents(width, height);
37676             var gridel = grid.getGridEl();
37677             gridel.setSize(size.width, size.height);
37678             /*
37679             var thd = grid.getGridEl().select('thead',true).first();
37680             var tbd = grid.getGridEl().select('tbody', true).first();
37681             if (tbd) {
37682                 tbd.setSize(width, height - thd.getHeight());
37683             }
37684             */
37685             grid.autoSize();
37686         }
37687     },
37688      
37689     
37690     
37691     beforeSlide : function(){
37692         this.grid.getView().scroller.clip();
37693     },
37694     
37695     afterSlide : function(){
37696         this.grid.getView().scroller.unclip();
37697     },
37698     
37699     destroy : function(){
37700         this.grid.destroy();
37701         delete this.grid;
37702         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37703     }
37704 });
37705
37706 /**
37707  * @class Roo.bootstrap.panel.Nest
37708  * @extends Roo.bootstrap.panel.Content
37709  * @constructor
37710  * Create a new Panel, that can contain a layout.Border.
37711  * 
37712  * 
37713  * @param {Roo.BorderLayout} layout The layout for this panel
37714  * @param {String/Object} config A string to set only the title or a config object
37715  */
37716 Roo.bootstrap.panel.Nest = function(config)
37717 {
37718     // construct with only one argument..
37719     /* FIXME - implement nicer consturctors
37720     if (layout.layout) {
37721         config = layout;
37722         layout = config.layout;
37723         delete config.layout;
37724     }
37725     if (layout.xtype && !layout.getEl) {
37726         // then layout needs constructing..
37727         layout = Roo.factory(layout, Roo);
37728     }
37729     */
37730     
37731     config.el =  config.layout.getEl();
37732     
37733     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37734     
37735     config.layout.monitorWindowResize = false; // turn off autosizing
37736     this.layout = config.layout;
37737     this.layout.getEl().addClass("roo-layout-nested-layout");
37738     
37739     
37740     
37741     
37742 };
37743
37744 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37745
37746     setSize : function(width, height){
37747         if(!this.ignoreResize(width, height)){
37748             var size = this.adjustForComponents(width, height);
37749             var el = this.layout.getEl();
37750             if (size.height < 1) {
37751                 el.setWidth(size.width);   
37752             } else {
37753                 el.setSize(size.width, size.height);
37754             }
37755             var touch = el.dom.offsetWidth;
37756             this.layout.layout();
37757             // ie requires a double layout on the first pass
37758             if(Roo.isIE && !this.initialized){
37759                 this.initialized = true;
37760                 this.layout.layout();
37761             }
37762         }
37763     },
37764     
37765     // activate all subpanels if not currently active..
37766     
37767     setActiveState : function(active){
37768         this.active = active;
37769         this.setActiveClass(active);
37770         
37771         if(!active){
37772             this.fireEvent("deactivate", this);
37773             return;
37774         }
37775         
37776         this.fireEvent("activate", this);
37777         // not sure if this should happen before or after..
37778         if (!this.layout) {
37779             return; // should not happen..
37780         }
37781         var reg = false;
37782         for (var r in this.layout.regions) {
37783             reg = this.layout.getRegion(r);
37784             if (reg.getActivePanel()) {
37785                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37786                 reg.setActivePanel(reg.getActivePanel());
37787                 continue;
37788             }
37789             if (!reg.panels.length) {
37790                 continue;
37791             }
37792             reg.showPanel(reg.getPanel(0));
37793         }
37794         
37795         
37796         
37797         
37798     },
37799     
37800     /**
37801      * Returns the nested BorderLayout for this panel
37802      * @return {Roo.BorderLayout} 
37803      */
37804     getLayout : function(){
37805         return this.layout;
37806     },
37807     
37808      /**
37809      * Adds a xtype elements to the layout of the nested panel
37810      * <pre><code>
37811
37812 panel.addxtype({
37813        xtype : 'ContentPanel',
37814        region: 'west',
37815        items: [ .... ]
37816    }
37817 );
37818
37819 panel.addxtype({
37820         xtype : 'NestedLayoutPanel',
37821         region: 'west',
37822         layout: {
37823            center: { },
37824            west: { }   
37825         },
37826         items : [ ... list of content panels or nested layout panels.. ]
37827    }
37828 );
37829 </code></pre>
37830      * @param {Object} cfg Xtype definition of item to add.
37831      */
37832     addxtype : function(cfg) {
37833         return this.layout.addxtype(cfg);
37834     
37835     }
37836 });        /*
37837  * Based on:
37838  * Ext JS Library 1.1.1
37839  * Copyright(c) 2006-2007, Ext JS, LLC.
37840  *
37841  * Originally Released Under LGPL - original licence link has changed is not relivant.
37842  *
37843  * Fork - LGPL
37844  * <script type="text/javascript">
37845  */
37846 /**
37847  * @class Roo.TabPanel
37848  * @extends Roo.util.Observable
37849  * A lightweight tab container.
37850  * <br><br>
37851  * Usage:
37852  * <pre><code>
37853 // basic tabs 1, built from existing content
37854 var tabs = new Roo.TabPanel("tabs1");
37855 tabs.addTab("script", "View Script");
37856 tabs.addTab("markup", "View Markup");
37857 tabs.activate("script");
37858
37859 // more advanced tabs, built from javascript
37860 var jtabs = new Roo.TabPanel("jtabs");
37861 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37862
37863 // set up the UpdateManager
37864 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37865 var updater = tab2.getUpdateManager();
37866 updater.setDefaultUrl("ajax1.htm");
37867 tab2.on('activate', updater.refresh, updater, true);
37868
37869 // Use setUrl for Ajax loading
37870 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37871 tab3.setUrl("ajax2.htm", null, true);
37872
37873 // Disabled tab
37874 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37875 tab4.disable();
37876
37877 jtabs.activate("jtabs-1");
37878  * </code></pre>
37879  * @constructor
37880  * Create a new TabPanel.
37881  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37882  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37883  */
37884 Roo.bootstrap.panel.Tabs = function(config){
37885     /**
37886     * The container element for this TabPanel.
37887     * @type Roo.Element
37888     */
37889     this.el = Roo.get(config.el);
37890     delete config.el;
37891     if(config){
37892         if(typeof config == "boolean"){
37893             this.tabPosition = config ? "bottom" : "top";
37894         }else{
37895             Roo.apply(this, config);
37896         }
37897     }
37898     
37899     if(this.tabPosition == "bottom"){
37900         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37901         this.el.addClass("roo-tabs-bottom");
37902     }
37903     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37904     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37905     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37906     if(Roo.isIE){
37907         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37908     }
37909     if(this.tabPosition != "bottom"){
37910         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37911          * @type Roo.Element
37912          */
37913         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37914         this.el.addClass("roo-tabs-top");
37915     }
37916     this.items = [];
37917
37918     this.bodyEl.setStyle("position", "relative");
37919
37920     this.active = null;
37921     this.activateDelegate = this.activate.createDelegate(this);
37922
37923     this.addEvents({
37924         /**
37925          * @event tabchange
37926          * Fires when the active tab changes
37927          * @param {Roo.TabPanel} this
37928          * @param {Roo.TabPanelItem} activePanel The new active tab
37929          */
37930         "tabchange": true,
37931         /**
37932          * @event beforetabchange
37933          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37934          * @param {Roo.TabPanel} this
37935          * @param {Object} e Set cancel to true on this object to cancel the tab change
37936          * @param {Roo.TabPanelItem} tab The tab being changed to
37937          */
37938         "beforetabchange" : true
37939     });
37940
37941     Roo.EventManager.onWindowResize(this.onResize, this);
37942     this.cpad = this.el.getPadding("lr");
37943     this.hiddenCount = 0;
37944
37945
37946     // toolbar on the tabbar support...
37947     if (this.toolbar) {
37948         alert("no toolbar support yet");
37949         this.toolbar  = false;
37950         /*
37951         var tcfg = this.toolbar;
37952         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37953         this.toolbar = new Roo.Toolbar(tcfg);
37954         if (Roo.isSafari) {
37955             var tbl = tcfg.container.child('table', true);
37956             tbl.setAttribute('width', '100%');
37957         }
37958         */
37959         
37960     }
37961    
37962
37963
37964     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37965 };
37966
37967 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37968     /*
37969      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37970      */
37971     tabPosition : "top",
37972     /*
37973      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37974      */
37975     currentTabWidth : 0,
37976     /*
37977      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37978      */
37979     minTabWidth : 40,
37980     /*
37981      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37982      */
37983     maxTabWidth : 250,
37984     /*
37985      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37986      */
37987     preferredTabWidth : 175,
37988     /*
37989      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37990      */
37991     resizeTabs : false,
37992     /*
37993      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37994      */
37995     monitorResize : true,
37996     /*
37997      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37998      */
37999     toolbar : false,
38000
38001     /**
38002      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38003      * @param {String} id The id of the div to use <b>or create</b>
38004      * @param {String} text The text for the tab
38005      * @param {String} content (optional) Content to put in the TabPanelItem body
38006      * @param {Boolean} closable (optional) True to create a close icon on the tab
38007      * @return {Roo.TabPanelItem} The created TabPanelItem
38008      */
38009     addTab : function(id, text, content, closable, tpl)
38010     {
38011         var item = new Roo.bootstrap.panel.TabItem({
38012             panel: this,
38013             id : id,
38014             text : text,
38015             closable : closable,
38016             tpl : tpl
38017         });
38018         this.addTabItem(item);
38019         if(content){
38020             item.setContent(content);
38021         }
38022         return item;
38023     },
38024
38025     /**
38026      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38027      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38028      * @return {Roo.TabPanelItem}
38029      */
38030     getTab : function(id){
38031         return this.items[id];
38032     },
38033
38034     /**
38035      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38036      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38037      */
38038     hideTab : function(id){
38039         var t = this.items[id];
38040         if(!t.isHidden()){
38041            t.setHidden(true);
38042            this.hiddenCount++;
38043            this.autoSizeTabs();
38044         }
38045     },
38046
38047     /**
38048      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38049      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38050      */
38051     unhideTab : function(id){
38052         var t = this.items[id];
38053         if(t.isHidden()){
38054            t.setHidden(false);
38055            this.hiddenCount--;
38056            this.autoSizeTabs();
38057         }
38058     },
38059
38060     /**
38061      * Adds an existing {@link Roo.TabPanelItem}.
38062      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38063      */
38064     addTabItem : function(item){
38065         this.items[item.id] = item;
38066         this.items.push(item);
38067       //  if(this.resizeTabs){
38068     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38069   //         this.autoSizeTabs();
38070 //        }else{
38071 //            item.autoSize();
38072        // }
38073     },
38074
38075     /**
38076      * Removes a {@link Roo.TabPanelItem}.
38077      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38078      */
38079     removeTab : function(id){
38080         var items = this.items;
38081         var tab = items[id];
38082         if(!tab) { return; }
38083         var index = items.indexOf(tab);
38084         if(this.active == tab && items.length > 1){
38085             var newTab = this.getNextAvailable(index);
38086             if(newTab) {
38087                 newTab.activate();
38088             }
38089         }
38090         this.stripEl.dom.removeChild(tab.pnode.dom);
38091         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38092             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38093         }
38094         items.splice(index, 1);
38095         delete this.items[tab.id];
38096         tab.fireEvent("close", tab);
38097         tab.purgeListeners();
38098         this.autoSizeTabs();
38099     },
38100
38101     getNextAvailable : function(start){
38102         var items = this.items;
38103         var index = start;
38104         // look for a next tab that will slide over to
38105         // replace the one being removed
38106         while(index < items.length){
38107             var item = items[++index];
38108             if(item && !item.isHidden()){
38109                 return item;
38110             }
38111         }
38112         // if one isn't found select the previous tab (on the left)
38113         index = start;
38114         while(index >= 0){
38115             var item = items[--index];
38116             if(item && !item.isHidden()){
38117                 return item;
38118             }
38119         }
38120         return null;
38121     },
38122
38123     /**
38124      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38125      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38126      */
38127     disableTab : function(id){
38128         var tab = this.items[id];
38129         if(tab && this.active != tab){
38130             tab.disable();
38131         }
38132     },
38133
38134     /**
38135      * Enables a {@link Roo.TabPanelItem} that is disabled.
38136      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38137      */
38138     enableTab : function(id){
38139         var tab = this.items[id];
38140         tab.enable();
38141     },
38142
38143     /**
38144      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38145      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38146      * @return {Roo.TabPanelItem} The TabPanelItem.
38147      */
38148     activate : function(id){
38149         var tab = this.items[id];
38150         if(!tab){
38151             return null;
38152         }
38153         if(tab == this.active || tab.disabled){
38154             return tab;
38155         }
38156         var e = {};
38157         this.fireEvent("beforetabchange", this, e, tab);
38158         if(e.cancel !== true && !tab.disabled){
38159             if(this.active){
38160                 this.active.hide();
38161             }
38162             this.active = this.items[id];
38163             this.active.show();
38164             this.fireEvent("tabchange", this, this.active);
38165         }
38166         return tab;
38167     },
38168
38169     /**
38170      * Gets the active {@link Roo.TabPanelItem}.
38171      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38172      */
38173     getActiveTab : function(){
38174         return this.active;
38175     },
38176
38177     /**
38178      * Updates the tab body element to fit the height of the container element
38179      * for overflow scrolling
38180      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38181      */
38182     syncHeight : function(targetHeight){
38183         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38184         var bm = this.bodyEl.getMargins();
38185         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38186         this.bodyEl.setHeight(newHeight);
38187         return newHeight;
38188     },
38189
38190     onResize : function(){
38191         if(this.monitorResize){
38192             this.autoSizeTabs();
38193         }
38194     },
38195
38196     /**
38197      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38198      */
38199     beginUpdate : function(){
38200         this.updating = true;
38201     },
38202
38203     /**
38204      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38205      */
38206     endUpdate : function(){
38207         this.updating = false;
38208         this.autoSizeTabs();
38209     },
38210
38211     /**
38212      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38213      */
38214     autoSizeTabs : function(){
38215         var count = this.items.length;
38216         var vcount = count - this.hiddenCount;
38217         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38218             return;
38219         }
38220         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38221         var availWidth = Math.floor(w / vcount);
38222         var b = this.stripBody;
38223         if(b.getWidth() > w){
38224             var tabs = this.items;
38225             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38226             if(availWidth < this.minTabWidth){
38227                 /*if(!this.sleft){    // incomplete scrolling code
38228                     this.createScrollButtons();
38229                 }
38230                 this.showScroll();
38231                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38232             }
38233         }else{
38234             if(this.currentTabWidth < this.preferredTabWidth){
38235                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38236             }
38237         }
38238     },
38239
38240     /**
38241      * Returns the number of tabs in this TabPanel.
38242      * @return {Number}
38243      */
38244      getCount : function(){
38245          return this.items.length;
38246      },
38247
38248     /**
38249      * Resizes all the tabs to the passed width
38250      * @param {Number} The new width
38251      */
38252     setTabWidth : function(width){
38253         this.currentTabWidth = width;
38254         for(var i = 0, len = this.items.length; i < len; i++) {
38255                 if(!this.items[i].isHidden()) {
38256                 this.items[i].setWidth(width);
38257             }
38258         }
38259     },
38260
38261     /**
38262      * Destroys this TabPanel
38263      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38264      */
38265     destroy : function(removeEl){
38266         Roo.EventManager.removeResizeListener(this.onResize, this);
38267         for(var i = 0, len = this.items.length; i < len; i++){
38268             this.items[i].purgeListeners();
38269         }
38270         if(removeEl === true){
38271             this.el.update("");
38272             this.el.remove();
38273         }
38274     },
38275     
38276     createStrip : function(container)
38277     {
38278         var strip = document.createElement("nav");
38279         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38280         container.appendChild(strip);
38281         return strip;
38282     },
38283     
38284     createStripList : function(strip)
38285     {
38286         // div wrapper for retard IE
38287         // returns the "tr" element.
38288         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38289         //'<div class="x-tabs-strip-wrap">'+
38290           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38291           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38292         return strip.firstChild; //.firstChild.firstChild.firstChild;
38293     },
38294     createBody : function(container)
38295     {
38296         var body = document.createElement("div");
38297         Roo.id(body, "tab-body");
38298         //Roo.fly(body).addClass("x-tabs-body");
38299         Roo.fly(body).addClass("tab-content");
38300         container.appendChild(body);
38301         return body;
38302     },
38303     createItemBody :function(bodyEl, id){
38304         var body = Roo.getDom(id);
38305         if(!body){
38306             body = document.createElement("div");
38307             body.id = id;
38308         }
38309         //Roo.fly(body).addClass("x-tabs-item-body");
38310         Roo.fly(body).addClass("tab-pane");
38311          bodyEl.insertBefore(body, bodyEl.firstChild);
38312         return body;
38313     },
38314     /** @private */
38315     createStripElements :  function(stripEl, text, closable, tpl)
38316     {
38317         var td = document.createElement("li"); // was td..
38318         
38319         
38320         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38321         
38322         
38323         stripEl.appendChild(td);
38324         /*if(closable){
38325             td.className = "x-tabs-closable";
38326             if(!this.closeTpl){
38327                 this.closeTpl = new Roo.Template(
38328                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38329                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38330                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38331                 );
38332             }
38333             var el = this.closeTpl.overwrite(td, {"text": text});
38334             var close = el.getElementsByTagName("div")[0];
38335             var inner = el.getElementsByTagName("em")[0];
38336             return {"el": el, "close": close, "inner": inner};
38337         } else {
38338         */
38339         // not sure what this is..
38340 //            if(!this.tabTpl){
38341                 //this.tabTpl = new Roo.Template(
38342                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38343                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38344                 //);
38345 //                this.tabTpl = new Roo.Template(
38346 //                   '<a href="#">' +
38347 //                   '<span unselectable="on"' +
38348 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38349 //                            ' >{text}</span></a>'
38350 //                );
38351 //                
38352 //            }
38353
38354
38355             var template = tpl || this.tabTpl || false;
38356             
38357             if(!template){
38358                 
38359                 template = new Roo.Template(
38360                    '<a href="#">' +
38361                    '<span unselectable="on"' +
38362                             (this.disableTooltips ? '' : ' title="{text}"') +
38363                             ' >{text}</span></a>'
38364                 );
38365             }
38366             
38367             switch (typeof(template)) {
38368                 case 'object' :
38369                     break;
38370                 case 'string' :
38371                     template = new Roo.Template(template);
38372                     break;
38373                 default :
38374                     break;
38375             }
38376             
38377             var el = template.overwrite(td, {"text": text});
38378             
38379             var inner = el.getElementsByTagName("span")[0];
38380             
38381             return {"el": el, "inner": inner};
38382             
38383     }
38384         
38385     
38386 });
38387
38388 /**
38389  * @class Roo.TabPanelItem
38390  * @extends Roo.util.Observable
38391  * Represents an individual item (tab plus body) in a TabPanel.
38392  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38393  * @param {String} id The id of this TabPanelItem
38394  * @param {String} text The text for the tab of this TabPanelItem
38395  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38396  */
38397 Roo.bootstrap.panel.TabItem = function(config){
38398     /**
38399      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38400      * @type Roo.TabPanel
38401      */
38402     this.tabPanel = config.panel;
38403     /**
38404      * The id for this TabPanelItem
38405      * @type String
38406      */
38407     this.id = config.id;
38408     /** @private */
38409     this.disabled = false;
38410     /** @private */
38411     this.text = config.text;
38412     /** @private */
38413     this.loaded = false;
38414     this.closable = config.closable;
38415
38416     /**
38417      * The body element for this TabPanelItem.
38418      * @type Roo.Element
38419      */
38420     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38421     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38422     this.bodyEl.setStyle("display", "block");
38423     this.bodyEl.setStyle("zoom", "1");
38424     //this.hideAction();
38425
38426     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38427     /** @private */
38428     this.el = Roo.get(els.el);
38429     this.inner = Roo.get(els.inner, true);
38430     this.textEl = Roo.get(this.el.dom.firstChild, true);
38431     this.pnode = Roo.get(els.el.parentNode, true);
38432 //    this.el.on("mousedown", this.onTabMouseDown, this);
38433     this.el.on("click", this.onTabClick, this);
38434     /** @private */
38435     if(config.closable){
38436         var c = Roo.get(els.close, true);
38437         c.dom.title = this.closeText;
38438         c.addClassOnOver("close-over");
38439         c.on("click", this.closeClick, this);
38440      }
38441
38442     this.addEvents({
38443          /**
38444          * @event activate
38445          * Fires when this tab becomes the active tab.
38446          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38447          * @param {Roo.TabPanelItem} this
38448          */
38449         "activate": true,
38450         /**
38451          * @event beforeclose
38452          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38453          * @param {Roo.TabPanelItem} this
38454          * @param {Object} e Set cancel to true on this object to cancel the close.
38455          */
38456         "beforeclose": true,
38457         /**
38458          * @event close
38459          * Fires when this tab is closed.
38460          * @param {Roo.TabPanelItem} this
38461          */
38462          "close": true,
38463         /**
38464          * @event deactivate
38465          * Fires when this tab is no longer the active tab.
38466          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38467          * @param {Roo.TabPanelItem} this
38468          */
38469          "deactivate" : true
38470     });
38471     this.hidden = false;
38472
38473     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38474 };
38475
38476 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38477            {
38478     purgeListeners : function(){
38479        Roo.util.Observable.prototype.purgeListeners.call(this);
38480        this.el.removeAllListeners();
38481     },
38482     /**
38483      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38484      */
38485     show : function(){
38486         this.pnode.addClass("active");
38487         this.showAction();
38488         if(Roo.isOpera){
38489             this.tabPanel.stripWrap.repaint();
38490         }
38491         this.fireEvent("activate", this.tabPanel, this);
38492     },
38493
38494     /**
38495      * Returns true if this tab is the active tab.
38496      * @return {Boolean}
38497      */
38498     isActive : function(){
38499         return this.tabPanel.getActiveTab() == this;
38500     },
38501
38502     /**
38503      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38504      */
38505     hide : function(){
38506         this.pnode.removeClass("active");
38507         this.hideAction();
38508         this.fireEvent("deactivate", this.tabPanel, this);
38509     },
38510
38511     hideAction : function(){
38512         this.bodyEl.hide();
38513         this.bodyEl.setStyle("position", "absolute");
38514         this.bodyEl.setLeft("-20000px");
38515         this.bodyEl.setTop("-20000px");
38516     },
38517
38518     showAction : function(){
38519         this.bodyEl.setStyle("position", "relative");
38520         this.bodyEl.setTop("");
38521         this.bodyEl.setLeft("");
38522         this.bodyEl.show();
38523     },
38524
38525     /**
38526      * Set the tooltip for the tab.
38527      * @param {String} tooltip The tab's tooltip
38528      */
38529     setTooltip : function(text){
38530         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38531             this.textEl.dom.qtip = text;
38532             this.textEl.dom.removeAttribute('title');
38533         }else{
38534             this.textEl.dom.title = text;
38535         }
38536     },
38537
38538     onTabClick : function(e){
38539         e.preventDefault();
38540         this.tabPanel.activate(this.id);
38541     },
38542
38543     onTabMouseDown : function(e){
38544         e.preventDefault();
38545         this.tabPanel.activate(this.id);
38546     },
38547 /*
38548     getWidth : function(){
38549         return this.inner.getWidth();
38550     },
38551
38552     setWidth : function(width){
38553         var iwidth = width - this.pnode.getPadding("lr");
38554         this.inner.setWidth(iwidth);
38555         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38556         this.pnode.setWidth(width);
38557     },
38558 */
38559     /**
38560      * Show or hide the tab
38561      * @param {Boolean} hidden True to hide or false to show.
38562      */
38563     setHidden : function(hidden){
38564         this.hidden = hidden;
38565         this.pnode.setStyle("display", hidden ? "none" : "");
38566     },
38567
38568     /**
38569      * Returns true if this tab is "hidden"
38570      * @return {Boolean}
38571      */
38572     isHidden : function(){
38573         return this.hidden;
38574     },
38575
38576     /**
38577      * Returns the text for this tab
38578      * @return {String}
38579      */
38580     getText : function(){
38581         return this.text;
38582     },
38583     /*
38584     autoSize : function(){
38585         //this.el.beginMeasure();
38586         this.textEl.setWidth(1);
38587         /*
38588          *  #2804 [new] Tabs in Roojs
38589          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38590          */
38591         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38592         //this.el.endMeasure();
38593     //},
38594
38595     /**
38596      * Sets the text for the tab (Note: this also sets the tooltip text)
38597      * @param {String} text The tab's text and tooltip
38598      */
38599     setText : function(text){
38600         this.text = text;
38601         this.textEl.update(text);
38602         this.setTooltip(text);
38603         //if(!this.tabPanel.resizeTabs){
38604         //    this.autoSize();
38605         //}
38606     },
38607     /**
38608      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38609      */
38610     activate : function(){
38611         this.tabPanel.activate(this.id);
38612     },
38613
38614     /**
38615      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38616      */
38617     disable : function(){
38618         if(this.tabPanel.active != this){
38619             this.disabled = true;
38620             this.pnode.addClass("disabled");
38621         }
38622     },
38623
38624     /**
38625      * Enables this TabPanelItem if it was previously disabled.
38626      */
38627     enable : function(){
38628         this.disabled = false;
38629         this.pnode.removeClass("disabled");
38630     },
38631
38632     /**
38633      * Sets the content for this TabPanelItem.
38634      * @param {String} content The content
38635      * @param {Boolean} loadScripts true to look for and load scripts
38636      */
38637     setContent : function(content, loadScripts){
38638         this.bodyEl.update(content, loadScripts);
38639     },
38640
38641     /**
38642      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38643      * @return {Roo.UpdateManager} The UpdateManager
38644      */
38645     getUpdateManager : function(){
38646         return this.bodyEl.getUpdateManager();
38647     },
38648
38649     /**
38650      * Set a URL to be used to load the content for this TabPanelItem.
38651      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38652      * @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)
38653      * @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)
38654      * @return {Roo.UpdateManager} The UpdateManager
38655      */
38656     setUrl : function(url, params, loadOnce){
38657         if(this.refreshDelegate){
38658             this.un('activate', this.refreshDelegate);
38659         }
38660         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38661         this.on("activate", this.refreshDelegate);
38662         return this.bodyEl.getUpdateManager();
38663     },
38664
38665     /** @private */
38666     _handleRefresh : function(url, params, loadOnce){
38667         if(!loadOnce || !this.loaded){
38668             var updater = this.bodyEl.getUpdateManager();
38669             updater.update(url, params, this._setLoaded.createDelegate(this));
38670         }
38671     },
38672
38673     /**
38674      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38675      *   Will fail silently if the setUrl method has not been called.
38676      *   This does not activate the panel, just updates its content.
38677      */
38678     refresh : function(){
38679         if(this.refreshDelegate){
38680            this.loaded = false;
38681            this.refreshDelegate();
38682         }
38683     },
38684
38685     /** @private */
38686     _setLoaded : function(){
38687         this.loaded = true;
38688     },
38689
38690     /** @private */
38691     closeClick : function(e){
38692         var o = {};
38693         e.stopEvent();
38694         this.fireEvent("beforeclose", this, o);
38695         if(o.cancel !== true){
38696             this.tabPanel.removeTab(this.id);
38697         }
38698     },
38699     /**
38700      * The text displayed in the tooltip for the close icon.
38701      * @type String
38702      */
38703     closeText : "Close this tab"
38704 });
38705 /**
38706 *    This script refer to:
38707 *    Title: International Telephone Input
38708 *    Author: Jack O'Connor
38709 *    Code version:  v12.1.12
38710 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38711 **/
38712
38713 Roo.bootstrap.PhoneInputData = function() {
38714     var d = [
38715       [
38716         "Afghanistan (‫افغانستان‬‎)",
38717         "af",
38718         "93"
38719       ],
38720       [
38721         "Albania (Shqipëri)",
38722         "al",
38723         "355"
38724       ],
38725       [
38726         "Algeria (‫الجزائر‬‎)",
38727         "dz",
38728         "213"
38729       ],
38730       [
38731         "American Samoa",
38732         "as",
38733         "1684"
38734       ],
38735       [
38736         "Andorra",
38737         "ad",
38738         "376"
38739       ],
38740       [
38741         "Angola",
38742         "ao",
38743         "244"
38744       ],
38745       [
38746         "Anguilla",
38747         "ai",
38748         "1264"
38749       ],
38750       [
38751         "Antigua and Barbuda",
38752         "ag",
38753         "1268"
38754       ],
38755       [
38756         "Argentina",
38757         "ar",
38758         "54"
38759       ],
38760       [
38761         "Armenia (Հայաստան)",
38762         "am",
38763         "374"
38764       ],
38765       [
38766         "Aruba",
38767         "aw",
38768         "297"
38769       ],
38770       [
38771         "Australia",
38772         "au",
38773         "61",
38774         0
38775       ],
38776       [
38777         "Austria (Österreich)",
38778         "at",
38779         "43"
38780       ],
38781       [
38782         "Azerbaijan (Azərbaycan)",
38783         "az",
38784         "994"
38785       ],
38786       [
38787         "Bahamas",
38788         "bs",
38789         "1242"
38790       ],
38791       [
38792         "Bahrain (‫البحرين‬‎)",
38793         "bh",
38794         "973"
38795       ],
38796       [
38797         "Bangladesh (বাংলাদেশ)",
38798         "bd",
38799         "880"
38800       ],
38801       [
38802         "Barbados",
38803         "bb",
38804         "1246"
38805       ],
38806       [
38807         "Belarus (Беларусь)",
38808         "by",
38809         "375"
38810       ],
38811       [
38812         "Belgium (België)",
38813         "be",
38814         "32"
38815       ],
38816       [
38817         "Belize",
38818         "bz",
38819         "501"
38820       ],
38821       [
38822         "Benin (Bénin)",
38823         "bj",
38824         "229"
38825       ],
38826       [
38827         "Bermuda",
38828         "bm",
38829         "1441"
38830       ],
38831       [
38832         "Bhutan (འབྲུག)",
38833         "bt",
38834         "975"
38835       ],
38836       [
38837         "Bolivia",
38838         "bo",
38839         "591"
38840       ],
38841       [
38842         "Bosnia and Herzegovina (Босна и Херцеговина)",
38843         "ba",
38844         "387"
38845       ],
38846       [
38847         "Botswana",
38848         "bw",
38849         "267"
38850       ],
38851       [
38852         "Brazil (Brasil)",
38853         "br",
38854         "55"
38855       ],
38856       [
38857         "British Indian Ocean Territory",
38858         "io",
38859         "246"
38860       ],
38861       [
38862         "British Virgin Islands",
38863         "vg",
38864         "1284"
38865       ],
38866       [
38867         "Brunei",
38868         "bn",
38869         "673"
38870       ],
38871       [
38872         "Bulgaria (България)",
38873         "bg",
38874         "359"
38875       ],
38876       [
38877         "Burkina Faso",
38878         "bf",
38879         "226"
38880       ],
38881       [
38882         "Burundi (Uburundi)",
38883         "bi",
38884         "257"
38885       ],
38886       [
38887         "Cambodia (កម្ពុជា)",
38888         "kh",
38889         "855"
38890       ],
38891       [
38892         "Cameroon (Cameroun)",
38893         "cm",
38894         "237"
38895       ],
38896       [
38897         "Canada",
38898         "ca",
38899         "1",
38900         1,
38901         ["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"]
38902       ],
38903       [
38904         "Cape Verde (Kabu Verdi)",
38905         "cv",
38906         "238"
38907       ],
38908       [
38909         "Caribbean Netherlands",
38910         "bq",
38911         "599",
38912         1
38913       ],
38914       [
38915         "Cayman Islands",
38916         "ky",
38917         "1345"
38918       ],
38919       [
38920         "Central African Republic (République centrafricaine)",
38921         "cf",
38922         "236"
38923       ],
38924       [
38925         "Chad (Tchad)",
38926         "td",
38927         "235"
38928       ],
38929       [
38930         "Chile",
38931         "cl",
38932         "56"
38933       ],
38934       [
38935         "China (中国)",
38936         "cn",
38937         "86"
38938       ],
38939       [
38940         "Christmas Island",
38941         "cx",
38942         "61",
38943         2
38944       ],
38945       [
38946         "Cocos (Keeling) Islands",
38947         "cc",
38948         "61",
38949         1
38950       ],
38951       [
38952         "Colombia",
38953         "co",
38954         "57"
38955       ],
38956       [
38957         "Comoros (‫جزر القمر‬‎)",
38958         "km",
38959         "269"
38960       ],
38961       [
38962         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38963         "cd",
38964         "243"
38965       ],
38966       [
38967         "Congo (Republic) (Congo-Brazzaville)",
38968         "cg",
38969         "242"
38970       ],
38971       [
38972         "Cook Islands",
38973         "ck",
38974         "682"
38975       ],
38976       [
38977         "Costa Rica",
38978         "cr",
38979         "506"
38980       ],
38981       [
38982         "Côte d’Ivoire",
38983         "ci",
38984         "225"
38985       ],
38986       [
38987         "Croatia (Hrvatska)",
38988         "hr",
38989         "385"
38990       ],
38991       [
38992         "Cuba",
38993         "cu",
38994         "53"
38995       ],
38996       [
38997         "Curaçao",
38998         "cw",
38999         "599",
39000         0
39001       ],
39002       [
39003         "Cyprus (Κύπρος)",
39004         "cy",
39005         "357"
39006       ],
39007       [
39008         "Czech Republic (Česká republika)",
39009         "cz",
39010         "420"
39011       ],
39012       [
39013         "Denmark (Danmark)",
39014         "dk",
39015         "45"
39016       ],
39017       [
39018         "Djibouti",
39019         "dj",
39020         "253"
39021       ],
39022       [
39023         "Dominica",
39024         "dm",
39025         "1767"
39026       ],
39027       [
39028         "Dominican Republic (República Dominicana)",
39029         "do",
39030         "1",
39031         2,
39032         ["809", "829", "849"]
39033       ],
39034       [
39035         "Ecuador",
39036         "ec",
39037         "593"
39038       ],
39039       [
39040         "Egypt (‫مصر‬‎)",
39041         "eg",
39042         "20"
39043       ],
39044       [
39045         "El Salvador",
39046         "sv",
39047         "503"
39048       ],
39049       [
39050         "Equatorial Guinea (Guinea Ecuatorial)",
39051         "gq",
39052         "240"
39053       ],
39054       [
39055         "Eritrea",
39056         "er",
39057         "291"
39058       ],
39059       [
39060         "Estonia (Eesti)",
39061         "ee",
39062         "372"
39063       ],
39064       [
39065         "Ethiopia",
39066         "et",
39067         "251"
39068       ],
39069       [
39070         "Falkland Islands (Islas Malvinas)",
39071         "fk",
39072         "500"
39073       ],
39074       [
39075         "Faroe Islands (Føroyar)",
39076         "fo",
39077         "298"
39078       ],
39079       [
39080         "Fiji",
39081         "fj",
39082         "679"
39083       ],
39084       [
39085         "Finland (Suomi)",
39086         "fi",
39087         "358",
39088         0
39089       ],
39090       [
39091         "France",
39092         "fr",
39093         "33"
39094       ],
39095       [
39096         "French Guiana (Guyane française)",
39097         "gf",
39098         "594"
39099       ],
39100       [
39101         "French Polynesia (Polynésie française)",
39102         "pf",
39103         "689"
39104       ],
39105       [
39106         "Gabon",
39107         "ga",
39108         "241"
39109       ],
39110       [
39111         "Gambia",
39112         "gm",
39113         "220"
39114       ],
39115       [
39116         "Georgia (საქართველო)",
39117         "ge",
39118         "995"
39119       ],
39120       [
39121         "Germany (Deutschland)",
39122         "de",
39123         "49"
39124       ],
39125       [
39126         "Ghana (Gaana)",
39127         "gh",
39128         "233"
39129       ],
39130       [
39131         "Gibraltar",
39132         "gi",
39133         "350"
39134       ],
39135       [
39136         "Greece (Ελλάδα)",
39137         "gr",
39138         "30"
39139       ],
39140       [
39141         "Greenland (Kalaallit Nunaat)",
39142         "gl",
39143         "299"
39144       ],
39145       [
39146         "Grenada",
39147         "gd",
39148         "1473"
39149       ],
39150       [
39151         "Guadeloupe",
39152         "gp",
39153         "590",
39154         0
39155       ],
39156       [
39157         "Guam",
39158         "gu",
39159         "1671"
39160       ],
39161       [
39162         "Guatemala",
39163         "gt",
39164         "502"
39165       ],
39166       [
39167         "Guernsey",
39168         "gg",
39169         "44",
39170         1
39171       ],
39172       [
39173         "Guinea (Guinée)",
39174         "gn",
39175         "224"
39176       ],
39177       [
39178         "Guinea-Bissau (Guiné Bissau)",
39179         "gw",
39180         "245"
39181       ],
39182       [
39183         "Guyana",
39184         "gy",
39185         "592"
39186       ],
39187       [
39188         "Haiti",
39189         "ht",
39190         "509"
39191       ],
39192       [
39193         "Honduras",
39194         "hn",
39195         "504"
39196       ],
39197       [
39198         "Hong Kong (香港)",
39199         "hk",
39200         "852"
39201       ],
39202       [
39203         "Hungary (Magyarország)",
39204         "hu",
39205         "36"
39206       ],
39207       [
39208         "Iceland (Ísland)",
39209         "is",
39210         "354"
39211       ],
39212       [
39213         "India (भारत)",
39214         "in",
39215         "91"
39216       ],
39217       [
39218         "Indonesia",
39219         "id",
39220         "62"
39221       ],
39222       [
39223         "Iran (‫ایران‬‎)",
39224         "ir",
39225         "98"
39226       ],
39227       [
39228         "Iraq (‫العراق‬‎)",
39229         "iq",
39230         "964"
39231       ],
39232       [
39233         "Ireland",
39234         "ie",
39235         "353"
39236       ],
39237       [
39238         "Isle of Man",
39239         "im",
39240         "44",
39241         2
39242       ],
39243       [
39244         "Israel (‫ישראל‬‎)",
39245         "il",
39246         "972"
39247       ],
39248       [
39249         "Italy (Italia)",
39250         "it",
39251         "39",
39252         0
39253       ],
39254       [
39255         "Jamaica",
39256         "jm",
39257         "1876"
39258       ],
39259       [
39260         "Japan (日本)",
39261         "jp",
39262         "81"
39263       ],
39264       [
39265         "Jersey",
39266         "je",
39267         "44",
39268         3
39269       ],
39270       [
39271         "Jordan (‫الأردن‬‎)",
39272         "jo",
39273         "962"
39274       ],
39275       [
39276         "Kazakhstan (Казахстан)",
39277         "kz",
39278         "7",
39279         1
39280       ],
39281       [
39282         "Kenya",
39283         "ke",
39284         "254"
39285       ],
39286       [
39287         "Kiribati",
39288         "ki",
39289         "686"
39290       ],
39291       [
39292         "Kosovo",
39293         "xk",
39294         "383"
39295       ],
39296       [
39297         "Kuwait (‫الكويت‬‎)",
39298         "kw",
39299         "965"
39300       ],
39301       [
39302         "Kyrgyzstan (Кыргызстан)",
39303         "kg",
39304         "996"
39305       ],
39306       [
39307         "Laos (ລາວ)",
39308         "la",
39309         "856"
39310       ],
39311       [
39312         "Latvia (Latvija)",
39313         "lv",
39314         "371"
39315       ],
39316       [
39317         "Lebanon (‫لبنان‬‎)",
39318         "lb",
39319         "961"
39320       ],
39321       [
39322         "Lesotho",
39323         "ls",
39324         "266"
39325       ],
39326       [
39327         "Liberia",
39328         "lr",
39329         "231"
39330       ],
39331       [
39332         "Libya (‫ليبيا‬‎)",
39333         "ly",
39334         "218"
39335       ],
39336       [
39337         "Liechtenstein",
39338         "li",
39339         "423"
39340       ],
39341       [
39342         "Lithuania (Lietuva)",
39343         "lt",
39344         "370"
39345       ],
39346       [
39347         "Luxembourg",
39348         "lu",
39349         "352"
39350       ],
39351       [
39352         "Macau (澳門)",
39353         "mo",
39354         "853"
39355       ],
39356       [
39357         "Macedonia (FYROM) (Македонија)",
39358         "mk",
39359         "389"
39360       ],
39361       [
39362         "Madagascar (Madagasikara)",
39363         "mg",
39364         "261"
39365       ],
39366       [
39367         "Malawi",
39368         "mw",
39369         "265"
39370       ],
39371       [
39372         "Malaysia",
39373         "my",
39374         "60"
39375       ],
39376       [
39377         "Maldives",
39378         "mv",
39379         "960"
39380       ],
39381       [
39382         "Mali",
39383         "ml",
39384         "223"
39385       ],
39386       [
39387         "Malta",
39388         "mt",
39389         "356"
39390       ],
39391       [
39392         "Marshall Islands",
39393         "mh",
39394         "692"
39395       ],
39396       [
39397         "Martinique",
39398         "mq",
39399         "596"
39400       ],
39401       [
39402         "Mauritania (‫موريتانيا‬‎)",
39403         "mr",
39404         "222"
39405       ],
39406       [
39407         "Mauritius (Moris)",
39408         "mu",
39409         "230"
39410       ],
39411       [
39412         "Mayotte",
39413         "yt",
39414         "262",
39415         1
39416       ],
39417       [
39418         "Mexico (México)",
39419         "mx",
39420         "52"
39421       ],
39422       [
39423         "Micronesia",
39424         "fm",
39425         "691"
39426       ],
39427       [
39428         "Moldova (Republica Moldova)",
39429         "md",
39430         "373"
39431       ],
39432       [
39433         "Monaco",
39434         "mc",
39435         "377"
39436       ],
39437       [
39438         "Mongolia (Монгол)",
39439         "mn",
39440         "976"
39441       ],
39442       [
39443         "Montenegro (Crna Gora)",
39444         "me",
39445         "382"
39446       ],
39447       [
39448         "Montserrat",
39449         "ms",
39450         "1664"
39451       ],
39452       [
39453         "Morocco (‫المغرب‬‎)",
39454         "ma",
39455         "212",
39456         0
39457       ],
39458       [
39459         "Mozambique (Moçambique)",
39460         "mz",
39461         "258"
39462       ],
39463       [
39464         "Myanmar (Burma) (မြန်မာ)",
39465         "mm",
39466         "95"
39467       ],
39468       [
39469         "Namibia (Namibië)",
39470         "na",
39471         "264"
39472       ],
39473       [
39474         "Nauru",
39475         "nr",
39476         "674"
39477       ],
39478       [
39479         "Nepal (नेपाल)",
39480         "np",
39481         "977"
39482       ],
39483       [
39484         "Netherlands (Nederland)",
39485         "nl",
39486         "31"
39487       ],
39488       [
39489         "New Caledonia (Nouvelle-Calédonie)",
39490         "nc",
39491         "687"
39492       ],
39493       [
39494         "New Zealand",
39495         "nz",
39496         "64"
39497       ],
39498       [
39499         "Nicaragua",
39500         "ni",
39501         "505"
39502       ],
39503       [
39504         "Niger (Nijar)",
39505         "ne",
39506         "227"
39507       ],
39508       [
39509         "Nigeria",
39510         "ng",
39511         "234"
39512       ],
39513       [
39514         "Niue",
39515         "nu",
39516         "683"
39517       ],
39518       [
39519         "Norfolk Island",
39520         "nf",
39521         "672"
39522       ],
39523       [
39524         "North Korea (조선 민주주의 인민 공화국)",
39525         "kp",
39526         "850"
39527       ],
39528       [
39529         "Northern Mariana Islands",
39530         "mp",
39531         "1670"
39532       ],
39533       [
39534         "Norway (Norge)",
39535         "no",
39536         "47",
39537         0
39538       ],
39539       [
39540         "Oman (‫عُمان‬‎)",
39541         "om",
39542         "968"
39543       ],
39544       [
39545         "Pakistan (‫پاکستان‬‎)",
39546         "pk",
39547         "92"
39548       ],
39549       [
39550         "Palau",
39551         "pw",
39552         "680"
39553       ],
39554       [
39555         "Palestine (‫فلسطين‬‎)",
39556         "ps",
39557         "970"
39558       ],
39559       [
39560         "Panama (Panamá)",
39561         "pa",
39562         "507"
39563       ],
39564       [
39565         "Papua New Guinea",
39566         "pg",
39567         "675"
39568       ],
39569       [
39570         "Paraguay",
39571         "py",
39572         "595"
39573       ],
39574       [
39575         "Peru (Perú)",
39576         "pe",
39577         "51"
39578       ],
39579       [
39580         "Philippines",
39581         "ph",
39582         "63"
39583       ],
39584       [
39585         "Poland (Polska)",
39586         "pl",
39587         "48"
39588       ],
39589       [
39590         "Portugal",
39591         "pt",
39592         "351"
39593       ],
39594       [
39595         "Puerto Rico",
39596         "pr",
39597         "1",
39598         3,
39599         ["787", "939"]
39600       ],
39601       [
39602         "Qatar (‫قطر‬‎)",
39603         "qa",
39604         "974"
39605       ],
39606       [
39607         "Réunion (La Réunion)",
39608         "re",
39609         "262",
39610         0
39611       ],
39612       [
39613         "Romania (România)",
39614         "ro",
39615         "40"
39616       ],
39617       [
39618         "Russia (Россия)",
39619         "ru",
39620         "7",
39621         0
39622       ],
39623       [
39624         "Rwanda",
39625         "rw",
39626         "250"
39627       ],
39628       [
39629         "Saint Barthélemy",
39630         "bl",
39631         "590",
39632         1
39633       ],
39634       [
39635         "Saint Helena",
39636         "sh",
39637         "290"
39638       ],
39639       [
39640         "Saint Kitts and Nevis",
39641         "kn",
39642         "1869"
39643       ],
39644       [
39645         "Saint Lucia",
39646         "lc",
39647         "1758"
39648       ],
39649       [
39650         "Saint Martin (Saint-Martin (partie française))",
39651         "mf",
39652         "590",
39653         2
39654       ],
39655       [
39656         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39657         "pm",
39658         "508"
39659       ],
39660       [
39661         "Saint Vincent and the Grenadines",
39662         "vc",
39663         "1784"
39664       ],
39665       [
39666         "Samoa",
39667         "ws",
39668         "685"
39669       ],
39670       [
39671         "San Marino",
39672         "sm",
39673         "378"
39674       ],
39675       [
39676         "São Tomé and Príncipe (São Tomé e Príncipe)",
39677         "st",
39678         "239"
39679       ],
39680       [
39681         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39682         "sa",
39683         "966"
39684       ],
39685       [
39686         "Senegal (Sénégal)",
39687         "sn",
39688         "221"
39689       ],
39690       [
39691         "Serbia (Србија)",
39692         "rs",
39693         "381"
39694       ],
39695       [
39696         "Seychelles",
39697         "sc",
39698         "248"
39699       ],
39700       [
39701         "Sierra Leone",
39702         "sl",
39703         "232"
39704       ],
39705       [
39706         "Singapore",
39707         "sg",
39708         "65"
39709       ],
39710       [
39711         "Sint Maarten",
39712         "sx",
39713         "1721"
39714       ],
39715       [
39716         "Slovakia (Slovensko)",
39717         "sk",
39718         "421"
39719       ],
39720       [
39721         "Slovenia (Slovenija)",
39722         "si",
39723         "386"
39724       ],
39725       [
39726         "Solomon Islands",
39727         "sb",
39728         "677"
39729       ],
39730       [
39731         "Somalia (Soomaaliya)",
39732         "so",
39733         "252"
39734       ],
39735       [
39736         "South Africa",
39737         "za",
39738         "27"
39739       ],
39740       [
39741         "South Korea (대한민국)",
39742         "kr",
39743         "82"
39744       ],
39745       [
39746         "South Sudan (‫جنوب السودان‬‎)",
39747         "ss",
39748         "211"
39749       ],
39750       [
39751         "Spain (España)",
39752         "es",
39753         "34"
39754       ],
39755       [
39756         "Sri Lanka (ශ්‍රී ලංකාව)",
39757         "lk",
39758         "94"
39759       ],
39760       [
39761         "Sudan (‫السودان‬‎)",
39762         "sd",
39763         "249"
39764       ],
39765       [
39766         "Suriname",
39767         "sr",
39768         "597"
39769       ],
39770       [
39771         "Svalbard and Jan Mayen",
39772         "sj",
39773         "47",
39774         1
39775       ],
39776       [
39777         "Swaziland",
39778         "sz",
39779         "268"
39780       ],
39781       [
39782         "Sweden (Sverige)",
39783         "se",
39784         "46"
39785       ],
39786       [
39787         "Switzerland (Schweiz)",
39788         "ch",
39789         "41"
39790       ],
39791       [
39792         "Syria (‫سوريا‬‎)",
39793         "sy",
39794         "963"
39795       ],
39796       [
39797         "Taiwan (台灣)",
39798         "tw",
39799         "886"
39800       ],
39801       [
39802         "Tajikistan",
39803         "tj",
39804         "992"
39805       ],
39806       [
39807         "Tanzania",
39808         "tz",
39809         "255"
39810       ],
39811       [
39812         "Thailand (ไทย)",
39813         "th",
39814         "66"
39815       ],
39816       [
39817         "Timor-Leste",
39818         "tl",
39819         "670"
39820       ],
39821       [
39822         "Togo",
39823         "tg",
39824         "228"
39825       ],
39826       [
39827         "Tokelau",
39828         "tk",
39829         "690"
39830       ],
39831       [
39832         "Tonga",
39833         "to",
39834         "676"
39835       ],
39836       [
39837         "Trinidad and Tobago",
39838         "tt",
39839         "1868"
39840       ],
39841       [
39842         "Tunisia (‫تونس‬‎)",
39843         "tn",
39844         "216"
39845       ],
39846       [
39847         "Turkey (Türkiye)",
39848         "tr",
39849         "90"
39850       ],
39851       [
39852         "Turkmenistan",
39853         "tm",
39854         "993"
39855       ],
39856       [
39857         "Turks and Caicos Islands",
39858         "tc",
39859         "1649"
39860       ],
39861       [
39862         "Tuvalu",
39863         "tv",
39864         "688"
39865       ],
39866       [
39867         "U.S. Virgin Islands",
39868         "vi",
39869         "1340"
39870       ],
39871       [
39872         "Uganda",
39873         "ug",
39874         "256"
39875       ],
39876       [
39877         "Ukraine (Україна)",
39878         "ua",
39879         "380"
39880       ],
39881       [
39882         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39883         "ae",
39884         "971"
39885       ],
39886       [
39887         "United Kingdom",
39888         "gb",
39889         "44",
39890         0
39891       ],
39892       [
39893         "United States",
39894         "us",
39895         "1",
39896         0
39897       ],
39898       [
39899         "Uruguay",
39900         "uy",
39901         "598"
39902       ],
39903       [
39904         "Uzbekistan (Oʻzbekiston)",
39905         "uz",
39906         "998"
39907       ],
39908       [
39909         "Vanuatu",
39910         "vu",
39911         "678"
39912       ],
39913       [
39914         "Vatican City (Città del Vaticano)",
39915         "va",
39916         "39",
39917         1
39918       ],
39919       [
39920         "Venezuela",
39921         "ve",
39922         "58"
39923       ],
39924       [
39925         "Vietnam (Việt Nam)",
39926         "vn",
39927         "84"
39928       ],
39929       [
39930         "Wallis and Futuna (Wallis-et-Futuna)",
39931         "wf",
39932         "681"
39933       ],
39934       [
39935         "Western Sahara (‫الصحراء الغربية‬‎)",
39936         "eh",
39937         "212",
39938         1
39939       ],
39940       [
39941         "Yemen (‫اليمن‬‎)",
39942         "ye",
39943         "967"
39944       ],
39945       [
39946         "Zambia",
39947         "zm",
39948         "260"
39949       ],
39950       [
39951         "Zimbabwe",
39952         "zw",
39953         "263"
39954       ],
39955       [
39956         "Åland Islands",
39957         "ax",
39958         "358",
39959         1
39960       ]
39961   ];
39962   
39963   return d;
39964 }/**
39965 *    This script refer to:
39966 *    Title: International Telephone Input
39967 *    Author: Jack O'Connor
39968 *    Code version:  v12.1.12
39969 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39970 **/
39971
39972 /**
39973  * @class Roo.bootstrap.PhoneInput
39974  * @extends Roo.bootstrap.TriggerField
39975  * An input with International dial-code selection
39976  
39977  * @cfg {String} defaultDialCode default '+852'
39978  * @cfg {Array} preferedCountries default []
39979   
39980  * @constructor
39981  * Create a new PhoneInput.
39982  * @param {Object} config Configuration options
39983  */
39984
39985 Roo.bootstrap.PhoneInput = function(config) {
39986     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39987 };
39988
39989 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39990         
39991         listWidth: undefined,
39992         
39993         selectedClass: 'active',
39994         
39995         invalidClass : "has-warning",
39996         
39997         validClass: 'has-success',
39998         
39999         allowed: '0123456789',
40000         
40001         max_length: 15,
40002         
40003         /**
40004          * @cfg {String} defaultDialCode The default dial code when initializing the input
40005          */
40006         defaultDialCode: '+852',
40007         
40008         /**
40009          * @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
40010          */
40011         preferedCountries: false,
40012         
40013         getAutoCreate : function()
40014         {
40015             var data = Roo.bootstrap.PhoneInputData();
40016             var align = this.labelAlign || this.parentLabelAlign();
40017             var id = Roo.id();
40018             
40019             this.allCountries = [];
40020             this.dialCodeMapping = [];
40021             
40022             for (var i = 0; i < data.length; i++) {
40023               var c = data[i];
40024               this.allCountries[i] = {
40025                 name: c[0],
40026                 iso2: c[1],
40027                 dialCode: c[2],
40028                 priority: c[3] || 0,
40029                 areaCodes: c[4] || null
40030               };
40031               this.dialCodeMapping[c[2]] = {
40032                   name: c[0],
40033                   iso2: c[1],
40034                   priority: c[3] || 0,
40035                   areaCodes: c[4] || null
40036               };
40037             }
40038             
40039             var cfg = {
40040                 cls: 'form-group',
40041                 cn: []
40042             };
40043             
40044             var input =  {
40045                 tag: 'input',
40046                 id : id,
40047                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40048                 maxlength: this.max_length,
40049                 cls : 'form-control tel-input',
40050                 autocomplete: 'new-password'
40051             };
40052             
40053             var hiddenInput = {
40054                 tag: 'input',
40055                 type: 'hidden',
40056                 cls: 'hidden-tel-input'
40057             };
40058             
40059             if (this.name) {
40060                 hiddenInput.name = this.name;
40061             }
40062             
40063             if (this.disabled) {
40064                 input.disabled = true;
40065             }
40066             
40067             var flag_container = {
40068                 tag: 'div',
40069                 cls: 'flag-box',
40070                 cn: [
40071                     {
40072                         tag: 'div',
40073                         cls: 'flag'
40074                     },
40075                     {
40076                         tag: 'div',
40077                         cls: 'caret'
40078                     }
40079                 ]
40080             };
40081             
40082             var box = {
40083                 tag: 'div',
40084                 cls: this.hasFeedback ? 'has-feedback' : '',
40085                 cn: [
40086                     hiddenInput,
40087                     input,
40088                     {
40089                         tag: 'input',
40090                         cls: 'dial-code-holder',
40091                         disabled: true
40092                     }
40093                 ]
40094             };
40095             
40096             var container = {
40097                 cls: 'roo-select2-container input-group',
40098                 cn: [
40099                     flag_container,
40100                     box
40101                 ]
40102             };
40103             
40104             if (this.fieldLabel.length) {
40105                 var indicator = {
40106                     tag: 'i',
40107                     tooltip: 'This field is required'
40108                 };
40109                 
40110                 var label = {
40111                     tag: 'label',
40112                     'for':  id,
40113                     cls: 'control-label',
40114                     cn: []
40115                 };
40116                 
40117                 var label_text = {
40118                     tag: 'span',
40119                     html: this.fieldLabel
40120                 };
40121                 
40122                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40123                 label.cn = [
40124                     indicator,
40125                     label_text
40126                 ];
40127                 
40128                 if(this.indicatorpos == 'right') {
40129                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40130                     label.cn = [
40131                         label_text,
40132                         indicator
40133                     ];
40134                 }
40135                 
40136                 if(align == 'left') {
40137                     container = {
40138                         tag: 'div',
40139                         cn: [
40140                             container
40141                         ]
40142                     };
40143                     
40144                     if(this.labelWidth > 12){
40145                         label.style = "width: " + this.labelWidth + 'px';
40146                     }
40147                     if(this.labelWidth < 13 && this.labelmd == 0){
40148                         this.labelmd = this.labelWidth;
40149                     }
40150                     if(this.labellg > 0){
40151                         label.cls += ' col-lg-' + this.labellg;
40152                         input.cls += ' col-lg-' + (12 - this.labellg);
40153                     }
40154                     if(this.labelmd > 0){
40155                         label.cls += ' col-md-' + this.labelmd;
40156                         container.cls += ' col-md-' + (12 - this.labelmd);
40157                     }
40158                     if(this.labelsm > 0){
40159                         label.cls += ' col-sm-' + this.labelsm;
40160                         container.cls += ' col-sm-' + (12 - this.labelsm);
40161                     }
40162                     if(this.labelxs > 0){
40163                         label.cls += ' col-xs-' + this.labelxs;
40164                         container.cls += ' col-xs-' + (12 - this.labelxs);
40165                     }
40166                 }
40167             }
40168             
40169             cfg.cn = [
40170                 label,
40171                 container
40172             ];
40173             
40174             var settings = this;
40175             
40176             ['xs','sm','md','lg'].map(function(size){
40177                 if (settings[size]) {
40178                     cfg.cls += ' col-' + size + '-' + settings[size];
40179                 }
40180             });
40181             
40182             this.store = new Roo.data.Store({
40183                 proxy : new Roo.data.MemoryProxy({}),
40184                 reader : new Roo.data.JsonReader({
40185                     fields : [
40186                         {
40187                             'name' : 'name',
40188                             'type' : 'string'
40189                         },
40190                         {
40191                             'name' : 'iso2',
40192                             'type' : 'string'
40193                         },
40194                         {
40195                             'name' : 'dialCode',
40196                             'type' : 'string'
40197                         },
40198                         {
40199                             'name' : 'priority',
40200                             'type' : 'string'
40201                         },
40202                         {
40203                             'name' : 'areaCodes',
40204                             'type' : 'string'
40205                         }
40206                     ]
40207                 })
40208             });
40209             
40210             if(!this.preferedCountries) {
40211                 this.preferedCountries = [
40212                     'hk',
40213                     'gb',
40214                     'us'
40215                 ];
40216             }
40217             
40218             var p = this.preferedCountries.reverse();
40219             
40220             if(p) {
40221                 for (var i = 0; i < p.length; i++) {
40222                     for (var j = 0; j < this.allCountries.length; j++) {
40223                         if(this.allCountries[j].iso2 == p[i]) {
40224                             var t = this.allCountries[j];
40225                             this.allCountries.splice(j,1);
40226                             this.allCountries.unshift(t);
40227                         }
40228                     } 
40229                 }
40230             }
40231             
40232             this.store.proxy.data = {
40233                 success: true,
40234                 data: this.allCountries
40235             };
40236             
40237             return cfg;
40238         },
40239         
40240         initEvents : function()
40241         {
40242             this.createList();
40243             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40244             
40245             this.indicator = this.indicatorEl();
40246             this.flag = this.flagEl();
40247             this.dialCodeHolder = this.dialCodeHolderEl();
40248             
40249             this.trigger = this.el.select('div.flag-box',true).first();
40250             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40251             
40252             var _this = this;
40253             
40254             (function(){
40255                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40256                 _this.list.setWidth(lw);
40257             }).defer(100);
40258             
40259             this.list.on('mouseover', this.onViewOver, this);
40260             this.list.on('mousemove', this.onViewMove, this);
40261             this.inputEl().on("keyup", this.onKeyUp, this);
40262             this.inputEl().on("keypress", this.onKeyPress, this);
40263             
40264             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40265
40266             this.view = new Roo.View(this.list, this.tpl, {
40267                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40268             });
40269             
40270             this.view.on('click', this.onViewClick, this);
40271             this.setValue(this.defaultDialCode);
40272         },
40273         
40274         onTriggerClick : function(e)
40275         {
40276             Roo.log('trigger click');
40277             if(this.disabled){
40278                 return;
40279             }
40280             
40281             if(this.isExpanded()){
40282                 this.collapse();
40283                 this.hasFocus = false;
40284             }else {
40285                 this.store.load({});
40286                 this.hasFocus = true;
40287                 this.expand();
40288             }
40289         },
40290         
40291         isExpanded : function()
40292         {
40293             return this.list.isVisible();
40294         },
40295         
40296         collapse : function()
40297         {
40298             if(!this.isExpanded()){
40299                 return;
40300             }
40301             this.list.hide();
40302             Roo.get(document).un('mousedown', this.collapseIf, this);
40303             Roo.get(document).un('mousewheel', this.collapseIf, this);
40304             this.fireEvent('collapse', this);
40305             this.validate();
40306         },
40307         
40308         expand : function()
40309         {
40310             Roo.log('expand');
40311
40312             if(this.isExpanded() || !this.hasFocus){
40313                 return;
40314             }
40315             
40316             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40317             this.list.setWidth(lw);
40318             
40319             this.list.show();
40320             this.restrictHeight();
40321             
40322             Roo.get(document).on('mousedown', this.collapseIf, this);
40323             Roo.get(document).on('mousewheel', this.collapseIf, this);
40324             
40325             this.fireEvent('expand', this);
40326         },
40327         
40328         restrictHeight : function()
40329         {
40330             this.list.alignTo(this.inputEl(), this.listAlign);
40331             this.list.alignTo(this.inputEl(), this.listAlign);
40332         },
40333         
40334         onViewOver : function(e, t)
40335         {
40336             if(this.inKeyMode){
40337                 return;
40338             }
40339             var item = this.view.findItemFromChild(t);
40340             
40341             if(item){
40342                 var index = this.view.indexOf(item);
40343                 this.select(index, false);
40344             }
40345         },
40346
40347         // private
40348         onViewClick : function(view, doFocus, el, e)
40349         {
40350             var index = this.view.getSelectedIndexes()[0];
40351             
40352             var r = this.store.getAt(index);
40353             
40354             if(r){
40355                 this.onSelect(r, index);
40356             }
40357             if(doFocus !== false && !this.blockFocus){
40358                 this.inputEl().focus();
40359             }
40360         },
40361         
40362         onViewMove : function(e, t)
40363         {
40364             this.inKeyMode = false;
40365         },
40366         
40367         select : function(index, scrollIntoView)
40368         {
40369             this.selectedIndex = index;
40370             this.view.select(index);
40371             if(scrollIntoView !== false){
40372                 var el = this.view.getNode(index);
40373                 if(el){
40374                     this.list.scrollChildIntoView(el, false);
40375                 }
40376             }
40377         },
40378         
40379         createList : function()
40380         {
40381             this.list = Roo.get(document.body).createChild({
40382                 tag: 'ul',
40383                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40384                 style: 'display:none'
40385             });
40386             
40387             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40388         },
40389         
40390         collapseIf : function(e)
40391         {
40392             var in_combo  = e.within(this.el);
40393             var in_list =  e.within(this.list);
40394             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40395             
40396             if (in_combo || in_list || is_list) {
40397                 return;
40398             }
40399             this.collapse();
40400         },
40401         
40402         onSelect : function(record, index)
40403         {
40404             if(this.fireEvent('beforeselect', this, record, index) !== false){
40405                 
40406                 this.setFlagClass(record.data.iso2);
40407                 this.setDialCode(record.data.dialCode);
40408                 this.hasFocus = false;
40409                 this.collapse();
40410                 this.fireEvent('select', this, record, index);
40411             }
40412         },
40413         
40414         flagEl : function()
40415         {
40416             var flag = this.el.select('div.flag',true).first();
40417             if(!flag){
40418                 return false;
40419             }
40420             return flag;
40421         },
40422         
40423         dialCodeHolderEl : function()
40424         {
40425             var d = this.el.select('input.dial-code-holder',true).first();
40426             if(!d){
40427                 return false;
40428             }
40429             return d;
40430         },
40431         
40432         setDialCode : function(v)
40433         {
40434             this.dialCodeHolder.dom.value = '+'+v;
40435         },
40436         
40437         setFlagClass : function(n)
40438         {
40439             this.flag.dom.className = 'flag '+n;
40440         },
40441         
40442         getValue : function()
40443         {
40444             var v = this.inputEl().getValue();
40445             if(this.dialCodeHolder) {
40446                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40447             }
40448             return v;
40449         },
40450         
40451         setValue : function(v)
40452         {
40453             var d = this.getDialCode(v);
40454             
40455             //invalid dial code
40456             if(v.length == 0 || !d || d.length == 0) {
40457                 if(this.rendered){
40458                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40459                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40460                 }
40461                 return;
40462             }
40463             
40464             //valid dial code
40465             this.setFlagClass(this.dialCodeMapping[d].iso2);
40466             this.setDialCode(d);
40467             this.inputEl().dom.value = v.replace('+'+d,'');
40468             this.hiddenEl().dom.value = this.getValue();
40469             
40470             this.validate();
40471         },
40472         
40473         getDialCode : function(v)
40474         {
40475             v = v ||  '';
40476             
40477             if (v.length == 0) {
40478                 return this.dialCodeHolder.dom.value;
40479             }
40480             
40481             var dialCode = "";
40482             if (v.charAt(0) != "+") {
40483                 return false;
40484             }
40485             var numericChars = "";
40486             for (var i = 1; i < v.length; i++) {
40487               var c = v.charAt(i);
40488               if (!isNaN(c)) {
40489                 numericChars += c;
40490                 if (this.dialCodeMapping[numericChars]) {
40491                   dialCode = v.substr(1, i);
40492                 }
40493                 if (numericChars.length == 4) {
40494                   break;
40495                 }
40496               }
40497             }
40498             return dialCode;
40499         },
40500         
40501         reset : function()
40502         {
40503             this.setValue(this.defaultDialCode);
40504             this.validate();
40505         },
40506         
40507         hiddenEl : function()
40508         {
40509             return this.el.select('input.hidden-tel-input',true).first();
40510         },
40511         
40512         // after setting val
40513         onKeyUp : function(e){
40514             this.setValue(this.getValue());
40515         },
40516         
40517         onKeyPress : function(e){
40518             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40519                 e.stopEvent();
40520             }
40521         }
40522         
40523 });
40524 /**
40525  * @class Roo.bootstrap.MoneyField
40526  * @extends Roo.bootstrap.ComboBox
40527  * Bootstrap MoneyField class
40528  * 
40529  * @constructor
40530  * Create a new MoneyField.
40531  * @param {Object} config Configuration options
40532  */
40533
40534 Roo.bootstrap.MoneyField = function(config) {
40535     
40536     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40537     
40538 };
40539
40540 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40541     
40542     /**
40543      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40544      */
40545     allowDecimals : true,
40546     /**
40547      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40548      */
40549     decimalSeparator : ".",
40550     /**
40551      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40552      */
40553     decimalPrecision : 0,
40554     /**
40555      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40556      */
40557     allowNegative : true,
40558     /**
40559      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40560      */
40561     allowZero: true,
40562     /**
40563      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40564      */
40565     minValue : Number.NEGATIVE_INFINITY,
40566     /**
40567      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40568      */
40569     maxValue : Number.MAX_VALUE,
40570     /**
40571      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40572      */
40573     minText : "The minimum value for this field is {0}",
40574     /**
40575      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40576      */
40577     maxText : "The maximum value for this field is {0}",
40578     /**
40579      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40580      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40581      */
40582     nanText : "{0} is not a valid number",
40583     /**
40584      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40585      */
40586     castInt : true,
40587     /**
40588      * @cfg {String} defaults currency of the MoneyField
40589      * value should be in lkey
40590      */
40591     defaultCurrency : false,
40592     /**
40593      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40594      */
40595     thousandsDelimiter : false,
40596     /**
40597      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40598      */
40599     max_length: false,
40600     
40601     inputlg : 9,
40602     inputmd : 9,
40603     inputsm : 9,
40604     inputxs : 6,
40605     
40606     store : false,
40607     
40608     getAutoCreate : function()
40609     {
40610         var align = this.labelAlign || this.parentLabelAlign();
40611         
40612         var id = Roo.id();
40613
40614         var cfg = {
40615             cls: 'form-group',
40616             cn: []
40617         };
40618
40619         var input =  {
40620             tag: 'input',
40621             id : id,
40622             cls : 'form-control roo-money-amount-input',
40623             autocomplete: 'new-password'
40624         };
40625         
40626         var hiddenInput = {
40627             tag: 'input',
40628             type: 'hidden',
40629             id: Roo.id(),
40630             cls: 'hidden-number-input'
40631         };
40632         
40633         if(this.max_length) {
40634             input.maxlength = this.max_length; 
40635         }
40636         
40637         if (this.name) {
40638             hiddenInput.name = this.name;
40639         }
40640
40641         if (this.disabled) {
40642             input.disabled = true;
40643         }
40644
40645         var clg = 12 - this.inputlg;
40646         var cmd = 12 - this.inputmd;
40647         var csm = 12 - this.inputsm;
40648         var cxs = 12 - this.inputxs;
40649         
40650         var container = {
40651             tag : 'div',
40652             cls : 'row roo-money-field',
40653             cn : [
40654                 {
40655                     tag : 'div',
40656                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40657                     cn : [
40658                         {
40659                             tag : 'div',
40660                             cls: 'roo-select2-container input-group',
40661                             cn: [
40662                                 {
40663                                     tag : 'input',
40664                                     cls : 'form-control roo-money-currency-input',
40665                                     autocomplete: 'new-password',
40666                                     readOnly : 1,
40667                                     name : this.currencyName
40668                                 },
40669                                 {
40670                                     tag :'span',
40671                                     cls : 'input-group-addon',
40672                                     cn : [
40673                                         {
40674                                             tag: 'span',
40675                                             cls: 'caret'
40676                                         }
40677                                     ]
40678                                 }
40679                             ]
40680                         }
40681                     ]
40682                 },
40683                 {
40684                     tag : 'div',
40685                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40686                     cn : [
40687                         {
40688                             tag: 'div',
40689                             cls: this.hasFeedback ? 'has-feedback' : '',
40690                             cn: [
40691                                 input
40692                             ]
40693                         }
40694                     ]
40695                 }
40696             ]
40697             
40698         };
40699         
40700         if (this.fieldLabel.length) {
40701             var indicator = {
40702                 tag: 'i',
40703                 tooltip: 'This field is required'
40704             };
40705
40706             var label = {
40707                 tag: 'label',
40708                 'for':  id,
40709                 cls: 'control-label',
40710                 cn: []
40711             };
40712
40713             var label_text = {
40714                 tag: 'span',
40715                 html: this.fieldLabel
40716             };
40717
40718             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40719             label.cn = [
40720                 indicator,
40721                 label_text
40722             ];
40723
40724             if(this.indicatorpos == 'right') {
40725                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40726                 label.cn = [
40727                     label_text,
40728                     indicator
40729                 ];
40730             }
40731
40732             if(align == 'left') {
40733                 container = {
40734                     tag: 'div',
40735                     cn: [
40736                         container
40737                     ]
40738                 };
40739
40740                 if(this.labelWidth > 12){
40741                     label.style = "width: " + this.labelWidth + 'px';
40742                 }
40743                 if(this.labelWidth < 13 && this.labelmd == 0){
40744                     this.labelmd = this.labelWidth;
40745                 }
40746                 if(this.labellg > 0){
40747                     label.cls += ' col-lg-' + this.labellg;
40748                     input.cls += ' col-lg-' + (12 - this.labellg);
40749                 }
40750                 if(this.labelmd > 0){
40751                     label.cls += ' col-md-' + this.labelmd;
40752                     container.cls += ' col-md-' + (12 - this.labelmd);
40753                 }
40754                 if(this.labelsm > 0){
40755                     label.cls += ' col-sm-' + this.labelsm;
40756                     container.cls += ' col-sm-' + (12 - this.labelsm);
40757                 }
40758                 if(this.labelxs > 0){
40759                     label.cls += ' col-xs-' + this.labelxs;
40760                     container.cls += ' col-xs-' + (12 - this.labelxs);
40761                 }
40762             }
40763         }
40764
40765         cfg.cn = [
40766             label,
40767             container,
40768             hiddenInput
40769         ];
40770         
40771         var settings = this;
40772
40773         ['xs','sm','md','lg'].map(function(size){
40774             if (settings[size]) {
40775                 cfg.cls += ' col-' + size + '-' + settings[size];
40776             }
40777         });
40778         
40779         return cfg;
40780     },
40781     
40782     initEvents : function()
40783     {
40784         this.indicator = this.indicatorEl();
40785         
40786         this.initCurrencyEvent();
40787         
40788         this.initNumberEvent();
40789     },
40790     
40791     initCurrencyEvent : function()
40792     {
40793         if (!this.store) {
40794             throw "can not find store for combo";
40795         }
40796         
40797         this.store = Roo.factory(this.store, Roo.data);
40798         this.store.parent = this;
40799         
40800         this.createList();
40801         
40802         this.triggerEl = this.el.select('.input-group-addon', true).first();
40803         
40804         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40805         
40806         var _this = this;
40807         
40808         (function(){
40809             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40810             _this.list.setWidth(lw);
40811         }).defer(100);
40812         
40813         this.list.on('mouseover', this.onViewOver, this);
40814         this.list.on('mousemove', this.onViewMove, this);
40815         this.list.on('scroll', this.onViewScroll, this);
40816         
40817         if(!this.tpl){
40818             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40819         }
40820         
40821         this.view = new Roo.View(this.list, this.tpl, {
40822             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40823         });
40824         
40825         this.view.on('click', this.onViewClick, this);
40826         
40827         this.store.on('beforeload', this.onBeforeLoad, this);
40828         this.store.on('load', this.onLoad, this);
40829         this.store.on('loadexception', this.onLoadException, this);
40830         
40831         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40832             "up" : function(e){
40833                 this.inKeyMode = true;
40834                 this.selectPrev();
40835             },
40836
40837             "down" : function(e){
40838                 if(!this.isExpanded()){
40839                     this.onTriggerClick();
40840                 }else{
40841                     this.inKeyMode = true;
40842                     this.selectNext();
40843                 }
40844             },
40845
40846             "enter" : function(e){
40847                 this.collapse();
40848                 
40849                 if(this.fireEvent("specialkey", this, e)){
40850                     this.onViewClick(false);
40851                 }
40852                 
40853                 return true;
40854             },
40855
40856             "esc" : function(e){
40857                 this.collapse();
40858             },
40859
40860             "tab" : function(e){
40861                 this.collapse();
40862                 
40863                 if(this.fireEvent("specialkey", this, e)){
40864                     this.onViewClick(false);
40865                 }
40866                 
40867                 return true;
40868             },
40869
40870             scope : this,
40871
40872             doRelay : function(foo, bar, hname){
40873                 if(hname == 'down' || this.scope.isExpanded()){
40874                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40875                 }
40876                 return true;
40877             },
40878
40879             forceKeyDown: true
40880         });
40881         
40882         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40883         
40884     },
40885     
40886     initNumberEvent : function(e)
40887     {
40888         this.inputEl().on("keydown" , this.fireKey,  this);
40889         this.inputEl().on("focus", this.onFocus,  this);
40890         this.inputEl().on("blur", this.onBlur,  this);
40891         
40892         this.inputEl().relayEvent('keyup', this);
40893         
40894         if(this.indicator){
40895             this.indicator.addClass('invisible');
40896         }
40897  
40898         this.originalValue = this.getValue();
40899         
40900         if(this.validationEvent == 'keyup'){
40901             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40902             this.inputEl().on('keyup', this.filterValidation, this);
40903         }
40904         else if(this.validationEvent !== false){
40905             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40906         }
40907         
40908         if(this.selectOnFocus){
40909             this.on("focus", this.preFocus, this);
40910             
40911         }
40912         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40913             this.inputEl().on("keypress", this.filterKeys, this);
40914         } else {
40915             this.inputEl().relayEvent('keypress', this);
40916         }
40917         
40918         var allowed = "0123456789";
40919         
40920         if(this.allowDecimals){
40921             allowed += this.decimalSeparator;
40922         }
40923         
40924         if(this.allowNegative){
40925             allowed += "-";
40926         }
40927         
40928         if(this.thousandsDelimiter) {
40929             allowed += ",";
40930         }
40931         
40932         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40933         
40934         var keyPress = function(e){
40935             
40936             var k = e.getKey();
40937             
40938             var c = e.getCharCode();
40939             
40940             if(
40941                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40942                     allowed.indexOf(String.fromCharCode(c)) === -1
40943             ){
40944                 e.stopEvent();
40945                 return;
40946             }
40947             
40948             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40949                 return;
40950             }
40951             
40952             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40953                 e.stopEvent();
40954             }
40955         };
40956         
40957         this.inputEl().on("keypress", keyPress, this);
40958         
40959     },
40960     
40961     onTriggerClick : function(e)
40962     {   
40963         if(this.disabled){
40964             return;
40965         }
40966         
40967         this.page = 0;
40968         this.loadNext = false;
40969         
40970         if(this.isExpanded()){
40971             this.collapse();
40972             return;
40973         }
40974         
40975         this.hasFocus = true;
40976         
40977         if(this.triggerAction == 'all') {
40978             this.doQuery(this.allQuery, true);
40979             return;
40980         }
40981         
40982         this.doQuery(this.getRawValue());
40983     },
40984     
40985     getCurrency : function()
40986     {   
40987         var v = this.currencyEl().getValue();
40988         
40989         return v;
40990     },
40991     
40992     restrictHeight : function()
40993     {
40994         this.list.alignTo(this.currencyEl(), this.listAlign);
40995         this.list.alignTo(this.currencyEl(), this.listAlign);
40996     },
40997     
40998     onViewClick : function(view, doFocus, el, e)
40999     {
41000         var index = this.view.getSelectedIndexes()[0];
41001         
41002         var r = this.store.getAt(index);
41003         
41004         if(r){
41005             this.onSelect(r, index);
41006         }
41007     },
41008     
41009     onSelect : function(record, index){
41010         
41011         if(this.fireEvent('beforeselect', this, record, index) !== false){
41012         
41013             this.setFromCurrencyData(index > -1 ? record.data : false);
41014             
41015             this.collapse();
41016             
41017             this.fireEvent('select', this, record, index);
41018         }
41019     },
41020     
41021     setFromCurrencyData : function(o)
41022     {
41023         var currency = '';
41024         
41025         this.lastCurrency = o;
41026         
41027         if (this.currencyField) {
41028             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41029         } else {
41030             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41031         }
41032         
41033         this.lastSelectionText = currency;
41034         
41035         //setting default currency
41036         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41037             this.setCurrency(this.defaultCurrency);
41038             return;
41039         }
41040         
41041         this.setCurrency(currency);
41042     },
41043     
41044     setFromData : function(o)
41045     {
41046         var c = {};
41047         
41048         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41049         
41050         this.setFromCurrencyData(c);
41051         
41052         var value = '';
41053         
41054         if (this.name) {
41055             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41056         } else {
41057             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41058         }
41059         
41060         this.setValue(value);
41061         
41062     },
41063     
41064     setCurrency : function(v)
41065     {   
41066         this.currencyValue = v;
41067         
41068         if(this.rendered){
41069             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41070             this.validate();
41071         }
41072     },
41073     
41074     setValue : function(v)
41075     {
41076         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41077         
41078         this.value = v;
41079         
41080         if(this.rendered){
41081             
41082             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41083             
41084             this.inputEl().dom.value = (v == '') ? '' :
41085                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41086             
41087             if(!this.allowZero && v === '0') {
41088                 this.hiddenEl().dom.value = '';
41089                 this.inputEl().dom.value = '';
41090             }
41091             
41092             this.validate();
41093         }
41094     },
41095     
41096     getRawValue : function()
41097     {
41098         var v = this.inputEl().getValue();
41099         
41100         return v;
41101     },
41102     
41103     getValue : function()
41104     {
41105         return this.fixPrecision(this.parseValue(this.getRawValue()));
41106     },
41107     
41108     parseValue : function(value)
41109     {
41110         if(this.thousandsDelimiter) {
41111             value += "";
41112             r = new RegExp(",", "g");
41113             value = value.replace(r, "");
41114         }
41115         
41116         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41117         return isNaN(value) ? '' : value;
41118         
41119     },
41120     
41121     fixPrecision : function(value)
41122     {
41123         if(this.thousandsDelimiter) {
41124             value += "";
41125             r = new RegExp(",", "g");
41126             value = value.replace(r, "");
41127         }
41128         
41129         var nan = isNaN(value);
41130         
41131         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41132             return nan ? '' : value;
41133         }
41134         return parseFloat(value).toFixed(this.decimalPrecision);
41135     },
41136     
41137     decimalPrecisionFcn : function(v)
41138     {
41139         return Math.floor(v);
41140     },
41141     
41142     validateValue : function(value)
41143     {
41144         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41145             return false;
41146         }
41147         
41148         var num = this.parseValue(value);
41149         
41150         if(isNaN(num)){
41151             this.markInvalid(String.format(this.nanText, value));
41152             return false;
41153         }
41154         
41155         if(num < this.minValue){
41156             this.markInvalid(String.format(this.minText, this.minValue));
41157             return false;
41158         }
41159         
41160         if(num > this.maxValue){
41161             this.markInvalid(String.format(this.maxText, this.maxValue));
41162             return false;
41163         }
41164         
41165         return true;
41166     },
41167     
41168     validate : function()
41169     {
41170         if(this.disabled || this.allowBlank){
41171             this.markValid();
41172             return true;
41173         }
41174         
41175         var currency = this.getCurrency();
41176         
41177         if(this.validateValue(this.getRawValue()) && currency.length){
41178             this.markValid();
41179             return true;
41180         }
41181         
41182         this.markInvalid();
41183         return false;
41184     },
41185     
41186     getName: function()
41187     {
41188         return this.name;
41189     },
41190     
41191     beforeBlur : function()
41192     {
41193         if(!this.castInt){
41194             return;
41195         }
41196         
41197         var v = this.parseValue(this.getRawValue());
41198         
41199         if(v || v == 0){
41200             this.setValue(v);
41201         }
41202     },
41203     
41204     onBlur : function()
41205     {
41206         this.beforeBlur();
41207         
41208         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41209             //this.el.removeClass(this.focusClass);
41210         }
41211         
41212         this.hasFocus = false;
41213         
41214         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41215             this.validate();
41216         }
41217         
41218         var v = this.getValue();
41219         
41220         if(String(v) !== String(this.startValue)){
41221             this.fireEvent('change', this, v, this.startValue);
41222         }
41223         
41224         this.fireEvent("blur", this);
41225     },
41226     
41227     inputEl : function()
41228     {
41229         return this.el.select('.roo-money-amount-input', true).first();
41230     },
41231     
41232     currencyEl : function()
41233     {
41234         return this.el.select('.roo-money-currency-input', true).first();
41235     },
41236     
41237     hiddenEl : function()
41238     {
41239         return this.el.select('input.hidden-number-input',true).first();
41240     }
41241     
41242 });