Roo/bootstrap/Navbar.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass('hidden');
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass('hidden');
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fs
593  * @cfg {String} badge text for badge
594  * @cfg {String} theme (default|glow)  
595  * @cfg {Boolean} inverse dark themed version
596  * @cfg {Boolean} toggle is it a slidy toggle button
597  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
598  * @cfg {String} ontext text for on slidy toggle state
599  * @cfg {String} offtext text for off slidy toggle state
600  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
601  * @cfg {Boolean} removeClass remove the standard class..
602  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
603  * 
604  * @constructor
605  * Create a new button
606  * @param {Object} config The config object
607  */
608
609
610 Roo.bootstrap.Button = function(config){
611     Roo.bootstrap.Button.superclass.constructor.call(this, config);
612     this.weightClass = ["btn-default btn-outline-secondary", 
613                        "btn-primary", 
614                        "btn-success", 
615                        "btn-info", 
616                        "btn-warning",
617                        "btn-danger",
618                        "btn-link"
619                       ],  
620     this.addEvents({
621         // raw events
622         /**
623          * @event click
624          * When a butotn is pressed
625          * @param {Roo.bootstrap.Button} btn
626          * @param {Roo.EventObject} e
627          */
628         "click" : true,
629          /**
630          * @event toggle
631          * After the button has been toggles
632          * @param {Roo.bootstrap.Button} btn
633          * @param {Roo.EventObject} e
634          * @param {boolean} pressed (also available as button.pressed)
635          */
636         "toggle" : true
637     });
638 };
639
640 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
641     html: false,
642     active: false,
643     weight: '',
644     badge_weight: '',
645     outline : false,
646     size: '',
647     tag: 'button',
648     href: '',
649     disabled: false,
650     isClose: false,
651     glyphicon: '',
652     fa: '',
653     badge: '',
654     theme: 'default',
655     inverse: false,
656     
657     toggle: false,
658     ontext: 'ON',
659     offtext: 'OFF',
660     defaulton: true,
661     preventDefault: true,
662     removeClass: false,
663     name: false,
664     target: false,
665      
666     pressed : null,
667      
668     
669     getAutoCreate : function(){
670         
671         var cfg = {
672             tag : 'button',
673             cls : 'roo-button',
674             html: ''
675         };
676         
677         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
678             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
679             this.tag = 'button';
680         } else {
681             cfg.tag = this.tag;
682         }
683         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
684         
685         if (this.toggle == true) {
686             cfg={
687                 tag: 'div',
688                 cls: 'slider-frame roo-button',
689                 cn: [
690                     {
691                         tag: 'span',
692                         'data-on-text':'ON',
693                         'data-off-text':'OFF',
694                         cls: 'slider-button',
695                         html: this.offtext
696                     }
697                 ]
698             };
699             
700             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
701                 cfg.cls += ' '+this.weight;
702             }
703             
704             return cfg;
705         }
706         
707         if (this.isClose) {
708             cfg.cls += ' close';
709             
710             cfg["aria-hidden"] = true;
711             
712             cfg.html = "&times;";
713             
714             return cfg;
715         }
716         
717          
718         if (this.theme==='default') {
719             cfg.cls = 'btn roo-button';
720             
721             //if (this.parentType != 'Navbar') {
722             this.weight = this.weight.length ?  this.weight : 'default';
723             //}
724             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
725                 
726                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
727                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
728                 cfg.cls += ' btn-' + outline + weight;
729                 if (this.weight == 'default') {
730                     // BC
731                     cfg.cls += ' btn-' + this.weight;
732                 }
733             }
734         } else if (this.theme==='glow') {
735             
736             cfg.tag = 'a';
737             cfg.cls = 'btn-glow roo-button';
738             
739             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
740                 
741                 cfg.cls += ' ' + this.weight;
742             }
743         }
744    
745         
746         if (this.inverse) {
747             this.cls += ' inverse';
748         }
749         
750         
751         if (this.active || this.pressed === true) {
752             cfg.cls += ' active';
753         }
754         
755         if (this.disabled) {
756             cfg.disabled = 'disabled';
757         }
758         
759         if (this.items) {
760             Roo.log('changing to ul' );
761             cfg.tag = 'ul';
762             this.glyphicon = 'caret';
763             if (Roo.bootstrap.version == 4) {
764                 this.fa = 'caret-down';
765             }
766             
767         }
768         
769         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
770          
771         //gsRoo.log(this.parentType);
772         if (this.parentType === 'Navbar' && !this.parent().bar) {
773             Roo.log('changing to li?');
774             
775             cfg.tag = 'li';
776             
777             cfg.cls = '';
778             cfg.cn =  [{
779                 tag : 'a',
780                 cls : 'roo-button',
781                 html : this.html,
782                 href : this.href || '#'
783             }];
784             if (this.menu) {
785                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
786                 cfg.cls += ' dropdown';
787             }   
788             
789             delete cfg.html;
790             
791         }
792         
793        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
794         
795         if (this.glyphicon) {
796             cfg.html = ' ' + cfg.html;
797             
798             cfg.cn = [
799                 {
800                     tag: 'span',
801                     cls: 'glyphicon glyphicon-' + this.glyphicon
802                 }
803             ];
804         }
805         if (this.fa) {
806             cfg.html = ' ' + cfg.html;
807             
808             cfg.cn = [
809                 {
810                     tag: 'i',
811                     cls: 'fa fas fa-' + this.fa
812                 }
813             ];
814         }
815         
816         if (this.badge) {
817             cfg.html += ' ';
818             
819             cfg.tag = 'a';
820             
821 //            cfg.cls='btn roo-button';
822             
823             cfg.href=this.href;
824             
825             var value = cfg.html;
826             
827             if(this.glyphicon){
828                 value = {
829                     tag: 'span',
830                     cls: 'glyphicon glyphicon-' + this.glyphicon,
831                     html: this.html
832                 };
833             }
834             if(this.fa){
835                 value = {
836                     tag: 'i',
837                     cls: 'fa fas fa-' + this.fa,
838                     html: this.html
839                 };
840             }
841             
842             var bw = this.badge_weight.length ? this.badge_weight :
843                 (this.weight.length ? this.weight : 'secondary');
844             bw = bw == 'default' ? 'secondary' : bw;
845             
846             cfg.cn = [
847                 value,
848                 {
849                     tag: 'span',
850                     cls: 'badge badge-' + bw,
851                     html: this.badge
852                 }
853             ];
854             
855             cfg.html='';
856         }
857         
858         if (this.menu) {
859             cfg.cls += ' dropdown';
860             cfg.html = typeof(cfg.html) != 'undefined' ?
861                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
862         }
863         
864         if (cfg.tag !== 'a' && this.href !== '') {
865             throw "Tag must be a to set href.";
866         } else if (this.href.length > 0) {
867             cfg.href = this.href;
868         }
869         
870         if(this.removeClass){
871             cfg.cls = '';
872         }
873         
874         if(this.target){
875             cfg.target = this.target;
876         }
877         
878         return cfg;
879     },
880     initEvents: function() {
881        // Roo.log('init events?');
882 //        Roo.log(this.el.dom);
883         // add the menu...
884         
885         if (typeof (this.menu) != 'undefined') {
886             this.menu.parentType = this.xtype;
887             this.menu.triggerEl = this.el;
888             this.addxtype(Roo.apply({}, this.menu));
889         }
890
891
892        if (this.el.hasClass('roo-button')) {
893             this.el.on('click', this.onClick, this);
894        } else {
895             this.el.select('.roo-button').on('click', this.onClick, this);
896        }
897        
898        if(this.removeClass){
899            this.el.on('click', this.onClick, this);
900        }
901        
902        this.el.enableDisplayMode();
903         
904     },
905     onClick : function(e)
906     {
907         if (this.disabled) {
908             return;
909         }
910         
911         Roo.log('button on click ');
912         if(this.preventDefault){
913             e.preventDefault();
914         }
915         
916         if (this.pressed === true || this.pressed === false) {
917             this.toggleActive(e);
918         }
919         
920         
921         this.fireEvent('click', this, e);
922     },
923     
924     /**
925      * Enables this button
926      */
927     enable : function()
928     {
929         this.disabled = false;
930         this.el.removeClass('disabled');
931     },
932     
933     /**
934      * Disable this button
935      */
936     disable : function()
937     {
938         this.disabled = true;
939         this.el.addClass('disabled');
940     },
941      /**
942      * sets the active state on/off, 
943      * @param {Boolean} state (optional) Force a particular state
944      */
945     setActive : function(v) {
946         
947         this.el[v ? 'addClass' : 'removeClass']('active');
948         this.pressed = v;
949     },
950      /**
951      * toggles the current active state 
952      */
953     toggleActive : function(e)
954     {
955         this.setActive(!this.pressed);
956         this.fireEvent('toggle', this, e, !this.pressed);
957     },
958      /**
959      * get the current active state
960      * @return {boolean} true if it's active
961      */
962     isActive : function()
963     {
964         return this.el.hasClass('active');
965     },
966     /**
967      * set the text of the first selected button
968      */
969     setText : function(str)
970     {
971         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
972     },
973     /**
974      * get the text of the first selected button
975      */
976     getText : function()
977     {
978         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
979     },
980     
981     setWeight : function(str)
982     {
983         this.el.removeClass(this.weightClass);
984         this.weight = str;
985         var outline = this.outline ? 'outline-' : '';
986         if (str == 'default') {
987             this.el.addClass('btn-default btn-outline-secondary');        
988             return;
989         }
990         this.el.addClass('btn-' + outline + str);        
991     }
992     
993     
994 });
995
996  /*
997  * - LGPL
998  *
999  * column
1000  * 
1001  */
1002
1003 /**
1004  * @class Roo.bootstrap.Column
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Column class
1007  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1008  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1009  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1010  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1011  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1012  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1013  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1014  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1015  *
1016  * 
1017  * @cfg {Boolean} hidden (true|false) hide the element
1018  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1019  * @cfg {String} fa (ban|check|...) font awesome icon
1020  * @cfg {Number} fasize (1|2|....) font awsome size
1021
1022  * @cfg {String} icon (info-sign|check|...) glyphicon name
1023
1024  * @cfg {String} html content of column.
1025  * 
1026  * @constructor
1027  * Create a new Column
1028  * @param {Object} config The config object
1029  */
1030
1031 Roo.bootstrap.Column = function(config){
1032     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1033 };
1034
1035 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1036     
1037     xs: false,
1038     sm: false,
1039     md: false,
1040     lg: false,
1041     xsoff: false,
1042     smoff: false,
1043     mdoff: false,
1044     lgoff: false,
1045     html: '',
1046     offset: 0,
1047     alert: false,
1048     fa: false,
1049     icon : false,
1050     hidden : false,
1051     fasize : 1,
1052     
1053     getAutoCreate : function(){
1054         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1055         
1056         cfg = {
1057             tag: 'div',
1058             cls: 'column'
1059         };
1060         
1061         var settings=this;
1062         ['xs','sm','md','lg'].map(function(size){
1063             //Roo.log( size + ':' + settings[size]);
1064             
1065             if (settings[size+'off'] !== false) {
1066                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1067             }
1068             
1069             if (settings[size] === false) {
1070                 return;
1071             }
1072             
1073             if (!settings[size]) { // 0 = hidden
1074                 cfg.cls += ' hidden-' + size;
1075                 return;
1076             }
1077             cfg.cls += ' col-' + size + '-' + settings[size];
1078             
1079         });
1080         
1081         if (this.hidden) {
1082             cfg.cls += ' hidden';
1083         }
1084         
1085         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1086             cfg.cls +=' alert alert-' + this.alert;
1087         }
1088         
1089         
1090         if (this.html.length) {
1091             cfg.html = this.html;
1092         }
1093         if (this.fa) {
1094             var fasize = '';
1095             if (this.fasize > 1) {
1096                 fasize = ' fa-' + this.fasize + 'x';
1097             }
1098             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1099             
1100             
1101         }
1102         if (this.icon) {
1103             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1104         }
1105         
1106         return cfg;
1107     }
1108    
1109 });
1110
1111  
1112
1113  /*
1114  * - LGPL
1115  *
1116  * page container.
1117  * 
1118  */
1119
1120
1121 /**
1122  * @class Roo.bootstrap.Container
1123  * @extends Roo.bootstrap.Component
1124  * Bootstrap Container class
1125  * @cfg {Boolean} jumbotron is it a jumbotron element
1126  * @cfg {String} html content of element
1127  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1128  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1129  * @cfg {String} header content of header (for panel)
1130  * @cfg {String} footer content of footer (for panel)
1131  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1132  * @cfg {String} tag (header|aside|section) type of HTML tag.
1133  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1134  * @cfg {String} fa font awesome icon
1135  * @cfg {String} icon (info-sign|check|...) glyphicon name
1136  * @cfg {Boolean} hidden (true|false) hide the element
1137  * @cfg {Boolean} expandable (true|false) default false
1138  * @cfg {Boolean} expanded (true|false) default true
1139  * @cfg {String} rheader contet on the right of header
1140  * @cfg {Boolean} clickable (true|false) default false
1141
1142  *     
1143  * @constructor
1144  * Create a new Container
1145  * @param {Object} config The config object
1146  */
1147
1148 Roo.bootstrap.Container = function(config){
1149     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1150     
1151     this.addEvents({
1152         // raw events
1153          /**
1154          * @event expand
1155          * After the panel has been expand
1156          * 
1157          * @param {Roo.bootstrap.Container} this
1158          */
1159         "expand" : true,
1160         /**
1161          * @event collapse
1162          * After the panel has been collapsed
1163          * 
1164          * @param {Roo.bootstrap.Container} this
1165          */
1166         "collapse" : true,
1167         /**
1168          * @event click
1169          * When a element is chick
1170          * @param {Roo.bootstrap.Container} this
1171          * @param {Roo.EventObject} e
1172          */
1173         "click" : true
1174     });
1175 };
1176
1177 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1178     
1179     jumbotron : false,
1180     well: '',
1181     panel : '',
1182     header: '',
1183     footer : '',
1184     sticky: '',
1185     tag : false,
1186     alert : false,
1187     fa: false,
1188     icon : false,
1189     expandable : false,
1190     rheader : '',
1191     expanded : true,
1192     clickable: false,
1193   
1194      
1195     getChildContainer : function() {
1196         
1197         if(!this.el){
1198             return false;
1199         }
1200         
1201         if (this.panel.length) {
1202             return this.el.select('.panel-body',true).first();
1203         }
1204         
1205         return this.el;
1206     },
1207     
1208     
1209     getAutoCreate : function(){
1210         
1211         var cfg = {
1212             tag : this.tag || 'div',
1213             html : '',
1214             cls : ''
1215         };
1216         if (this.jumbotron) {
1217             cfg.cls = 'jumbotron';
1218         }
1219         
1220         
1221         
1222         // - this is applied by the parent..
1223         //if (this.cls) {
1224         //    cfg.cls = this.cls + '';
1225         //}
1226         
1227         if (this.sticky.length) {
1228             
1229             var bd = Roo.get(document.body);
1230             if (!bd.hasClass('bootstrap-sticky')) {
1231                 bd.addClass('bootstrap-sticky');
1232                 Roo.select('html',true).setStyle('height', '100%');
1233             }
1234              
1235             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1236         }
1237         
1238         
1239         if (this.well.length) {
1240             switch (this.well) {
1241                 case 'lg':
1242                 case 'sm':
1243                     cfg.cls +=' well well-' +this.well;
1244                     break;
1245                 default:
1246                     cfg.cls +=' well';
1247                     break;
1248             }
1249         }
1250         
1251         if (this.hidden) {
1252             cfg.cls += ' hidden';
1253         }
1254         
1255         
1256         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1257             cfg.cls +=' alert alert-' + this.alert;
1258         }
1259         
1260         var body = cfg;
1261         
1262         if (this.panel.length) {
1263             cfg.cls += ' panel panel-' + this.panel;
1264             cfg.cn = [];
1265             if (this.header.length) {
1266                 
1267                 var h = [];
1268                 
1269                 if(this.expandable){
1270                     
1271                     cfg.cls = cfg.cls + ' expandable';
1272                     
1273                     h.push({
1274                         tag: 'i',
1275                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1276                     });
1277                     
1278                 }
1279                 
1280                 h.push(
1281                     {
1282                         tag: 'span',
1283                         cls : 'panel-title',
1284                         html : (this.expandable ? '&nbsp;' : '') + this.header
1285                     },
1286                     {
1287                         tag: 'span',
1288                         cls: 'panel-header-right',
1289                         html: this.rheader
1290                     }
1291                 );
1292                 
1293                 cfg.cn.push({
1294                     cls : 'panel-heading',
1295                     style : this.expandable ? 'cursor: pointer' : '',
1296                     cn : h
1297                 });
1298                 
1299             }
1300             
1301             body = false;
1302             cfg.cn.push({
1303                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1304                 html : this.html
1305             });
1306             
1307             
1308             if (this.footer.length) {
1309                 cfg.cn.push({
1310                     cls : 'panel-footer',
1311                     html : this.footer
1312                     
1313                 });
1314             }
1315             
1316         }
1317         
1318         if (body) {
1319             body.html = this.html || cfg.html;
1320             // prefix with the icons..
1321             if (this.fa) {
1322                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1323             }
1324             if (this.icon) {
1325                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1326             }
1327             
1328             
1329         }
1330         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1331             cfg.cls =  'container';
1332         }
1333         
1334         return cfg;
1335     },
1336     
1337     initEvents: function() 
1338     {
1339         if(this.expandable){
1340             var headerEl = this.headerEl();
1341         
1342             if(headerEl){
1343                 headerEl.on('click', this.onToggleClick, this);
1344             }
1345         }
1346         
1347         if(this.clickable){
1348             this.el.on('click', this.onClick, this);
1349         }
1350         
1351     },
1352     
1353     onToggleClick : function()
1354     {
1355         var headerEl = this.headerEl();
1356         
1357         if(!headerEl){
1358             return;
1359         }
1360         
1361         if(this.expanded){
1362             this.collapse();
1363             return;
1364         }
1365         
1366         this.expand();
1367     },
1368     
1369     expand : function()
1370     {
1371         if(this.fireEvent('expand', this)) {
1372             
1373             this.expanded = true;
1374             
1375             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1376             
1377             this.el.select('.panel-body',true).first().removeClass('hide');
1378             
1379             var toggleEl = this.toggleEl();
1380
1381             if(!toggleEl){
1382                 return;
1383             }
1384
1385             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1386         }
1387         
1388     },
1389     
1390     collapse : function()
1391     {
1392         if(this.fireEvent('collapse', this)) {
1393             
1394             this.expanded = false;
1395             
1396             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1397             this.el.select('.panel-body',true).first().addClass('hide');
1398         
1399             var toggleEl = this.toggleEl();
1400
1401             if(!toggleEl){
1402                 return;
1403             }
1404
1405             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1406         }
1407     },
1408     
1409     toggleEl : function()
1410     {
1411         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1412             return;
1413         }
1414         
1415         return this.el.select('.panel-heading .fa',true).first();
1416     },
1417     
1418     headerEl : function()
1419     {
1420         if(!this.el || !this.panel.length || !this.header.length){
1421             return;
1422         }
1423         
1424         return this.el.select('.panel-heading',true).first()
1425     },
1426     
1427     bodyEl : function()
1428     {
1429         if(!this.el || !this.panel.length){
1430             return;
1431         }
1432         
1433         return this.el.select('.panel-body',true).first()
1434     },
1435     
1436     titleEl : function()
1437     {
1438         if(!this.el || !this.panel.length || !this.header.length){
1439             return;
1440         }
1441         
1442         return this.el.select('.panel-title',true).first();
1443     },
1444     
1445     setTitle : function(v)
1446     {
1447         var titleEl = this.titleEl();
1448         
1449         if(!titleEl){
1450             return;
1451         }
1452         
1453         titleEl.dom.innerHTML = v;
1454     },
1455     
1456     getTitle : function()
1457     {
1458         
1459         var titleEl = this.titleEl();
1460         
1461         if(!titleEl){
1462             return '';
1463         }
1464         
1465         return titleEl.dom.innerHTML;
1466     },
1467     
1468     setRightTitle : function(v)
1469     {
1470         var t = this.el.select('.panel-header-right',true).first();
1471         
1472         if(!t){
1473             return;
1474         }
1475         
1476         t.dom.innerHTML = v;
1477     },
1478     
1479     onClick : function(e)
1480     {
1481         e.preventDefault();
1482         
1483         this.fireEvent('click', this, e);
1484     }
1485 });
1486
1487  /*
1488  * - LGPL
1489  *
1490  * image
1491  * 
1492  */
1493
1494
1495 /**
1496  * @class Roo.bootstrap.Img
1497  * @extends Roo.bootstrap.Component
1498  * Bootstrap Img class
1499  * @cfg {Boolean} imgResponsive false | true
1500  * @cfg {String} border rounded | circle | thumbnail
1501  * @cfg {String} src image source
1502  * @cfg {String} alt image alternative text
1503  * @cfg {String} href a tag href
1504  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1505  * @cfg {String} xsUrl xs image source
1506  * @cfg {String} smUrl sm image source
1507  * @cfg {String} mdUrl md image source
1508  * @cfg {String} lgUrl lg image source
1509  * 
1510  * @constructor
1511  * Create a new Input
1512  * @param {Object} config The config object
1513  */
1514
1515 Roo.bootstrap.Img = function(config){
1516     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1517     
1518     this.addEvents({
1519         // img events
1520         /**
1521          * @event click
1522          * The img click event for the img.
1523          * @param {Roo.EventObject} e
1524          */
1525         "click" : true
1526     });
1527 };
1528
1529 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1530     
1531     imgResponsive: true,
1532     border: '',
1533     src: 'about:blank',
1534     href: false,
1535     target: false,
1536     xsUrl: '',
1537     smUrl: '',
1538     mdUrl: '',
1539     lgUrl: '',
1540
1541     getAutoCreate : function()
1542     {   
1543         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1544             return this.createSingleImg();
1545         }
1546         
1547         var cfg = {
1548             tag: 'div',
1549             cls: 'roo-image-responsive-group',
1550             cn: []
1551         };
1552         var _this = this;
1553         
1554         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1555             
1556             if(!_this[size + 'Url']){
1557                 return;
1558             }
1559             
1560             var img = {
1561                 tag: 'img',
1562                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1563                 html: _this.html || cfg.html,
1564                 src: _this[size + 'Url']
1565             };
1566             
1567             img.cls += ' roo-image-responsive-' + size;
1568             
1569             var s = ['xs', 'sm', 'md', 'lg'];
1570             
1571             s.splice(s.indexOf(size), 1);
1572             
1573             Roo.each(s, function(ss){
1574                 img.cls += ' hidden-' + ss;
1575             });
1576             
1577             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1578                 cfg.cls += ' img-' + _this.border;
1579             }
1580             
1581             if(_this.alt){
1582                 cfg.alt = _this.alt;
1583             }
1584             
1585             if(_this.href){
1586                 var a = {
1587                     tag: 'a',
1588                     href: _this.href,
1589                     cn: [
1590                         img
1591                     ]
1592                 };
1593
1594                 if(this.target){
1595                     a.target = _this.target;
1596                 }
1597             }
1598             
1599             cfg.cn.push((_this.href) ? a : img);
1600             
1601         });
1602         
1603         return cfg;
1604     },
1605     
1606     createSingleImg : function()
1607     {
1608         var cfg = {
1609             tag: 'img',
1610             cls: (this.imgResponsive) ? 'img-responsive' : '',
1611             html : null,
1612             src : 'about:blank'  // just incase src get's set to undefined?!?
1613         };
1614         
1615         cfg.html = this.html || cfg.html;
1616         
1617         cfg.src = this.src || cfg.src;
1618         
1619         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1620             cfg.cls += ' img-' + this.border;
1621         }
1622         
1623         if(this.alt){
1624             cfg.alt = this.alt;
1625         }
1626         
1627         if(this.href){
1628             var a = {
1629                 tag: 'a',
1630                 href: this.href,
1631                 cn: [
1632                     cfg
1633                 ]
1634             };
1635             
1636             if(this.target){
1637                 a.target = this.target;
1638             }
1639             
1640         }
1641         
1642         return (this.href) ? a : cfg;
1643     },
1644     
1645     initEvents: function() 
1646     {
1647         if(!this.href){
1648             this.el.on('click', this.onClick, this);
1649         }
1650         
1651     },
1652     
1653     onClick : function(e)
1654     {
1655         Roo.log('img onclick');
1656         this.fireEvent('click', this, e);
1657     },
1658     /**
1659      * Sets the url of the image - used to update it
1660      * @param {String} url the url of the image
1661      */
1662     
1663     setSrc : function(url)
1664     {
1665         this.src =  url;
1666         
1667         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1668             this.el.dom.src =  url;
1669             return;
1670         }
1671         
1672         this.el.select('img', true).first().dom.src =  url;
1673     }
1674     
1675     
1676    
1677 });
1678
1679  /*
1680  * - LGPL
1681  *
1682  * image
1683  * 
1684  */
1685
1686
1687 /**
1688  * @class Roo.bootstrap.Link
1689  * @extends Roo.bootstrap.Component
1690  * Bootstrap Link Class
1691  * @cfg {String} alt image alternative text
1692  * @cfg {String} href a tag href
1693  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1694  * @cfg {String} html the content of the link.
1695  * @cfg {String} anchor name for the anchor link
1696  * @cfg {String} fa - favicon
1697
1698  * @cfg {Boolean} preventDefault (true | false) default false
1699
1700  * 
1701  * @constructor
1702  * Create a new Input
1703  * @param {Object} config The config object
1704  */
1705
1706 Roo.bootstrap.Link = function(config){
1707     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1708     
1709     this.addEvents({
1710         // img events
1711         /**
1712          * @event click
1713          * The img click event for the img.
1714          * @param {Roo.EventObject} e
1715          */
1716         "click" : true
1717     });
1718 };
1719
1720 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1721     
1722     href: false,
1723     target: false,
1724     preventDefault: false,
1725     anchor : false,
1726     alt : false,
1727     fa: false,
1728
1729
1730     getAutoCreate : function()
1731     {
1732         var html = this.html || '';
1733         
1734         if (this.fa !== false) {
1735             html = '<i class="fa fa-' + this.fa + '"></i>';
1736         }
1737         var cfg = {
1738             tag: 'a'
1739         };
1740         // anchor's do not require html/href...
1741         if (this.anchor === false) {
1742             cfg.html = html;
1743             cfg.href = this.href || '#';
1744         } else {
1745             cfg.name = this.anchor;
1746             if (this.html !== false || this.fa !== false) {
1747                 cfg.html = html;
1748             }
1749             if (this.href !== false) {
1750                 cfg.href = this.href;
1751             }
1752         }
1753         
1754         if(this.alt !== false){
1755             cfg.alt = this.alt;
1756         }
1757         
1758         
1759         if(this.target !== false) {
1760             cfg.target = this.target;
1761         }
1762         
1763         return cfg;
1764     },
1765     
1766     initEvents: function() {
1767         
1768         if(!this.href || this.preventDefault){
1769             this.el.on('click', this.onClick, this);
1770         }
1771     },
1772     
1773     onClick : function(e)
1774     {
1775         if(this.preventDefault){
1776             e.preventDefault();
1777         }
1778         //Roo.log('img onclick');
1779         this.fireEvent('click', this, e);
1780     }
1781    
1782 });
1783
1784  /*
1785  * - LGPL
1786  *
1787  * header
1788  * 
1789  */
1790
1791 /**
1792  * @class Roo.bootstrap.Header
1793  * @extends Roo.bootstrap.Component
1794  * Bootstrap Header class
1795  * @cfg {String} html content of header
1796  * @cfg {Number} level (1|2|3|4|5|6) default 1
1797  * 
1798  * @constructor
1799  * Create a new Header
1800  * @param {Object} config The config object
1801  */
1802
1803
1804 Roo.bootstrap.Header  = function(config){
1805     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1806 };
1807
1808 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1809     
1810     //href : false,
1811     html : false,
1812     level : 1,
1813     
1814     
1815     
1816     getAutoCreate : function(){
1817         
1818         
1819         
1820         var cfg = {
1821             tag: 'h' + (1 *this.level),
1822             html: this.html || ''
1823         } ;
1824         
1825         return cfg;
1826     }
1827    
1828 });
1829
1830  
1831
1832  /*
1833  * Based on:
1834  * Ext JS Library 1.1.1
1835  * Copyright(c) 2006-2007, Ext JS, LLC.
1836  *
1837  * Originally Released Under LGPL - original licence link has changed is not relivant.
1838  *
1839  * Fork - LGPL
1840  * <script type="text/javascript">
1841  */
1842  
1843 /**
1844  * @class Roo.bootstrap.MenuMgr
1845  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1846  * @singleton
1847  */
1848 Roo.bootstrap.MenuMgr = function(){
1849    var menus, active, groups = {}, attached = false, lastShow = new Date();
1850
1851    // private - called when first menu is created
1852    function init(){
1853        menus = {};
1854        active = new Roo.util.MixedCollection();
1855        Roo.get(document).addKeyListener(27, function(){
1856            if(active.length > 0){
1857                hideAll();
1858            }
1859        });
1860    }
1861
1862    // private
1863    function hideAll(){
1864        if(active && active.length > 0){
1865            var c = active.clone();
1866            c.each(function(m){
1867                m.hide();
1868            });
1869        }
1870    }
1871
1872    // private
1873    function onHide(m){
1874        active.remove(m);
1875        if(active.length < 1){
1876            Roo.get(document).un("mouseup", onMouseDown);
1877             
1878            attached = false;
1879        }
1880    }
1881
1882    // private
1883    function onShow(m){
1884        var last = active.last();
1885        lastShow = new Date();
1886        active.add(m);
1887        if(!attached){
1888           Roo.get(document).on("mouseup", onMouseDown);
1889            
1890            attached = true;
1891        }
1892        if(m.parentMenu){
1893           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1894           m.parentMenu.activeChild = m;
1895        }else if(last && last.isVisible()){
1896           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1897        }
1898    }
1899
1900    // private
1901    function onBeforeHide(m){
1902        if(m.activeChild){
1903            m.activeChild.hide();
1904        }
1905        if(m.autoHideTimer){
1906            clearTimeout(m.autoHideTimer);
1907            delete m.autoHideTimer;
1908        }
1909    }
1910
1911    // private
1912    function onBeforeShow(m){
1913        var pm = m.parentMenu;
1914        if(!pm && !m.allowOtherMenus){
1915            hideAll();
1916        }else if(pm && pm.activeChild && active != m){
1917            pm.activeChild.hide();
1918        }
1919    }
1920
1921    // private this should really trigger on mouseup..
1922    function onMouseDown(e){
1923         Roo.log("on Mouse Up");
1924         
1925         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1926             Roo.log("MenuManager hideAll");
1927             hideAll();
1928             e.stopEvent();
1929         }
1930         
1931         
1932    }
1933
1934    // private
1935    function onBeforeCheck(mi, state){
1936        if(state){
1937            var g = groups[mi.group];
1938            for(var i = 0, l = g.length; i < l; i++){
1939                if(g[i] != mi){
1940                    g[i].setChecked(false);
1941                }
1942            }
1943        }
1944    }
1945
1946    return {
1947
1948        /**
1949         * Hides all menus that are currently visible
1950         */
1951        hideAll : function(){
1952             hideAll();  
1953        },
1954
1955        // private
1956        register : function(menu){
1957            if(!menus){
1958                init();
1959            }
1960            menus[menu.id] = menu;
1961            menu.on("beforehide", onBeforeHide);
1962            menu.on("hide", onHide);
1963            menu.on("beforeshow", onBeforeShow);
1964            menu.on("show", onShow);
1965            var g = menu.group;
1966            if(g && menu.events["checkchange"]){
1967                if(!groups[g]){
1968                    groups[g] = [];
1969                }
1970                groups[g].push(menu);
1971                menu.on("checkchange", onCheck);
1972            }
1973        },
1974
1975         /**
1976          * Returns a {@link Roo.menu.Menu} object
1977          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1978          * be used to generate and return a new Menu instance.
1979          */
1980        get : function(menu){
1981            if(typeof menu == "string"){ // menu id
1982                return menus[menu];
1983            }else if(menu.events){  // menu instance
1984                return menu;
1985            }
1986            /*else if(typeof menu.length == 'number'){ // array of menu items?
1987                return new Roo.bootstrap.Menu({items:menu});
1988            }else{ // otherwise, must be a config
1989                return new Roo.bootstrap.Menu(menu);
1990            }
1991            */
1992            return false;
1993        },
1994
1995        // private
1996        unregister : function(menu){
1997            delete menus[menu.id];
1998            menu.un("beforehide", onBeforeHide);
1999            menu.un("hide", onHide);
2000            menu.un("beforeshow", onBeforeShow);
2001            menu.un("show", onShow);
2002            var g = menu.group;
2003            if(g && menu.events["checkchange"]){
2004                groups[g].remove(menu);
2005                menu.un("checkchange", onCheck);
2006            }
2007        },
2008
2009        // private
2010        registerCheckable : function(menuItem){
2011            var g = menuItem.group;
2012            if(g){
2013                if(!groups[g]){
2014                    groups[g] = [];
2015                }
2016                groups[g].push(menuItem);
2017                menuItem.on("beforecheckchange", onBeforeCheck);
2018            }
2019        },
2020
2021        // private
2022        unregisterCheckable : function(menuItem){
2023            var g = menuItem.group;
2024            if(g){
2025                groups[g].remove(menuItem);
2026                menuItem.un("beforecheckchange", onBeforeCheck);
2027            }
2028        }
2029    };
2030 }();/*
2031  * - LGPL
2032  *
2033  * menu
2034  * 
2035  */
2036
2037 /**
2038  * @class Roo.bootstrap.Menu
2039  * @extends Roo.bootstrap.Component
2040  * Bootstrap Menu class - container for MenuItems
2041  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2042  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2043  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2044  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2045  * 
2046  * @constructor
2047  * Create a new Menu
2048  * @param {Object} config The config object
2049  */
2050
2051
2052 Roo.bootstrap.Menu = function(config){
2053     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2054     if (this.registerMenu && this.type != 'treeview')  {
2055         Roo.bootstrap.MenuMgr.register(this);
2056     }
2057     
2058     
2059     this.addEvents({
2060         /**
2061          * @event beforeshow
2062          * Fires before this menu is displayed
2063          * @param {Roo.menu.Menu} this
2064          */
2065         beforeshow : true,
2066         /**
2067          * @event beforehide
2068          * Fires before this menu is hidden
2069          * @param {Roo.menu.Menu} this
2070          */
2071         beforehide : true,
2072         /**
2073          * @event show
2074          * Fires after this menu is displayed
2075          * @param {Roo.menu.Menu} this
2076          */
2077         show : true,
2078         /**
2079          * @event hide
2080          * Fires after this menu is hidden
2081          * @param {Roo.menu.Menu} this
2082          */
2083         hide : true,
2084         /**
2085          * @event click
2086          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2087          * @param {Roo.menu.Menu} this
2088          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2089          * @param {Roo.EventObject} e
2090          */
2091         click : true,
2092         /**
2093          * @event mouseover
2094          * Fires when the mouse is hovering over this menu
2095          * @param {Roo.menu.Menu} this
2096          * @param {Roo.EventObject} e
2097          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2098          */
2099         mouseover : true,
2100         /**
2101          * @event mouseout
2102          * Fires when the mouse exits this menu
2103          * @param {Roo.menu.Menu} this
2104          * @param {Roo.EventObject} e
2105          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2106          */
2107         mouseout : true,
2108         /**
2109          * @event itemclick
2110          * Fires when a menu item contained in this menu is clicked
2111          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2112          * @param {Roo.EventObject} e
2113          */
2114         itemclick: true
2115     });
2116     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2117 };
2118
2119 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2120     
2121    /// html : false,
2122     //align : '',
2123     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2124     type: false,
2125     /**
2126      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2127      */
2128     registerMenu : true,
2129     
2130     menuItems :false, // stores the menu items..
2131     
2132     hidden:true,
2133         
2134     parentMenu : false,
2135     
2136     stopEvent : true,
2137     
2138     isLink : false,
2139     
2140     getChildContainer : function() {
2141         return this.el;  
2142     },
2143     
2144     getAutoCreate : function(){
2145          
2146         //if (['right'].indexOf(this.align)!==-1) {
2147         //    cfg.cn[1].cls += ' pull-right'
2148         //}
2149         
2150         
2151         var cfg = {
2152             tag : 'ul',
2153             cls : 'dropdown-menu' ,
2154             style : 'z-index:1000'
2155             
2156         };
2157         
2158         if (this.type === 'submenu') {
2159             cfg.cls = 'submenu active';
2160         }
2161         if (this.type === 'treeview') {
2162             cfg.cls = 'treeview-menu';
2163         }
2164         
2165         return cfg;
2166     },
2167     initEvents : function() {
2168         
2169        // Roo.log("ADD event");
2170        // Roo.log(this.triggerEl.dom);
2171         
2172         this.triggerEl.on('click', this.onTriggerClick, this);
2173         
2174         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2175         
2176         
2177         if (this.triggerEl.hasClass('nav-item')) {
2178             // dropdown toggle on the 'a' in BS4?
2179             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2180         } else {
2181             this.triggerEl.addClass('dropdown-toggle');
2182         }
2183         if (Roo.isTouch) {
2184             this.el.on('touchstart'  , this.onTouch, this);
2185         }
2186         this.el.on('click' , this.onClick, this);
2187
2188         this.el.on("mouseover", this.onMouseOver, this);
2189         this.el.on("mouseout", this.onMouseOut, this);
2190         
2191     },
2192     
2193     findTargetItem : function(e)
2194     {
2195         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2196         if(!t){
2197             return false;
2198         }
2199         //Roo.log(t);         Roo.log(t.id);
2200         if(t && t.id){
2201             //Roo.log(this.menuitems);
2202             return this.menuitems.get(t.id);
2203             
2204             //return this.items.get(t.menuItemId);
2205         }
2206         
2207         return false;
2208     },
2209     
2210     onTouch : function(e) 
2211     {
2212         Roo.log("menu.onTouch");
2213         //e.stopEvent(); this make the user popdown broken
2214         this.onClick(e);
2215     },
2216     
2217     onClick : function(e)
2218     {
2219         Roo.log("menu.onClick");
2220         
2221         var t = this.findTargetItem(e);
2222         if(!t || t.isContainer){
2223             return;
2224         }
2225         Roo.log(e);
2226         /*
2227         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2228             if(t == this.activeItem && t.shouldDeactivate(e)){
2229                 this.activeItem.deactivate();
2230                 delete this.activeItem;
2231                 return;
2232             }
2233             if(t.canActivate){
2234                 this.setActiveItem(t, true);
2235             }
2236             return;
2237             
2238             
2239         }
2240         */
2241        
2242         Roo.log('pass click event');
2243         
2244         t.onClick(e);
2245         
2246         this.fireEvent("click", this, t, e);
2247         
2248         var _this = this;
2249         
2250         if(!t.href.length || t.href == '#'){
2251             (function() { _this.hide(); }).defer(100);
2252         }
2253         
2254     },
2255     
2256     onMouseOver : function(e){
2257         var t  = this.findTargetItem(e);
2258         //Roo.log(t);
2259         //if(t){
2260         //    if(t.canActivate && !t.disabled){
2261         //        this.setActiveItem(t, true);
2262         //    }
2263         //}
2264         
2265         this.fireEvent("mouseover", this, e, t);
2266     },
2267     isVisible : function(){
2268         return !this.hidden;
2269     },
2270      onMouseOut : function(e){
2271         var t  = this.findTargetItem(e);
2272         
2273         //if(t ){
2274         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2275         //        this.activeItem.deactivate();
2276         //        delete this.activeItem;
2277         //    }
2278         //}
2279         this.fireEvent("mouseout", this, e, t);
2280     },
2281     
2282     
2283     /**
2284      * Displays this menu relative to another element
2285      * @param {String/HTMLElement/Roo.Element} element The element to align to
2286      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2287      * the element (defaults to this.defaultAlign)
2288      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2289      */
2290     show : function(el, pos, parentMenu){
2291         this.parentMenu = parentMenu;
2292         if(!this.el){
2293             this.render();
2294         }
2295         this.fireEvent("beforeshow", this);
2296         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2297     },
2298      /**
2299      * Displays this menu at a specific xy position
2300      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2301      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2302      */
2303     showAt : function(xy, parentMenu, /* private: */_e){
2304         this.parentMenu = parentMenu;
2305         if(!this.el){
2306             this.render();
2307         }
2308         if(_e !== false){
2309             this.fireEvent("beforeshow", this);
2310             //xy = this.el.adjustForConstraints(xy);
2311         }
2312         
2313         //this.el.show();
2314         this.hideMenuItems();
2315         this.hidden = false;
2316         this.triggerEl.addClass('open');
2317         this.el.addClass('show');
2318         
2319         // reassign x when hitting right
2320         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2321             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2322         }
2323         
2324         // reassign y when hitting bottom
2325         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2326             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2327         }
2328         
2329         // but the list may align on trigger left or trigger top... should it be a properity?
2330         
2331         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2332             this.el.setXY(xy);
2333         }
2334         
2335         this.focus();
2336         this.fireEvent("show", this);
2337     },
2338     
2339     focus : function(){
2340         return;
2341         if(!this.hidden){
2342             this.doFocus.defer(50, this);
2343         }
2344     },
2345
2346     doFocus : function(){
2347         if(!this.hidden){
2348             this.focusEl.focus();
2349         }
2350     },
2351
2352     /**
2353      * Hides this menu and optionally all parent menus
2354      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2355      */
2356     hide : function(deep)
2357     {
2358         
2359         this.hideMenuItems();
2360         if(this.el && this.isVisible()){
2361             this.fireEvent("beforehide", this);
2362             if(this.activeItem){
2363                 this.activeItem.deactivate();
2364                 this.activeItem = null;
2365             }
2366             this.triggerEl.removeClass('open');;
2367             this.el.removeClass('show');
2368             this.hidden = true;
2369             this.fireEvent("hide", this);
2370         }
2371         if(deep === true && this.parentMenu){
2372             this.parentMenu.hide(true);
2373         }
2374     },
2375     
2376     onTriggerClick : function(e)
2377     {
2378         Roo.log('trigger click');
2379         
2380         var target = e.getTarget();
2381         
2382         Roo.log(target.nodeName.toLowerCase());
2383         
2384         if(target.nodeName.toLowerCase() === 'i'){
2385             e.preventDefault();
2386         }
2387         
2388     },
2389     
2390     onTriggerPress  : function(e)
2391     {
2392         Roo.log('trigger press');
2393         //Roo.log(e.getTarget());
2394        // Roo.log(this.triggerEl.dom);
2395        
2396         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2397         var pel = Roo.get(e.getTarget());
2398         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2399             Roo.log('is treeview or dropdown?');
2400             return;
2401         }
2402         
2403         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2404             return;
2405         }
2406         
2407         if (this.isVisible()) {
2408             Roo.log('hide');
2409             this.hide();
2410         } else {
2411             Roo.log('show');
2412             this.show(this.triggerEl, false, false);
2413         }
2414         
2415         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2416             e.stopEvent();
2417         }
2418         
2419     },
2420        
2421     
2422     hideMenuItems : function()
2423     {
2424         Roo.log("hide Menu Items");
2425         if (!this.el) { 
2426             return;
2427         }
2428         //$(backdrop).remove()
2429         this.el.select('.open',true).each(function(aa) {
2430             
2431             aa.removeClass('open');
2432           //var parent = getParent($(this))
2433           //var relatedTarget = { relatedTarget: this }
2434           
2435            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2436           //if (e.isDefaultPrevented()) return
2437            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2438         });
2439     },
2440     addxtypeChild : function (tree, cntr) {
2441         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2442           
2443         this.menuitems.add(comp);
2444         return comp;
2445
2446     },
2447     getEl : function()
2448     {
2449         Roo.log(this.el);
2450         return this.el;
2451     },
2452     
2453     clear : function()
2454     {
2455         this.getEl().dom.innerHTML = '';
2456         this.menuitems.clear();
2457     }
2458 });
2459
2460  
2461  /*
2462  * - LGPL
2463  *
2464  * menu item
2465  * 
2466  */
2467
2468
2469 /**
2470  * @class Roo.bootstrap.MenuItem
2471  * @extends Roo.bootstrap.Component
2472  * Bootstrap MenuItem class
2473  * @cfg {String} html the menu label
2474  * @cfg {String} href the link
2475  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2476  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2477  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2478  * @cfg {String} fa favicon to show on left of menu item.
2479  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2480  * 
2481  * 
2482  * @constructor
2483  * Create a new MenuItem
2484  * @param {Object} config The config object
2485  */
2486
2487
2488 Roo.bootstrap.MenuItem = function(config){
2489     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2490     this.addEvents({
2491         // raw events
2492         /**
2493          * @event click
2494          * The raw click event for the entire grid.
2495          * @param {Roo.bootstrap.MenuItem} this
2496          * @param {Roo.EventObject} e
2497          */
2498         "click" : true
2499     });
2500 };
2501
2502 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2503     
2504     href : false,
2505     html : false,
2506     preventDefault: false,
2507     isContainer : false,
2508     active : false,
2509     fa: false,
2510     
2511     getAutoCreate : function(){
2512         
2513         if(this.isContainer){
2514             return {
2515                 tag: 'li',
2516                 cls: 'dropdown-menu-item dropdown-item'
2517             };
2518         }
2519         var ctag = {
2520             tag: 'span',
2521             html: 'Link'
2522         };
2523         
2524         var anc = {
2525             tag : 'a',
2526             href : '#',
2527             cn : [  ]
2528         };
2529         
2530         if (this.fa !== false) {
2531             anc.cn.push({
2532                 tag : 'i',
2533                 cls : 'fa fa-' + this.fa
2534             });
2535         }
2536         
2537         anc.cn.push(ctag);
2538         
2539         
2540         var cfg= {
2541             tag: 'li',
2542             cls: 'dropdown-menu-item dropdown-item',
2543             cn: [ anc ]
2544         };
2545         if (this.parent().type == 'treeview') {
2546             cfg.cls = 'treeview-menu';
2547         }
2548         if (this.active) {
2549             cfg.cls += ' active';
2550         }
2551         
2552         
2553         
2554         anc.href = this.href || cfg.cn[0].href ;
2555         ctag.html = this.html || cfg.cn[0].html ;
2556         return cfg;
2557     },
2558     
2559     initEvents: function()
2560     {
2561         if (this.parent().type == 'treeview') {
2562             this.el.select('a').on('click', this.onClick, this);
2563         }
2564         
2565         if (this.menu) {
2566             this.menu.parentType = this.xtype;
2567             this.menu.triggerEl = this.el;
2568             this.menu = this.addxtype(Roo.apply({}, this.menu));
2569         }
2570         
2571     },
2572     onClick : function(e)
2573     {
2574         Roo.log('item on click ');
2575         
2576         if(this.preventDefault){
2577             e.preventDefault();
2578         }
2579         //this.parent().hideMenuItems();
2580         
2581         this.fireEvent('click', this, e);
2582     },
2583     getEl : function()
2584     {
2585         return this.el;
2586     } 
2587 });
2588
2589  
2590
2591  /*
2592  * - LGPL
2593  *
2594  * menu separator
2595  * 
2596  */
2597
2598
2599 /**
2600  * @class Roo.bootstrap.MenuSeparator
2601  * @extends Roo.bootstrap.Component
2602  * Bootstrap MenuSeparator class
2603  * 
2604  * @constructor
2605  * Create a new MenuItem
2606  * @param {Object} config The config object
2607  */
2608
2609
2610 Roo.bootstrap.MenuSeparator = function(config){
2611     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2612 };
2613
2614 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2615     
2616     getAutoCreate : function(){
2617         var cfg = {
2618             cls: 'divider',
2619             tag : 'li'
2620         };
2621         
2622         return cfg;
2623     }
2624    
2625 });
2626
2627  
2628
2629  
2630 /*
2631 * Licence: LGPL
2632 */
2633
2634 /**
2635  * @class Roo.bootstrap.Modal
2636  * @extends Roo.bootstrap.Component
2637  * Bootstrap Modal class
2638  * @cfg {String} title Title of dialog
2639  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2640  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2641  * @cfg {Boolean} specificTitle default false
2642  * @cfg {Array} buttons Array of buttons or standard button set..
2643  * @cfg {String} buttonPosition (left|right|center) default right
2644  * @cfg {Boolean} animate default true
2645  * @cfg {Boolean} allow_close default true
2646  * @cfg {Boolean} fitwindow default false
2647  * @cfg {String} size (sm|lg) default empty
2648  * @cfg {Number} max_width set the max width of modal
2649  *
2650  *
2651  * @constructor
2652  * Create a new Modal Dialog
2653  * @param {Object} config The config object
2654  */
2655
2656 Roo.bootstrap.Modal = function(config){
2657     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2658     this.addEvents({
2659         // raw events
2660         /**
2661          * @event btnclick
2662          * The raw btnclick event for the button
2663          * @param {Roo.EventObject} e
2664          */
2665         "btnclick" : true,
2666         /**
2667          * @event resize
2668          * Fire when dialog resize
2669          * @param {Roo.bootstrap.Modal} this
2670          * @param {Roo.EventObject} e
2671          */
2672         "resize" : true
2673     });
2674     this.buttons = this.buttons || [];
2675
2676     if (this.tmpl) {
2677         this.tmpl = Roo.factory(this.tmpl);
2678     }
2679
2680 };
2681
2682 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2683
2684     title : 'test dialog',
2685
2686     buttons : false,
2687
2688     // set on load...
2689
2690     html: false,
2691
2692     tmp: false,
2693
2694     specificTitle: false,
2695
2696     buttonPosition: 'right',
2697
2698     allow_close : true,
2699
2700     animate : true,
2701
2702     fitwindow: false,
2703     
2704      // private
2705     dialogEl: false,
2706     bodyEl:  false,
2707     footerEl:  false,
2708     titleEl:  false,
2709     closeEl:  false,
2710
2711     size: '',
2712     
2713     max_width: 0,
2714     
2715     max_height: 0,
2716     
2717     fit_content: false,
2718
2719     onRender : function(ct, position)
2720     {
2721         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2722
2723         if(!this.el){
2724             var cfg = Roo.apply({},  this.getAutoCreate());
2725             cfg.id = Roo.id();
2726             //if(!cfg.name){
2727             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2728             //}
2729             //if (!cfg.name.length) {
2730             //    delete cfg.name;
2731            // }
2732             if (this.cls) {
2733                 cfg.cls += ' ' + this.cls;
2734             }
2735             if (this.style) {
2736                 cfg.style = this.style;
2737             }
2738             this.el = Roo.get(document.body).createChild(cfg, position);
2739         }
2740         //var type = this.el.dom.type;
2741
2742
2743         if(this.tabIndex !== undefined){
2744             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2745         }
2746
2747         this.dialogEl = this.el.select('.modal-dialog',true).first();
2748         this.bodyEl = this.el.select('.modal-body',true).first();
2749         this.closeEl = this.el.select('.modal-header .close', true).first();
2750         this.headerEl = this.el.select('.modal-header',true).first();
2751         this.titleEl = this.el.select('.modal-title',true).first();
2752         this.footerEl = this.el.select('.modal-footer',true).first();
2753
2754         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2755         
2756         //this.el.addClass("x-dlg-modal");
2757
2758         if (this.buttons.length) {
2759             Roo.each(this.buttons, function(bb) {
2760                 var b = Roo.apply({}, bb);
2761                 b.xns = b.xns || Roo.bootstrap;
2762                 b.xtype = b.xtype || 'Button';
2763                 if (typeof(b.listeners) == 'undefined') {
2764                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2765                 }
2766
2767                 var btn = Roo.factory(b);
2768
2769                 btn.render(this.el.select('.modal-footer div').first());
2770
2771             },this);
2772         }
2773         // render the children.
2774         var nitems = [];
2775
2776         if(typeof(this.items) != 'undefined'){
2777             var items = this.items;
2778             delete this.items;
2779
2780             for(var i =0;i < items.length;i++) {
2781                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2782             }
2783         }
2784
2785         this.items = nitems;
2786
2787         // where are these used - they used to be body/close/footer
2788
2789
2790         this.initEvents();
2791         //this.el.addClass([this.fieldClass, this.cls]);
2792
2793     },
2794
2795     getAutoCreate : function()
2796     {
2797         var bdy = {
2798                 cls : 'modal-body',
2799                 html : this.html || ''
2800         };
2801
2802         var title = {
2803             tag: 'h4',
2804             cls : 'modal-title',
2805             html : this.title
2806         };
2807
2808         if(this.specificTitle){
2809             title = this.title;
2810
2811         };
2812
2813         var header = [];
2814         if (this.allow_close && Roo.bootstrap.version == 3) {
2815             header.push({
2816                 tag: 'button',
2817                 cls : 'close',
2818                 html : '&times'
2819             });
2820         }
2821
2822         header.push(title);
2823
2824         if (this.allow_close && Roo.bootstrap.version == 4) {
2825             header.push({
2826                 tag: 'button',
2827                 cls : 'close',
2828                 html : '&times'
2829             });
2830         }
2831         
2832         var size = '';
2833
2834         if(this.size.length){
2835             size = 'modal-' + this.size;
2836         }
2837
2838         var modal = {
2839             cls: "modal",
2840              cn : [
2841                 {
2842                     cls: "modal-dialog " + size,
2843                     cn : [
2844                         {
2845                             cls : "modal-content",
2846                             cn : [
2847                                 {
2848                                     cls : 'modal-header',
2849                                     cn : header
2850                                 },
2851                                 bdy,
2852                                 {
2853                                     cls : 'modal-footer',
2854                                     cn : [
2855                                         {
2856                                             tag: 'div',
2857                                             cls: 'btn-' + this.buttonPosition
2858                                         }
2859                                     ]
2860
2861                                 }
2862
2863
2864                             ]
2865
2866                         }
2867                     ]
2868
2869                 }
2870             ]
2871         };
2872
2873         if(this.animate){
2874             modal.cls += ' fade';
2875         }
2876
2877         return modal;
2878
2879     },
2880     getChildContainer : function() {
2881
2882          return this.bodyEl;
2883
2884     },
2885     getButtonContainer : function() {
2886          return this.el.select('.modal-footer div',true).first();
2887
2888     },
2889     initEvents : function()
2890     {
2891         if (this.allow_close) {
2892             this.closeEl.on('click', this.hide, this);
2893         }
2894         Roo.EventManager.onWindowResize(this.resize, this, true);
2895
2896
2897     },
2898
2899     resize : function()
2900     {
2901         this.maskEl.setSize(
2902             Roo.lib.Dom.getViewWidth(true),
2903             Roo.lib.Dom.getViewHeight(true)
2904         );
2905         
2906         if (this.fitwindow) {
2907             this.setSize(
2908                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2909                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2910             );
2911             return;
2912         }
2913         
2914         if(this.max_width !== 0) {
2915             
2916             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2917             
2918             if(this.height) {
2919                 this.setSize(w, this.height);
2920                 return;
2921             }
2922             
2923             if(this.max_height) {
2924                 this.setSize(w,Math.min(
2925                     this.max_height,
2926                     Roo.lib.Dom.getViewportHeight(true) - 60
2927                 ));
2928                 
2929                 return;
2930             }
2931             
2932             if(!this.fit_content) {
2933                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2934                 return;
2935             }
2936             
2937             this.setSize(w, Math.min(
2938                 60 +
2939                 this.headerEl.getHeight() + 
2940                 this.footerEl.getHeight() + 
2941                 this.getChildHeight(this.bodyEl.dom.childNodes),
2942                 Roo.lib.Dom.getViewportHeight(true) - 60)
2943             );
2944         }
2945         
2946     },
2947
2948     setSize : function(w,h)
2949     {
2950         if (!w && !h) {
2951             return;
2952         }
2953         
2954         this.resizeTo(w,h);
2955     },
2956
2957     show : function() {
2958
2959         if (!this.rendered) {
2960             this.render();
2961         }
2962
2963         //this.el.setStyle('display', 'block');
2964         this.el.removeClass('hideing');
2965         this.el.dom.style.display='block';
2966         
2967         Roo.get(document.body).addClass('modal-open');
2968  
2969         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2970             var _this = this;
2971             (function(){
2972                 this.el.addClass('show');
2973                 this.el.addClass('in');
2974             }).defer(50, this);
2975         }else{
2976             this.el.addClass('show');
2977             this.el.addClass('in');
2978         }
2979
2980         // not sure how we can show data in here..
2981         //if (this.tmpl) {
2982         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2983         //}
2984
2985         Roo.get(document.body).addClass("x-body-masked");
2986         
2987         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2988         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2989         this.maskEl.dom.style.display = 'block';
2990         this.maskEl.addClass('show');
2991         
2992         
2993         this.resize();
2994         
2995         this.fireEvent('show', this);
2996
2997         // set zindex here - otherwise it appears to be ignored...
2998         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2999
3000         (function () {
3001             this.items.forEach( function(e) {
3002                 e.layout ? e.layout() : false;
3003
3004             });
3005         }).defer(100,this);
3006
3007     },
3008     hide : function()
3009     {
3010         if(this.fireEvent("beforehide", this) !== false){
3011             
3012             this.maskEl.removeClass('show');
3013             
3014             this.maskEl.dom.style.display = '';
3015             Roo.get(document.body).removeClass("x-body-masked");
3016             this.el.removeClass('in');
3017             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3018
3019             if(this.animate){ // why
3020                 this.el.addClass('hideing');
3021                 this.el.removeClass('show');
3022                 (function(){
3023                     if (!this.el.hasClass('hideing')) {
3024                         return; // it's been shown again...
3025                     }
3026                     
3027                     this.el.dom.style.display='';
3028
3029                     Roo.get(document.body).removeClass('modal-open');
3030                     this.el.removeClass('hideing');
3031                 }).defer(150,this);
3032                 
3033             }else{
3034                 this.el.removeClass('show');
3035                 this.el.dom.style.display='';
3036                 Roo.get(document.body).removeClass('modal-open');
3037
3038             }
3039             this.fireEvent('hide', this);
3040         }
3041     },
3042     isVisible : function()
3043     {
3044         
3045         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3046         
3047     },
3048
3049     addButton : function(str, cb)
3050     {
3051
3052
3053         var b = Roo.apply({}, { html : str } );
3054         b.xns = b.xns || Roo.bootstrap;
3055         b.xtype = b.xtype || 'Button';
3056         if (typeof(b.listeners) == 'undefined') {
3057             b.listeners = { click : cb.createDelegate(this)  };
3058         }
3059
3060         var btn = Roo.factory(b);
3061
3062         btn.render(this.el.select('.modal-footer div').first());
3063
3064         return btn;
3065
3066     },
3067
3068     setDefaultButton : function(btn)
3069     {
3070         //this.el.select('.modal-footer').()
3071     },
3072     diff : false,
3073
3074     resizeTo: function(w,h)
3075     {
3076         // skip.. ?? why??
3077
3078         this.dialogEl.setWidth(w);
3079         if (this.diff === false) {
3080             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3081         }
3082
3083         this.bodyEl.setHeight(h - this.diff);
3084
3085         this.fireEvent('resize', this);
3086
3087     },
3088     setContentSize  : function(w, h)
3089     {
3090
3091     },
3092     onButtonClick: function(btn,e)
3093     {
3094         //Roo.log([a,b,c]);
3095         this.fireEvent('btnclick', btn.name, e);
3096     },
3097      /**
3098      * Set the title of the Dialog
3099      * @param {String} str new Title
3100      */
3101     setTitle: function(str) {
3102         this.titleEl.dom.innerHTML = str;
3103     },
3104     /**
3105      * Set the body of the Dialog
3106      * @param {String} str new Title
3107      */
3108     setBody: function(str) {
3109         this.bodyEl.dom.innerHTML = str;
3110     },
3111     /**
3112      * Set the body of the Dialog using the template
3113      * @param {Obj} data - apply this data to the template and replace the body contents.
3114      */
3115     applyBody: function(obj)
3116     {
3117         if (!this.tmpl) {
3118             Roo.log("Error - using apply Body without a template");
3119             //code
3120         }
3121         this.tmpl.overwrite(this.bodyEl, obj);
3122     },
3123     
3124     getChildHeight : function(child_nodes)
3125     {
3126         if(
3127             !child_nodes ||
3128             child_nodes.length == 0
3129         ) {
3130             return;
3131         }
3132         
3133         var child_height = 0;
3134         
3135         for(var i = 0; i < child_nodes.length; i++) {
3136             
3137             /*
3138             * for modal with tabs...
3139             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3140                 
3141                 var layout_childs = child_nodes[i].childNodes;
3142                 
3143                 for(var j = 0; j < layout_childs.length; j++) {
3144                     
3145                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3146                         
3147                         var layout_body_childs = layout_childs[j].childNodes;
3148                         
3149                         for(var k = 0; k < layout_body_childs.length; k++) {
3150                             
3151                             if(layout_body_childs[k].classList.contains('navbar')) {
3152                                 child_height += layout_body_childs[k].offsetHeight;
3153                                 continue;
3154                             }
3155                             
3156                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3157                                 
3158                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3159                                 
3160                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3161                                     
3162                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3163                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3164                                         continue;
3165                                     }
3166                                     
3167                                 }
3168                                 
3169                             }
3170                             
3171                         }
3172                     }
3173                 }
3174                 continue;
3175             }
3176             */
3177             
3178             child_height += child_nodes[i].offsetHeight;
3179             // Roo.log(child_nodes[i].offsetHeight);
3180         }
3181         
3182         return child_height;
3183     }
3184
3185 });
3186
3187
3188 Roo.apply(Roo.bootstrap.Modal,  {
3189     /**
3190          * Button config that displays a single OK button
3191          * @type Object
3192          */
3193         OK :  [{
3194             name : 'ok',
3195             weight : 'primary',
3196             html : 'OK'
3197         }],
3198         /**
3199          * Button config that displays Yes and No buttons
3200          * @type Object
3201          */
3202         YESNO : [
3203             {
3204                 name  : 'no',
3205                 html : 'No'
3206             },
3207             {
3208                 name  :'yes',
3209                 weight : 'primary',
3210                 html : 'Yes'
3211             }
3212         ],
3213
3214         /**
3215          * Button config that displays OK and Cancel buttons
3216          * @type Object
3217          */
3218         OKCANCEL : [
3219             {
3220                name : 'cancel',
3221                 html : 'Cancel'
3222             },
3223             {
3224                 name : 'ok',
3225                 weight : 'primary',
3226                 html : 'OK'
3227             }
3228         ],
3229         /**
3230          * Button config that displays Yes, No and Cancel buttons
3231          * @type Object
3232          */
3233         YESNOCANCEL : [
3234             {
3235                 name : 'yes',
3236                 weight : 'primary',
3237                 html : 'Yes'
3238             },
3239             {
3240                 name : 'no',
3241                 html : 'No'
3242             },
3243             {
3244                 name : 'cancel',
3245                 html : 'Cancel'
3246             }
3247         ],
3248         
3249         zIndex : 10001
3250 });
3251 /*
3252  * - LGPL
3253  *
3254  * messagebox - can be used as a replace
3255  * 
3256  */
3257 /**
3258  * @class Roo.MessageBox
3259  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3260  * Example usage:
3261  *<pre><code>
3262 // Basic alert:
3263 Roo.Msg.alert('Status', 'Changes saved successfully.');
3264
3265 // Prompt for user data:
3266 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3267     if (btn == 'ok'){
3268         // process text value...
3269     }
3270 });
3271
3272 // Show a dialog using config options:
3273 Roo.Msg.show({
3274    title:'Save Changes?',
3275    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3276    buttons: Roo.Msg.YESNOCANCEL,
3277    fn: processResult,
3278    animEl: 'elId'
3279 });
3280 </code></pre>
3281  * @singleton
3282  */
3283 Roo.bootstrap.MessageBox = function(){
3284     var dlg, opt, mask, waitTimer;
3285     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3286     var buttons, activeTextEl, bwidth;
3287
3288     
3289     // private
3290     var handleButton = function(button){
3291         dlg.hide();
3292         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3293     };
3294
3295     // private
3296     var handleHide = function(){
3297         if(opt && opt.cls){
3298             dlg.el.removeClass(opt.cls);
3299         }
3300         //if(waitTimer){
3301         //    Roo.TaskMgr.stop(waitTimer);
3302         //    waitTimer = null;
3303         //}
3304     };
3305
3306     // private
3307     var updateButtons = function(b){
3308         var width = 0;
3309         if(!b){
3310             buttons["ok"].hide();
3311             buttons["cancel"].hide();
3312             buttons["yes"].hide();
3313             buttons["no"].hide();
3314             //dlg.footer.dom.style.display = 'none';
3315             return width;
3316         }
3317         dlg.footerEl.dom.style.display = '';
3318         for(var k in buttons){
3319             if(typeof buttons[k] != "function"){
3320                 if(b[k]){
3321                     buttons[k].show();
3322                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3323                     width += buttons[k].el.getWidth()+15;
3324                 }else{
3325                     buttons[k].hide();
3326                 }
3327             }
3328         }
3329         return width;
3330     };
3331
3332     // private
3333     var handleEsc = function(d, k, e){
3334         if(opt && opt.closable !== false){
3335             dlg.hide();
3336         }
3337         if(e){
3338             e.stopEvent();
3339         }
3340     };
3341
3342     return {
3343         /**
3344          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3345          * @return {Roo.BasicDialog} The BasicDialog element
3346          */
3347         getDialog : function(){
3348            if(!dlg){
3349                 dlg = new Roo.bootstrap.Modal( {
3350                     //draggable: true,
3351                     //resizable:false,
3352                     //constraintoviewport:false,
3353                     //fixedcenter:true,
3354                     //collapsible : false,
3355                     //shim:true,
3356                     //modal: true,
3357                 //    width: 'auto',
3358                   //  height:100,
3359                     //buttonAlign:"center",
3360                     closeClick : function(){
3361                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3362                             handleButton("no");
3363                         }else{
3364                             handleButton("cancel");
3365                         }
3366                     }
3367                 });
3368                 dlg.render();
3369                 dlg.on("hide", handleHide);
3370                 mask = dlg.mask;
3371                 //dlg.addKeyListener(27, handleEsc);
3372                 buttons = {};
3373                 this.buttons = buttons;
3374                 var bt = this.buttonText;
3375                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3376                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3377                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3378                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3379                 //Roo.log(buttons);
3380                 bodyEl = dlg.bodyEl.createChild({
3381
3382                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3383                         '<textarea class="roo-mb-textarea"></textarea>' +
3384                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3385                 });
3386                 msgEl = bodyEl.dom.firstChild;
3387                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3388                 textboxEl.enableDisplayMode();
3389                 textboxEl.addKeyListener([10,13], function(){
3390                     if(dlg.isVisible() && opt && opt.buttons){
3391                         if(opt.buttons.ok){
3392                             handleButton("ok");
3393                         }else if(opt.buttons.yes){
3394                             handleButton("yes");
3395                         }
3396                     }
3397                 });
3398                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3399                 textareaEl.enableDisplayMode();
3400                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3401                 progressEl.enableDisplayMode();
3402                 
3403                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3404                 var pf = progressEl.dom.firstChild;
3405                 if (pf) {
3406                     pp = Roo.get(pf.firstChild);
3407                     pp.setHeight(pf.offsetHeight);
3408                 }
3409                 
3410             }
3411             return dlg;
3412         },
3413
3414         /**
3415          * Updates the message box body text
3416          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3417          * the XHTML-compliant non-breaking space character '&amp;#160;')
3418          * @return {Roo.MessageBox} This message box
3419          */
3420         updateText : function(text)
3421         {
3422             if(!dlg.isVisible() && !opt.width){
3423                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3424                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3425             }
3426             msgEl.innerHTML = text || '&#160;';
3427       
3428             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3429             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3430             var w = Math.max(
3431                     Math.min(opt.width || cw , this.maxWidth), 
3432                     Math.max(opt.minWidth || this.minWidth, bwidth)
3433             );
3434             if(opt.prompt){
3435                 activeTextEl.setWidth(w);
3436             }
3437             if(dlg.isVisible()){
3438                 dlg.fixedcenter = false;
3439             }
3440             // to big, make it scroll. = But as usual stupid IE does not support
3441             // !important..
3442             
3443             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3444                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3445                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3446             } else {
3447                 bodyEl.dom.style.height = '';
3448                 bodyEl.dom.style.overflowY = '';
3449             }
3450             if (cw > w) {
3451                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3452             } else {
3453                 bodyEl.dom.style.overflowX = '';
3454             }
3455             
3456             dlg.setContentSize(w, bodyEl.getHeight());
3457             if(dlg.isVisible()){
3458                 dlg.fixedcenter = true;
3459             }
3460             return this;
3461         },
3462
3463         /**
3464          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3465          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3466          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3467          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3468          * @return {Roo.MessageBox} This message box
3469          */
3470         updateProgress : function(value, text){
3471             if(text){
3472                 this.updateText(text);
3473             }
3474             
3475             if (pp) { // weird bug on my firefox - for some reason this is not defined
3476                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3477                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3478             }
3479             return this;
3480         },        
3481
3482         /**
3483          * Returns true if the message box is currently displayed
3484          * @return {Boolean} True if the message box is visible, else false
3485          */
3486         isVisible : function(){
3487             return dlg && dlg.isVisible();  
3488         },
3489
3490         /**
3491          * Hides the message box if it is displayed
3492          */
3493         hide : function(){
3494             if(this.isVisible()){
3495                 dlg.hide();
3496             }  
3497         },
3498
3499         /**
3500          * Displays a new message box, or reinitializes an existing message box, based on the config options
3501          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3502          * The following config object properties are supported:
3503          * <pre>
3504 Property    Type             Description
3505 ----------  ---------------  ------------------------------------------------------------------------------------
3506 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3507                                    closes (defaults to undefined)
3508 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3509                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3510 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3511                                    progress and wait dialogs will ignore this property and always hide the
3512                                    close button as they can only be closed programmatically.
3513 cls               String           A custom CSS class to apply to the message box element
3514 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3515                                    displayed (defaults to 75)
3516 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3517                                    function will be btn (the name of the button that was clicked, if applicable,
3518                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3519                                    Progress and wait dialogs will ignore this option since they do not respond to
3520                                    user actions and can only be closed programmatically, so any required function
3521                                    should be called by the same code after it closes the dialog.
3522 icon              String           A CSS class that provides a background image to be used as an icon for
3523                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3524 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3525 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3526 modal             Boolean          False to allow user interaction with the page while the message box is
3527                                    displayed (defaults to true)
3528 msg               String           A string that will replace the existing message box body text (defaults
3529                                    to the XHTML-compliant non-breaking space character '&#160;')
3530 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3531 progress          Boolean          True to display a progress bar (defaults to false)
3532 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3533 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3534 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3535 title             String           The title text
3536 value             String           The string value to set into the active textbox element if displayed
3537 wait              Boolean          True to display a progress bar (defaults to false)
3538 width             Number           The width of the dialog in pixels
3539 </pre>
3540          *
3541          * Example usage:
3542          * <pre><code>
3543 Roo.Msg.show({
3544    title: 'Address',
3545    msg: 'Please enter your address:',
3546    width: 300,
3547    buttons: Roo.MessageBox.OKCANCEL,
3548    multiline: true,
3549    fn: saveAddress,
3550    animEl: 'addAddressBtn'
3551 });
3552 </code></pre>
3553          * @param {Object} config Configuration options
3554          * @return {Roo.MessageBox} This message box
3555          */
3556         show : function(options)
3557         {
3558             
3559             // this causes nightmares if you show one dialog after another
3560             // especially on callbacks..
3561              
3562             if(this.isVisible()){
3563                 
3564                 this.hide();
3565                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3566                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3567                 Roo.log("New Dialog Message:" +  options.msg )
3568                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3569                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3570                 
3571             }
3572             var d = this.getDialog();
3573             opt = options;
3574             d.setTitle(opt.title || "&#160;");
3575             d.closeEl.setDisplayed(opt.closable !== false);
3576             activeTextEl = textboxEl;
3577             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3578             if(opt.prompt){
3579                 if(opt.multiline){
3580                     textboxEl.hide();
3581                     textareaEl.show();
3582                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3583                         opt.multiline : this.defaultTextHeight);
3584                     activeTextEl = textareaEl;
3585                 }else{
3586                     textboxEl.show();
3587                     textareaEl.hide();
3588                 }
3589             }else{
3590                 textboxEl.hide();
3591                 textareaEl.hide();
3592             }
3593             progressEl.setDisplayed(opt.progress === true);
3594             this.updateProgress(0);
3595             activeTextEl.dom.value = opt.value || "";
3596             if(opt.prompt){
3597                 dlg.setDefaultButton(activeTextEl);
3598             }else{
3599                 var bs = opt.buttons;
3600                 var db = null;
3601                 if(bs && bs.ok){
3602                     db = buttons["ok"];
3603                 }else if(bs && bs.yes){
3604                     db = buttons["yes"];
3605                 }
3606                 dlg.setDefaultButton(db);
3607             }
3608             bwidth = updateButtons(opt.buttons);
3609             this.updateText(opt.msg);
3610             if(opt.cls){
3611                 d.el.addClass(opt.cls);
3612             }
3613             d.proxyDrag = opt.proxyDrag === true;
3614             d.modal = opt.modal !== false;
3615             d.mask = opt.modal !== false ? mask : false;
3616             if(!d.isVisible()){
3617                 // force it to the end of the z-index stack so it gets a cursor in FF
3618                 document.body.appendChild(dlg.el.dom);
3619                 d.animateTarget = null;
3620                 d.show(options.animEl);
3621             }
3622             return this;
3623         },
3624
3625         /**
3626          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3627          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3628          * and closing the message box when the process is complete.
3629          * @param {String} title The title bar text
3630          * @param {String} msg The message box body text
3631          * @return {Roo.MessageBox} This message box
3632          */
3633         progress : function(title, msg){
3634             this.show({
3635                 title : title,
3636                 msg : msg,
3637                 buttons: false,
3638                 progress:true,
3639                 closable:false,
3640                 minWidth: this.minProgressWidth,
3641                 modal : true
3642             });
3643             return this;
3644         },
3645
3646         /**
3647          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3648          * If a callback function is passed it will be called after the user clicks the button, and the
3649          * id of the button that was clicked will be passed as the only parameter to the callback
3650          * (could also be the top-right close button).
3651          * @param {String} title The title bar text
3652          * @param {String} msg The message box body text
3653          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3654          * @param {Object} scope (optional) The scope of the callback function
3655          * @return {Roo.MessageBox} This message box
3656          */
3657         alert : function(title, msg, fn, scope)
3658         {
3659             this.show({
3660                 title : title,
3661                 msg : msg,
3662                 buttons: this.OK,
3663                 fn: fn,
3664                 closable : false,
3665                 scope : scope,
3666                 modal : true
3667             });
3668             return this;
3669         },
3670
3671         /**
3672          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3673          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3674          * You are responsible for closing the message box when the process is complete.
3675          * @param {String} msg The message box body text
3676          * @param {String} title (optional) The title bar text
3677          * @return {Roo.MessageBox} This message box
3678          */
3679         wait : function(msg, title){
3680             this.show({
3681                 title : title,
3682                 msg : msg,
3683                 buttons: false,
3684                 closable:false,
3685                 progress:true,
3686                 modal:true,
3687                 width:300,
3688                 wait:true
3689             });
3690             waitTimer = Roo.TaskMgr.start({
3691                 run: function(i){
3692                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3693                 },
3694                 interval: 1000
3695             });
3696             return this;
3697         },
3698
3699         /**
3700          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3701          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3702          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3703          * @param {String} title The title bar text
3704          * @param {String} msg The message box body text
3705          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3706          * @param {Object} scope (optional) The scope of the callback function
3707          * @return {Roo.MessageBox} This message box
3708          */
3709         confirm : function(title, msg, fn, scope){
3710             this.show({
3711                 title : title,
3712                 msg : msg,
3713                 buttons: this.YESNO,
3714                 fn: fn,
3715                 scope : scope,
3716                 modal : true
3717             });
3718             return this;
3719         },
3720
3721         /**
3722          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3723          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3724          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3725          * (could also be the top-right close button) and the text that was entered will be passed as the two
3726          * parameters to the callback.
3727          * @param {String} title The title bar text
3728          * @param {String} msg The message box body text
3729          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3730          * @param {Object} scope (optional) The scope of the callback function
3731          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3732          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3733          * @return {Roo.MessageBox} This message box
3734          */
3735         prompt : function(title, msg, fn, scope, multiline){
3736             this.show({
3737                 title : title,
3738                 msg : msg,
3739                 buttons: this.OKCANCEL,
3740                 fn: fn,
3741                 minWidth:250,
3742                 scope : scope,
3743                 prompt:true,
3744                 multiline: multiline,
3745                 modal : true
3746             });
3747             return this;
3748         },
3749
3750         /**
3751          * Button config that displays a single OK button
3752          * @type Object
3753          */
3754         OK : {ok:true},
3755         /**
3756          * Button config that displays Yes and No buttons
3757          * @type Object
3758          */
3759         YESNO : {yes:true, no:true},
3760         /**
3761          * Button config that displays OK and Cancel buttons
3762          * @type Object
3763          */
3764         OKCANCEL : {ok:true, cancel:true},
3765         /**
3766          * Button config that displays Yes, No and Cancel buttons
3767          * @type Object
3768          */
3769         YESNOCANCEL : {yes:true, no:true, cancel:true},
3770
3771         /**
3772          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3773          * @type Number
3774          */
3775         defaultTextHeight : 75,
3776         /**
3777          * The maximum width in pixels of the message box (defaults to 600)
3778          * @type Number
3779          */
3780         maxWidth : 600,
3781         /**
3782          * The minimum width in pixels of the message box (defaults to 100)
3783          * @type Number
3784          */
3785         minWidth : 100,
3786         /**
3787          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3788          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3789          * @type Number
3790          */
3791         minProgressWidth : 250,
3792         /**
3793          * An object containing the default button text strings that can be overriden for localized language support.
3794          * Supported properties are: ok, cancel, yes and no.
3795          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3796          * @type Object
3797          */
3798         buttonText : {
3799             ok : "OK",
3800             cancel : "Cancel",
3801             yes : "Yes",
3802             no : "No"
3803         }
3804     };
3805 }();
3806
3807 /**
3808  * Shorthand for {@link Roo.MessageBox}
3809  */
3810 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3811 Roo.Msg = Roo.Msg || Roo.MessageBox;
3812 /*
3813  * - LGPL
3814  *
3815  * navbar
3816  * 
3817  */
3818
3819 /**
3820  * @class Roo.bootstrap.Navbar
3821  * @extends Roo.bootstrap.Component
3822  * Bootstrap Navbar class
3823
3824  * @constructor
3825  * Create a new Navbar
3826  * @param {Object} config The config object
3827  */
3828
3829
3830 Roo.bootstrap.Navbar = function(config){
3831     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3832     this.addEvents({
3833         // raw events
3834         /**
3835          * @event beforetoggle
3836          * Fire before toggle the menu
3837          * @param {Roo.EventObject} e
3838          */
3839         "beforetoggle" : true
3840     });
3841 };
3842
3843 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3844     
3845     
3846    
3847     // private
3848     navItems : false,
3849     loadMask : false,
3850     
3851     
3852     getAutoCreate : function(){
3853         
3854         
3855         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3856         
3857     },
3858     
3859     initEvents :function ()
3860     {
3861         //Roo.log(this.el.select('.navbar-toggle',true));
3862         this.el.select('.navbar-toggle',true).on('click', function() {
3863             if(this.fireEvent('beforetoggle', this) !== false){
3864                 var ce = this.el.select('.navbar-collapse',true).first();
3865                 ce.toggleClass('in'); // old...
3866                 if (ce.hasClass('collapse')) {
3867                     // show it...
3868                     ce.removeClass('collapse');
3869                     ce.addClass('collapsing');
3870                     var h = ce.getHeight();
3871                     ce.setHeight(0); // resize it ...
3872                     
3873                     
3874                      
3875                     // now flag it as moving..
3876                     
3877                     (function() {
3878                         ce.removeClass('collapsing');
3879                         ce.addClass('show');
3880                         ce.removeClass('collapse');
3881
3882                         ce.dom.style.height = '';
3883                     }).defer(500);
3884                     ce.setHeight(h);
3885                     
3886                 } else {
3887                     ce.addClass('collapsing');
3888                     ce.removeClass('show');
3889                     (function() {
3890                         ce.removeClass('collapsing');
3891                         ce.addClass('collapse');
3892                         
3893                     }).defer(200);
3894                     
3895                 }
3896             }
3897             
3898         }, this);
3899         
3900         var mark = {
3901             tag: "div",
3902             cls:"x-dlg-mask"
3903         };
3904         
3905         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3906         
3907         var size = this.el.getSize();
3908         this.maskEl.setSize(size.width, size.height);
3909         this.maskEl.enableDisplayMode("block");
3910         this.maskEl.hide();
3911         
3912         if(this.loadMask){
3913             this.maskEl.show();
3914         }
3915     },
3916     
3917     
3918     getChildContainer : function()
3919     {
3920         if (this.el.select('.collapse').getCount()) {
3921             return this.el.select('.collapse',true).first();
3922         }
3923         
3924         return this.el;
3925     },
3926     
3927     mask : function()
3928     {
3929         this.maskEl.show();
3930     },
3931     
3932     unmask : function()
3933     {
3934         this.maskEl.hide();
3935     } 
3936     
3937     
3938     
3939     
3940 });
3941
3942
3943
3944  
3945
3946  /*
3947  * - LGPL
3948  *
3949  * navbar
3950  * 
3951  */
3952
3953 /**
3954  * @class Roo.bootstrap.NavSimplebar
3955  * @extends Roo.bootstrap.Navbar
3956  * Bootstrap Sidebar class
3957  *
3958  * @cfg {Boolean} inverse is inverted color
3959  * 
3960  * @cfg {String} type (nav | pills | tabs)
3961  * @cfg {Boolean} arrangement stacked | justified
3962  * @cfg {String} align (left | right) alignment
3963  * 
3964  * @cfg {Boolean} main (true|false) main nav bar? default false
3965  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3966  * 
3967  * @cfg {String} tag (header|footer|nav|div) default is nav 
3968
3969  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3970  * 
3971  * 
3972  * @constructor
3973  * Create a new Sidebar
3974  * @param {Object} config The config object
3975  */
3976
3977
3978 Roo.bootstrap.NavSimplebar = function(config){
3979     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3980 };
3981
3982 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3983     
3984     inverse: false,
3985     
3986     type: false,
3987     arrangement: '',
3988     align : false,
3989     
3990     weight : 'light',
3991     
3992     main : false,
3993     
3994     
3995     tag : false,
3996     
3997     
3998     getAutoCreate : function(){
3999         
4000         
4001         var cfg = {
4002             tag : this.tag || 'div',
4003             cls : 'navbar navbar-expand-lg'
4004         };
4005         if (['light','white'].indexOf(this.weight) > -1) {
4006             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4007         }
4008         cfg.cls += ' bg-' + this.weight;
4009         
4010           
4011         
4012         cfg.cn = [
4013             {
4014                 cls: 'nav',
4015                 tag : 'ul'
4016             }
4017         ];
4018         
4019          
4020         this.type = this.type || 'nav';
4021         if (['tabs','pills'].indexOf(this.type)!==-1) {
4022             cfg.cn[0].cls += ' nav-' + this.type
4023         
4024         
4025         } else {
4026             if (this.type!=='nav') {
4027                 Roo.log('nav type must be nav/tabs/pills')
4028             }
4029             cfg.cn[0].cls += ' navbar-nav'
4030         }
4031         
4032         
4033         
4034         
4035         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4036             cfg.cn[0].cls += ' nav-' + this.arrangement;
4037         }
4038         
4039         
4040         if (this.align === 'right') {
4041             cfg.cn[0].cls += ' navbar-right';
4042         }
4043         
4044         if (this.inverse) {
4045             cfg.cls += ' navbar-inverse';
4046             
4047         }
4048         
4049         
4050         return cfg;
4051     
4052         
4053     }
4054     
4055     
4056     
4057 });
4058
4059
4060
4061  
4062
4063  
4064        /*
4065  * - LGPL
4066  *
4067  * navbar
4068  * navbar-fixed-top
4069  * navbar-expand-md  fixed-top 
4070  */
4071
4072 /**
4073  * @class Roo.bootstrap.NavHeaderbar
4074  * @extends Roo.bootstrap.NavSimplebar
4075  * Bootstrap Sidebar class
4076  *
4077  * @cfg {String} brand what is brand
4078  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4079  * @cfg {String} brand_href href of the brand
4080  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4081  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4082  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4083  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4084  * 
4085  * @constructor
4086  * Create a new Sidebar
4087  * @param {Object} config The config object
4088  */
4089
4090
4091 Roo.bootstrap.NavHeaderbar = function(config){
4092     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4093       
4094 };
4095
4096 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4097     
4098     position: '',
4099     brand: '',
4100     brand_href: false,
4101     srButton : true,
4102     autohide : false,
4103     desktopCenter : false,
4104    
4105     
4106     getAutoCreate : function(){
4107         
4108         var   cfg = {
4109             tag: this.nav || 'nav',
4110             cls: 'navbar navbar-expand-md',
4111             role: 'navigation',
4112             cn: []
4113         };
4114         
4115         var cn = cfg.cn;
4116         if (this.desktopCenter) {
4117             cn.push({cls : 'container', cn : []});
4118             cn = cn[0].cn;
4119         }
4120         
4121         if(this.srButton){
4122             var btn = {
4123                 tag: 'button',
4124                 type: 'button',
4125                 cls: 'navbar-toggle navbar-toggler',
4126                 'data-toggle': 'collapse',
4127                 cn: [
4128                     {
4129                         tag: 'span',
4130                         cls: 'sr-only',
4131                         html: 'Toggle navigation'
4132                     },
4133                     {
4134                         tag: 'span',
4135                         cls: 'icon-bar navbar-toggler-icon'
4136                     },
4137                     {
4138                         tag: 'span',
4139                         cls: 'icon-bar'
4140                     },
4141                     {
4142                         tag: 'span',
4143                         cls: 'icon-bar'
4144                     }
4145                 ]
4146             };
4147             
4148             cn.push( Roo.bootstrap.version == 4 ? btn : {
4149                 tag: 'div',
4150                 cls: 'navbar-header',
4151                 cn: [
4152                     btn
4153                 ]
4154             });
4155         }
4156         
4157         cn.push({
4158             tag: 'div',
4159             cls: 'collapse navbar-collapse',
4160             cn : []
4161         });
4162         
4163         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4164         
4165         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4166             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4167             
4168             // tag can override this..
4169             
4170             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4171         }
4172         
4173         if (this.brand !== '') {
4174             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4175             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4176                 tag: 'a',
4177                 href: this.brand_href ? this.brand_href : '#',
4178                 cls: 'navbar-brand',
4179                 cn: [
4180                 this.brand
4181                 ]
4182             });
4183         }
4184         
4185         if(this.main){
4186             cfg.cls += ' main-nav';
4187         }
4188         
4189         
4190         return cfg;
4191
4192         
4193     },
4194     getHeaderChildContainer : function()
4195     {
4196         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4197             return this.el.select('.navbar-header',true).first();
4198         }
4199         
4200         return this.getChildContainer();
4201     },
4202     
4203     
4204     initEvents : function()
4205     {
4206         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4207         
4208         if (this.autohide) {
4209             
4210             var prevScroll = 0;
4211             var ft = this.el;
4212             
4213             Roo.get(document).on('scroll',function(e) {
4214                 var ns = Roo.get(document).getScroll().top;
4215                 var os = prevScroll;
4216                 prevScroll = ns;
4217                 
4218                 if(ns > os){
4219                     ft.removeClass('slideDown');
4220                     ft.addClass('slideUp');
4221                     return;
4222                 }
4223                 ft.removeClass('slideUp');
4224                 ft.addClass('slideDown');
4225                  
4226               
4227           },this);
4228         }
4229     }    
4230     
4231 });
4232
4233
4234
4235  
4236
4237  /*
4238  * - LGPL
4239  *
4240  * navbar
4241  * 
4242  */
4243
4244 /**
4245  * @class Roo.bootstrap.NavSidebar
4246  * @extends Roo.bootstrap.Navbar
4247  * Bootstrap Sidebar class
4248  * 
4249  * @constructor
4250  * Create a new Sidebar
4251  * @param {Object} config The config object
4252  */
4253
4254
4255 Roo.bootstrap.NavSidebar = function(config){
4256     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4257 };
4258
4259 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4260     
4261     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4262     
4263     getAutoCreate : function(){
4264         
4265         
4266         return  {
4267             tag: 'div',
4268             cls: 'sidebar sidebar-nav'
4269         };
4270     
4271         
4272     }
4273     
4274     
4275     
4276 });
4277
4278
4279
4280  
4281
4282  /*
4283  * - LGPL
4284  *
4285  * nav group
4286  * 
4287  */
4288
4289 /**
4290  * @class Roo.bootstrap.NavGroup
4291  * @extends Roo.bootstrap.Component
4292  * Bootstrap NavGroup class
4293  * @cfg {String} align (left|right)
4294  * @cfg {Boolean} inverse
4295  * @cfg {String} type (nav|pills|tab) default nav
4296  * @cfg {String} navId - reference Id for navbar.
4297
4298  * 
4299  * @constructor
4300  * Create a new nav group
4301  * @param {Object} config The config object
4302  */
4303
4304 Roo.bootstrap.NavGroup = function(config){
4305     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4306     this.navItems = [];
4307    
4308     Roo.bootstrap.NavGroup.register(this);
4309      this.addEvents({
4310         /**
4311              * @event changed
4312              * Fires when the active item changes
4313              * @param {Roo.bootstrap.NavGroup} this
4314              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4315              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4316          */
4317         'changed': true
4318      });
4319     
4320 };
4321
4322 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4323     
4324     align: '',
4325     inverse: false,
4326     form: false,
4327     type: 'nav',
4328     navId : '',
4329     // private
4330     
4331     navItems : false, 
4332     
4333     getAutoCreate : function()
4334     {
4335         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4336         
4337         cfg = {
4338             tag : 'ul',
4339             cls: 'nav' 
4340         };
4341         
4342         if (['tabs','pills'].indexOf(this.type)!==-1) {
4343             cfg.cls += ' nav-' + this.type
4344         } else {
4345             if (this.type!=='nav') {
4346                 Roo.log('nav type must be nav/tabs/pills')
4347             }
4348             cfg.cls += ' navbar-nav'
4349         }
4350         
4351         if (this.parent() && this.parent().sidebar) {
4352             cfg = {
4353                 tag: 'ul',
4354                 cls: 'dashboard-menu sidebar-menu'
4355             };
4356             
4357             return cfg;
4358         }
4359         
4360         if (this.form === true) {
4361             cfg = {
4362                 tag: 'form',
4363                 cls: 'navbar-form'
4364             };
4365             
4366             if (this.align === 'right') {
4367                 cfg.cls += ' navbar-right ml-md-auto';
4368             } else {
4369                 cfg.cls += ' navbar-left';
4370             }
4371         }
4372         
4373         if (this.align === 'right') {
4374             cfg.cls += ' navbar-right ml-md-auto';
4375         } else {
4376             cfg.cls += ' mr-auto';
4377         }
4378         
4379         if (this.inverse) {
4380             cfg.cls += ' navbar-inverse';
4381             
4382         }
4383         
4384         
4385         return cfg;
4386     },
4387     /**
4388     * sets the active Navigation item
4389     * @param {Roo.bootstrap.NavItem} the new current navitem
4390     */
4391     setActiveItem : function(item)
4392     {
4393         var prev = false;
4394         Roo.each(this.navItems, function(v){
4395             if (v == item) {
4396                 return ;
4397             }
4398             if (v.isActive()) {
4399                 v.setActive(false, true);
4400                 prev = v;
4401                 
4402             }
4403             
4404         });
4405
4406         item.setActive(true, true);
4407         this.fireEvent('changed', this, item, prev);
4408         
4409         
4410     },
4411     /**
4412     * gets the active Navigation item
4413     * @return {Roo.bootstrap.NavItem} the current navitem
4414     */
4415     getActive : function()
4416     {
4417         
4418         var prev = false;
4419         Roo.each(this.navItems, function(v){
4420             
4421             if (v.isActive()) {
4422                 prev = v;
4423                 
4424             }
4425             
4426         });
4427         return prev;
4428     },
4429     
4430     indexOfNav : function()
4431     {
4432         
4433         var prev = false;
4434         Roo.each(this.navItems, function(v,i){
4435             
4436             if (v.isActive()) {
4437                 prev = i;
4438                 
4439             }
4440             
4441         });
4442         return prev;
4443     },
4444     /**
4445     * adds a Navigation item
4446     * @param {Roo.bootstrap.NavItem} the navitem to add
4447     */
4448     addItem : function(cfg)
4449     {
4450         var cn = new Roo.bootstrap.NavItem(cfg);
4451         this.register(cn);
4452         cn.parentId = this.id;
4453         cn.onRender(this.el, null);
4454         return cn;
4455     },
4456     /**
4457     * register a Navigation item
4458     * @param {Roo.bootstrap.NavItem} the navitem to add
4459     */
4460     register : function(item)
4461     {
4462         this.navItems.push( item);
4463         item.navId = this.navId;
4464     
4465     },
4466     
4467     /**
4468     * clear all the Navigation item
4469     */
4470    
4471     clearAll : function()
4472     {
4473         this.navItems = [];
4474         this.el.dom.innerHTML = '';
4475     },
4476     
4477     getNavItem: function(tabId)
4478     {
4479         var ret = false;
4480         Roo.each(this.navItems, function(e) {
4481             if (e.tabId == tabId) {
4482                ret =  e;
4483                return false;
4484             }
4485             return true;
4486             
4487         });
4488         return ret;
4489     },
4490     
4491     setActiveNext : function()
4492     {
4493         var i = this.indexOfNav(this.getActive());
4494         if (i > this.navItems.length) {
4495             return;
4496         }
4497         this.setActiveItem(this.navItems[i+1]);
4498     },
4499     setActivePrev : function()
4500     {
4501         var i = this.indexOfNav(this.getActive());
4502         if (i  < 1) {
4503             return;
4504         }
4505         this.setActiveItem(this.navItems[i-1]);
4506     },
4507     clearWasActive : function(except) {
4508         Roo.each(this.navItems, function(e) {
4509             if (e.tabId != except.tabId && e.was_active) {
4510                e.was_active = false;
4511                return false;
4512             }
4513             return true;
4514             
4515         });
4516     },
4517     getWasActive : function ()
4518     {
4519         var r = false;
4520         Roo.each(this.navItems, function(e) {
4521             if (e.was_active) {
4522                r = e;
4523                return false;
4524             }
4525             return true;
4526             
4527         });
4528         return r;
4529     }
4530     
4531     
4532 });
4533
4534  
4535 Roo.apply(Roo.bootstrap.NavGroup, {
4536     
4537     groups: {},
4538      /**
4539     * register a Navigation Group
4540     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4541     */
4542     register : function(navgrp)
4543     {
4544         this.groups[navgrp.navId] = navgrp;
4545         
4546     },
4547     /**
4548     * fetch a Navigation Group based on the navigation ID
4549     * @param {string} the navgroup to add
4550     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4551     */
4552     get: function(navId) {
4553         if (typeof(this.groups[navId]) == 'undefined') {
4554             return false;
4555             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4556         }
4557         return this.groups[navId] ;
4558     }
4559     
4560     
4561     
4562 });
4563
4564  /*
4565  * - LGPL
4566  *
4567  * row
4568  * 
4569  */
4570
4571 /**
4572  * @class Roo.bootstrap.NavItem
4573  * @extends Roo.bootstrap.Component
4574  * Bootstrap Navbar.NavItem class
4575  * @cfg {String} href  link to
4576  * @cfg {String} html content of button
4577  * @cfg {String} badge text inside badge
4578  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4579  * @cfg {String} glyphicon DEPRICATED - use fa
4580  * @cfg {String} icon DEPRICATED - use fa
4581  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4582  * @cfg {Boolean} active Is item active
4583  * @cfg {Boolean} disabled Is item disabled
4584  
4585  * @cfg {Boolean} preventDefault (true | false) default false
4586  * @cfg {String} tabId the tab that this item activates.
4587  * @cfg {String} tagtype (a|span) render as a href or span?
4588  * @cfg {Boolean} animateRef (true|false) link to element default false  
4589   
4590  * @constructor
4591  * Create a new Navbar Item
4592  * @param {Object} config The config object
4593  */
4594 Roo.bootstrap.NavItem = function(config){
4595     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4596     this.addEvents({
4597         // raw events
4598         /**
4599          * @event click
4600          * The raw click event for the entire grid.
4601          * @param {Roo.EventObject} e
4602          */
4603         "click" : true,
4604          /**
4605             * @event changed
4606             * Fires when the active item active state changes
4607             * @param {Roo.bootstrap.NavItem} this
4608             * @param {boolean} state the new state
4609              
4610          */
4611         'changed': true,
4612         /**
4613             * @event scrollto
4614             * Fires when scroll to element
4615             * @param {Roo.bootstrap.NavItem} this
4616             * @param {Object} options
4617             * @param {Roo.EventObject} e
4618              
4619          */
4620         'scrollto': true
4621     });
4622    
4623 };
4624
4625 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4626     
4627     href: false,
4628     html: '',
4629     badge: '',
4630     icon: false,
4631     fa : false,
4632     glyphicon: false,
4633     active: false,
4634     preventDefault : false,
4635     tabId : false,
4636     tagtype : 'a',
4637     disabled : false,
4638     animateRef : false,
4639     was_active : false,
4640     
4641     getAutoCreate : function(){
4642          
4643         var cfg = {
4644             tag: 'li',
4645             cls: 'nav-item'
4646             
4647         };
4648         
4649         if (this.active) {
4650             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4651         }
4652         if (this.disabled) {
4653             cfg.cls += ' disabled';
4654         }
4655         
4656         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4657             cfg.cn = [
4658                 {
4659                     tag: this.tagtype,
4660                     href : this.href || "#",
4661                     html: this.html || ''
4662                 }
4663             ];
4664             if (this.tagtype == 'a') {
4665                 cfg.cn[0].cls = 'nav-link';
4666             }
4667             if (this.icon) {
4668                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4669             }
4670             if (this.fa) {
4671                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4672             }
4673             if(this.glyphicon) {
4674                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4675             }
4676             
4677             if (this.menu) {
4678                 
4679                 cfg.cn[0].html += " <span class='caret'></span>";
4680              
4681             }
4682             
4683             if (this.badge !== '') {
4684                  
4685                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4686             }
4687         }
4688         
4689         
4690         
4691         return cfg;
4692     },
4693     initEvents: function() 
4694     {
4695         if (typeof (this.menu) != 'undefined') {
4696             this.menu.parentType = this.xtype;
4697             this.menu.triggerEl = this.el;
4698             this.menu = this.addxtype(Roo.apply({}, this.menu));
4699         }
4700         
4701         this.el.select('a',true).on('click', this.onClick, this);
4702         
4703         if(this.tagtype == 'span'){
4704             this.el.select('span',true).on('click', this.onClick, this);
4705         }
4706        
4707         // at this point parent should be available..
4708         this.parent().register(this);
4709     },
4710     
4711     onClick : function(e)
4712     {
4713         if (e.getTarget('.dropdown-menu-item')) {
4714             // did you click on a menu itemm.... - then don't trigger onclick..
4715             return;
4716         }
4717         
4718         if(
4719                 this.preventDefault || 
4720                 this.href == '#' 
4721         ){
4722             Roo.log("NavItem - prevent Default?");
4723             e.preventDefault();
4724         }
4725         
4726         if (this.disabled) {
4727             return;
4728         }
4729         
4730         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4731         if (tg && tg.transition) {
4732             Roo.log("waiting for the transitionend");
4733             return;
4734         }
4735         
4736         
4737         
4738         //Roo.log("fire event clicked");
4739         if(this.fireEvent('click', this, e) === false){
4740             return;
4741         };
4742         
4743         if(this.tagtype == 'span'){
4744             return;
4745         }
4746         
4747         //Roo.log(this.href);
4748         var ael = this.el.select('a',true).first();
4749         //Roo.log(ael);
4750         
4751         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4752             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4753             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4754                 return; // ignore... - it's a 'hash' to another page.
4755             }
4756             Roo.log("NavItem - prevent Default?");
4757             e.preventDefault();
4758             this.scrollToElement(e);
4759         }
4760         
4761         
4762         var p =  this.parent();
4763    
4764         if (['tabs','pills'].indexOf(p.type)!==-1) {
4765             if (typeof(p.setActiveItem) !== 'undefined') {
4766                 p.setActiveItem(this);
4767             }
4768         }
4769         
4770         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4771         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4772             // remove the collapsed menu expand...
4773             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4774         }
4775     },
4776     
4777     isActive: function () {
4778         return this.active
4779     },
4780     setActive : function(state, fire, is_was_active)
4781     {
4782         if (this.active && !state && this.navId) {
4783             this.was_active = true;
4784             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4785             if (nv) {
4786                 nv.clearWasActive(this);
4787             }
4788             
4789         }
4790         this.active = state;
4791         
4792         if (!state ) {
4793             this.el.removeClass('active');
4794         } else if (!this.el.hasClass('active')) {
4795             this.el.addClass('active');
4796         }
4797         if (fire) {
4798             this.fireEvent('changed', this, state);
4799         }
4800         
4801         // show a panel if it's registered and related..
4802         
4803         if (!this.navId || !this.tabId || !state || is_was_active) {
4804             return;
4805         }
4806         
4807         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4808         if (!tg) {
4809             return;
4810         }
4811         var pan = tg.getPanelByName(this.tabId);
4812         if (!pan) {
4813             return;
4814         }
4815         // if we can not flip to new panel - go back to old nav highlight..
4816         if (false == tg.showPanel(pan)) {
4817             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4818             if (nv) {
4819                 var onav = nv.getWasActive();
4820                 if (onav) {
4821                     onav.setActive(true, false, true);
4822                 }
4823             }
4824             
4825         }
4826         
4827         
4828         
4829     },
4830      // this should not be here...
4831     setDisabled : function(state)
4832     {
4833         this.disabled = state;
4834         if (!state ) {
4835             this.el.removeClass('disabled');
4836         } else if (!this.el.hasClass('disabled')) {
4837             this.el.addClass('disabled');
4838         }
4839         
4840     },
4841     
4842     /**
4843      * Fetch the element to display the tooltip on.
4844      * @return {Roo.Element} defaults to this.el
4845      */
4846     tooltipEl : function()
4847     {
4848         return this.el.select('' + this.tagtype + '', true).first();
4849     },
4850     
4851     scrollToElement : function(e)
4852     {
4853         var c = document.body;
4854         
4855         /*
4856          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4857          */
4858         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4859             c = document.documentElement;
4860         }
4861         
4862         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4863         
4864         if(!target){
4865             return;
4866         }
4867
4868         var o = target.calcOffsetsTo(c);
4869         
4870         var options = {
4871             target : target,
4872             value : o[1]
4873         };
4874         
4875         this.fireEvent('scrollto', this, options, e);
4876         
4877         Roo.get(c).scrollTo('top', options.value, true);
4878         
4879         return;
4880     }
4881 });
4882  
4883
4884  /*
4885  * - LGPL
4886  *
4887  * sidebar item
4888  *
4889  *  li
4890  *    <span> icon </span>
4891  *    <span> text </span>
4892  *    <span>badge </span>
4893  */
4894
4895 /**
4896  * @class Roo.bootstrap.NavSidebarItem
4897  * @extends Roo.bootstrap.NavItem
4898  * Bootstrap Navbar.NavSidebarItem class
4899  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4900  * {Boolean} open is the menu open
4901  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4902  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4903  * {String} buttonSize (sm|md|lg)the extra classes for the button
4904  * {Boolean} showArrow show arrow next to the text (default true)
4905  * @constructor
4906  * Create a new Navbar Button
4907  * @param {Object} config The config object
4908  */
4909 Roo.bootstrap.NavSidebarItem = function(config){
4910     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4911     this.addEvents({
4912         // raw events
4913         /**
4914          * @event click
4915          * The raw click event for the entire grid.
4916          * @param {Roo.EventObject} e
4917          */
4918         "click" : true,
4919          /**
4920             * @event changed
4921             * Fires when the active item active state changes
4922             * @param {Roo.bootstrap.NavSidebarItem} this
4923             * @param {boolean} state the new state
4924              
4925          */
4926         'changed': true
4927     });
4928    
4929 };
4930
4931 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4932     
4933     badgeWeight : 'default',
4934     
4935     open: false,
4936     
4937     buttonView : false,
4938     
4939     buttonWeight : 'default',
4940     
4941     buttonSize : 'md',
4942     
4943     showArrow : true,
4944     
4945     getAutoCreate : function(){
4946         
4947         
4948         var a = {
4949                 tag: 'a',
4950                 href : this.href || '#',
4951                 cls: '',
4952                 html : '',
4953                 cn : []
4954         };
4955         
4956         if(this.buttonView){
4957             a = {
4958                 tag: 'button',
4959                 href : this.href || '#',
4960                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4961                 html : this.html,
4962                 cn : []
4963             };
4964         }
4965         
4966         var cfg = {
4967             tag: 'li',
4968             cls: '',
4969             cn: [ a ]
4970         };
4971         
4972         if (this.active) {
4973             cfg.cls += ' active';
4974         }
4975         
4976         if (this.disabled) {
4977             cfg.cls += ' disabled';
4978         }
4979         if (this.open) {
4980             cfg.cls += ' open x-open';
4981         }
4982         // left icon..
4983         if (this.glyphicon || this.icon) {
4984             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4985             a.cn.push({ tag : 'i', cls : c }) ;
4986         }
4987         
4988         if(!this.buttonView){
4989             var span = {
4990                 tag: 'span',
4991                 html : this.html || ''
4992             };
4993
4994             a.cn.push(span);
4995             
4996         }
4997         
4998         if (this.badge !== '') {
4999             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5000         }
5001         
5002         if (this.menu) {
5003             
5004             if(this.showArrow){
5005                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5006             }
5007             
5008             a.cls += ' dropdown-toggle treeview' ;
5009         }
5010         
5011         return cfg;
5012     },
5013     
5014     initEvents : function()
5015     { 
5016         if (typeof (this.menu) != 'undefined') {
5017             this.menu.parentType = this.xtype;
5018             this.menu.triggerEl = this.el;
5019             this.menu = this.addxtype(Roo.apply({}, this.menu));
5020         }
5021         
5022         this.el.on('click', this.onClick, this);
5023         
5024         if(this.badge !== ''){
5025             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5026         }
5027         
5028     },
5029     
5030     onClick : function(e)
5031     {
5032         if(this.disabled){
5033             e.preventDefault();
5034             return;
5035         }
5036         
5037         if(this.preventDefault){
5038             e.preventDefault();
5039         }
5040         
5041         this.fireEvent('click', this);
5042     },
5043     
5044     disable : function()
5045     {
5046         this.setDisabled(true);
5047     },
5048     
5049     enable : function()
5050     {
5051         this.setDisabled(false);
5052     },
5053     
5054     setDisabled : function(state)
5055     {
5056         if(this.disabled == state){
5057             return;
5058         }
5059         
5060         this.disabled = state;
5061         
5062         if (state) {
5063             this.el.addClass('disabled');
5064             return;
5065         }
5066         
5067         this.el.removeClass('disabled');
5068         
5069         return;
5070     },
5071     
5072     setActive : function(state)
5073     {
5074         if(this.active == state){
5075             return;
5076         }
5077         
5078         this.active = state;
5079         
5080         if (state) {
5081             this.el.addClass('active');
5082             return;
5083         }
5084         
5085         this.el.removeClass('active');
5086         
5087         return;
5088     },
5089     
5090     isActive: function () 
5091     {
5092         return this.active;
5093     },
5094     
5095     setBadge : function(str)
5096     {
5097         if(!this.badgeEl){
5098             return;
5099         }
5100         
5101         this.badgeEl.dom.innerHTML = str;
5102     }
5103     
5104    
5105      
5106  
5107 });
5108  
5109
5110  /*
5111  * - LGPL
5112  *
5113  * row
5114  * 
5115  */
5116
5117 /**
5118  * @class Roo.bootstrap.Row
5119  * @extends Roo.bootstrap.Component
5120  * Bootstrap Row class (contains columns...)
5121  * 
5122  * @constructor
5123  * Create a new Row
5124  * @param {Object} config The config object
5125  */
5126
5127 Roo.bootstrap.Row = function(config){
5128     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5129 };
5130
5131 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5132     
5133     getAutoCreate : function(){
5134        return {
5135             cls: 'row clearfix'
5136        };
5137     }
5138     
5139     
5140 });
5141
5142  
5143
5144  /*
5145  * - LGPL
5146  *
5147  * element
5148  * 
5149  */
5150
5151 /**
5152  * @class Roo.bootstrap.Element
5153  * @extends Roo.bootstrap.Component
5154  * Bootstrap Element class
5155  * @cfg {String} html contents of the element
5156  * @cfg {String} tag tag of the element
5157  * @cfg {String} cls class of the element
5158  * @cfg {Boolean} preventDefault (true|false) default false
5159  * @cfg {Boolean} clickable (true|false) default false
5160  * 
5161  * @constructor
5162  * Create a new Element
5163  * @param {Object} config The config object
5164  */
5165
5166 Roo.bootstrap.Element = function(config){
5167     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5168     
5169     this.addEvents({
5170         // raw events
5171         /**
5172          * @event click
5173          * When a element is chick
5174          * @param {Roo.bootstrap.Element} this
5175          * @param {Roo.EventObject} e
5176          */
5177         "click" : true
5178     });
5179 };
5180
5181 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5182     
5183     tag: 'div',
5184     cls: '',
5185     html: '',
5186     preventDefault: false, 
5187     clickable: false,
5188     
5189     getAutoCreate : function(){
5190         
5191         var cfg = {
5192             tag: this.tag,
5193             // cls: this.cls, double assign in parent class Component.js :: onRender
5194             html: this.html
5195         };
5196         
5197         return cfg;
5198     },
5199     
5200     initEvents: function() 
5201     {
5202         Roo.bootstrap.Element.superclass.initEvents.call(this);
5203         
5204         if(this.clickable){
5205             this.el.on('click', this.onClick, this);
5206         }
5207         
5208     },
5209     
5210     onClick : function(e)
5211     {
5212         if(this.preventDefault){
5213             e.preventDefault();
5214         }
5215         
5216         this.fireEvent('click', this, e);
5217     },
5218     
5219     getValue : function()
5220     {
5221         return this.el.dom.innerHTML;
5222     },
5223     
5224     setValue : function(value)
5225     {
5226         this.el.dom.innerHTML = value;
5227     }
5228    
5229 });
5230
5231  
5232
5233  /*
5234  * - LGPL
5235  *
5236  * pagination
5237  * 
5238  */
5239
5240 /**
5241  * @class Roo.bootstrap.Pagination
5242  * @extends Roo.bootstrap.Component
5243  * Bootstrap Pagination class
5244  * @cfg {String} size xs | sm | md | lg
5245  * @cfg {Boolean} inverse false | true
5246  * 
5247  * @constructor
5248  * Create a new Pagination
5249  * @param {Object} config The config object
5250  */
5251
5252 Roo.bootstrap.Pagination = function(config){
5253     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5254 };
5255
5256 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5257     
5258     cls: false,
5259     size: false,
5260     inverse: false,
5261     
5262     getAutoCreate : function(){
5263         var cfg = {
5264             tag: 'ul',
5265                 cls: 'pagination'
5266         };
5267         if (this.inverse) {
5268             cfg.cls += ' inverse';
5269         }
5270         if (this.html) {
5271             cfg.html=this.html;
5272         }
5273         if (this.cls) {
5274             cfg.cls += " " + this.cls;
5275         }
5276         return cfg;
5277     }
5278    
5279 });
5280
5281  
5282
5283  /*
5284  * - LGPL
5285  *
5286  * Pagination item
5287  * 
5288  */
5289
5290
5291 /**
5292  * @class Roo.bootstrap.PaginationItem
5293  * @extends Roo.bootstrap.Component
5294  * Bootstrap PaginationItem class
5295  * @cfg {String} html text
5296  * @cfg {String} href the link
5297  * @cfg {Boolean} preventDefault (true | false) default true
5298  * @cfg {Boolean} active (true | false) default false
5299  * @cfg {Boolean} disabled default false
5300  * 
5301  * 
5302  * @constructor
5303  * Create a new PaginationItem
5304  * @param {Object} config The config object
5305  */
5306
5307
5308 Roo.bootstrap.PaginationItem = function(config){
5309     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5310     this.addEvents({
5311         // raw events
5312         /**
5313          * @event click
5314          * The raw click event for the entire grid.
5315          * @param {Roo.EventObject} e
5316          */
5317         "click" : true
5318     });
5319 };
5320
5321 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5322     
5323     href : false,
5324     html : false,
5325     preventDefault: true,
5326     active : false,
5327     cls : false,
5328     disabled: false,
5329     
5330     getAutoCreate : function(){
5331         var cfg= {
5332             tag: 'li',
5333             cn: [
5334                 {
5335                     tag : 'a',
5336                     href : this.href ? this.href : '#',
5337                     html : this.html ? this.html : ''
5338                 }
5339             ]
5340         };
5341         
5342         if(this.cls){
5343             cfg.cls = this.cls;
5344         }
5345         
5346         if(this.disabled){
5347             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5348         }
5349         
5350         if(this.active){
5351             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5352         }
5353         
5354         return cfg;
5355     },
5356     
5357     initEvents: function() {
5358         
5359         this.el.on('click', this.onClick, this);
5360         
5361     },
5362     onClick : function(e)
5363     {
5364         Roo.log('PaginationItem on click ');
5365         if(this.preventDefault){
5366             e.preventDefault();
5367         }
5368         
5369         if(this.disabled){
5370             return;
5371         }
5372         
5373         this.fireEvent('click', this, e);
5374     }
5375    
5376 });
5377
5378  
5379
5380  /*
5381  * - LGPL
5382  *
5383  * slider
5384  * 
5385  */
5386
5387
5388 /**
5389  * @class Roo.bootstrap.Slider
5390  * @extends Roo.bootstrap.Component
5391  * Bootstrap Slider class
5392  *    
5393  * @constructor
5394  * Create a new Slider
5395  * @param {Object} config The config object
5396  */
5397
5398 Roo.bootstrap.Slider = function(config){
5399     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5400 };
5401
5402 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5403     
5404     getAutoCreate : function(){
5405         
5406         var cfg = {
5407             tag: 'div',
5408             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5409             cn: [
5410                 {
5411                     tag: 'a',
5412                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5413                 }
5414             ]
5415         };
5416         
5417         return cfg;
5418     }
5419    
5420 });
5421
5422  /*
5423  * Based on:
5424  * Ext JS Library 1.1.1
5425  * Copyright(c) 2006-2007, Ext JS, LLC.
5426  *
5427  * Originally Released Under LGPL - original licence link has changed is not relivant.
5428  *
5429  * Fork - LGPL
5430  * <script type="text/javascript">
5431  */
5432  
5433
5434 /**
5435  * @class Roo.grid.ColumnModel
5436  * @extends Roo.util.Observable
5437  * This is the default implementation of a ColumnModel used by the Grid. It defines
5438  * the columns in the grid.
5439  * <br>Usage:<br>
5440  <pre><code>
5441  var colModel = new Roo.grid.ColumnModel([
5442         {header: "Ticker", width: 60, sortable: true, locked: true},
5443         {header: "Company Name", width: 150, sortable: true},
5444         {header: "Market Cap.", width: 100, sortable: true},
5445         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5446         {header: "Employees", width: 100, sortable: true, resizable: false}
5447  ]);
5448  </code></pre>
5449  * <p>
5450  
5451  * The config options listed for this class are options which may appear in each
5452  * individual column definition.
5453  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5454  * @constructor
5455  * @param {Object} config An Array of column config objects. See this class's
5456  * config objects for details.
5457 */
5458 Roo.grid.ColumnModel = function(config){
5459         /**
5460      * The config passed into the constructor
5461      */
5462     this.config = config;
5463     this.lookup = {};
5464
5465     // if no id, create one
5466     // if the column does not have a dataIndex mapping,
5467     // map it to the order it is in the config
5468     for(var i = 0, len = config.length; i < len; i++){
5469         var c = config[i];
5470         if(typeof c.dataIndex == "undefined"){
5471             c.dataIndex = i;
5472         }
5473         if(typeof c.renderer == "string"){
5474             c.renderer = Roo.util.Format[c.renderer];
5475         }
5476         if(typeof c.id == "undefined"){
5477             c.id = Roo.id();
5478         }
5479         if(c.editor && c.editor.xtype){
5480             c.editor  = Roo.factory(c.editor, Roo.grid);
5481         }
5482         if(c.editor && c.editor.isFormField){
5483             c.editor = new Roo.grid.GridEditor(c.editor);
5484         }
5485         this.lookup[c.id] = c;
5486     }
5487
5488     /**
5489      * The width of columns which have no width specified (defaults to 100)
5490      * @type Number
5491      */
5492     this.defaultWidth = 100;
5493
5494     /**
5495      * Default sortable of columns which have no sortable specified (defaults to false)
5496      * @type Boolean
5497      */
5498     this.defaultSortable = false;
5499
5500     this.addEvents({
5501         /**
5502              * @event widthchange
5503              * Fires when the width of a column changes.
5504              * @param {ColumnModel} this
5505              * @param {Number} columnIndex The column index
5506              * @param {Number} newWidth The new width
5507              */
5508             "widthchange": true,
5509         /**
5510              * @event headerchange
5511              * Fires when the text of a header changes.
5512              * @param {ColumnModel} this
5513              * @param {Number} columnIndex The column index
5514              * @param {Number} newText The new header text
5515              */
5516             "headerchange": true,
5517         /**
5518              * @event hiddenchange
5519              * Fires when a column is hidden or "unhidden".
5520              * @param {ColumnModel} this
5521              * @param {Number} columnIndex The column index
5522              * @param {Boolean} hidden true if hidden, false otherwise
5523              */
5524             "hiddenchange": true,
5525             /**
5526          * @event columnmoved
5527          * Fires when a column is moved.
5528          * @param {ColumnModel} this
5529          * @param {Number} oldIndex
5530          * @param {Number} newIndex
5531          */
5532         "columnmoved" : true,
5533         /**
5534          * @event columlockchange
5535          * Fires when a column's locked state is changed
5536          * @param {ColumnModel} this
5537          * @param {Number} colIndex
5538          * @param {Boolean} locked true if locked
5539          */
5540         "columnlockchange" : true
5541     });
5542     Roo.grid.ColumnModel.superclass.constructor.call(this);
5543 };
5544 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5545     /**
5546      * @cfg {String} header The header text to display in the Grid view.
5547      */
5548     /**
5549      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5550      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5551      * specified, the column's index is used as an index into the Record's data Array.
5552      */
5553     /**
5554      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5555      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5556      */
5557     /**
5558      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5559      * Defaults to the value of the {@link #defaultSortable} property.
5560      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5561      */
5562     /**
5563      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5564      */
5565     /**
5566      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5567      */
5568     /**
5569      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5570      */
5571     /**
5572      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5573      */
5574     /**
5575      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5576      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5577      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5578      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5579      */
5580        /**
5581      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5582      */
5583     /**
5584      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5585      */
5586     /**
5587      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5588      */
5589     /**
5590      * @cfg {String} cursor (Optional)
5591      */
5592     /**
5593      * @cfg {String} tooltip (Optional)
5594      */
5595     /**
5596      * @cfg {Number} xs (Optional)
5597      */
5598     /**
5599      * @cfg {Number} sm (Optional)
5600      */
5601     /**
5602      * @cfg {Number} md (Optional)
5603      */
5604     /**
5605      * @cfg {Number} lg (Optional)
5606      */
5607     /**
5608      * Returns the id of the column at the specified index.
5609      * @param {Number} index The column index
5610      * @return {String} the id
5611      */
5612     getColumnId : function(index){
5613         return this.config[index].id;
5614     },
5615
5616     /**
5617      * Returns the column for a specified id.
5618      * @param {String} id The column id
5619      * @return {Object} the column
5620      */
5621     getColumnById : function(id){
5622         return this.lookup[id];
5623     },
5624
5625     
5626     /**
5627      * Returns the column for a specified dataIndex.
5628      * @param {String} dataIndex The column dataIndex
5629      * @return {Object|Boolean} the column or false if not found
5630      */
5631     getColumnByDataIndex: function(dataIndex){
5632         var index = this.findColumnIndex(dataIndex);
5633         return index > -1 ? this.config[index] : false;
5634     },
5635     
5636     /**
5637      * Returns the index for a specified column id.
5638      * @param {String} id The column id
5639      * @return {Number} the index, or -1 if not found
5640      */
5641     getIndexById : function(id){
5642         for(var i = 0, len = this.config.length; i < len; i++){
5643             if(this.config[i].id == id){
5644                 return i;
5645             }
5646         }
5647         return -1;
5648     },
5649     
5650     /**
5651      * Returns the index for a specified column dataIndex.
5652      * @param {String} dataIndex The column dataIndex
5653      * @return {Number} the index, or -1 if not found
5654      */
5655     
5656     findColumnIndex : function(dataIndex){
5657         for(var i = 0, len = this.config.length; i < len; i++){
5658             if(this.config[i].dataIndex == dataIndex){
5659                 return i;
5660             }
5661         }
5662         return -1;
5663     },
5664     
5665     
5666     moveColumn : function(oldIndex, newIndex){
5667         var c = this.config[oldIndex];
5668         this.config.splice(oldIndex, 1);
5669         this.config.splice(newIndex, 0, c);
5670         this.dataMap = null;
5671         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5672     },
5673
5674     isLocked : function(colIndex){
5675         return this.config[colIndex].locked === true;
5676     },
5677
5678     setLocked : function(colIndex, value, suppressEvent){
5679         if(this.isLocked(colIndex) == value){
5680             return;
5681         }
5682         this.config[colIndex].locked = value;
5683         if(!suppressEvent){
5684             this.fireEvent("columnlockchange", this, colIndex, value);
5685         }
5686     },
5687
5688     getTotalLockedWidth : function(){
5689         var totalWidth = 0;
5690         for(var i = 0; i < this.config.length; i++){
5691             if(this.isLocked(i) && !this.isHidden(i)){
5692                 this.totalWidth += this.getColumnWidth(i);
5693             }
5694         }
5695         return totalWidth;
5696     },
5697
5698     getLockedCount : function(){
5699         for(var i = 0, len = this.config.length; i < len; i++){
5700             if(!this.isLocked(i)){
5701                 return i;
5702             }
5703         }
5704         
5705         return this.config.length;
5706     },
5707
5708     /**
5709      * Returns the number of columns.
5710      * @return {Number}
5711      */
5712     getColumnCount : function(visibleOnly){
5713         if(visibleOnly === true){
5714             var c = 0;
5715             for(var i = 0, len = this.config.length; i < len; i++){
5716                 if(!this.isHidden(i)){
5717                     c++;
5718                 }
5719             }
5720             return c;
5721         }
5722         return this.config.length;
5723     },
5724
5725     /**
5726      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5727      * @param {Function} fn
5728      * @param {Object} scope (optional)
5729      * @return {Array} result
5730      */
5731     getColumnsBy : function(fn, scope){
5732         var r = [];
5733         for(var i = 0, len = this.config.length; i < len; i++){
5734             var c = this.config[i];
5735             if(fn.call(scope||this, c, i) === true){
5736                 r[r.length] = c;
5737             }
5738         }
5739         return r;
5740     },
5741
5742     /**
5743      * Returns true if the specified column is sortable.
5744      * @param {Number} col The column index
5745      * @return {Boolean}
5746      */
5747     isSortable : function(col){
5748         if(typeof this.config[col].sortable == "undefined"){
5749             return this.defaultSortable;
5750         }
5751         return this.config[col].sortable;
5752     },
5753
5754     /**
5755      * Returns the rendering (formatting) function defined for the column.
5756      * @param {Number} col The column index.
5757      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5758      */
5759     getRenderer : function(col){
5760         if(!this.config[col].renderer){
5761             return Roo.grid.ColumnModel.defaultRenderer;
5762         }
5763         return this.config[col].renderer;
5764     },
5765
5766     /**
5767      * Sets the rendering (formatting) function for a column.
5768      * @param {Number} col The column index
5769      * @param {Function} fn The function to use to process the cell's raw data
5770      * to return HTML markup for the grid view. The render function is called with
5771      * the following parameters:<ul>
5772      * <li>Data value.</li>
5773      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5774      * <li>css A CSS style string to apply to the table cell.</li>
5775      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5776      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5777      * <li>Row index</li>
5778      * <li>Column index</li>
5779      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5780      */
5781     setRenderer : function(col, fn){
5782         this.config[col].renderer = fn;
5783     },
5784
5785     /**
5786      * Returns the width for the specified column.
5787      * @param {Number} col The column index
5788      * @return {Number}
5789      */
5790     getColumnWidth : function(col){
5791         return this.config[col].width * 1 || this.defaultWidth;
5792     },
5793
5794     /**
5795      * Sets the width for a column.
5796      * @param {Number} col The column index
5797      * @param {Number} width The new width
5798      */
5799     setColumnWidth : function(col, width, suppressEvent){
5800         this.config[col].width = width;
5801         this.totalWidth = null;
5802         if(!suppressEvent){
5803              this.fireEvent("widthchange", this, col, width);
5804         }
5805     },
5806
5807     /**
5808      * Returns the total width of all columns.
5809      * @param {Boolean} includeHidden True to include hidden column widths
5810      * @return {Number}
5811      */
5812     getTotalWidth : function(includeHidden){
5813         if(!this.totalWidth){
5814             this.totalWidth = 0;
5815             for(var i = 0, len = this.config.length; i < len; i++){
5816                 if(includeHidden || !this.isHidden(i)){
5817                     this.totalWidth += this.getColumnWidth(i);
5818                 }
5819             }
5820         }
5821         return this.totalWidth;
5822     },
5823
5824     /**
5825      * Returns the header for the specified column.
5826      * @param {Number} col The column index
5827      * @return {String}
5828      */
5829     getColumnHeader : function(col){
5830         return this.config[col].header;
5831     },
5832
5833     /**
5834      * Sets the header for a column.
5835      * @param {Number} col The column index
5836      * @param {String} header The new header
5837      */
5838     setColumnHeader : function(col, header){
5839         this.config[col].header = header;
5840         this.fireEvent("headerchange", this, col, header);
5841     },
5842
5843     /**
5844      * Returns the tooltip for the specified column.
5845      * @param {Number} col The column index
5846      * @return {String}
5847      */
5848     getColumnTooltip : function(col){
5849             return this.config[col].tooltip;
5850     },
5851     /**
5852      * Sets the tooltip for a column.
5853      * @param {Number} col The column index
5854      * @param {String} tooltip The new tooltip
5855      */
5856     setColumnTooltip : function(col, tooltip){
5857             this.config[col].tooltip = tooltip;
5858     },
5859
5860     /**
5861      * Returns the dataIndex for the specified column.
5862      * @param {Number} col The column index
5863      * @return {Number}
5864      */
5865     getDataIndex : function(col){
5866         return this.config[col].dataIndex;
5867     },
5868
5869     /**
5870      * Sets the dataIndex for a column.
5871      * @param {Number} col The column index
5872      * @param {Number} dataIndex The new dataIndex
5873      */
5874     setDataIndex : function(col, dataIndex){
5875         this.config[col].dataIndex = dataIndex;
5876     },
5877
5878     
5879     
5880     /**
5881      * Returns true if the cell is editable.
5882      * @param {Number} colIndex The column index
5883      * @param {Number} rowIndex The row index - this is nto actually used..?
5884      * @return {Boolean}
5885      */
5886     isCellEditable : function(colIndex, rowIndex){
5887         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5888     },
5889
5890     /**
5891      * Returns the editor defined for the cell/column.
5892      * return false or null to disable editing.
5893      * @param {Number} colIndex The column index
5894      * @param {Number} rowIndex The row index
5895      * @return {Object}
5896      */
5897     getCellEditor : function(colIndex, rowIndex){
5898         return this.config[colIndex].editor;
5899     },
5900
5901     /**
5902      * Sets if a column is editable.
5903      * @param {Number} col The column index
5904      * @param {Boolean} editable True if the column is editable
5905      */
5906     setEditable : function(col, editable){
5907         this.config[col].editable = editable;
5908     },
5909
5910
5911     /**
5912      * Returns true if the column is hidden.
5913      * @param {Number} colIndex The column index
5914      * @return {Boolean}
5915      */
5916     isHidden : function(colIndex){
5917         return this.config[colIndex].hidden;
5918     },
5919
5920
5921     /**
5922      * Returns true if the column width cannot be changed
5923      */
5924     isFixed : function(colIndex){
5925         return this.config[colIndex].fixed;
5926     },
5927
5928     /**
5929      * Returns true if the column can be resized
5930      * @return {Boolean}
5931      */
5932     isResizable : function(colIndex){
5933         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5934     },
5935     /**
5936      * Sets if a column is hidden.
5937      * @param {Number} colIndex The column index
5938      * @param {Boolean} hidden True if the column is hidden
5939      */
5940     setHidden : function(colIndex, hidden){
5941         this.config[colIndex].hidden = hidden;
5942         this.totalWidth = null;
5943         this.fireEvent("hiddenchange", this, colIndex, hidden);
5944     },
5945
5946     /**
5947      * Sets the editor for a column.
5948      * @param {Number} col The column index
5949      * @param {Object} editor The editor object
5950      */
5951     setEditor : function(col, editor){
5952         this.config[col].editor = editor;
5953     }
5954 });
5955
5956 Roo.grid.ColumnModel.defaultRenderer = function(value)
5957 {
5958     if(typeof value == "object") {
5959         return value;
5960     }
5961         if(typeof value == "string" && value.length < 1){
5962             return "&#160;";
5963         }
5964     
5965         return String.format("{0}", value);
5966 };
5967
5968 // Alias for backwards compatibility
5969 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5970 /*
5971  * Based on:
5972  * Ext JS Library 1.1.1
5973  * Copyright(c) 2006-2007, Ext JS, LLC.
5974  *
5975  * Originally Released Under LGPL - original licence link has changed is not relivant.
5976  *
5977  * Fork - LGPL
5978  * <script type="text/javascript">
5979  */
5980  
5981 /**
5982  * @class Roo.LoadMask
5983  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5984  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5985  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5986  * element's UpdateManager load indicator and will be destroyed after the initial load.
5987  * @constructor
5988  * Create a new LoadMask
5989  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5990  * @param {Object} config The config object
5991  */
5992 Roo.LoadMask = function(el, config){
5993     this.el = Roo.get(el);
5994     Roo.apply(this, config);
5995     if(this.store){
5996         this.store.on('beforeload', this.onBeforeLoad, this);
5997         this.store.on('load', this.onLoad, this);
5998         this.store.on('loadexception', this.onLoadException, this);
5999         this.removeMask = false;
6000     }else{
6001         var um = this.el.getUpdateManager();
6002         um.showLoadIndicator = false; // disable the default indicator
6003         um.on('beforeupdate', this.onBeforeLoad, this);
6004         um.on('update', this.onLoad, this);
6005         um.on('failure', this.onLoad, this);
6006         this.removeMask = true;
6007     }
6008 };
6009
6010 Roo.LoadMask.prototype = {
6011     /**
6012      * @cfg {Boolean} removeMask
6013      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6014      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6015      */
6016     /**
6017      * @cfg {String} msg
6018      * The text to display in a centered loading message box (defaults to 'Loading...')
6019      */
6020     msg : 'Loading...',
6021     /**
6022      * @cfg {String} msgCls
6023      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6024      */
6025     msgCls : 'x-mask-loading',
6026
6027     /**
6028      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6029      * @type Boolean
6030      */
6031     disabled: false,
6032
6033     /**
6034      * Disables the mask to prevent it from being displayed
6035      */
6036     disable : function(){
6037        this.disabled = true;
6038     },
6039
6040     /**
6041      * Enables the mask so that it can be displayed
6042      */
6043     enable : function(){
6044         this.disabled = false;
6045     },
6046     
6047     onLoadException : function()
6048     {
6049         Roo.log(arguments);
6050         
6051         if (typeof(arguments[3]) != 'undefined') {
6052             Roo.MessageBox.alert("Error loading",arguments[3]);
6053         } 
6054         /*
6055         try {
6056             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6057                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6058             }   
6059         } catch(e) {
6060             
6061         }
6062         */
6063     
6064         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6065     },
6066     // private
6067     onLoad : function()
6068     {
6069         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6070     },
6071
6072     // private
6073     onBeforeLoad : function(){
6074         if(!this.disabled){
6075             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6076         }
6077     },
6078
6079     // private
6080     destroy : function(){
6081         if(this.store){
6082             this.store.un('beforeload', this.onBeforeLoad, this);
6083             this.store.un('load', this.onLoad, this);
6084             this.store.un('loadexception', this.onLoadException, this);
6085         }else{
6086             var um = this.el.getUpdateManager();
6087             um.un('beforeupdate', this.onBeforeLoad, this);
6088             um.un('update', this.onLoad, this);
6089             um.un('failure', this.onLoad, this);
6090         }
6091     }
6092 };/*
6093  * - LGPL
6094  *
6095  * table
6096  * 
6097  */
6098
6099 /**
6100  * @class Roo.bootstrap.Table
6101  * @extends Roo.bootstrap.Component
6102  * Bootstrap Table class
6103  * @cfg {String} cls table class
6104  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6105  * @cfg {String} bgcolor Specifies the background color for a table
6106  * @cfg {Number} border Specifies whether the table cells should have borders or not
6107  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6108  * @cfg {Number} cellspacing Specifies the space between cells
6109  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6110  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6111  * @cfg {String} sortable Specifies that the table should be sortable
6112  * @cfg {String} summary Specifies a summary of the content of a table
6113  * @cfg {Number} width Specifies the width of a table
6114  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6115  * 
6116  * @cfg {boolean} striped Should the rows be alternative striped
6117  * @cfg {boolean} bordered Add borders to the table
6118  * @cfg {boolean} hover Add hover highlighting
6119  * @cfg {boolean} condensed Format condensed
6120  * @cfg {boolean} responsive Format condensed
6121  * @cfg {Boolean} loadMask (true|false) default false
6122  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6123  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6124  * @cfg {Boolean} rowSelection (true|false) default false
6125  * @cfg {Boolean} cellSelection (true|false) default false
6126  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6127  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6128  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6129  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6130  
6131  * 
6132  * @constructor
6133  * Create a new Table
6134  * @param {Object} config The config object
6135  */
6136
6137 Roo.bootstrap.Table = function(config){
6138     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6139     
6140   
6141     
6142     // BC...
6143     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6144     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6145     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6146     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6147     
6148     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6149     if (this.sm) {
6150         this.sm.grid = this;
6151         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6152         this.sm = this.selModel;
6153         this.sm.xmodule = this.xmodule || false;
6154     }
6155     
6156     if (this.cm && typeof(this.cm.config) == 'undefined') {
6157         this.colModel = new Roo.grid.ColumnModel(this.cm);
6158         this.cm = this.colModel;
6159         this.cm.xmodule = this.xmodule || false;
6160     }
6161     if (this.store) {
6162         this.store= Roo.factory(this.store, Roo.data);
6163         this.ds = this.store;
6164         this.ds.xmodule = this.xmodule || false;
6165          
6166     }
6167     if (this.footer && this.store) {
6168         this.footer.dataSource = this.ds;
6169         this.footer = Roo.factory(this.footer);
6170     }
6171     
6172     /** @private */
6173     this.addEvents({
6174         /**
6175          * @event cellclick
6176          * Fires when a cell is clicked
6177          * @param {Roo.bootstrap.Table} this
6178          * @param {Roo.Element} el
6179          * @param {Number} rowIndex
6180          * @param {Number} columnIndex
6181          * @param {Roo.EventObject} e
6182          */
6183         "cellclick" : true,
6184         /**
6185          * @event celldblclick
6186          * Fires when a cell is double clicked
6187          * @param {Roo.bootstrap.Table} this
6188          * @param {Roo.Element} el
6189          * @param {Number} rowIndex
6190          * @param {Number} columnIndex
6191          * @param {Roo.EventObject} e
6192          */
6193         "celldblclick" : true,
6194         /**
6195          * @event rowclick
6196          * Fires when a row is clicked
6197          * @param {Roo.bootstrap.Table} this
6198          * @param {Roo.Element} el
6199          * @param {Number} rowIndex
6200          * @param {Roo.EventObject} e
6201          */
6202         "rowclick" : true,
6203         /**
6204          * @event rowdblclick
6205          * Fires when a row is double clicked
6206          * @param {Roo.bootstrap.Table} this
6207          * @param {Roo.Element} el
6208          * @param {Number} rowIndex
6209          * @param {Roo.EventObject} e
6210          */
6211         "rowdblclick" : true,
6212         /**
6213          * @event mouseover
6214          * Fires when a mouseover occur
6215          * @param {Roo.bootstrap.Table} this
6216          * @param {Roo.Element} el
6217          * @param {Number} rowIndex
6218          * @param {Number} columnIndex
6219          * @param {Roo.EventObject} e
6220          */
6221         "mouseover" : true,
6222         /**
6223          * @event mouseout
6224          * Fires when a mouseout occur
6225          * @param {Roo.bootstrap.Table} this
6226          * @param {Roo.Element} el
6227          * @param {Number} rowIndex
6228          * @param {Number} columnIndex
6229          * @param {Roo.EventObject} e
6230          */
6231         "mouseout" : true,
6232         /**
6233          * @event rowclass
6234          * Fires when a row is rendered, so you can change add a style to it.
6235          * @param {Roo.bootstrap.Table} this
6236          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6237          */
6238         'rowclass' : true,
6239           /**
6240          * @event rowsrendered
6241          * Fires when all the  rows have been rendered
6242          * @param {Roo.bootstrap.Table} this
6243          */
6244         'rowsrendered' : true,
6245         /**
6246          * @event contextmenu
6247          * The raw contextmenu event for the entire grid.
6248          * @param {Roo.EventObject} e
6249          */
6250         "contextmenu" : true,
6251         /**
6252          * @event rowcontextmenu
6253          * Fires when a row is right clicked
6254          * @param {Roo.bootstrap.Table} this
6255          * @param {Number} rowIndex
6256          * @param {Roo.EventObject} e
6257          */
6258         "rowcontextmenu" : true,
6259         /**
6260          * @event cellcontextmenu
6261          * Fires when a cell is right clicked
6262          * @param {Roo.bootstrap.Table} this
6263          * @param {Number} rowIndex
6264          * @param {Number} cellIndex
6265          * @param {Roo.EventObject} e
6266          */
6267          "cellcontextmenu" : true,
6268          /**
6269          * @event headercontextmenu
6270          * Fires when a header is right clicked
6271          * @param {Roo.bootstrap.Table} this
6272          * @param {Number} columnIndex
6273          * @param {Roo.EventObject} e
6274          */
6275         "headercontextmenu" : true
6276     });
6277 };
6278
6279 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6280     
6281     cls: false,
6282     align: false,
6283     bgcolor: false,
6284     border: false,
6285     cellpadding: false,
6286     cellspacing: false,
6287     frame: false,
6288     rules: false,
6289     sortable: false,
6290     summary: false,
6291     width: false,
6292     striped : false,
6293     scrollBody : false,
6294     bordered: false,
6295     hover:  false,
6296     condensed : false,
6297     responsive : false,
6298     sm : false,
6299     cm : false,
6300     store : false,
6301     loadMask : false,
6302     footerShow : true,
6303     headerShow : true,
6304   
6305     rowSelection : false,
6306     cellSelection : false,
6307     layout : false,
6308     
6309     // Roo.Element - the tbody
6310     mainBody: false,
6311     // Roo.Element - thead element
6312     mainHead: false,
6313     
6314     container: false, // used by gridpanel...
6315     
6316     lazyLoad : false,
6317     
6318     CSS : Roo.util.CSS,
6319     
6320     auto_hide_footer : false,
6321     
6322     getAutoCreate : function()
6323     {
6324         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6325         
6326         cfg = {
6327             tag: 'table',
6328             cls : 'table',
6329             cn : []
6330         };
6331         if (this.scrollBody) {
6332             cfg.cls += ' table-body-fixed';
6333         }    
6334         if (this.striped) {
6335             cfg.cls += ' table-striped';
6336         }
6337         
6338         if (this.hover) {
6339             cfg.cls += ' table-hover';
6340         }
6341         if (this.bordered) {
6342             cfg.cls += ' table-bordered';
6343         }
6344         if (this.condensed) {
6345             cfg.cls += ' table-condensed';
6346         }
6347         if (this.responsive) {
6348             cfg.cls += ' table-responsive';
6349         }
6350         
6351         if (this.cls) {
6352             cfg.cls+=  ' ' +this.cls;
6353         }
6354         
6355         // this lot should be simplifed...
6356         var _t = this;
6357         var cp = [
6358             'align',
6359             'bgcolor',
6360             'border',
6361             'cellpadding',
6362             'cellspacing',
6363             'frame',
6364             'rules',
6365             'sortable',
6366             'summary',
6367             'width'
6368         ].forEach(function(k) {
6369             if (_t[k]) {
6370                 cfg[k] = _t[k];
6371             }
6372         });
6373         
6374         
6375         if (this.layout) {
6376             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6377         }
6378         
6379         if(this.store || this.cm){
6380             if(this.headerShow){
6381                 cfg.cn.push(this.renderHeader());
6382             }
6383             
6384             cfg.cn.push(this.renderBody());
6385             
6386             if(this.footerShow){
6387                 cfg.cn.push(this.renderFooter());
6388             }
6389             // where does this come from?
6390             //cfg.cls+=  ' TableGrid';
6391         }
6392         
6393         return { cn : [ cfg ] };
6394     },
6395     
6396     initEvents : function()
6397     {   
6398         if(!this.store || !this.cm){
6399             return;
6400         }
6401         if (this.selModel) {
6402             this.selModel.initEvents();
6403         }
6404         
6405         
6406         //Roo.log('initEvents with ds!!!!');
6407         
6408         this.mainBody = this.el.select('tbody', true).first();
6409         this.mainHead = this.el.select('thead', true).first();
6410         this.mainFoot = this.el.select('tfoot', true).first();
6411         
6412         
6413         
6414         var _this = this;
6415         
6416         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6417             e.on('click', _this.sort, _this);
6418         });
6419         
6420         this.mainBody.on("click", this.onClick, this);
6421         this.mainBody.on("dblclick", this.onDblClick, this);
6422         
6423         // why is this done????? = it breaks dialogs??
6424         //this.parent().el.setStyle('position', 'relative');
6425         
6426         
6427         if (this.footer) {
6428             this.footer.parentId = this.id;
6429             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6430             
6431             if(this.lazyLoad){
6432                 this.el.select('tfoot tr td').first().addClass('hide');
6433             }
6434         } 
6435         
6436         if(this.loadMask) {
6437             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6438         }
6439         
6440         this.store.on('load', this.onLoad, this);
6441         this.store.on('beforeload', this.onBeforeLoad, this);
6442         this.store.on('update', this.onUpdate, this);
6443         this.store.on('add', this.onAdd, this);
6444         this.store.on("clear", this.clear, this);
6445         
6446         this.el.on("contextmenu", this.onContextMenu, this);
6447         
6448         this.mainBody.on('scroll', this.onBodyScroll, this);
6449         
6450         this.cm.on("headerchange", this.onHeaderChange, this);
6451         
6452         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6453         
6454     },
6455     
6456     onContextMenu : function(e, t)
6457     {
6458         this.processEvent("contextmenu", e);
6459     },
6460     
6461     processEvent : function(name, e)
6462     {
6463         if (name != 'touchstart' ) {
6464             this.fireEvent(name, e);    
6465         }
6466         
6467         var t = e.getTarget();
6468         
6469         var cell = Roo.get(t);
6470         
6471         if(!cell){
6472             return;
6473         }
6474         
6475         if(cell.findParent('tfoot', false, true)){
6476             return;
6477         }
6478         
6479         if(cell.findParent('thead', false, true)){
6480             
6481             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6482                 cell = Roo.get(t).findParent('th', false, true);
6483                 if (!cell) {
6484                     Roo.log("failed to find th in thead?");
6485                     Roo.log(e.getTarget());
6486                     return;
6487                 }
6488             }
6489             
6490             var cellIndex = cell.dom.cellIndex;
6491             
6492             var ename = name == 'touchstart' ? 'click' : name;
6493             this.fireEvent("header" + ename, this, cellIndex, e);
6494             
6495             return;
6496         }
6497         
6498         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6499             cell = Roo.get(t).findParent('td', false, true);
6500             if (!cell) {
6501                 Roo.log("failed to find th in tbody?");
6502                 Roo.log(e.getTarget());
6503                 return;
6504             }
6505         }
6506         
6507         var row = cell.findParent('tr', false, true);
6508         var cellIndex = cell.dom.cellIndex;
6509         var rowIndex = row.dom.rowIndex - 1;
6510         
6511         if(row !== false){
6512             
6513             this.fireEvent("row" + name, this, rowIndex, e);
6514             
6515             if(cell !== false){
6516             
6517                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6518             }
6519         }
6520         
6521     },
6522     
6523     onMouseover : function(e, el)
6524     {
6525         var cell = Roo.get(el);
6526         
6527         if(!cell){
6528             return;
6529         }
6530         
6531         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6532             cell = cell.findParent('td', false, true);
6533         }
6534         
6535         var row = cell.findParent('tr', false, true);
6536         var cellIndex = cell.dom.cellIndex;
6537         var rowIndex = row.dom.rowIndex - 1; // start from 0
6538         
6539         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6540         
6541     },
6542     
6543     onMouseout : function(e, el)
6544     {
6545         var cell = Roo.get(el);
6546         
6547         if(!cell){
6548             return;
6549         }
6550         
6551         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6552             cell = cell.findParent('td', false, true);
6553         }
6554         
6555         var row = cell.findParent('tr', false, true);
6556         var cellIndex = cell.dom.cellIndex;
6557         var rowIndex = row.dom.rowIndex - 1; // start from 0
6558         
6559         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6560         
6561     },
6562     
6563     onClick : function(e, el)
6564     {
6565         var cell = Roo.get(el);
6566         
6567         if(!cell || (!this.cellSelection && !this.rowSelection)){
6568             return;
6569         }
6570         
6571         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6572             cell = cell.findParent('td', false, true);
6573         }
6574         
6575         if(!cell || typeof(cell) == 'undefined'){
6576             return;
6577         }
6578         
6579         var row = cell.findParent('tr', false, true);
6580         
6581         if(!row || typeof(row) == 'undefined'){
6582             return;
6583         }
6584         
6585         var cellIndex = cell.dom.cellIndex;
6586         var rowIndex = this.getRowIndex(row);
6587         
6588         // why??? - should these not be based on SelectionModel?
6589         if(this.cellSelection){
6590             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6591         }
6592         
6593         if(this.rowSelection){
6594             this.fireEvent('rowclick', this, row, rowIndex, e);
6595         }
6596         
6597         
6598     },
6599         
6600     onDblClick : function(e,el)
6601     {
6602         var cell = Roo.get(el);
6603         
6604         if(!cell || (!this.cellSelection && !this.rowSelection)){
6605             return;
6606         }
6607         
6608         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6609             cell = cell.findParent('td', false, true);
6610         }
6611         
6612         if(!cell || typeof(cell) == 'undefined'){
6613             return;
6614         }
6615         
6616         var row = cell.findParent('tr', false, true);
6617         
6618         if(!row || typeof(row) == 'undefined'){
6619             return;
6620         }
6621         
6622         var cellIndex = cell.dom.cellIndex;
6623         var rowIndex = this.getRowIndex(row);
6624         
6625         if(this.cellSelection){
6626             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6627         }
6628         
6629         if(this.rowSelection){
6630             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6631         }
6632     },
6633     
6634     sort : function(e,el)
6635     {
6636         var col = Roo.get(el);
6637         
6638         if(!col.hasClass('sortable')){
6639             return;
6640         }
6641         
6642         var sort = col.attr('sort');
6643         var dir = 'ASC';
6644         
6645         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6646             dir = 'DESC';
6647         }
6648         
6649         this.store.sortInfo = {field : sort, direction : dir};
6650         
6651         if (this.footer) {
6652             Roo.log("calling footer first");
6653             this.footer.onClick('first');
6654         } else {
6655         
6656             this.store.load({ params : { start : 0 } });
6657         }
6658     },
6659     
6660     renderHeader : function()
6661     {
6662         var header = {
6663             tag: 'thead',
6664             cn : []
6665         };
6666         
6667         var cm = this.cm;
6668         this.totalWidth = 0;
6669         
6670         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6671             
6672             var config = cm.config[i];
6673             
6674             var c = {
6675                 tag: 'th',
6676                 cls : 'x-hcol-' + i,
6677                 style : '',
6678                 html: cm.getColumnHeader(i)
6679             };
6680             
6681             var hh = '';
6682             
6683             if(typeof(config.sortable) != 'undefined' && config.sortable){
6684                 c.cls = 'sortable';
6685                 c.html = '<i class="glyphicon"></i>' + c.html;
6686             }
6687             
6688             if(typeof(config.lgHeader) != 'undefined'){
6689                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6690             }
6691             
6692             if(typeof(config.mdHeader) != 'undefined'){
6693                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6694             }
6695             
6696             if(typeof(config.smHeader) != 'undefined'){
6697                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6698             }
6699             
6700             if(typeof(config.xsHeader) != 'undefined'){
6701                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6702             }
6703             
6704             if(hh.length){
6705                 c.html = hh;
6706             }
6707             
6708             if(typeof(config.tooltip) != 'undefined'){
6709                 c.tooltip = config.tooltip;
6710             }
6711             
6712             if(typeof(config.colspan) != 'undefined'){
6713                 c.colspan = config.colspan;
6714             }
6715             
6716             if(typeof(config.hidden) != 'undefined' && config.hidden){
6717                 c.style += ' display:none;';
6718             }
6719             
6720             if(typeof(config.dataIndex) != 'undefined'){
6721                 c.sort = config.dataIndex;
6722             }
6723             
6724            
6725             
6726             if(typeof(config.align) != 'undefined' && config.align.length){
6727                 c.style += ' text-align:' + config.align + ';';
6728             }
6729             
6730             if(typeof(config.width) != 'undefined'){
6731                 c.style += ' width:' + config.width + 'px;';
6732                 this.totalWidth += config.width;
6733             } else {
6734                 this.totalWidth += 100; // assume minimum of 100 per column?
6735             }
6736             
6737             if(typeof(config.cls) != 'undefined'){
6738                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6739             }
6740             
6741             ['xs','sm','md','lg'].map(function(size){
6742                 
6743                 if(typeof(config[size]) == 'undefined'){
6744                     return;
6745                 }
6746                 
6747                 if (!config[size]) { // 0 = hidden
6748                     c.cls += ' hidden-' + size;
6749                     return;
6750                 }
6751                 
6752                 c.cls += ' col-' + size + '-' + config[size];
6753
6754             });
6755             
6756             header.cn.push(c)
6757         }
6758         
6759         return header;
6760     },
6761     
6762     renderBody : function()
6763     {
6764         var body = {
6765             tag: 'tbody',
6766             cn : [
6767                 {
6768                     tag: 'tr',
6769                     cn : [
6770                         {
6771                             tag : 'td',
6772                             colspan :  this.cm.getColumnCount()
6773                         }
6774                     ]
6775                 }
6776             ]
6777         };
6778         
6779         return body;
6780     },
6781     
6782     renderFooter : function()
6783     {
6784         var footer = {
6785             tag: 'tfoot',
6786             cn : [
6787                 {
6788                     tag: 'tr',
6789                     cn : [
6790                         {
6791                             tag : 'td',
6792                             colspan :  this.cm.getColumnCount()
6793                         }
6794                     ]
6795                 }
6796             ]
6797         };
6798         
6799         return footer;
6800     },
6801     
6802     
6803     
6804     onLoad : function()
6805     {
6806 //        Roo.log('ds onload');
6807         this.clear();
6808         
6809         var _this = this;
6810         var cm = this.cm;
6811         var ds = this.store;
6812         
6813         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6814             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6815             if (_this.store.sortInfo) {
6816                     
6817                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6818                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6819                 }
6820                 
6821                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6822                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6823                 }
6824             }
6825         });
6826         
6827         var tbody =  this.mainBody;
6828               
6829         if(ds.getCount() > 0){
6830             ds.data.each(function(d,rowIndex){
6831                 var row =  this.renderRow(cm, ds, rowIndex);
6832                 
6833                 tbody.createChild(row);
6834                 
6835                 var _this = this;
6836                 
6837                 if(row.cellObjects.length){
6838                     Roo.each(row.cellObjects, function(r){
6839                         _this.renderCellObject(r);
6840                     })
6841                 }
6842                 
6843             }, this);
6844         }
6845         
6846         var tfoot = this.el.select('tfoot', true).first();
6847         
6848         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6849             
6850             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6851             
6852             var total = this.ds.getTotalCount();
6853             
6854             if(this.footer.pageSize < total){
6855                 this.mainFoot.show();
6856             }
6857         }
6858         
6859         Roo.each(this.el.select('tbody td', true).elements, function(e){
6860             e.on('mouseover', _this.onMouseover, _this);
6861         });
6862         
6863         Roo.each(this.el.select('tbody td', true).elements, function(e){
6864             e.on('mouseout', _this.onMouseout, _this);
6865         });
6866         this.fireEvent('rowsrendered', this);
6867         
6868         this.autoSize();
6869     },
6870     
6871     
6872     onUpdate : function(ds,record)
6873     {
6874         this.refreshRow(record);
6875         this.autoSize();
6876     },
6877     
6878     onRemove : function(ds, record, index, isUpdate){
6879         if(isUpdate !== true){
6880             this.fireEvent("beforerowremoved", this, index, record);
6881         }
6882         var bt = this.mainBody.dom;
6883         
6884         var rows = this.el.select('tbody > tr', true).elements;
6885         
6886         if(typeof(rows[index]) != 'undefined'){
6887             bt.removeChild(rows[index].dom);
6888         }
6889         
6890 //        if(bt.rows[index]){
6891 //            bt.removeChild(bt.rows[index]);
6892 //        }
6893         
6894         if(isUpdate !== true){
6895             //this.stripeRows(index);
6896             //this.syncRowHeights(index, index);
6897             //this.layout();
6898             this.fireEvent("rowremoved", this, index, record);
6899         }
6900     },
6901     
6902     onAdd : function(ds, records, rowIndex)
6903     {
6904         //Roo.log('on Add called');
6905         // - note this does not handle multiple adding very well..
6906         var bt = this.mainBody.dom;
6907         for (var i =0 ; i < records.length;i++) {
6908             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6909             //Roo.log(records[i]);
6910             //Roo.log(this.store.getAt(rowIndex+i));
6911             this.insertRow(this.store, rowIndex + i, false);
6912             return;
6913         }
6914         
6915     },
6916     
6917     
6918     refreshRow : function(record){
6919         var ds = this.store, index;
6920         if(typeof record == 'number'){
6921             index = record;
6922             record = ds.getAt(index);
6923         }else{
6924             index = ds.indexOf(record);
6925         }
6926         this.insertRow(ds, index, true);
6927         this.autoSize();
6928         this.onRemove(ds, record, index+1, true);
6929         this.autoSize();
6930         //this.syncRowHeights(index, index);
6931         //this.layout();
6932         this.fireEvent("rowupdated", this, index, record);
6933     },
6934     
6935     insertRow : function(dm, rowIndex, isUpdate){
6936         
6937         if(!isUpdate){
6938             this.fireEvent("beforerowsinserted", this, rowIndex);
6939         }
6940             //var s = this.getScrollState();
6941         var row = this.renderRow(this.cm, this.store, rowIndex);
6942         // insert before rowIndex..
6943         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6944         
6945         var _this = this;
6946                 
6947         if(row.cellObjects.length){
6948             Roo.each(row.cellObjects, function(r){
6949                 _this.renderCellObject(r);
6950             })
6951         }
6952             
6953         if(!isUpdate){
6954             this.fireEvent("rowsinserted", this, rowIndex);
6955             //this.syncRowHeights(firstRow, lastRow);
6956             //this.stripeRows(firstRow);
6957             //this.layout();
6958         }
6959         
6960     },
6961     
6962     
6963     getRowDom : function(rowIndex)
6964     {
6965         var rows = this.el.select('tbody > tr', true).elements;
6966         
6967         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6968         
6969     },
6970     // returns the object tree for a tr..
6971   
6972     
6973     renderRow : function(cm, ds, rowIndex) 
6974     {
6975         var d = ds.getAt(rowIndex);
6976         
6977         var row = {
6978             tag : 'tr',
6979             cls : 'x-row-' + rowIndex,
6980             cn : []
6981         };
6982             
6983         var cellObjects = [];
6984         
6985         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6986             var config = cm.config[i];
6987             
6988             var renderer = cm.getRenderer(i);
6989             var value = '';
6990             var id = false;
6991             
6992             if(typeof(renderer) !== 'undefined'){
6993                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6994             }
6995             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6996             // and are rendered into the cells after the row is rendered - using the id for the element.
6997             
6998             if(typeof(value) === 'object'){
6999                 id = Roo.id();
7000                 cellObjects.push({
7001                     container : id,
7002                     cfg : value 
7003                 })
7004             }
7005             
7006             var rowcfg = {
7007                 record: d,
7008                 rowIndex : rowIndex,
7009                 colIndex : i,
7010                 rowClass : ''
7011             };
7012
7013             this.fireEvent('rowclass', this, rowcfg);
7014             
7015             var td = {
7016                 tag: 'td',
7017                 cls : rowcfg.rowClass + ' x-col-' + i,
7018                 style: '',
7019                 html: (typeof(value) === 'object') ? '' : value
7020             };
7021             
7022             if (id) {
7023                 td.id = id;
7024             }
7025             
7026             if(typeof(config.colspan) != 'undefined'){
7027                 td.colspan = config.colspan;
7028             }
7029             
7030             if(typeof(config.hidden) != 'undefined' && config.hidden){
7031                 td.style += ' display:none;';
7032             }
7033             
7034             if(typeof(config.align) != 'undefined' && config.align.length){
7035                 td.style += ' text-align:' + config.align + ';';
7036             }
7037             if(typeof(config.valign) != 'undefined' && config.valign.length){
7038                 td.style += ' vertical-align:' + config.valign + ';';
7039             }
7040             
7041             if(typeof(config.width) != 'undefined'){
7042                 td.style += ' width:' +  config.width + 'px;';
7043             }
7044             
7045             if(typeof(config.cursor) != 'undefined'){
7046                 td.style += ' cursor:' +  config.cursor + ';';
7047             }
7048             
7049             if(typeof(config.cls) != 'undefined'){
7050                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7051             }
7052             
7053             ['xs','sm','md','lg'].map(function(size){
7054                 
7055                 if(typeof(config[size]) == 'undefined'){
7056                     return;
7057                 }
7058                 
7059                 if (!config[size]) { // 0 = hidden
7060                     td.cls += ' hidden-' + size;
7061                     return;
7062                 }
7063                 
7064                 td.cls += ' col-' + size + '-' + config[size];
7065
7066             });
7067             
7068             row.cn.push(td);
7069            
7070         }
7071         
7072         row.cellObjects = cellObjects;
7073         
7074         return row;
7075           
7076     },
7077     
7078     
7079     
7080     onBeforeLoad : function()
7081     {
7082         
7083     },
7084      /**
7085      * Remove all rows
7086      */
7087     clear : function()
7088     {
7089         this.el.select('tbody', true).first().dom.innerHTML = '';
7090     },
7091     /**
7092      * Show or hide a row.
7093      * @param {Number} rowIndex to show or hide
7094      * @param {Boolean} state hide
7095      */
7096     setRowVisibility : function(rowIndex, state)
7097     {
7098         var bt = this.mainBody.dom;
7099         
7100         var rows = this.el.select('tbody > tr', true).elements;
7101         
7102         if(typeof(rows[rowIndex]) == 'undefined'){
7103             return;
7104         }
7105         rows[rowIndex].dom.style.display = state ? '' : 'none';
7106     },
7107     
7108     
7109     getSelectionModel : function(){
7110         if(!this.selModel){
7111             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7112         }
7113         return this.selModel;
7114     },
7115     /*
7116      * Render the Roo.bootstrap object from renderder
7117      */
7118     renderCellObject : function(r)
7119     {
7120         var _this = this;
7121         
7122         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7123         
7124         var t = r.cfg.render(r.container);
7125         
7126         if(r.cfg.cn){
7127             Roo.each(r.cfg.cn, function(c){
7128                 var child = {
7129                     container: t.getChildContainer(),
7130                     cfg: c
7131                 };
7132                 _this.renderCellObject(child);
7133             })
7134         }
7135     },
7136     
7137     getRowIndex : function(row)
7138     {
7139         var rowIndex = -1;
7140         
7141         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7142             if(el != row){
7143                 return;
7144             }
7145             
7146             rowIndex = index;
7147         });
7148         
7149         return rowIndex;
7150     },
7151      /**
7152      * Returns the grid's underlying element = used by panel.Grid
7153      * @return {Element} The element
7154      */
7155     getGridEl : function(){
7156         return this.el;
7157     },
7158      /**
7159      * Forces a resize - used by panel.Grid
7160      * @return {Element} The element
7161      */
7162     autoSize : function()
7163     {
7164         //var ctr = Roo.get(this.container.dom.parentElement);
7165         var ctr = Roo.get(this.el.dom);
7166         
7167         var thd = this.getGridEl().select('thead',true).first();
7168         var tbd = this.getGridEl().select('tbody', true).first();
7169         var tfd = this.getGridEl().select('tfoot', true).first();
7170         
7171         var cw = ctr.getWidth();
7172         
7173         if (tbd) {
7174             
7175             tbd.setSize(ctr.getWidth(),
7176                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7177             );
7178             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7179             cw -= barsize;
7180         }
7181         cw = Math.max(cw, this.totalWidth);
7182         this.getGridEl().select('tr',true).setWidth(cw);
7183         // resize 'expandable coloumn?
7184         
7185         return; // we doe not have a view in this design..
7186         
7187     },
7188     onBodyScroll: function()
7189     {
7190         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7191         if(this.mainHead){
7192             this.mainHead.setStyle({
7193                 'position' : 'relative',
7194                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7195             });
7196         }
7197         
7198         if(this.lazyLoad){
7199             
7200             var scrollHeight = this.mainBody.dom.scrollHeight;
7201             
7202             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7203             
7204             var height = this.mainBody.getHeight();
7205             
7206             if(scrollHeight - height == scrollTop) {
7207                 
7208                 var total = this.ds.getTotalCount();
7209                 
7210                 if(this.footer.cursor + this.footer.pageSize < total){
7211                     
7212                     this.footer.ds.load({
7213                         params : {
7214                             start : this.footer.cursor + this.footer.pageSize,
7215                             limit : this.footer.pageSize
7216                         },
7217                         add : true
7218                     });
7219                 }
7220             }
7221             
7222         }
7223     },
7224     
7225     onHeaderChange : function()
7226     {
7227         var header = this.renderHeader();
7228         var table = this.el.select('table', true).first();
7229         
7230         this.mainHead.remove();
7231         this.mainHead = table.createChild(header, this.mainBody, false);
7232     },
7233     
7234     onHiddenChange : function(colModel, colIndex, hidden)
7235     {
7236         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7237         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7238         
7239         this.CSS.updateRule(thSelector, "display", "");
7240         this.CSS.updateRule(tdSelector, "display", "");
7241         
7242         if(hidden){
7243             this.CSS.updateRule(thSelector, "display", "none");
7244             this.CSS.updateRule(tdSelector, "display", "none");
7245         }
7246         
7247         this.onHeaderChange();
7248         this.onLoad();
7249     },
7250     
7251     setColumnWidth: function(col_index, width)
7252     {
7253         // width = "md-2 xs-2..."
7254         if(!this.colModel.config[col_index]) {
7255             return;
7256         }
7257         
7258         var w = width.split(" ");
7259         
7260         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7261         
7262         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7263         
7264         
7265         for(var j = 0; j < w.length; j++) {
7266             
7267             if(!w[j]) {
7268                 continue;
7269             }
7270             
7271             var size_cls = w[j].split("-");
7272             
7273             if(!Number.isInteger(size_cls[1] * 1)) {
7274                 continue;
7275             }
7276             
7277             if(!this.colModel.config[col_index][size_cls[0]]) {
7278                 continue;
7279             }
7280             
7281             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7282                 continue;
7283             }
7284             
7285             h_row[0].classList.replace(
7286                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7287                 "col-"+size_cls[0]+"-"+size_cls[1]
7288             );
7289             
7290             for(var i = 0; i < rows.length; i++) {
7291                 
7292                 var size_cls = w[j].split("-");
7293                 
7294                 if(!Number.isInteger(size_cls[1] * 1)) {
7295                     continue;
7296                 }
7297                 
7298                 if(!this.colModel.config[col_index][size_cls[0]]) {
7299                     continue;
7300                 }
7301                 
7302                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7303                     continue;
7304                 }
7305                 
7306                 rows[i].classList.replace(
7307                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7308                     "col-"+size_cls[0]+"-"+size_cls[1]
7309                 );
7310             }
7311             
7312             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7313         }
7314     }
7315 });
7316
7317  
7318
7319  /*
7320  * - LGPL
7321  *
7322  * table cell
7323  * 
7324  */
7325
7326 /**
7327  * @class Roo.bootstrap.TableCell
7328  * @extends Roo.bootstrap.Component
7329  * Bootstrap TableCell class
7330  * @cfg {String} html cell contain text
7331  * @cfg {String} cls cell class
7332  * @cfg {String} tag cell tag (td|th) default td
7333  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7334  * @cfg {String} align Aligns the content in a cell
7335  * @cfg {String} axis Categorizes cells
7336  * @cfg {String} bgcolor Specifies the background color of a cell
7337  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7338  * @cfg {Number} colspan Specifies the number of columns a cell should span
7339  * @cfg {String} headers Specifies one or more header cells a cell is related to
7340  * @cfg {Number} height Sets the height of a cell
7341  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7342  * @cfg {Number} rowspan Sets the number of rows a cell should span
7343  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7344  * @cfg {String} valign Vertical aligns the content in a cell
7345  * @cfg {Number} width Specifies the width of a cell
7346  * 
7347  * @constructor
7348  * Create a new TableCell
7349  * @param {Object} config The config object
7350  */
7351
7352 Roo.bootstrap.TableCell = function(config){
7353     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7354 };
7355
7356 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7357     
7358     html: false,
7359     cls: false,
7360     tag: false,
7361     abbr: false,
7362     align: false,
7363     axis: false,
7364     bgcolor: false,
7365     charoff: false,
7366     colspan: false,
7367     headers: false,
7368     height: false,
7369     nowrap: false,
7370     rowspan: false,
7371     scope: false,
7372     valign: false,
7373     width: false,
7374     
7375     
7376     getAutoCreate : function(){
7377         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7378         
7379         cfg = {
7380             tag: 'td'
7381         };
7382         
7383         if(this.tag){
7384             cfg.tag = this.tag;
7385         }
7386         
7387         if (this.html) {
7388             cfg.html=this.html
7389         }
7390         if (this.cls) {
7391             cfg.cls=this.cls
7392         }
7393         if (this.abbr) {
7394             cfg.abbr=this.abbr
7395         }
7396         if (this.align) {
7397             cfg.align=this.align
7398         }
7399         if (this.axis) {
7400             cfg.axis=this.axis
7401         }
7402         if (this.bgcolor) {
7403             cfg.bgcolor=this.bgcolor
7404         }
7405         if (this.charoff) {
7406             cfg.charoff=this.charoff
7407         }
7408         if (this.colspan) {
7409             cfg.colspan=this.colspan
7410         }
7411         if (this.headers) {
7412             cfg.headers=this.headers
7413         }
7414         if (this.height) {
7415             cfg.height=this.height
7416         }
7417         if (this.nowrap) {
7418             cfg.nowrap=this.nowrap
7419         }
7420         if (this.rowspan) {
7421             cfg.rowspan=this.rowspan
7422         }
7423         if (this.scope) {
7424             cfg.scope=this.scope
7425         }
7426         if (this.valign) {
7427             cfg.valign=this.valign
7428         }
7429         if (this.width) {
7430             cfg.width=this.width
7431         }
7432         
7433         
7434         return cfg;
7435     }
7436    
7437 });
7438
7439  
7440
7441  /*
7442  * - LGPL
7443  *
7444  * table row
7445  * 
7446  */
7447
7448 /**
7449  * @class Roo.bootstrap.TableRow
7450  * @extends Roo.bootstrap.Component
7451  * Bootstrap TableRow class
7452  * @cfg {String} cls row class
7453  * @cfg {String} align Aligns the content in a table row
7454  * @cfg {String} bgcolor Specifies a background color for a table row
7455  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7456  * @cfg {String} valign Vertical aligns the content in a table row
7457  * 
7458  * @constructor
7459  * Create a new TableRow
7460  * @param {Object} config The config object
7461  */
7462
7463 Roo.bootstrap.TableRow = function(config){
7464     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7465 };
7466
7467 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7468     
7469     cls: false,
7470     align: false,
7471     bgcolor: false,
7472     charoff: false,
7473     valign: false,
7474     
7475     getAutoCreate : function(){
7476         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7477         
7478         cfg = {
7479             tag: 'tr'
7480         };
7481             
7482         if(this.cls){
7483             cfg.cls = this.cls;
7484         }
7485         if(this.align){
7486             cfg.align = this.align;
7487         }
7488         if(this.bgcolor){
7489             cfg.bgcolor = this.bgcolor;
7490         }
7491         if(this.charoff){
7492             cfg.charoff = this.charoff;
7493         }
7494         if(this.valign){
7495             cfg.valign = this.valign;
7496         }
7497         
7498         return cfg;
7499     }
7500    
7501 });
7502
7503  
7504
7505  /*
7506  * - LGPL
7507  *
7508  * table body
7509  * 
7510  */
7511
7512 /**
7513  * @class Roo.bootstrap.TableBody
7514  * @extends Roo.bootstrap.Component
7515  * Bootstrap TableBody class
7516  * @cfg {String} cls element class
7517  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7518  * @cfg {String} align Aligns the content inside the element
7519  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7520  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7521  * 
7522  * @constructor
7523  * Create a new TableBody
7524  * @param {Object} config The config object
7525  */
7526
7527 Roo.bootstrap.TableBody = function(config){
7528     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7529 };
7530
7531 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7532     
7533     cls: false,
7534     tag: false,
7535     align: false,
7536     charoff: false,
7537     valign: false,
7538     
7539     getAutoCreate : function(){
7540         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7541         
7542         cfg = {
7543             tag: 'tbody'
7544         };
7545             
7546         if (this.cls) {
7547             cfg.cls=this.cls
7548         }
7549         if(this.tag){
7550             cfg.tag = this.tag;
7551         }
7552         
7553         if(this.align){
7554             cfg.align = this.align;
7555         }
7556         if(this.charoff){
7557             cfg.charoff = this.charoff;
7558         }
7559         if(this.valign){
7560             cfg.valign = this.valign;
7561         }
7562         
7563         return cfg;
7564     }
7565     
7566     
7567 //    initEvents : function()
7568 //    {
7569 //        
7570 //        if(!this.store){
7571 //            return;
7572 //        }
7573 //        
7574 //        this.store = Roo.factory(this.store, Roo.data);
7575 //        this.store.on('load', this.onLoad, this);
7576 //        
7577 //        this.store.load();
7578 //        
7579 //    },
7580 //    
7581 //    onLoad: function () 
7582 //    {   
7583 //        this.fireEvent('load', this);
7584 //    }
7585 //    
7586 //   
7587 });
7588
7589  
7590
7591  /*
7592  * Based on:
7593  * Ext JS Library 1.1.1
7594  * Copyright(c) 2006-2007, Ext JS, LLC.
7595  *
7596  * Originally Released Under LGPL - original licence link has changed is not relivant.
7597  *
7598  * Fork - LGPL
7599  * <script type="text/javascript">
7600  */
7601
7602 // as we use this in bootstrap.
7603 Roo.namespace('Roo.form');
7604  /**
7605  * @class Roo.form.Action
7606  * Internal Class used to handle form actions
7607  * @constructor
7608  * @param {Roo.form.BasicForm} el The form element or its id
7609  * @param {Object} config Configuration options
7610  */
7611
7612  
7613  
7614 // define the action interface
7615 Roo.form.Action = function(form, options){
7616     this.form = form;
7617     this.options = options || {};
7618 };
7619 /**
7620  * Client Validation Failed
7621  * @const 
7622  */
7623 Roo.form.Action.CLIENT_INVALID = 'client';
7624 /**
7625  * Server Validation Failed
7626  * @const 
7627  */
7628 Roo.form.Action.SERVER_INVALID = 'server';
7629  /**
7630  * Connect to Server Failed
7631  * @const 
7632  */
7633 Roo.form.Action.CONNECT_FAILURE = 'connect';
7634 /**
7635  * Reading Data from Server Failed
7636  * @const 
7637  */
7638 Roo.form.Action.LOAD_FAILURE = 'load';
7639
7640 Roo.form.Action.prototype = {
7641     type : 'default',
7642     failureType : undefined,
7643     response : undefined,
7644     result : undefined,
7645
7646     // interface method
7647     run : function(options){
7648
7649     },
7650
7651     // interface method
7652     success : function(response){
7653
7654     },
7655
7656     // interface method
7657     handleResponse : function(response){
7658
7659     },
7660
7661     // default connection failure
7662     failure : function(response){
7663         
7664         this.response = response;
7665         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7666         this.form.afterAction(this, false);
7667     },
7668
7669     processResponse : function(response){
7670         this.response = response;
7671         if(!response.responseText){
7672             return true;
7673         }
7674         this.result = this.handleResponse(response);
7675         return this.result;
7676     },
7677
7678     // utility functions used internally
7679     getUrl : function(appendParams){
7680         var url = this.options.url || this.form.url || this.form.el.dom.action;
7681         if(appendParams){
7682             var p = this.getParams();
7683             if(p){
7684                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7685             }
7686         }
7687         return url;
7688     },
7689
7690     getMethod : function(){
7691         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7692     },
7693
7694     getParams : function(){
7695         var bp = this.form.baseParams;
7696         var p = this.options.params;
7697         if(p){
7698             if(typeof p == "object"){
7699                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7700             }else if(typeof p == 'string' && bp){
7701                 p += '&' + Roo.urlEncode(bp);
7702             }
7703         }else if(bp){
7704             p = Roo.urlEncode(bp);
7705         }
7706         return p;
7707     },
7708
7709     createCallback : function(){
7710         return {
7711             success: this.success,
7712             failure: this.failure,
7713             scope: this,
7714             timeout: (this.form.timeout*1000),
7715             upload: this.form.fileUpload ? this.success : undefined
7716         };
7717     }
7718 };
7719
7720 Roo.form.Action.Submit = function(form, options){
7721     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7722 };
7723
7724 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7725     type : 'submit',
7726
7727     haveProgress : false,
7728     uploadComplete : false,
7729     
7730     // uploadProgress indicator.
7731     uploadProgress : function()
7732     {
7733         if (!this.form.progressUrl) {
7734             return;
7735         }
7736         
7737         if (!this.haveProgress) {
7738             Roo.MessageBox.progress("Uploading", "Uploading");
7739         }
7740         if (this.uploadComplete) {
7741            Roo.MessageBox.hide();
7742            return;
7743         }
7744         
7745         this.haveProgress = true;
7746    
7747         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7748         
7749         var c = new Roo.data.Connection();
7750         c.request({
7751             url : this.form.progressUrl,
7752             params: {
7753                 id : uid
7754             },
7755             method: 'GET',
7756             success : function(req){
7757                //console.log(data);
7758                 var rdata = false;
7759                 var edata;
7760                 try  {
7761                    rdata = Roo.decode(req.responseText)
7762                 } catch (e) {
7763                     Roo.log("Invalid data from server..");
7764                     Roo.log(edata);
7765                     return;
7766                 }
7767                 if (!rdata || !rdata.success) {
7768                     Roo.log(rdata);
7769                     Roo.MessageBox.alert(Roo.encode(rdata));
7770                     return;
7771                 }
7772                 var data = rdata.data;
7773                 
7774                 if (this.uploadComplete) {
7775                    Roo.MessageBox.hide();
7776                    return;
7777                 }
7778                    
7779                 if (data){
7780                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7781                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7782                     );
7783                 }
7784                 this.uploadProgress.defer(2000,this);
7785             },
7786        
7787             failure: function(data) {
7788                 Roo.log('progress url failed ');
7789                 Roo.log(data);
7790             },
7791             scope : this
7792         });
7793            
7794     },
7795     
7796     
7797     run : function()
7798     {
7799         // run get Values on the form, so it syncs any secondary forms.
7800         this.form.getValues();
7801         
7802         var o = this.options;
7803         var method = this.getMethod();
7804         var isPost = method == 'POST';
7805         if(o.clientValidation === false || this.form.isValid()){
7806             
7807             if (this.form.progressUrl) {
7808                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7809                     (new Date() * 1) + '' + Math.random());
7810                     
7811             } 
7812             
7813             
7814             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7815                 form:this.form.el.dom,
7816                 url:this.getUrl(!isPost),
7817                 method: method,
7818                 params:isPost ? this.getParams() : null,
7819                 isUpload: this.form.fileUpload
7820             }));
7821             
7822             this.uploadProgress();
7823
7824         }else if (o.clientValidation !== false){ // client validation failed
7825             this.failureType = Roo.form.Action.CLIENT_INVALID;
7826             this.form.afterAction(this, false);
7827         }
7828     },
7829
7830     success : function(response)
7831     {
7832         this.uploadComplete= true;
7833         if (this.haveProgress) {
7834             Roo.MessageBox.hide();
7835         }
7836         
7837         
7838         var result = this.processResponse(response);
7839         if(result === true || result.success){
7840             this.form.afterAction(this, true);
7841             return;
7842         }
7843         if(result.errors){
7844             this.form.markInvalid(result.errors);
7845             this.failureType = Roo.form.Action.SERVER_INVALID;
7846         }
7847         this.form.afterAction(this, false);
7848     },
7849     failure : function(response)
7850     {
7851         this.uploadComplete= true;
7852         if (this.haveProgress) {
7853             Roo.MessageBox.hide();
7854         }
7855         
7856         this.response = response;
7857         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7858         this.form.afterAction(this, false);
7859     },
7860     
7861     handleResponse : function(response){
7862         if(this.form.errorReader){
7863             var rs = this.form.errorReader.read(response);
7864             var errors = [];
7865             if(rs.records){
7866                 for(var i = 0, len = rs.records.length; i < len; i++) {
7867                     var r = rs.records[i];
7868                     errors[i] = r.data;
7869                 }
7870             }
7871             if(errors.length < 1){
7872                 errors = null;
7873             }
7874             return {
7875                 success : rs.success,
7876                 errors : errors
7877             };
7878         }
7879         var ret = false;
7880         try {
7881             ret = Roo.decode(response.responseText);
7882         } catch (e) {
7883             ret = {
7884                 success: false,
7885                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7886                 errors : []
7887             };
7888         }
7889         return ret;
7890         
7891     }
7892 });
7893
7894
7895 Roo.form.Action.Load = function(form, options){
7896     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7897     this.reader = this.form.reader;
7898 };
7899
7900 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7901     type : 'load',
7902
7903     run : function(){
7904         
7905         Roo.Ajax.request(Roo.apply(
7906                 this.createCallback(), {
7907                     method:this.getMethod(),
7908                     url:this.getUrl(false),
7909                     params:this.getParams()
7910         }));
7911     },
7912
7913     success : function(response){
7914         
7915         var result = this.processResponse(response);
7916         if(result === true || !result.success || !result.data){
7917             this.failureType = Roo.form.Action.LOAD_FAILURE;
7918             this.form.afterAction(this, false);
7919             return;
7920         }
7921         this.form.clearInvalid();
7922         this.form.setValues(result.data);
7923         this.form.afterAction(this, true);
7924     },
7925
7926     handleResponse : function(response){
7927         if(this.form.reader){
7928             var rs = this.form.reader.read(response);
7929             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7930             return {
7931                 success : rs.success,
7932                 data : data
7933             };
7934         }
7935         return Roo.decode(response.responseText);
7936     }
7937 });
7938
7939 Roo.form.Action.ACTION_TYPES = {
7940     'load' : Roo.form.Action.Load,
7941     'submit' : Roo.form.Action.Submit
7942 };/*
7943  * - LGPL
7944  *
7945  * form
7946  *
7947  */
7948
7949 /**
7950  * @class Roo.bootstrap.Form
7951  * @extends Roo.bootstrap.Component
7952  * Bootstrap Form class
7953  * @cfg {String} method  GET | POST (default POST)
7954  * @cfg {String} labelAlign top | left (default top)
7955  * @cfg {String} align left  | right - for navbars
7956  * @cfg {Boolean} loadMask load mask when submit (default true)
7957
7958  *
7959  * @constructor
7960  * Create a new Form
7961  * @param {Object} config The config object
7962  */
7963
7964
7965 Roo.bootstrap.Form = function(config){
7966     
7967     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7968     
7969     Roo.bootstrap.Form.popover.apply();
7970     
7971     this.addEvents({
7972         /**
7973          * @event clientvalidation
7974          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7975          * @param {Form} this
7976          * @param {Boolean} valid true if the form has passed client-side validation
7977          */
7978         clientvalidation: true,
7979         /**
7980          * @event beforeaction
7981          * Fires before any action is performed. Return false to cancel the action.
7982          * @param {Form} this
7983          * @param {Action} action The action to be performed
7984          */
7985         beforeaction: true,
7986         /**
7987          * @event actionfailed
7988          * Fires when an action fails.
7989          * @param {Form} this
7990          * @param {Action} action The action that failed
7991          */
7992         actionfailed : true,
7993         /**
7994          * @event actioncomplete
7995          * Fires when an action is completed.
7996          * @param {Form} this
7997          * @param {Action} action The action that completed
7998          */
7999         actioncomplete : true
8000     });
8001 };
8002
8003 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8004
8005      /**
8006      * @cfg {String} method
8007      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8008      */
8009     method : 'POST',
8010     /**
8011      * @cfg {String} url
8012      * The URL to use for form actions if one isn't supplied in the action options.
8013      */
8014     /**
8015      * @cfg {Boolean} fileUpload
8016      * Set to true if this form is a file upload.
8017      */
8018
8019     /**
8020      * @cfg {Object} baseParams
8021      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8022      */
8023
8024     /**
8025      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8026      */
8027     timeout: 30,
8028     /**
8029      * @cfg {Sting} align (left|right) for navbar forms
8030      */
8031     align : 'left',
8032
8033     // private
8034     activeAction : null,
8035
8036     /**
8037      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8038      * element by passing it or its id or mask the form itself by passing in true.
8039      * @type Mixed
8040      */
8041     waitMsgTarget : false,
8042
8043     loadMask : true,
8044     
8045     /**
8046      * @cfg {Boolean} errorMask (true|false) default false
8047      */
8048     errorMask : false,
8049     
8050     /**
8051      * @cfg {Number} maskOffset Default 100
8052      */
8053     maskOffset : 100,
8054     
8055     /**
8056      * @cfg {Boolean} maskBody
8057      */
8058     maskBody : false,
8059
8060     getAutoCreate : function(){
8061
8062         var cfg = {
8063             tag: 'form',
8064             method : this.method || 'POST',
8065             id : this.id || Roo.id(),
8066             cls : ''
8067         };
8068         if (this.parent().xtype.match(/^Nav/)) {
8069             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8070
8071         }
8072
8073         if (this.labelAlign == 'left' ) {
8074             cfg.cls += ' form-horizontal';
8075         }
8076
8077
8078         return cfg;
8079     },
8080     initEvents : function()
8081     {
8082         this.el.on('submit', this.onSubmit, this);
8083         // this was added as random key presses on the form where triggering form submit.
8084         this.el.on('keypress', function(e) {
8085             if (e.getCharCode() != 13) {
8086                 return true;
8087             }
8088             // we might need to allow it for textareas.. and some other items.
8089             // check e.getTarget().
8090
8091             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8092                 return true;
8093             }
8094
8095             Roo.log("keypress blocked");
8096
8097             e.preventDefault();
8098             return false;
8099         });
8100         
8101     },
8102     // private
8103     onSubmit : function(e){
8104         e.stopEvent();
8105     },
8106
8107      /**
8108      * Returns true if client-side validation on the form is successful.
8109      * @return Boolean
8110      */
8111     isValid : function(){
8112         var items = this.getItems();
8113         var valid = true;
8114         var target = false;
8115         
8116         items.each(function(f){
8117             
8118             if(f.validate()){
8119                 return;
8120             }
8121             
8122             Roo.log('invalid field: ' + f.name);
8123             
8124             valid = false;
8125
8126             if(!target && f.el.isVisible(true)){
8127                 target = f;
8128             }
8129            
8130         });
8131         
8132         if(this.errorMask && !valid){
8133             Roo.bootstrap.Form.popover.mask(this, target);
8134         }
8135         
8136         return valid;
8137     },
8138     
8139     /**
8140      * Returns true if any fields in this form have changed since their original load.
8141      * @return Boolean
8142      */
8143     isDirty : function(){
8144         var dirty = false;
8145         var items = this.getItems();
8146         items.each(function(f){
8147            if(f.isDirty()){
8148                dirty = true;
8149                return false;
8150            }
8151            return true;
8152         });
8153         return dirty;
8154     },
8155      /**
8156      * Performs a predefined action (submit or load) or custom actions you define on this form.
8157      * @param {String} actionName The name of the action type
8158      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8159      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8160      * accept other config options):
8161      * <pre>
8162 Property          Type             Description
8163 ----------------  ---------------  ----------------------------------------------------------------------------------
8164 url               String           The url for the action (defaults to the form's url)
8165 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8166 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8167 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8168                                    validate the form on the client (defaults to false)
8169      * </pre>
8170      * @return {BasicForm} this
8171      */
8172     doAction : function(action, options){
8173         if(typeof action == 'string'){
8174             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8175         }
8176         if(this.fireEvent('beforeaction', this, action) !== false){
8177             this.beforeAction(action);
8178             action.run.defer(100, action);
8179         }
8180         return this;
8181     },
8182
8183     // private
8184     beforeAction : function(action){
8185         var o = action.options;
8186         
8187         if(this.loadMask){
8188             
8189             if(this.maskBody){
8190                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8191             } else {
8192                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8193             }
8194         }
8195         // not really supported yet.. ??
8196
8197         //if(this.waitMsgTarget === true){
8198         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8199         //}else if(this.waitMsgTarget){
8200         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8201         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8202         //}else {
8203         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8204        // }
8205
8206     },
8207
8208     // private
8209     afterAction : function(action, success){
8210         this.activeAction = null;
8211         var o = action.options;
8212
8213         if(this.loadMask){
8214             
8215             if(this.maskBody){
8216                 Roo.get(document.body).unmask();
8217             } else {
8218                 this.el.unmask();
8219             }
8220         }
8221         
8222         //if(this.waitMsgTarget === true){
8223 //            this.el.unmask();
8224         //}else if(this.waitMsgTarget){
8225         //    this.waitMsgTarget.unmask();
8226         //}else{
8227         //    Roo.MessageBox.updateProgress(1);
8228         //    Roo.MessageBox.hide();
8229        // }
8230         //
8231         if(success){
8232             if(o.reset){
8233                 this.reset();
8234             }
8235             Roo.callback(o.success, o.scope, [this, action]);
8236             this.fireEvent('actioncomplete', this, action);
8237
8238         }else{
8239
8240             // failure condition..
8241             // we have a scenario where updates need confirming.
8242             // eg. if a locking scenario exists..
8243             // we look for { errors : { needs_confirm : true }} in the response.
8244             if (
8245                 (typeof(action.result) != 'undefined')  &&
8246                 (typeof(action.result.errors) != 'undefined')  &&
8247                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8248            ){
8249                 var _t = this;
8250                 Roo.log("not supported yet");
8251                  /*
8252
8253                 Roo.MessageBox.confirm(
8254                     "Change requires confirmation",
8255                     action.result.errorMsg,
8256                     function(r) {
8257                         if (r != 'yes') {
8258                             return;
8259                         }
8260                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8261                     }
8262
8263                 );
8264                 */
8265
8266
8267                 return;
8268             }
8269
8270             Roo.callback(o.failure, o.scope, [this, action]);
8271             // show an error message if no failed handler is set..
8272             if (!this.hasListener('actionfailed')) {
8273                 Roo.log("need to add dialog support");
8274                 /*
8275                 Roo.MessageBox.alert("Error",
8276                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8277                         action.result.errorMsg :
8278                         "Saving Failed, please check your entries or try again"
8279                 );
8280                 */
8281             }
8282
8283             this.fireEvent('actionfailed', this, action);
8284         }
8285
8286     },
8287     /**
8288      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8289      * @param {String} id The value to search for
8290      * @return Field
8291      */
8292     findField : function(id){
8293         var items = this.getItems();
8294         var field = items.get(id);
8295         if(!field){
8296              items.each(function(f){
8297                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8298                     field = f;
8299                     return false;
8300                 }
8301                 return true;
8302             });
8303         }
8304         return field || null;
8305     },
8306      /**
8307      * Mark fields in this form invalid in bulk.
8308      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8309      * @return {BasicForm} this
8310      */
8311     markInvalid : function(errors){
8312         if(errors instanceof Array){
8313             for(var i = 0, len = errors.length; i < len; i++){
8314                 var fieldError = errors[i];
8315                 var f = this.findField(fieldError.id);
8316                 if(f){
8317                     f.markInvalid(fieldError.msg);
8318                 }
8319             }
8320         }else{
8321             var field, id;
8322             for(id in errors){
8323                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8324                     field.markInvalid(errors[id]);
8325                 }
8326             }
8327         }
8328         //Roo.each(this.childForms || [], function (f) {
8329         //    f.markInvalid(errors);
8330         //});
8331
8332         return this;
8333     },
8334
8335     /**
8336      * Set values for fields in this form in bulk.
8337      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8338      * @return {BasicForm} this
8339      */
8340     setValues : function(values){
8341         if(values instanceof Array){ // array of objects
8342             for(var i = 0, len = values.length; i < len; i++){
8343                 var v = values[i];
8344                 var f = this.findField(v.id);
8345                 if(f){
8346                     f.setValue(v.value);
8347                     if(this.trackResetOnLoad){
8348                         f.originalValue = f.getValue();
8349                     }
8350                 }
8351             }
8352         }else{ // object hash
8353             var field, id;
8354             for(id in values){
8355                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8356
8357                     if (field.setFromData &&
8358                         field.valueField &&
8359                         field.displayField &&
8360                         // combos' with local stores can
8361                         // be queried via setValue()
8362                         // to set their value..
8363                         (field.store && !field.store.isLocal)
8364                         ) {
8365                         // it's a combo
8366                         var sd = { };
8367                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8368                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8369                         field.setFromData(sd);
8370
8371                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8372                         
8373                         field.setFromData(values);
8374                         
8375                     } else {
8376                         field.setValue(values[id]);
8377                     }
8378
8379
8380                     if(this.trackResetOnLoad){
8381                         field.originalValue = field.getValue();
8382                     }
8383                 }
8384             }
8385         }
8386
8387         //Roo.each(this.childForms || [], function (f) {
8388         //    f.setValues(values);
8389         //});
8390
8391         return this;
8392     },
8393
8394     /**
8395      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8396      * they are returned as an array.
8397      * @param {Boolean} asString
8398      * @return {Object}
8399      */
8400     getValues : function(asString){
8401         //if (this.childForms) {
8402             // copy values from the child forms
8403         //    Roo.each(this.childForms, function (f) {
8404         //        this.setValues(f.getValues());
8405         //    }, this);
8406         //}
8407
8408
8409
8410         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8411         if(asString === true){
8412             return fs;
8413         }
8414         return Roo.urlDecode(fs);
8415     },
8416
8417     /**
8418      * Returns the fields in this form as an object with key/value pairs.
8419      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8420      * @return {Object}
8421      */
8422     getFieldValues : function(with_hidden)
8423     {
8424         var items = this.getItems();
8425         var ret = {};
8426         items.each(function(f){
8427             
8428             if (!f.getName()) {
8429                 return;
8430             }
8431             
8432             var v = f.getValue();
8433             
8434             if (f.inputType =='radio') {
8435                 if (typeof(ret[f.getName()]) == 'undefined') {
8436                     ret[f.getName()] = ''; // empty..
8437                 }
8438
8439                 if (!f.el.dom.checked) {
8440                     return;
8441
8442                 }
8443                 v = f.el.dom.value;
8444
8445             }
8446             
8447             if(f.xtype == 'MoneyField'){
8448                 ret[f.currencyName] = f.getCurrency();
8449             }
8450
8451             // not sure if this supported any more..
8452             if ((typeof(v) == 'object') && f.getRawValue) {
8453                 v = f.getRawValue() ; // dates..
8454             }
8455             // combo boxes where name != hiddenName...
8456             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8457                 ret[f.name] = f.getRawValue();
8458             }
8459             ret[f.getName()] = v;
8460         });
8461
8462         return ret;
8463     },
8464
8465     /**
8466      * Clears all invalid messages in this form.
8467      * @return {BasicForm} this
8468      */
8469     clearInvalid : function(){
8470         var items = this.getItems();
8471
8472         items.each(function(f){
8473            f.clearInvalid();
8474         });
8475
8476         return this;
8477     },
8478
8479     /**
8480      * Resets this form.
8481      * @return {BasicForm} this
8482      */
8483     reset : function(){
8484         var items = this.getItems();
8485         items.each(function(f){
8486             f.reset();
8487         });
8488
8489         Roo.each(this.childForms || [], function (f) {
8490             f.reset();
8491         });
8492
8493
8494         return this;
8495     },
8496     
8497     getItems : function()
8498     {
8499         var r=new Roo.util.MixedCollection(false, function(o){
8500             return o.id || (o.id = Roo.id());
8501         });
8502         var iter = function(el) {
8503             if (el.inputEl) {
8504                 r.add(el);
8505             }
8506             if (!el.items) {
8507                 return;
8508             }
8509             Roo.each(el.items,function(e) {
8510                 iter(e);
8511             });
8512         };
8513
8514         iter(this);
8515         return r;
8516     },
8517     
8518     hideFields : function(items)
8519     {
8520         Roo.each(items, function(i){
8521             
8522             var f = this.findField(i);
8523             
8524             if(!f){
8525                 return;
8526             }
8527             
8528             f.hide();
8529             
8530         }, this);
8531     },
8532     
8533     showFields : function(items)
8534     {
8535         Roo.each(items, function(i){
8536             
8537             var f = this.findField(i);
8538             
8539             if(!f){
8540                 return;
8541             }
8542             
8543             f.show();
8544             
8545         }, this);
8546     }
8547
8548 });
8549
8550 Roo.apply(Roo.bootstrap.Form, {
8551     
8552     popover : {
8553         
8554         padding : 5,
8555         
8556         isApplied : false,
8557         
8558         isMasked : false,
8559         
8560         form : false,
8561         
8562         target : false,
8563         
8564         toolTip : false,
8565         
8566         intervalID : false,
8567         
8568         maskEl : false,
8569         
8570         apply : function()
8571         {
8572             if(this.isApplied){
8573                 return;
8574             }
8575             
8576             this.maskEl = {
8577                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8578                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8579                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8580                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8581             };
8582             
8583             this.maskEl.top.enableDisplayMode("block");
8584             this.maskEl.left.enableDisplayMode("block");
8585             this.maskEl.bottom.enableDisplayMode("block");
8586             this.maskEl.right.enableDisplayMode("block");
8587             
8588             this.toolTip = new Roo.bootstrap.Tooltip({
8589                 cls : 'roo-form-error-popover',
8590                 alignment : {
8591                     'left' : ['r-l', [-2,0], 'right'],
8592                     'right' : ['l-r', [2,0], 'left'],
8593                     'bottom' : ['tl-bl', [0,2], 'top'],
8594                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8595                 }
8596             });
8597             
8598             this.toolTip.render(Roo.get(document.body));
8599
8600             this.toolTip.el.enableDisplayMode("block");
8601             
8602             Roo.get(document.body).on('click', function(){
8603                 this.unmask();
8604             }, this);
8605             
8606             Roo.get(document.body).on('touchstart', function(){
8607                 this.unmask();
8608             }, this);
8609             
8610             this.isApplied = true
8611         },
8612         
8613         mask : function(form, target)
8614         {
8615             this.form = form;
8616             
8617             this.target = target;
8618             
8619             if(!this.form.errorMask || !target.el){
8620                 return;
8621             }
8622             
8623             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8624             
8625             Roo.log(scrollable);
8626             
8627             var ot = this.target.el.calcOffsetsTo(scrollable);
8628             
8629             var scrollTo = ot[1] - this.form.maskOffset;
8630             
8631             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8632             
8633             scrollable.scrollTo('top', scrollTo);
8634             
8635             var box = this.target.el.getBox();
8636             Roo.log(box);
8637             var zIndex = Roo.bootstrap.Modal.zIndex++;
8638
8639             
8640             this.maskEl.top.setStyle('position', 'absolute');
8641             this.maskEl.top.setStyle('z-index', zIndex);
8642             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8643             this.maskEl.top.setLeft(0);
8644             this.maskEl.top.setTop(0);
8645             this.maskEl.top.show();
8646             
8647             this.maskEl.left.setStyle('position', 'absolute');
8648             this.maskEl.left.setStyle('z-index', zIndex);
8649             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8650             this.maskEl.left.setLeft(0);
8651             this.maskEl.left.setTop(box.y - this.padding);
8652             this.maskEl.left.show();
8653
8654             this.maskEl.bottom.setStyle('position', 'absolute');
8655             this.maskEl.bottom.setStyle('z-index', zIndex);
8656             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8657             this.maskEl.bottom.setLeft(0);
8658             this.maskEl.bottom.setTop(box.bottom + this.padding);
8659             this.maskEl.bottom.show();
8660
8661             this.maskEl.right.setStyle('position', 'absolute');
8662             this.maskEl.right.setStyle('z-index', zIndex);
8663             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8664             this.maskEl.right.setLeft(box.right + this.padding);
8665             this.maskEl.right.setTop(box.y - this.padding);
8666             this.maskEl.right.show();
8667
8668             this.toolTip.bindEl = this.target.el;
8669
8670             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8671
8672             var tip = this.target.blankText;
8673
8674             if(this.target.getValue() !== '' ) {
8675                 
8676                 if (this.target.invalidText.length) {
8677                     tip = this.target.invalidText;
8678                 } else if (this.target.regexText.length){
8679                     tip = this.target.regexText;
8680                 }
8681             }
8682
8683             this.toolTip.show(tip);
8684
8685             this.intervalID = window.setInterval(function() {
8686                 Roo.bootstrap.Form.popover.unmask();
8687             }, 10000);
8688
8689             window.onwheel = function(){ return false;};
8690             
8691             (function(){ this.isMasked = true; }).defer(500, this);
8692             
8693         },
8694         
8695         unmask : function()
8696         {
8697             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8698                 return;
8699             }
8700             
8701             this.maskEl.top.setStyle('position', 'absolute');
8702             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8703             this.maskEl.top.hide();
8704
8705             this.maskEl.left.setStyle('position', 'absolute');
8706             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8707             this.maskEl.left.hide();
8708
8709             this.maskEl.bottom.setStyle('position', 'absolute');
8710             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8711             this.maskEl.bottom.hide();
8712
8713             this.maskEl.right.setStyle('position', 'absolute');
8714             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8715             this.maskEl.right.hide();
8716             
8717             this.toolTip.hide();
8718             
8719             this.toolTip.el.hide();
8720             
8721             window.onwheel = function(){ return true;};
8722             
8723             if(this.intervalID){
8724                 window.clearInterval(this.intervalID);
8725                 this.intervalID = false;
8726             }
8727             
8728             this.isMasked = false;
8729             
8730         }
8731         
8732     }
8733     
8734 });
8735
8736 /*
8737  * Based on:
8738  * Ext JS Library 1.1.1
8739  * Copyright(c) 2006-2007, Ext JS, LLC.
8740  *
8741  * Originally Released Under LGPL - original licence link has changed is not relivant.
8742  *
8743  * Fork - LGPL
8744  * <script type="text/javascript">
8745  */
8746 /**
8747  * @class Roo.form.VTypes
8748  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8749  * @singleton
8750  */
8751 Roo.form.VTypes = function(){
8752     // closure these in so they are only created once.
8753     var alpha = /^[a-zA-Z_]+$/;
8754     var alphanum = /^[a-zA-Z0-9_]+$/;
8755     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8756     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8757
8758     // All these messages and functions are configurable
8759     return {
8760         /**
8761          * The function used to validate email addresses
8762          * @param {String} value The email address
8763          */
8764         'email' : function(v){
8765             return email.test(v);
8766         },
8767         /**
8768          * The error text to display when the email validation function returns false
8769          * @type String
8770          */
8771         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8772         /**
8773          * The keystroke filter mask to be applied on email input
8774          * @type RegExp
8775          */
8776         'emailMask' : /[a-z0-9_\.\-@]/i,
8777
8778         /**
8779          * The function used to validate URLs
8780          * @param {String} value The URL
8781          */
8782         'url' : function(v){
8783             return url.test(v);
8784         },
8785         /**
8786          * The error text to display when the url validation function returns false
8787          * @type String
8788          */
8789         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8790         
8791         /**
8792          * The function used to validate alpha values
8793          * @param {String} value The value
8794          */
8795         'alpha' : function(v){
8796             return alpha.test(v);
8797         },
8798         /**
8799          * The error text to display when the alpha validation function returns false
8800          * @type String
8801          */
8802         'alphaText' : 'This field should only contain letters and _',
8803         /**
8804          * The keystroke filter mask to be applied on alpha input
8805          * @type RegExp
8806          */
8807         'alphaMask' : /[a-z_]/i,
8808
8809         /**
8810          * The function used to validate alphanumeric values
8811          * @param {String} value The value
8812          */
8813         'alphanum' : function(v){
8814             return alphanum.test(v);
8815         },
8816         /**
8817          * The error text to display when the alphanumeric validation function returns false
8818          * @type String
8819          */
8820         'alphanumText' : 'This field should only contain letters, numbers and _',
8821         /**
8822          * The keystroke filter mask to be applied on alphanumeric input
8823          * @type RegExp
8824          */
8825         'alphanumMask' : /[a-z0-9_]/i
8826     };
8827 }();/*
8828  * - LGPL
8829  *
8830  * Input
8831  * 
8832  */
8833
8834 /**
8835  * @class Roo.bootstrap.Input
8836  * @extends Roo.bootstrap.Component
8837  * Bootstrap Input class
8838  * @cfg {Boolean} disabled is it disabled
8839  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8840  * @cfg {String} name name of the input
8841  * @cfg {string} fieldLabel - the label associated
8842  * @cfg {string} placeholder - placeholder to put in text.
8843  * @cfg {string}  before - input group add on before
8844  * @cfg {string} after - input group add on after
8845  * @cfg {string} size - (lg|sm) or leave empty..
8846  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8847  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8848  * @cfg {Number} md colspan out of 12 for computer-sized screens
8849  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8850  * @cfg {string} value default value of the input
8851  * @cfg {Number} labelWidth set the width of label 
8852  * @cfg {Number} labellg set the width of label (1-12)
8853  * @cfg {Number} labelmd set the width of label (1-12)
8854  * @cfg {Number} labelsm set the width of label (1-12)
8855  * @cfg {Number} labelxs set the width of label (1-12)
8856  * @cfg {String} labelAlign (top|left)
8857  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8858  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8859  * @cfg {String} indicatorpos (left|right) default left
8860  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8861  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8862
8863  * @cfg {String} align (left|center|right) Default left
8864  * @cfg {Boolean} forceFeedback (true|false) Default false
8865  * 
8866  * @constructor
8867  * Create a new Input
8868  * @param {Object} config The config object
8869  */
8870
8871 Roo.bootstrap.Input = function(config){
8872     
8873     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8874     
8875     this.addEvents({
8876         /**
8877          * @event focus
8878          * Fires when this field receives input focus.
8879          * @param {Roo.form.Field} this
8880          */
8881         focus : true,
8882         /**
8883          * @event blur
8884          * Fires when this field loses input focus.
8885          * @param {Roo.form.Field} this
8886          */
8887         blur : true,
8888         /**
8889          * @event specialkey
8890          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8891          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8892          * @param {Roo.form.Field} this
8893          * @param {Roo.EventObject} e The event object
8894          */
8895         specialkey : true,
8896         /**
8897          * @event change
8898          * Fires just before the field blurs if the field value has changed.
8899          * @param {Roo.form.Field} this
8900          * @param {Mixed} newValue The new value
8901          * @param {Mixed} oldValue The original value
8902          */
8903         change : true,
8904         /**
8905          * @event invalid
8906          * Fires after the field has been marked as invalid.
8907          * @param {Roo.form.Field} this
8908          * @param {String} msg The validation message
8909          */
8910         invalid : true,
8911         /**
8912          * @event valid
8913          * Fires after the field has been validated with no errors.
8914          * @param {Roo.form.Field} this
8915          */
8916         valid : true,
8917          /**
8918          * @event keyup
8919          * Fires after the key up
8920          * @param {Roo.form.Field} this
8921          * @param {Roo.EventObject}  e The event Object
8922          */
8923         keyup : true
8924     });
8925 };
8926
8927 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8928      /**
8929      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8930       automatic validation (defaults to "keyup").
8931      */
8932     validationEvent : "keyup",
8933      /**
8934      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8935      */
8936     validateOnBlur : true,
8937     /**
8938      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8939      */
8940     validationDelay : 250,
8941      /**
8942      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8943      */
8944     focusClass : "x-form-focus",  // not needed???
8945     
8946        
8947     /**
8948      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8949      */
8950     invalidClass : "has-warning",
8951     
8952     /**
8953      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8954      */
8955     validClass : "has-success",
8956     
8957     /**
8958      * @cfg {Boolean} hasFeedback (true|false) default true
8959      */
8960     hasFeedback : true,
8961     
8962     /**
8963      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8964      */
8965     invalidFeedbackClass : "glyphicon-warning-sign",
8966     
8967     /**
8968      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8969      */
8970     validFeedbackClass : "glyphicon-ok",
8971     
8972     /**
8973      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8974      */
8975     selectOnFocus : false,
8976     
8977      /**
8978      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8979      */
8980     maskRe : null,
8981        /**
8982      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8983      */
8984     vtype : null,
8985     
8986       /**
8987      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8988      */
8989     disableKeyFilter : false,
8990     
8991        /**
8992      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8993      */
8994     disabled : false,
8995      /**
8996      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8997      */
8998     allowBlank : true,
8999     /**
9000      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9001      */
9002     blankText : "Please complete this mandatory field",
9003     
9004      /**
9005      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9006      */
9007     minLength : 0,
9008     /**
9009      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9010      */
9011     maxLength : Number.MAX_VALUE,
9012     /**
9013      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9014      */
9015     minLengthText : "The minimum length for this field is {0}",
9016     /**
9017      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9018      */
9019     maxLengthText : "The maximum length for this field is {0}",
9020   
9021     
9022     /**
9023      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9024      * If available, this function will be called only after the basic validators all return true, and will be passed the
9025      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9026      */
9027     validator : null,
9028     /**
9029      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9030      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9031      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9032      */
9033     regex : null,
9034     /**
9035      * @cfg {String} regexText -- Depricated - use Invalid Text
9036      */
9037     regexText : "",
9038     
9039     /**
9040      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9041      */
9042     invalidText : "",
9043     
9044     
9045     
9046     autocomplete: false,
9047     
9048     
9049     fieldLabel : '',
9050     inputType : 'text',
9051     
9052     name : false,
9053     placeholder: false,
9054     before : false,
9055     after : false,
9056     size : false,
9057     hasFocus : false,
9058     preventMark: false,
9059     isFormField : true,
9060     value : '',
9061     labelWidth : 2,
9062     labelAlign : false,
9063     readOnly : false,
9064     align : false,
9065     formatedValue : false,
9066     forceFeedback : false,
9067     
9068     indicatorpos : 'left',
9069     
9070     labellg : 0,
9071     labelmd : 0,
9072     labelsm : 0,
9073     labelxs : 0,
9074     
9075     capture : '',
9076     accept : '',
9077     
9078     parentLabelAlign : function()
9079     {
9080         var parent = this;
9081         while (parent.parent()) {
9082             parent = parent.parent();
9083             if (typeof(parent.labelAlign) !='undefined') {
9084                 return parent.labelAlign;
9085             }
9086         }
9087         return 'left';
9088         
9089     },
9090     
9091     getAutoCreate : function()
9092     {
9093         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9094         
9095         var id = Roo.id();
9096         
9097         var cfg = {};
9098         
9099         if(this.inputType != 'hidden'){
9100             cfg.cls = 'form-group' //input-group
9101         }
9102         
9103         var input =  {
9104             tag: 'input',
9105             id : id,
9106             type : this.inputType,
9107             value : this.value,
9108             cls : 'form-control',
9109             placeholder : this.placeholder || '',
9110             autocomplete : this.autocomplete || 'new-password'
9111         };
9112         
9113         if(this.capture.length){
9114             input.capture = this.capture;
9115         }
9116         
9117         if(this.accept.length){
9118             input.accept = this.accept + "/*";
9119         }
9120         
9121         if(this.align){
9122             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9123         }
9124         
9125         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9126             input.maxLength = this.maxLength;
9127         }
9128         
9129         if (this.disabled) {
9130             input.disabled=true;
9131         }
9132         
9133         if (this.readOnly) {
9134             input.readonly=true;
9135         }
9136         
9137         if (this.name) {
9138             input.name = this.name;
9139         }
9140         
9141         if (this.size) {
9142             input.cls += ' input-' + this.size;
9143         }
9144         
9145         var settings=this;
9146         ['xs','sm','md','lg'].map(function(size){
9147             if (settings[size]) {
9148                 cfg.cls += ' col-' + size + '-' + settings[size];
9149             }
9150         });
9151         
9152         var inputblock = input;
9153         
9154         var feedback = {
9155             tag: 'span',
9156             cls: 'glyphicon form-control-feedback'
9157         };
9158             
9159         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9160             
9161             inputblock = {
9162                 cls : 'has-feedback',
9163                 cn :  [
9164                     input,
9165                     feedback
9166                 ] 
9167             };  
9168         }
9169         
9170         if (this.before || this.after) {
9171             
9172             inputblock = {
9173                 cls : 'input-group',
9174                 cn :  [] 
9175             };
9176             
9177             if (this.before && typeof(this.before) == 'string') {
9178                 
9179                 inputblock.cn.push({
9180                     tag :'span',
9181                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9182                     html : this.before
9183                 });
9184             }
9185             if (this.before && typeof(this.before) == 'object') {
9186                 this.before = Roo.factory(this.before);
9187                 
9188                 inputblock.cn.push({
9189                     tag :'span',
9190                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9191                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9192                 });
9193             }
9194             
9195             inputblock.cn.push(input);
9196             
9197             if (this.after && typeof(this.after) == 'string') {
9198                 inputblock.cn.push({
9199                     tag :'span',
9200                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9201                     html : this.after
9202                 });
9203             }
9204             if (this.after && typeof(this.after) == 'object') {
9205                 this.after = Roo.factory(this.after);
9206                 
9207                 inputblock.cn.push({
9208                     tag :'span',
9209                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9210                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9211                 });
9212             }
9213             
9214             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9215                 inputblock.cls += ' has-feedback';
9216                 inputblock.cn.push(feedback);
9217             }
9218         };
9219         var indicator = {
9220             tag : 'i',
9221             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9222             tooltip : 'This field is required'
9223         };
9224         if (Roo.bootstrap.version == 4) {
9225             indicator = {
9226                 tag : 'i',
9227                 style : 'display-none'
9228             };
9229         }
9230         if (align ==='left' && this.fieldLabel.length) {
9231             
9232             cfg.cls += ' roo-form-group-label-left row';
9233             
9234             cfg.cn = [
9235                 indicator,
9236                 {
9237                     tag: 'label',
9238                     'for' :  id,
9239                     cls : 'control-label col-form-label',
9240                     html : this.fieldLabel
9241
9242                 },
9243                 {
9244                     cls : "", 
9245                     cn: [
9246                         inputblock
9247                     ]
9248                 }
9249             ];
9250             
9251             var labelCfg = cfg.cn[1];
9252             var contentCfg = cfg.cn[2];
9253             
9254             if(this.indicatorpos == 'right'){
9255                 cfg.cn = [
9256                     {
9257                         tag: 'label',
9258                         'for' :  id,
9259                         cls : 'control-label col-form-label',
9260                         cn : [
9261                             {
9262                                 tag : 'span',
9263                                 html : this.fieldLabel
9264                             },
9265                             indicator
9266                         ]
9267                     },
9268                     {
9269                         cls : "",
9270                         cn: [
9271                             inputblock
9272                         ]
9273                     }
9274
9275                 ];
9276                 
9277                 labelCfg = cfg.cn[0];
9278                 contentCfg = cfg.cn[1];
9279             
9280             }
9281             
9282             if(this.labelWidth > 12){
9283                 labelCfg.style = "width: " + this.labelWidth + 'px';
9284             }
9285             
9286             if(this.labelWidth < 13 && this.labelmd == 0){
9287                 this.labelmd = this.labelWidth;
9288             }
9289             
9290             if(this.labellg > 0){
9291                 labelCfg.cls += ' col-lg-' + this.labellg;
9292                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9293             }
9294             
9295             if(this.labelmd > 0){
9296                 labelCfg.cls += ' col-md-' + this.labelmd;
9297                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9298             }
9299             
9300             if(this.labelsm > 0){
9301                 labelCfg.cls += ' col-sm-' + this.labelsm;
9302                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9303             }
9304             
9305             if(this.labelxs > 0){
9306                 labelCfg.cls += ' col-xs-' + this.labelxs;
9307                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9308             }
9309             
9310             
9311         } else if ( this.fieldLabel.length) {
9312                 
9313             cfg.cn = [
9314                 {
9315                     tag : 'i',
9316                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9317                     tooltip : 'This field is required'
9318                 },
9319                 {
9320                     tag: 'label',
9321                    //cls : 'input-group-addon',
9322                     html : this.fieldLabel
9323
9324                 },
9325
9326                inputblock
9327
9328            ];
9329            
9330            if(this.indicatorpos == 'right'){
9331                 
9332                 cfg.cn = [
9333                     {
9334                         tag: 'label',
9335                        //cls : 'input-group-addon',
9336                         html : this.fieldLabel
9337
9338                     },
9339                     {
9340                         tag : 'i',
9341                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9342                         tooltip : 'This field is required'
9343                     },
9344
9345                    inputblock
9346
9347                ];
9348
9349             }
9350
9351         } else {
9352             
9353             cfg.cn = [
9354
9355                     inputblock
9356
9357             ];
9358                 
9359                 
9360         };
9361         
9362         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9363            cfg.cls += ' navbar-form';
9364         }
9365         
9366         if (this.parentType === 'NavGroup') {
9367            cfg.cls += ' navbar-form';
9368            cfg.tag = 'li';
9369         }
9370         
9371         return cfg;
9372         
9373     },
9374     /**
9375      * return the real input element.
9376      */
9377     inputEl: function ()
9378     {
9379         return this.el.select('input.form-control',true).first();
9380     },
9381     
9382     tooltipEl : function()
9383     {
9384         return this.inputEl();
9385     },
9386     
9387     indicatorEl : function()
9388     {
9389         if (Roo.bootstrap.version == 4) {
9390             return false; // not enabled in v4 yet.
9391         }
9392         
9393         var indicator = this.el.select('i.roo-required-indicator',true).first();
9394         
9395         if(!indicator){
9396             return false;
9397         }
9398         
9399         return indicator;
9400         
9401     },
9402     
9403     setDisabled : function(v)
9404     {
9405         var i  = this.inputEl().dom;
9406         if (!v) {
9407             i.removeAttribute('disabled');
9408             return;
9409             
9410         }
9411         i.setAttribute('disabled','true');
9412     },
9413     initEvents : function()
9414     {
9415           
9416         this.inputEl().on("keydown" , this.fireKey,  this);
9417         this.inputEl().on("focus", this.onFocus,  this);
9418         this.inputEl().on("blur", this.onBlur,  this);
9419         
9420         this.inputEl().relayEvent('keyup', this);
9421         
9422         this.indicator = this.indicatorEl();
9423         
9424         if(this.indicator){
9425             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9426         }
9427  
9428         // reference to original value for reset
9429         this.originalValue = this.getValue();
9430         //Roo.form.TextField.superclass.initEvents.call(this);
9431         if(this.validationEvent == 'keyup'){
9432             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9433             this.inputEl().on('keyup', this.filterValidation, this);
9434         }
9435         else if(this.validationEvent !== false){
9436             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9437         }
9438         
9439         if(this.selectOnFocus){
9440             this.on("focus", this.preFocus, this);
9441             
9442         }
9443         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9444             this.inputEl().on("keypress", this.filterKeys, this);
9445         } else {
9446             this.inputEl().relayEvent('keypress', this);
9447         }
9448        /* if(this.grow){
9449             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9450             this.el.on("click", this.autoSize,  this);
9451         }
9452         */
9453         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9454             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9455         }
9456         
9457         if (typeof(this.before) == 'object') {
9458             this.before.render(this.el.select('.roo-input-before',true).first());
9459         }
9460         if (typeof(this.after) == 'object') {
9461             this.after.render(this.el.select('.roo-input-after',true).first());
9462         }
9463         
9464         this.inputEl().on('change', this.onChange, this);
9465         
9466     },
9467     filterValidation : function(e){
9468         if(!e.isNavKeyPress()){
9469             this.validationTask.delay(this.validationDelay);
9470         }
9471     },
9472      /**
9473      * Validates the field value
9474      * @return {Boolean} True if the value is valid, else false
9475      */
9476     validate : function(){
9477         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9478         if(this.disabled || this.validateValue(this.getRawValue())){
9479             this.markValid();
9480             return true;
9481         }
9482         
9483         this.markInvalid();
9484         return false;
9485     },
9486     
9487     
9488     /**
9489      * Validates a value according to the field's validation rules and marks the field as invalid
9490      * if the validation fails
9491      * @param {Mixed} value The value to validate
9492      * @return {Boolean} True if the value is valid, else false
9493      */
9494     validateValue : function(value)
9495     {
9496         if(this.getVisibilityEl().hasClass('hidden')){
9497             return true;
9498         }
9499         
9500         if(value.length < 1)  { // if it's blank
9501             if(this.allowBlank){
9502                 return true;
9503             }
9504             return false;
9505         }
9506         
9507         if(value.length < this.minLength){
9508             return false;
9509         }
9510         if(value.length > this.maxLength){
9511             return false;
9512         }
9513         if(this.vtype){
9514             var vt = Roo.form.VTypes;
9515             if(!vt[this.vtype](value, this)){
9516                 return false;
9517             }
9518         }
9519         if(typeof this.validator == "function"){
9520             var msg = this.validator(value);
9521             if(msg !== true){
9522                 return false;
9523             }
9524             if (typeof(msg) == 'string') {
9525                 this.invalidText = msg;
9526             }
9527         }
9528         
9529         if(this.regex && !this.regex.test(value)){
9530             return false;
9531         }
9532         
9533         return true;
9534     },
9535     
9536      // private
9537     fireKey : function(e){
9538         //Roo.log('field ' + e.getKey());
9539         if(e.isNavKeyPress()){
9540             this.fireEvent("specialkey", this, e);
9541         }
9542     },
9543     focus : function (selectText){
9544         if(this.rendered){
9545             this.inputEl().focus();
9546             if(selectText === true){
9547                 this.inputEl().dom.select();
9548             }
9549         }
9550         return this;
9551     } ,
9552     
9553     onFocus : function(){
9554         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9555            // this.el.addClass(this.focusClass);
9556         }
9557         if(!this.hasFocus){
9558             this.hasFocus = true;
9559             this.startValue = this.getValue();
9560             this.fireEvent("focus", this);
9561         }
9562     },
9563     
9564     beforeBlur : Roo.emptyFn,
9565
9566     
9567     // private
9568     onBlur : function(){
9569         this.beforeBlur();
9570         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9571             //this.el.removeClass(this.focusClass);
9572         }
9573         this.hasFocus = false;
9574         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9575             this.validate();
9576         }
9577         var v = this.getValue();
9578         if(String(v) !== String(this.startValue)){
9579             this.fireEvent('change', this, v, this.startValue);
9580         }
9581         this.fireEvent("blur", this);
9582     },
9583     
9584     onChange : function(e)
9585     {
9586         var v = this.getValue();
9587         if(String(v) !== String(this.startValue)){
9588             this.fireEvent('change', this, v, this.startValue);
9589         }
9590         
9591     },
9592     
9593     /**
9594      * Resets the current field value to the originally loaded value and clears any validation messages
9595      */
9596     reset : function(){
9597         this.setValue(this.originalValue);
9598         this.validate();
9599     },
9600      /**
9601      * Returns the name of the field
9602      * @return {Mixed} name The name field
9603      */
9604     getName: function(){
9605         return this.name;
9606     },
9607      /**
9608      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9609      * @return {Mixed} value The field value
9610      */
9611     getValue : function(){
9612         
9613         var v = this.inputEl().getValue();
9614         
9615         return v;
9616     },
9617     /**
9618      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9619      * @return {Mixed} value The field value
9620      */
9621     getRawValue : function(){
9622         var v = this.inputEl().getValue();
9623         
9624         return v;
9625     },
9626     
9627     /**
9628      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9629      * @param {Mixed} value The value to set
9630      */
9631     setRawValue : function(v){
9632         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9633     },
9634     
9635     selectText : function(start, end){
9636         var v = this.getRawValue();
9637         if(v.length > 0){
9638             start = start === undefined ? 0 : start;
9639             end = end === undefined ? v.length : end;
9640             var d = this.inputEl().dom;
9641             if(d.setSelectionRange){
9642                 d.setSelectionRange(start, end);
9643             }else if(d.createTextRange){
9644                 var range = d.createTextRange();
9645                 range.moveStart("character", start);
9646                 range.moveEnd("character", v.length-end);
9647                 range.select();
9648             }
9649         }
9650     },
9651     
9652     /**
9653      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9654      * @param {Mixed} value The value to set
9655      */
9656     setValue : function(v){
9657         this.value = v;
9658         if(this.rendered){
9659             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9660             this.validate();
9661         }
9662     },
9663     
9664     /*
9665     processValue : function(value){
9666         if(this.stripCharsRe){
9667             var newValue = value.replace(this.stripCharsRe, '');
9668             if(newValue !== value){
9669                 this.setRawValue(newValue);
9670                 return newValue;
9671             }
9672         }
9673         return value;
9674     },
9675   */
9676     preFocus : function(){
9677         
9678         if(this.selectOnFocus){
9679             this.inputEl().dom.select();
9680         }
9681     },
9682     filterKeys : function(e){
9683         var k = e.getKey();
9684         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9685             return;
9686         }
9687         var c = e.getCharCode(), cc = String.fromCharCode(c);
9688         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9689             return;
9690         }
9691         if(!this.maskRe.test(cc)){
9692             e.stopEvent();
9693         }
9694     },
9695      /**
9696      * Clear any invalid styles/messages for this field
9697      */
9698     clearInvalid : function(){
9699         
9700         if(!this.el || this.preventMark){ // not rendered
9701             return;
9702         }
9703         
9704      
9705         this.el.removeClass(this.invalidClass);
9706         
9707         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9708             
9709             var feedback = this.el.select('.form-control-feedback', true).first();
9710             
9711             if(feedback){
9712                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9713             }
9714             
9715         }
9716         
9717         if(this.indicator){
9718             this.indicator.removeClass('visible');
9719             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9720         }
9721         
9722         this.fireEvent('valid', this);
9723     },
9724     
9725      /**
9726      * Mark this field as valid
9727      */
9728     markValid : function()
9729     {
9730         if(!this.el  || this.preventMark){ // not rendered...
9731             return;
9732         }
9733         
9734         this.el.removeClass([this.invalidClass, this.validClass]);
9735         
9736         var feedback = this.el.select('.form-control-feedback', true).first();
9737             
9738         if(feedback){
9739             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9740         }
9741         
9742         if(this.indicator){
9743             this.indicator.removeClass('visible');
9744             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9745         }
9746         
9747         if(this.disabled){
9748             return;
9749         }
9750         
9751         if(this.allowBlank && !this.getRawValue().length){
9752             return;
9753         }
9754         
9755         this.el.addClass(this.validClass);
9756         
9757         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9758             
9759             var feedback = this.el.select('.form-control-feedback', true).first();
9760             
9761             if(feedback){
9762                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9763                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9764             }
9765             
9766         }
9767         
9768         this.fireEvent('valid', this);
9769     },
9770     
9771      /**
9772      * Mark this field as invalid
9773      * @param {String} msg The validation message
9774      */
9775     markInvalid : function(msg)
9776     {
9777         if(!this.el  || this.preventMark){ // not rendered
9778             return;
9779         }
9780         
9781         this.el.removeClass([this.invalidClass, this.validClass]);
9782         
9783         var feedback = this.el.select('.form-control-feedback', true).first();
9784             
9785         if(feedback){
9786             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9787         }
9788
9789         if(this.disabled){
9790             return;
9791         }
9792         
9793         if(this.allowBlank && !this.getRawValue().length){
9794             return;
9795         }
9796         
9797         if(this.indicator){
9798             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9799             this.indicator.addClass('visible');
9800         }
9801         
9802         this.el.addClass(this.invalidClass);
9803         
9804         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9805             
9806             var feedback = this.el.select('.form-control-feedback', true).first();
9807             
9808             if(feedback){
9809                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9810                 
9811                 if(this.getValue().length || this.forceFeedback){
9812                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9813                 }
9814                 
9815             }
9816             
9817         }
9818         
9819         this.fireEvent('invalid', this, msg);
9820     },
9821     // private
9822     SafariOnKeyDown : function(event)
9823     {
9824         // this is a workaround for a password hang bug on chrome/ webkit.
9825         if (this.inputEl().dom.type != 'password') {
9826             return;
9827         }
9828         
9829         var isSelectAll = false;
9830         
9831         if(this.inputEl().dom.selectionEnd > 0){
9832             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9833         }
9834         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9835             event.preventDefault();
9836             this.setValue('');
9837             return;
9838         }
9839         
9840         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9841             
9842             event.preventDefault();
9843             // this is very hacky as keydown always get's upper case.
9844             //
9845             var cc = String.fromCharCode(event.getCharCode());
9846             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9847             
9848         }
9849     },
9850     adjustWidth : function(tag, w){
9851         tag = tag.toLowerCase();
9852         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9853             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9854                 if(tag == 'input'){
9855                     return w + 2;
9856                 }
9857                 if(tag == 'textarea'){
9858                     return w-2;
9859                 }
9860             }else if(Roo.isOpera){
9861                 if(tag == 'input'){
9862                     return w + 2;
9863                 }
9864                 if(tag == 'textarea'){
9865                     return w-2;
9866                 }
9867             }
9868         }
9869         return w;
9870     },
9871     
9872     setFieldLabel : function(v)
9873     {
9874         if(!this.rendered){
9875             return;
9876         }
9877         
9878         if(this.indicatorEl()){
9879             var ar = this.el.select('label > span',true);
9880             
9881             if (ar.elements.length) {
9882                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9883                 this.fieldLabel = v;
9884                 return;
9885             }
9886             
9887             var br = this.el.select('label',true);
9888             
9889             if(br.elements.length) {
9890                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9891                 this.fieldLabel = v;
9892                 return;
9893             }
9894             
9895             Roo.log('Cannot Found any of label > span || label in input');
9896             return;
9897         }
9898         
9899         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9900         this.fieldLabel = v;
9901         
9902         
9903     }
9904 });
9905
9906  
9907 /*
9908  * - LGPL
9909  *
9910  * Input
9911  * 
9912  */
9913
9914 /**
9915  * @class Roo.bootstrap.TextArea
9916  * @extends Roo.bootstrap.Input
9917  * Bootstrap TextArea class
9918  * @cfg {Number} cols Specifies the visible width of a text area
9919  * @cfg {Number} rows Specifies the visible number of lines in a text area
9920  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9921  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9922  * @cfg {string} html text
9923  * 
9924  * @constructor
9925  * Create a new TextArea
9926  * @param {Object} config The config object
9927  */
9928
9929 Roo.bootstrap.TextArea = function(config){
9930     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9931    
9932 };
9933
9934 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9935      
9936     cols : false,
9937     rows : 5,
9938     readOnly : false,
9939     warp : 'soft',
9940     resize : false,
9941     value: false,
9942     html: false,
9943     
9944     getAutoCreate : function(){
9945         
9946         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9947         
9948         var id = Roo.id();
9949         
9950         var cfg = {};
9951         
9952         if(this.inputType != 'hidden'){
9953             cfg.cls = 'form-group' //input-group
9954         }
9955         
9956         var input =  {
9957             tag: 'textarea',
9958             id : id,
9959             warp : this.warp,
9960             rows : this.rows,
9961             value : this.value || '',
9962             html: this.html || '',
9963             cls : 'form-control',
9964             placeholder : this.placeholder || '' 
9965             
9966         };
9967         
9968         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9969             input.maxLength = this.maxLength;
9970         }
9971         
9972         if(this.resize){
9973             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9974         }
9975         
9976         if(this.cols){
9977             input.cols = this.cols;
9978         }
9979         
9980         if (this.readOnly) {
9981             input.readonly = true;
9982         }
9983         
9984         if (this.name) {
9985             input.name = this.name;
9986         }
9987         
9988         if (this.size) {
9989             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9990         }
9991         
9992         var settings=this;
9993         ['xs','sm','md','lg'].map(function(size){
9994             if (settings[size]) {
9995                 cfg.cls += ' col-' + size + '-' + settings[size];
9996             }
9997         });
9998         
9999         var inputblock = input;
10000         
10001         if(this.hasFeedback && !this.allowBlank){
10002             
10003             var feedback = {
10004                 tag: 'span',
10005                 cls: 'glyphicon form-control-feedback'
10006             };
10007
10008             inputblock = {
10009                 cls : 'has-feedback',
10010                 cn :  [
10011                     input,
10012                     feedback
10013                 ] 
10014             };  
10015         }
10016         
10017         
10018         if (this.before || this.after) {
10019             
10020             inputblock = {
10021                 cls : 'input-group',
10022                 cn :  [] 
10023             };
10024             if (this.before) {
10025                 inputblock.cn.push({
10026                     tag :'span',
10027                     cls : 'input-group-addon',
10028                     html : this.before
10029                 });
10030             }
10031             
10032             inputblock.cn.push(input);
10033             
10034             if(this.hasFeedback && !this.allowBlank){
10035                 inputblock.cls += ' has-feedback';
10036                 inputblock.cn.push(feedback);
10037             }
10038             
10039             if (this.after) {
10040                 inputblock.cn.push({
10041                     tag :'span',
10042                     cls : 'input-group-addon',
10043                     html : this.after
10044                 });
10045             }
10046             
10047         }
10048         
10049         if (align ==='left' && this.fieldLabel.length) {
10050             cfg.cn = [
10051                 {
10052                     tag: 'label',
10053                     'for' :  id,
10054                     cls : 'control-label',
10055                     html : this.fieldLabel
10056                 },
10057                 {
10058                     cls : "",
10059                     cn: [
10060                         inputblock
10061                     ]
10062                 }
10063
10064             ];
10065             
10066             if(this.labelWidth > 12){
10067                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10068             }
10069
10070             if(this.labelWidth < 13 && this.labelmd == 0){
10071                 this.labelmd = this.labelWidth;
10072             }
10073
10074             if(this.labellg > 0){
10075                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10076                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10077             }
10078
10079             if(this.labelmd > 0){
10080                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10081                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10082             }
10083
10084             if(this.labelsm > 0){
10085                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10086                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10087             }
10088
10089             if(this.labelxs > 0){
10090                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10091                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10092             }
10093             
10094         } else if ( this.fieldLabel.length) {
10095             cfg.cn = [
10096
10097                {
10098                    tag: 'label',
10099                    //cls : 'input-group-addon',
10100                    html : this.fieldLabel
10101
10102                },
10103
10104                inputblock
10105
10106            ];
10107
10108         } else {
10109
10110             cfg.cn = [
10111
10112                 inputblock
10113
10114             ];
10115                 
10116         }
10117         
10118         if (this.disabled) {
10119             input.disabled=true;
10120         }
10121         
10122         return cfg;
10123         
10124     },
10125     /**
10126      * return the real textarea element.
10127      */
10128     inputEl: function ()
10129     {
10130         return this.el.select('textarea.form-control',true).first();
10131     },
10132     
10133     /**
10134      * Clear any invalid styles/messages for this field
10135      */
10136     clearInvalid : function()
10137     {
10138         
10139         if(!this.el || this.preventMark){ // not rendered
10140             return;
10141         }
10142         
10143         var label = this.el.select('label', true).first();
10144         var icon = this.el.select('i.fa-star', true).first();
10145         
10146         if(label && icon){
10147             icon.remove();
10148         }
10149         
10150         this.el.removeClass(this.invalidClass);
10151         
10152         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10153             
10154             var feedback = this.el.select('.form-control-feedback', true).first();
10155             
10156             if(feedback){
10157                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10158             }
10159             
10160         }
10161         
10162         this.fireEvent('valid', this);
10163     },
10164     
10165      /**
10166      * Mark this field as valid
10167      */
10168     markValid : function()
10169     {
10170         if(!this.el  || this.preventMark){ // not rendered
10171             return;
10172         }
10173         
10174         this.el.removeClass([this.invalidClass, this.validClass]);
10175         
10176         var feedback = this.el.select('.form-control-feedback', true).first();
10177             
10178         if(feedback){
10179             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10180         }
10181
10182         if(this.disabled || this.allowBlank){
10183             return;
10184         }
10185         
10186         var label = this.el.select('label', true).first();
10187         var icon = this.el.select('i.fa-star', true).first();
10188         
10189         if(label && icon){
10190             icon.remove();
10191         }
10192         
10193         this.el.addClass(this.validClass);
10194         
10195         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10196             
10197             var feedback = this.el.select('.form-control-feedback', true).first();
10198             
10199             if(feedback){
10200                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10201                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10202             }
10203             
10204         }
10205         
10206         this.fireEvent('valid', this);
10207     },
10208     
10209      /**
10210      * Mark this field as invalid
10211      * @param {String} msg The validation message
10212      */
10213     markInvalid : function(msg)
10214     {
10215         if(!this.el  || this.preventMark){ // not rendered
10216             return;
10217         }
10218         
10219         this.el.removeClass([this.invalidClass, this.validClass]);
10220         
10221         var feedback = this.el.select('.form-control-feedback', true).first();
10222             
10223         if(feedback){
10224             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10225         }
10226
10227         if(this.disabled || this.allowBlank){
10228             return;
10229         }
10230         
10231         var label = this.el.select('label', true).first();
10232         var icon = this.el.select('i.fa-star', true).first();
10233         
10234         if(!this.getValue().length && label && !icon){
10235             this.el.createChild({
10236                 tag : 'i',
10237                 cls : 'text-danger fa fa-lg fa-star',
10238                 tooltip : 'This field is required',
10239                 style : 'margin-right:5px;'
10240             }, label, true);
10241         }
10242
10243         this.el.addClass(this.invalidClass);
10244         
10245         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10246             
10247             var feedback = this.el.select('.form-control-feedback', true).first();
10248             
10249             if(feedback){
10250                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10251                 
10252                 if(this.getValue().length || this.forceFeedback){
10253                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10254                 }
10255                 
10256             }
10257             
10258         }
10259         
10260         this.fireEvent('invalid', this, msg);
10261     }
10262 });
10263
10264  
10265 /*
10266  * - LGPL
10267  *
10268  * trigger field - base class for combo..
10269  * 
10270  */
10271  
10272 /**
10273  * @class Roo.bootstrap.TriggerField
10274  * @extends Roo.bootstrap.Input
10275  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10276  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10277  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10278  * for which you can provide a custom implementation.  For example:
10279  * <pre><code>
10280 var trigger = new Roo.bootstrap.TriggerField();
10281 trigger.onTriggerClick = myTriggerFn;
10282 trigger.applyTo('my-field');
10283 </code></pre>
10284  *
10285  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10286  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10287  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10288  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10289  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10290
10291  * @constructor
10292  * Create a new TriggerField.
10293  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10294  * to the base TextField)
10295  */
10296 Roo.bootstrap.TriggerField = function(config){
10297     this.mimicing = false;
10298     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10299 };
10300
10301 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10302     /**
10303      * @cfg {String} triggerClass A CSS class to apply to the trigger
10304      */
10305      /**
10306      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10307      */
10308     hideTrigger:false,
10309
10310     /**
10311      * @cfg {Boolean} removable (true|false) special filter default false
10312      */
10313     removable : false,
10314     
10315     /** @cfg {Boolean} grow @hide */
10316     /** @cfg {Number} growMin @hide */
10317     /** @cfg {Number} growMax @hide */
10318
10319     /**
10320      * @hide 
10321      * @method
10322      */
10323     autoSize: Roo.emptyFn,
10324     // private
10325     monitorTab : true,
10326     // private
10327     deferHeight : true,
10328
10329     
10330     actionMode : 'wrap',
10331     
10332     caret : false,
10333     
10334     
10335     getAutoCreate : function(){
10336        
10337         var align = this.labelAlign || this.parentLabelAlign();
10338         
10339         var id = Roo.id();
10340         
10341         var cfg = {
10342             cls: 'form-group' //input-group
10343         };
10344         
10345         
10346         var input =  {
10347             tag: 'input',
10348             id : id,
10349             type : this.inputType,
10350             cls : 'form-control',
10351             autocomplete: 'new-password',
10352             placeholder : this.placeholder || '' 
10353             
10354         };
10355         if (this.name) {
10356             input.name = this.name;
10357         }
10358         if (this.size) {
10359             input.cls += ' input-' + this.size;
10360         }
10361         
10362         if (this.disabled) {
10363             input.disabled=true;
10364         }
10365         
10366         var inputblock = input;
10367         
10368         if(this.hasFeedback && !this.allowBlank){
10369             
10370             var feedback = {
10371                 tag: 'span',
10372                 cls: 'glyphicon form-control-feedback'
10373             };
10374             
10375             if(this.removable && !this.editable && !this.tickable){
10376                 inputblock = {
10377                     cls : 'has-feedback',
10378                     cn :  [
10379                         inputblock,
10380                         {
10381                             tag: 'button',
10382                             html : 'x',
10383                             cls : 'roo-combo-removable-btn close'
10384                         },
10385                         feedback
10386                     ] 
10387                 };
10388             } else {
10389                 inputblock = {
10390                     cls : 'has-feedback',
10391                     cn :  [
10392                         inputblock,
10393                         feedback
10394                     ] 
10395                 };
10396             }
10397
10398         } else {
10399             if(this.removable && !this.editable && !this.tickable){
10400                 inputblock = {
10401                     cls : 'roo-removable',
10402                     cn :  [
10403                         inputblock,
10404                         {
10405                             tag: 'button',
10406                             html : 'x',
10407                             cls : 'roo-combo-removable-btn close'
10408                         }
10409                     ] 
10410                 };
10411             }
10412         }
10413         
10414         if (this.before || this.after) {
10415             
10416             inputblock = {
10417                 cls : 'input-group',
10418                 cn :  [] 
10419             };
10420             if (this.before) {
10421                 inputblock.cn.push({
10422                     tag :'span',
10423                     cls : 'input-group-addon input-group-prepend input-group-text',
10424                     html : this.before
10425                 });
10426             }
10427             
10428             inputblock.cn.push(input);
10429             
10430             if(this.hasFeedback && !this.allowBlank){
10431                 inputblock.cls += ' has-feedback';
10432                 inputblock.cn.push(feedback);
10433             }
10434             
10435             if (this.after) {
10436                 inputblock.cn.push({
10437                     tag :'span',
10438                     cls : 'input-group-addon input-group-append input-group-text',
10439                     html : this.after
10440                 });
10441             }
10442             
10443         };
10444         
10445       
10446         
10447         var ibwrap = inputblock;
10448         
10449         if(this.multiple){
10450             ibwrap = {
10451                 tag: 'ul',
10452                 cls: 'roo-select2-choices',
10453                 cn:[
10454                     {
10455                         tag: 'li',
10456                         cls: 'roo-select2-search-field',
10457                         cn: [
10458
10459                             inputblock
10460                         ]
10461                     }
10462                 ]
10463             };
10464                 
10465         }
10466         
10467         var combobox = {
10468             cls: 'roo-select2-container input-group',
10469             cn: [
10470                  {
10471                     tag: 'input',
10472                     type : 'hidden',
10473                     cls: 'form-hidden-field'
10474                 },
10475                 ibwrap
10476             ]
10477         };
10478         
10479         if(!this.multiple && this.showToggleBtn){
10480             
10481             var caret = {
10482                         tag: 'span',
10483                         cls: 'caret'
10484              };
10485             if (this.caret != false) {
10486                 caret = {
10487                      tag: 'i',
10488                      cls: 'fa fa-' + this.caret
10489                 };
10490                 
10491             }
10492             
10493             combobox.cn.push({
10494                 tag :'span',
10495                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10496                 cn : [
10497                     caret,
10498                     {
10499                         tag: 'span',
10500                         cls: 'combobox-clear',
10501                         cn  : [
10502                             {
10503                                 tag : 'i',
10504                                 cls: 'icon-remove'
10505                             }
10506                         ]
10507                     }
10508                 ]
10509
10510             })
10511         }
10512         
10513         if(this.multiple){
10514             combobox.cls += ' roo-select2-container-multi';
10515         }
10516          var indicator = {
10517             tag : 'i',
10518             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10519             tooltip : 'This field is required'
10520         };
10521         if (Roo.bootstrap.version == 4) {
10522             indicator = {
10523                 tag : 'i',
10524                 style : 'display:none'
10525             };
10526         }
10527         
10528         
10529         if (align ==='left' && this.fieldLabel.length) {
10530             
10531             cfg.cls += ' roo-form-group-label-left row';
10532
10533             cfg.cn = [
10534                 indicator,
10535                 {
10536                     tag: 'label',
10537                     'for' :  id,
10538                     cls : 'control-label',
10539                     html : this.fieldLabel
10540
10541                 },
10542                 {
10543                     cls : "", 
10544                     cn: [
10545                         combobox
10546                     ]
10547                 }
10548
10549             ];
10550             
10551             var labelCfg = cfg.cn[1];
10552             var contentCfg = cfg.cn[2];
10553             
10554             if(this.indicatorpos == 'right'){
10555                 cfg.cn = [
10556                     {
10557                         tag: 'label',
10558                         'for' :  id,
10559                         cls : 'control-label',
10560                         cn : [
10561                             {
10562                                 tag : 'span',
10563                                 html : this.fieldLabel
10564                             },
10565                             indicator
10566                         ]
10567                     },
10568                     {
10569                         cls : "", 
10570                         cn: [
10571                             combobox
10572                         ]
10573                     }
10574
10575                 ];
10576                 
10577                 labelCfg = cfg.cn[0];
10578                 contentCfg = cfg.cn[1];
10579             }
10580             
10581             if(this.labelWidth > 12){
10582                 labelCfg.style = "width: " + this.labelWidth + 'px';
10583             }
10584             
10585             if(this.labelWidth < 13 && this.labelmd == 0){
10586                 this.labelmd = this.labelWidth;
10587             }
10588             
10589             if(this.labellg > 0){
10590                 labelCfg.cls += ' col-lg-' + this.labellg;
10591                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10592             }
10593             
10594             if(this.labelmd > 0){
10595                 labelCfg.cls += ' col-md-' + this.labelmd;
10596                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10597             }
10598             
10599             if(this.labelsm > 0){
10600                 labelCfg.cls += ' col-sm-' + this.labelsm;
10601                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10602             }
10603             
10604             if(this.labelxs > 0){
10605                 labelCfg.cls += ' col-xs-' + this.labelxs;
10606                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10607             }
10608             
10609         } else if ( this.fieldLabel.length) {
10610 //                Roo.log(" label");
10611             cfg.cn = [
10612                 indicator,
10613                {
10614                    tag: 'label',
10615                    //cls : 'input-group-addon',
10616                    html : this.fieldLabel
10617
10618                },
10619
10620                combobox
10621
10622             ];
10623             
10624             if(this.indicatorpos == 'right'){
10625                 
10626                 cfg.cn = [
10627                     {
10628                        tag: 'label',
10629                        cn : [
10630                            {
10631                                tag : 'span',
10632                                html : this.fieldLabel
10633                            },
10634                            indicator
10635                        ]
10636
10637                     },
10638                     combobox
10639
10640                 ];
10641
10642             }
10643
10644         } else {
10645             
10646 //                Roo.log(" no label && no align");
10647                 cfg = combobox
10648                      
10649                 
10650         }
10651         
10652         var settings=this;
10653         ['xs','sm','md','lg'].map(function(size){
10654             if (settings[size]) {
10655                 cfg.cls += ' col-' + size + '-' + settings[size];
10656             }
10657         });
10658         
10659         return cfg;
10660         
10661     },
10662     
10663     
10664     
10665     // private
10666     onResize : function(w, h){
10667 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10668 //        if(typeof w == 'number'){
10669 //            var x = w - this.trigger.getWidth();
10670 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10671 //            this.trigger.setStyle('left', x+'px');
10672 //        }
10673     },
10674
10675     // private
10676     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10677
10678     // private
10679     getResizeEl : function(){
10680         return this.inputEl();
10681     },
10682
10683     // private
10684     getPositionEl : function(){
10685         return this.inputEl();
10686     },
10687
10688     // private
10689     alignErrorIcon : function(){
10690         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10691     },
10692
10693     // private
10694     initEvents : function(){
10695         
10696         this.createList();
10697         
10698         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10699         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10700         if(!this.multiple && this.showToggleBtn){
10701             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10702             if(this.hideTrigger){
10703                 this.trigger.setDisplayed(false);
10704             }
10705             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10706         }
10707         
10708         if(this.multiple){
10709             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10710         }
10711         
10712         if(this.removable && !this.editable && !this.tickable){
10713             var close = this.closeTriggerEl();
10714             
10715             if(close){
10716                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10717                 close.on('click', this.removeBtnClick, this, close);
10718             }
10719         }
10720         
10721         //this.trigger.addClassOnOver('x-form-trigger-over');
10722         //this.trigger.addClassOnClick('x-form-trigger-click');
10723         
10724         //if(!this.width){
10725         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10726         //}
10727     },
10728     
10729     closeTriggerEl : function()
10730     {
10731         var close = this.el.select('.roo-combo-removable-btn', true).first();
10732         return close ? close : false;
10733     },
10734     
10735     removeBtnClick : function(e, h, el)
10736     {
10737         e.preventDefault();
10738         
10739         if(this.fireEvent("remove", this) !== false){
10740             this.reset();
10741             this.fireEvent("afterremove", this)
10742         }
10743     },
10744     
10745     createList : function()
10746     {
10747         this.list = Roo.get(document.body).createChild({
10748             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10749             cls: 'typeahead typeahead-long dropdown-menu',
10750             style: 'display:none'
10751         });
10752         
10753         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10754         
10755     },
10756
10757     // private
10758     initTrigger : function(){
10759        
10760     },
10761
10762     // private
10763     onDestroy : function(){
10764         if(this.trigger){
10765             this.trigger.removeAllListeners();
10766           //  this.trigger.remove();
10767         }
10768         //if(this.wrap){
10769         //    this.wrap.remove();
10770         //}
10771         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10772     },
10773
10774     // private
10775     onFocus : function(){
10776         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10777         /*
10778         if(!this.mimicing){
10779             this.wrap.addClass('x-trigger-wrap-focus');
10780             this.mimicing = true;
10781             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10782             if(this.monitorTab){
10783                 this.el.on("keydown", this.checkTab, this);
10784             }
10785         }
10786         */
10787     },
10788
10789     // private
10790     checkTab : function(e){
10791         if(e.getKey() == e.TAB){
10792             this.triggerBlur();
10793         }
10794     },
10795
10796     // private
10797     onBlur : function(){
10798         // do nothing
10799     },
10800
10801     // private
10802     mimicBlur : function(e, t){
10803         /*
10804         if(!this.wrap.contains(t) && this.validateBlur()){
10805             this.triggerBlur();
10806         }
10807         */
10808     },
10809
10810     // private
10811     triggerBlur : function(){
10812         this.mimicing = false;
10813         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10814         if(this.monitorTab){
10815             this.el.un("keydown", this.checkTab, this);
10816         }
10817         //this.wrap.removeClass('x-trigger-wrap-focus');
10818         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10819     },
10820
10821     // private
10822     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10823     validateBlur : function(e, t){
10824         return true;
10825     },
10826
10827     // private
10828     onDisable : function(){
10829         this.inputEl().dom.disabled = true;
10830         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10831         //if(this.wrap){
10832         //    this.wrap.addClass('x-item-disabled');
10833         //}
10834     },
10835
10836     // private
10837     onEnable : function(){
10838         this.inputEl().dom.disabled = false;
10839         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10840         //if(this.wrap){
10841         //    this.el.removeClass('x-item-disabled');
10842         //}
10843     },
10844
10845     // private
10846     onShow : function(){
10847         var ae = this.getActionEl();
10848         
10849         if(ae){
10850             ae.dom.style.display = '';
10851             ae.dom.style.visibility = 'visible';
10852         }
10853     },
10854
10855     // private
10856     
10857     onHide : function(){
10858         var ae = this.getActionEl();
10859         ae.dom.style.display = 'none';
10860     },
10861
10862     /**
10863      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10864      * by an implementing function.
10865      * @method
10866      * @param {EventObject} e
10867      */
10868     onTriggerClick : Roo.emptyFn
10869 });
10870  /*
10871  * Based on:
10872  * Ext JS Library 1.1.1
10873  * Copyright(c) 2006-2007, Ext JS, LLC.
10874  *
10875  * Originally Released Under LGPL - original licence link has changed is not relivant.
10876  *
10877  * Fork - LGPL
10878  * <script type="text/javascript">
10879  */
10880
10881
10882 /**
10883  * @class Roo.data.SortTypes
10884  * @singleton
10885  * Defines the default sorting (casting?) comparison functions used when sorting data.
10886  */
10887 Roo.data.SortTypes = {
10888     /**
10889      * Default sort that does nothing
10890      * @param {Mixed} s The value being converted
10891      * @return {Mixed} The comparison value
10892      */
10893     none : function(s){
10894         return s;
10895     },
10896     
10897     /**
10898      * The regular expression used to strip tags
10899      * @type {RegExp}
10900      * @property
10901      */
10902     stripTagsRE : /<\/?[^>]+>/gi,
10903     
10904     /**
10905      * Strips all HTML tags to sort on text only
10906      * @param {Mixed} s The value being converted
10907      * @return {String} The comparison value
10908      */
10909     asText : function(s){
10910         return String(s).replace(this.stripTagsRE, "");
10911     },
10912     
10913     /**
10914      * Strips all HTML tags to sort on text only - Case insensitive
10915      * @param {Mixed} s The value being converted
10916      * @return {String} The comparison value
10917      */
10918     asUCText : function(s){
10919         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10920     },
10921     
10922     /**
10923      * Case insensitive string
10924      * @param {Mixed} s The value being converted
10925      * @return {String} The comparison value
10926      */
10927     asUCString : function(s) {
10928         return String(s).toUpperCase();
10929     },
10930     
10931     /**
10932      * Date sorting
10933      * @param {Mixed} s The value being converted
10934      * @return {Number} The comparison value
10935      */
10936     asDate : function(s) {
10937         if(!s){
10938             return 0;
10939         }
10940         if(s instanceof Date){
10941             return s.getTime();
10942         }
10943         return Date.parse(String(s));
10944     },
10945     
10946     /**
10947      * Float sorting
10948      * @param {Mixed} s The value being converted
10949      * @return {Float} The comparison value
10950      */
10951     asFloat : function(s) {
10952         var val = parseFloat(String(s).replace(/,/g, ""));
10953         if(isNaN(val)) {
10954             val = 0;
10955         }
10956         return val;
10957     },
10958     
10959     /**
10960      * Integer sorting
10961      * @param {Mixed} s The value being converted
10962      * @return {Number} The comparison value
10963      */
10964     asInt : function(s) {
10965         var val = parseInt(String(s).replace(/,/g, ""));
10966         if(isNaN(val)) {
10967             val = 0;
10968         }
10969         return val;
10970     }
10971 };/*
10972  * Based on:
10973  * Ext JS Library 1.1.1
10974  * Copyright(c) 2006-2007, Ext JS, LLC.
10975  *
10976  * Originally Released Under LGPL - original licence link has changed is not relivant.
10977  *
10978  * Fork - LGPL
10979  * <script type="text/javascript">
10980  */
10981
10982 /**
10983 * @class Roo.data.Record
10984  * Instances of this class encapsulate both record <em>definition</em> information, and record
10985  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10986  * to access Records cached in an {@link Roo.data.Store} object.<br>
10987  * <p>
10988  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10989  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10990  * objects.<br>
10991  * <p>
10992  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10993  * @constructor
10994  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10995  * {@link #create}. The parameters are the same.
10996  * @param {Array} data An associative Array of data values keyed by the field name.
10997  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10998  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10999  * not specified an integer id is generated.
11000  */
11001 Roo.data.Record = function(data, id){
11002     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11003     this.data = data;
11004 };
11005
11006 /**
11007  * Generate a constructor for a specific record layout.
11008  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11009  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11010  * Each field definition object may contain the following properties: <ul>
11011  * <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,
11012  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11013  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11014  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11015  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11016  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11017  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11018  * this may be omitted.</p></li>
11019  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11020  * <ul><li>auto (Default, implies no conversion)</li>
11021  * <li>string</li>
11022  * <li>int</li>
11023  * <li>float</li>
11024  * <li>boolean</li>
11025  * <li>date</li></ul></p></li>
11026  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11027  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11028  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11029  * by the Reader into an object that will be stored in the Record. It is passed the
11030  * following parameters:<ul>
11031  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11032  * </ul></p></li>
11033  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11034  * </ul>
11035  * <br>usage:<br><pre><code>
11036 var TopicRecord = Roo.data.Record.create(
11037     {name: 'title', mapping: 'topic_title'},
11038     {name: 'author', mapping: 'username'},
11039     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11040     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11041     {name: 'lastPoster', mapping: 'user2'},
11042     {name: 'excerpt', mapping: 'post_text'}
11043 );
11044
11045 var myNewRecord = new TopicRecord({
11046     title: 'Do my job please',
11047     author: 'noobie',
11048     totalPosts: 1,
11049     lastPost: new Date(),
11050     lastPoster: 'Animal',
11051     excerpt: 'No way dude!'
11052 });
11053 myStore.add(myNewRecord);
11054 </code></pre>
11055  * @method create
11056  * @static
11057  */
11058 Roo.data.Record.create = function(o){
11059     var f = function(){
11060         f.superclass.constructor.apply(this, arguments);
11061     };
11062     Roo.extend(f, Roo.data.Record);
11063     var p = f.prototype;
11064     p.fields = new Roo.util.MixedCollection(false, function(field){
11065         return field.name;
11066     });
11067     for(var i = 0, len = o.length; i < len; i++){
11068         p.fields.add(new Roo.data.Field(o[i]));
11069     }
11070     f.getField = function(name){
11071         return p.fields.get(name);  
11072     };
11073     return f;
11074 };
11075
11076 Roo.data.Record.AUTO_ID = 1000;
11077 Roo.data.Record.EDIT = 'edit';
11078 Roo.data.Record.REJECT = 'reject';
11079 Roo.data.Record.COMMIT = 'commit';
11080
11081 Roo.data.Record.prototype = {
11082     /**
11083      * Readonly flag - true if this record has been modified.
11084      * @type Boolean
11085      */
11086     dirty : false,
11087     editing : false,
11088     error: null,
11089     modified: null,
11090
11091     // private
11092     join : function(store){
11093         this.store = store;
11094     },
11095
11096     /**
11097      * Set the named field to the specified value.
11098      * @param {String} name The name of the field to set.
11099      * @param {Object} value The value to set the field to.
11100      */
11101     set : function(name, value){
11102         if(this.data[name] == value){
11103             return;
11104         }
11105         this.dirty = true;
11106         if(!this.modified){
11107             this.modified = {};
11108         }
11109         if(typeof this.modified[name] == 'undefined'){
11110             this.modified[name] = this.data[name];
11111         }
11112         this.data[name] = value;
11113         if(!this.editing && this.store){
11114             this.store.afterEdit(this);
11115         }       
11116     },
11117
11118     /**
11119      * Get the value of the named field.
11120      * @param {String} name The name of the field to get the value of.
11121      * @return {Object} The value of the field.
11122      */
11123     get : function(name){
11124         return this.data[name]; 
11125     },
11126
11127     // private
11128     beginEdit : function(){
11129         this.editing = true;
11130         this.modified = {}; 
11131     },
11132
11133     // private
11134     cancelEdit : function(){
11135         this.editing = false;
11136         delete this.modified;
11137     },
11138
11139     // private
11140     endEdit : function(){
11141         this.editing = false;
11142         if(this.dirty && this.store){
11143             this.store.afterEdit(this);
11144         }
11145     },
11146
11147     /**
11148      * Usually called by the {@link Roo.data.Store} which owns the Record.
11149      * Rejects all changes made to the Record since either creation, or the last commit operation.
11150      * Modified fields are reverted to their original values.
11151      * <p>
11152      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11153      * of reject operations.
11154      */
11155     reject : function(){
11156         var m = this.modified;
11157         for(var n in m){
11158             if(typeof m[n] != "function"){
11159                 this.data[n] = m[n];
11160             }
11161         }
11162         this.dirty = false;
11163         delete this.modified;
11164         this.editing = false;
11165         if(this.store){
11166             this.store.afterReject(this);
11167         }
11168     },
11169
11170     /**
11171      * Usually called by the {@link Roo.data.Store} which owns the Record.
11172      * Commits all changes made to the Record since either creation, or the last commit operation.
11173      * <p>
11174      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11175      * of commit operations.
11176      */
11177     commit : function(){
11178         this.dirty = false;
11179         delete this.modified;
11180         this.editing = false;
11181         if(this.store){
11182             this.store.afterCommit(this);
11183         }
11184     },
11185
11186     // private
11187     hasError : function(){
11188         return this.error != null;
11189     },
11190
11191     // private
11192     clearError : function(){
11193         this.error = null;
11194     },
11195
11196     /**
11197      * Creates a copy of this record.
11198      * @param {String} id (optional) A new record id if you don't want to use this record's id
11199      * @return {Record}
11200      */
11201     copy : function(newId) {
11202         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11203     }
11204 };/*
11205  * Based on:
11206  * Ext JS Library 1.1.1
11207  * Copyright(c) 2006-2007, Ext JS, LLC.
11208  *
11209  * Originally Released Under LGPL - original licence link has changed is not relivant.
11210  *
11211  * Fork - LGPL
11212  * <script type="text/javascript">
11213  */
11214
11215
11216
11217 /**
11218  * @class Roo.data.Store
11219  * @extends Roo.util.Observable
11220  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11221  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11222  * <p>
11223  * 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
11224  * has no knowledge of the format of the data returned by the Proxy.<br>
11225  * <p>
11226  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11227  * instances from the data object. These records are cached and made available through accessor functions.
11228  * @constructor
11229  * Creates a new Store.
11230  * @param {Object} config A config object containing the objects needed for the Store to access data,
11231  * and read the data into Records.
11232  */
11233 Roo.data.Store = function(config){
11234     this.data = new Roo.util.MixedCollection(false);
11235     this.data.getKey = function(o){
11236         return o.id;
11237     };
11238     this.baseParams = {};
11239     // private
11240     this.paramNames = {
11241         "start" : "start",
11242         "limit" : "limit",
11243         "sort" : "sort",
11244         "dir" : "dir",
11245         "multisort" : "_multisort"
11246     };
11247
11248     if(config && config.data){
11249         this.inlineData = config.data;
11250         delete config.data;
11251     }
11252
11253     Roo.apply(this, config);
11254     
11255     if(this.reader){ // reader passed
11256         this.reader = Roo.factory(this.reader, Roo.data);
11257         this.reader.xmodule = this.xmodule || false;
11258         if(!this.recordType){
11259             this.recordType = this.reader.recordType;
11260         }
11261         if(this.reader.onMetaChange){
11262             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11263         }
11264     }
11265
11266     if(this.recordType){
11267         this.fields = this.recordType.prototype.fields;
11268     }
11269     this.modified = [];
11270
11271     this.addEvents({
11272         /**
11273          * @event datachanged
11274          * Fires when the data cache has changed, and a widget which is using this Store
11275          * as a Record cache should refresh its view.
11276          * @param {Store} this
11277          */
11278         datachanged : true,
11279         /**
11280          * @event metachange
11281          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11282          * @param {Store} this
11283          * @param {Object} meta The JSON metadata
11284          */
11285         metachange : true,
11286         /**
11287          * @event add
11288          * Fires when Records have been added to the Store
11289          * @param {Store} this
11290          * @param {Roo.data.Record[]} records The array of Records added
11291          * @param {Number} index The index at which the record(s) were added
11292          */
11293         add : true,
11294         /**
11295          * @event remove
11296          * Fires when a Record has been removed from the Store
11297          * @param {Store} this
11298          * @param {Roo.data.Record} record The Record that was removed
11299          * @param {Number} index The index at which the record was removed
11300          */
11301         remove : true,
11302         /**
11303          * @event update
11304          * Fires when a Record has been updated
11305          * @param {Store} this
11306          * @param {Roo.data.Record} record The Record that was updated
11307          * @param {String} operation The update operation being performed.  Value may be one of:
11308          * <pre><code>
11309  Roo.data.Record.EDIT
11310  Roo.data.Record.REJECT
11311  Roo.data.Record.COMMIT
11312          * </code></pre>
11313          */
11314         update : true,
11315         /**
11316          * @event clear
11317          * Fires when the data cache has been cleared.
11318          * @param {Store} this
11319          */
11320         clear : true,
11321         /**
11322          * @event beforeload
11323          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11324          * the load action will be canceled.
11325          * @param {Store} this
11326          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11327          */
11328         beforeload : true,
11329         /**
11330          * @event beforeloadadd
11331          * Fires after a new set of Records has been loaded.
11332          * @param {Store} this
11333          * @param {Roo.data.Record[]} records The Records that were loaded
11334          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11335          */
11336         beforeloadadd : true,
11337         /**
11338          * @event load
11339          * Fires after a new set of Records has been loaded, before they are added to the store.
11340          * @param {Store} this
11341          * @param {Roo.data.Record[]} records The Records that were loaded
11342          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11343          * @params {Object} return from reader
11344          */
11345         load : true,
11346         /**
11347          * @event loadexception
11348          * Fires if an exception occurs in the Proxy during loading.
11349          * Called with the signature of the Proxy's "loadexception" event.
11350          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11351          * 
11352          * @param {Proxy} 
11353          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11354          * @param {Object} load options 
11355          * @param {Object} jsonData from your request (normally this contains the Exception)
11356          */
11357         loadexception : true
11358     });
11359     
11360     if(this.proxy){
11361         this.proxy = Roo.factory(this.proxy, Roo.data);
11362         this.proxy.xmodule = this.xmodule || false;
11363         this.relayEvents(this.proxy,  ["loadexception"]);
11364     }
11365     this.sortToggle = {};
11366     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11367
11368     Roo.data.Store.superclass.constructor.call(this);
11369
11370     if(this.inlineData){
11371         this.loadData(this.inlineData);
11372         delete this.inlineData;
11373     }
11374 };
11375
11376 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11377      /**
11378     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11379     * without a remote query - used by combo/forms at present.
11380     */
11381     
11382     /**
11383     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11384     */
11385     /**
11386     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11387     */
11388     /**
11389     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11390     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11391     */
11392     /**
11393     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11394     * on any HTTP request
11395     */
11396     /**
11397     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11398     */
11399     /**
11400     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11401     */
11402     multiSort: false,
11403     /**
11404     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11405     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11406     */
11407     remoteSort : false,
11408
11409     /**
11410     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11411      * loaded or when a record is removed. (defaults to false).
11412     */
11413     pruneModifiedRecords : false,
11414
11415     // private
11416     lastOptions : null,
11417
11418     /**
11419      * Add Records to the Store and fires the add event.
11420      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11421      */
11422     add : function(records){
11423         records = [].concat(records);
11424         for(var i = 0, len = records.length; i < len; i++){
11425             records[i].join(this);
11426         }
11427         var index = this.data.length;
11428         this.data.addAll(records);
11429         this.fireEvent("add", this, records, index);
11430     },
11431
11432     /**
11433      * Remove a Record from the Store and fires the remove event.
11434      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11435      */
11436     remove : function(record){
11437         var index = this.data.indexOf(record);
11438         this.data.removeAt(index);
11439  
11440         if(this.pruneModifiedRecords){
11441             this.modified.remove(record);
11442         }
11443         this.fireEvent("remove", this, record, index);
11444     },
11445
11446     /**
11447      * Remove all Records from the Store and fires the clear event.
11448      */
11449     removeAll : function(){
11450         this.data.clear();
11451         if(this.pruneModifiedRecords){
11452             this.modified = [];
11453         }
11454         this.fireEvent("clear", this);
11455     },
11456
11457     /**
11458      * Inserts Records to the Store at the given index and fires the add event.
11459      * @param {Number} index The start index at which to insert the passed Records.
11460      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11461      */
11462     insert : function(index, records){
11463         records = [].concat(records);
11464         for(var i = 0, len = records.length; i < len; i++){
11465             this.data.insert(index, records[i]);
11466             records[i].join(this);
11467         }
11468         this.fireEvent("add", this, records, index);
11469     },
11470
11471     /**
11472      * Get the index within the cache of the passed Record.
11473      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11474      * @return {Number} The index of the passed Record. Returns -1 if not found.
11475      */
11476     indexOf : function(record){
11477         return this.data.indexOf(record);
11478     },
11479
11480     /**
11481      * Get the index within the cache of the Record with the passed id.
11482      * @param {String} id The id of the Record to find.
11483      * @return {Number} The index of the Record. Returns -1 if not found.
11484      */
11485     indexOfId : function(id){
11486         return this.data.indexOfKey(id);
11487     },
11488
11489     /**
11490      * Get the Record with the specified id.
11491      * @param {String} id The id of the Record to find.
11492      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11493      */
11494     getById : function(id){
11495         return this.data.key(id);
11496     },
11497
11498     /**
11499      * Get the Record at the specified index.
11500      * @param {Number} index The index of the Record to find.
11501      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11502      */
11503     getAt : function(index){
11504         return this.data.itemAt(index);
11505     },
11506
11507     /**
11508      * Returns a range of Records between specified indices.
11509      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11510      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11511      * @return {Roo.data.Record[]} An array of Records
11512      */
11513     getRange : function(start, end){
11514         return this.data.getRange(start, end);
11515     },
11516
11517     // private
11518     storeOptions : function(o){
11519         o = Roo.apply({}, o);
11520         delete o.callback;
11521         delete o.scope;
11522         this.lastOptions = o;
11523     },
11524
11525     /**
11526      * Loads the Record cache from the configured Proxy using the configured Reader.
11527      * <p>
11528      * If using remote paging, then the first load call must specify the <em>start</em>
11529      * and <em>limit</em> properties in the options.params property to establish the initial
11530      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11531      * <p>
11532      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11533      * and this call will return before the new data has been loaded. Perform any post-processing
11534      * in a callback function, or in a "load" event handler.</strong>
11535      * <p>
11536      * @param {Object} options An object containing properties which control loading options:<ul>
11537      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11538      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11539      * passed the following arguments:<ul>
11540      * <li>r : Roo.data.Record[]</li>
11541      * <li>options: Options object from the load call</li>
11542      * <li>success: Boolean success indicator</li></ul></li>
11543      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11544      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11545      * </ul>
11546      */
11547     load : function(options){
11548         options = options || {};
11549         if(this.fireEvent("beforeload", this, options) !== false){
11550             this.storeOptions(options);
11551             var p = Roo.apply(options.params || {}, this.baseParams);
11552             // if meta was not loaded from remote source.. try requesting it.
11553             if (!this.reader.metaFromRemote) {
11554                 p._requestMeta = 1;
11555             }
11556             if(this.sortInfo && this.remoteSort){
11557                 var pn = this.paramNames;
11558                 p[pn["sort"]] = this.sortInfo.field;
11559                 p[pn["dir"]] = this.sortInfo.direction;
11560             }
11561             if (this.multiSort) {
11562                 var pn = this.paramNames;
11563                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11564             }
11565             
11566             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11567         }
11568     },
11569
11570     /**
11571      * Reloads the Record cache from the configured Proxy using the configured Reader and
11572      * the options from the last load operation performed.
11573      * @param {Object} options (optional) An object containing properties which may override the options
11574      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11575      * the most recently used options are reused).
11576      */
11577     reload : function(options){
11578         this.load(Roo.applyIf(options||{}, this.lastOptions));
11579     },
11580
11581     // private
11582     // Called as a callback by the Reader during a load operation.
11583     loadRecords : function(o, options, success){
11584         if(!o || success === false){
11585             if(success !== false){
11586                 this.fireEvent("load", this, [], options, o);
11587             }
11588             if(options.callback){
11589                 options.callback.call(options.scope || this, [], options, false);
11590             }
11591             return;
11592         }
11593         // if data returned failure - throw an exception.
11594         if (o.success === false) {
11595             // show a message if no listener is registered.
11596             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11597                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11598             }
11599             // loadmask wil be hooked into this..
11600             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11601             return;
11602         }
11603         var r = o.records, t = o.totalRecords || r.length;
11604         
11605         this.fireEvent("beforeloadadd", this, r, options, o);
11606         
11607         if(!options || options.add !== true){
11608             if(this.pruneModifiedRecords){
11609                 this.modified = [];
11610             }
11611             for(var i = 0, len = r.length; i < len; i++){
11612                 r[i].join(this);
11613             }
11614             if(this.snapshot){
11615                 this.data = this.snapshot;
11616                 delete this.snapshot;
11617             }
11618             this.data.clear();
11619             this.data.addAll(r);
11620             this.totalLength = t;
11621             this.applySort();
11622             this.fireEvent("datachanged", this);
11623         }else{
11624             this.totalLength = Math.max(t, this.data.length+r.length);
11625             this.add(r);
11626         }
11627         
11628         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11629                 
11630             var e = new Roo.data.Record({});
11631
11632             e.set(this.parent.displayField, this.parent.emptyTitle);
11633             e.set(this.parent.valueField, '');
11634
11635             this.insert(0, e);
11636         }
11637             
11638         this.fireEvent("load", this, r, options, o);
11639         if(options.callback){
11640             options.callback.call(options.scope || this, r, options, true);
11641         }
11642     },
11643
11644
11645     /**
11646      * Loads data from a passed data block. A Reader which understands the format of the data
11647      * must have been configured in the constructor.
11648      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11649      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11650      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11651      */
11652     loadData : function(o, append){
11653         var r = this.reader.readRecords(o);
11654         this.loadRecords(r, {add: append}, true);
11655     },
11656
11657     /**
11658      * Gets the number of cached records.
11659      * <p>
11660      * <em>If using paging, this may not be the total size of the dataset. If the data object
11661      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11662      * the data set size</em>
11663      */
11664     getCount : function(){
11665         return this.data.length || 0;
11666     },
11667
11668     /**
11669      * Gets the total number of records in the dataset as returned by the server.
11670      * <p>
11671      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11672      * the dataset size</em>
11673      */
11674     getTotalCount : function(){
11675         return this.totalLength || 0;
11676     },
11677
11678     /**
11679      * Returns the sort state of the Store as an object with two properties:
11680      * <pre><code>
11681  field {String} The name of the field by which the Records are sorted
11682  direction {String} The sort order, "ASC" or "DESC"
11683      * </code></pre>
11684      */
11685     getSortState : function(){
11686         return this.sortInfo;
11687     },
11688
11689     // private
11690     applySort : function(){
11691         if(this.sortInfo && !this.remoteSort){
11692             var s = this.sortInfo, f = s.field;
11693             var st = this.fields.get(f).sortType;
11694             var fn = function(r1, r2){
11695                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11696                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11697             };
11698             this.data.sort(s.direction, fn);
11699             if(this.snapshot && this.snapshot != this.data){
11700                 this.snapshot.sort(s.direction, fn);
11701             }
11702         }
11703     },
11704
11705     /**
11706      * Sets the default sort column and order to be used by the next load operation.
11707      * @param {String} fieldName The name of the field to sort by.
11708      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11709      */
11710     setDefaultSort : function(field, dir){
11711         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11712     },
11713
11714     /**
11715      * Sort the Records.
11716      * If remote sorting is used, the sort is performed on the server, and the cache is
11717      * reloaded. If local sorting is used, the cache is sorted internally.
11718      * @param {String} fieldName The name of the field to sort by.
11719      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11720      */
11721     sort : function(fieldName, dir){
11722         var f = this.fields.get(fieldName);
11723         if(!dir){
11724             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11725             
11726             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11727                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11728             }else{
11729                 dir = f.sortDir;
11730             }
11731         }
11732         this.sortToggle[f.name] = dir;
11733         this.sortInfo = {field: f.name, direction: dir};
11734         if(!this.remoteSort){
11735             this.applySort();
11736             this.fireEvent("datachanged", this);
11737         }else{
11738             this.load(this.lastOptions);
11739         }
11740     },
11741
11742     /**
11743      * Calls the specified function for each of the Records in the cache.
11744      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11745      * Returning <em>false</em> aborts and exits the iteration.
11746      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11747      */
11748     each : function(fn, scope){
11749         this.data.each(fn, scope);
11750     },
11751
11752     /**
11753      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11754      * (e.g., during paging).
11755      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11756      */
11757     getModifiedRecords : function(){
11758         return this.modified;
11759     },
11760
11761     // private
11762     createFilterFn : function(property, value, anyMatch){
11763         if(!value.exec){ // not a regex
11764             value = String(value);
11765             if(value.length == 0){
11766                 return false;
11767             }
11768             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11769         }
11770         return function(r){
11771             return value.test(r.data[property]);
11772         };
11773     },
11774
11775     /**
11776      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11777      * @param {String} property A field on your records
11778      * @param {Number} start The record index to start at (defaults to 0)
11779      * @param {Number} end The last record index to include (defaults to length - 1)
11780      * @return {Number} The sum
11781      */
11782     sum : function(property, start, end){
11783         var rs = this.data.items, v = 0;
11784         start = start || 0;
11785         end = (end || end === 0) ? end : rs.length-1;
11786
11787         for(var i = start; i <= end; i++){
11788             v += (rs[i].data[property] || 0);
11789         }
11790         return v;
11791     },
11792
11793     /**
11794      * Filter the records by a specified property.
11795      * @param {String} field A field on your records
11796      * @param {String/RegExp} value Either a string that the field
11797      * should start with or a RegExp to test against the field
11798      * @param {Boolean} anyMatch True to match any part not just the beginning
11799      */
11800     filter : function(property, value, anyMatch){
11801         var fn = this.createFilterFn(property, value, anyMatch);
11802         return fn ? this.filterBy(fn) : this.clearFilter();
11803     },
11804
11805     /**
11806      * Filter by a function. The specified function will be called with each
11807      * record in this data source. If the function returns true the record is included,
11808      * otherwise it is filtered.
11809      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11810      * @param {Object} scope (optional) The scope of the function (defaults to this)
11811      */
11812     filterBy : function(fn, scope){
11813         this.snapshot = this.snapshot || this.data;
11814         this.data = this.queryBy(fn, scope||this);
11815         this.fireEvent("datachanged", this);
11816     },
11817
11818     /**
11819      * Query the records by a specified property.
11820      * @param {String} field A field on your records
11821      * @param {String/RegExp} value Either a string that the field
11822      * should start with or a RegExp to test against the field
11823      * @param {Boolean} anyMatch True to match any part not just the beginning
11824      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11825      */
11826     query : function(property, value, anyMatch){
11827         var fn = this.createFilterFn(property, value, anyMatch);
11828         return fn ? this.queryBy(fn) : this.data.clone();
11829     },
11830
11831     /**
11832      * Query by a function. The specified function will be called with each
11833      * record in this data source. If the function returns true the record is included
11834      * in the results.
11835      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11836      * @param {Object} scope (optional) The scope of the function (defaults to this)
11837       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11838      **/
11839     queryBy : function(fn, scope){
11840         var data = this.snapshot || this.data;
11841         return data.filterBy(fn, scope||this);
11842     },
11843
11844     /**
11845      * Collects unique values for a particular dataIndex from this store.
11846      * @param {String} dataIndex The property to collect
11847      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11848      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11849      * @return {Array} An array of the unique values
11850      **/
11851     collect : function(dataIndex, allowNull, bypassFilter){
11852         var d = (bypassFilter === true && this.snapshot) ?
11853                 this.snapshot.items : this.data.items;
11854         var v, sv, r = [], l = {};
11855         for(var i = 0, len = d.length; i < len; i++){
11856             v = d[i].data[dataIndex];
11857             sv = String(v);
11858             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11859                 l[sv] = true;
11860                 r[r.length] = v;
11861             }
11862         }
11863         return r;
11864     },
11865
11866     /**
11867      * Revert to a view of the Record cache with no filtering applied.
11868      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11869      */
11870     clearFilter : function(suppressEvent){
11871         if(this.snapshot && this.snapshot != this.data){
11872             this.data = this.snapshot;
11873             delete this.snapshot;
11874             if(suppressEvent !== true){
11875                 this.fireEvent("datachanged", this);
11876             }
11877         }
11878     },
11879
11880     // private
11881     afterEdit : function(record){
11882         if(this.modified.indexOf(record) == -1){
11883             this.modified.push(record);
11884         }
11885         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11886     },
11887     
11888     // private
11889     afterReject : function(record){
11890         this.modified.remove(record);
11891         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11892     },
11893
11894     // private
11895     afterCommit : function(record){
11896         this.modified.remove(record);
11897         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11898     },
11899
11900     /**
11901      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11902      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11903      */
11904     commitChanges : function(){
11905         var m = this.modified.slice(0);
11906         this.modified = [];
11907         for(var i = 0, len = m.length; i < len; i++){
11908             m[i].commit();
11909         }
11910     },
11911
11912     /**
11913      * Cancel outstanding changes on all changed records.
11914      */
11915     rejectChanges : function(){
11916         var m = this.modified.slice(0);
11917         this.modified = [];
11918         for(var i = 0, len = m.length; i < len; i++){
11919             m[i].reject();
11920         }
11921     },
11922
11923     onMetaChange : function(meta, rtype, o){
11924         this.recordType = rtype;
11925         this.fields = rtype.prototype.fields;
11926         delete this.snapshot;
11927         this.sortInfo = meta.sortInfo || this.sortInfo;
11928         this.modified = [];
11929         this.fireEvent('metachange', this, this.reader.meta);
11930     },
11931     
11932     moveIndex : function(data, type)
11933     {
11934         var index = this.indexOf(data);
11935         
11936         var newIndex = index + type;
11937         
11938         this.remove(data);
11939         
11940         this.insert(newIndex, data);
11941         
11942     }
11943 });/*
11944  * Based on:
11945  * Ext JS Library 1.1.1
11946  * Copyright(c) 2006-2007, Ext JS, LLC.
11947  *
11948  * Originally Released Under LGPL - original licence link has changed is not relivant.
11949  *
11950  * Fork - LGPL
11951  * <script type="text/javascript">
11952  */
11953
11954 /**
11955  * @class Roo.data.SimpleStore
11956  * @extends Roo.data.Store
11957  * Small helper class to make creating Stores from Array data easier.
11958  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11959  * @cfg {Array} fields An array of field definition objects, or field name strings.
11960  * @cfg {Array} data The multi-dimensional array of data
11961  * @constructor
11962  * @param {Object} config
11963  */
11964 Roo.data.SimpleStore = function(config){
11965     Roo.data.SimpleStore.superclass.constructor.call(this, {
11966         isLocal : true,
11967         reader: new Roo.data.ArrayReader({
11968                 id: config.id
11969             },
11970             Roo.data.Record.create(config.fields)
11971         ),
11972         proxy : new Roo.data.MemoryProxy(config.data)
11973     });
11974     this.load();
11975 };
11976 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11977  * Based on:
11978  * Ext JS Library 1.1.1
11979  * Copyright(c) 2006-2007, Ext JS, LLC.
11980  *
11981  * Originally Released Under LGPL - original licence link has changed is not relivant.
11982  *
11983  * Fork - LGPL
11984  * <script type="text/javascript">
11985  */
11986
11987 /**
11988 /**
11989  * @extends Roo.data.Store
11990  * @class Roo.data.JsonStore
11991  * Small helper class to make creating Stores for JSON data easier. <br/>
11992 <pre><code>
11993 var store = new Roo.data.JsonStore({
11994     url: 'get-images.php',
11995     root: 'images',
11996     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11997 });
11998 </code></pre>
11999  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12000  * JsonReader and HttpProxy (unless inline data is provided).</b>
12001  * @cfg {Array} fields An array of field definition objects, or field name strings.
12002  * @constructor
12003  * @param {Object} config
12004  */
12005 Roo.data.JsonStore = function(c){
12006     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12007         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12008         reader: new Roo.data.JsonReader(c, c.fields)
12009     }));
12010 };
12011 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12012  * Based on:
12013  * Ext JS Library 1.1.1
12014  * Copyright(c) 2006-2007, Ext JS, LLC.
12015  *
12016  * Originally Released Under LGPL - original licence link has changed is not relivant.
12017  *
12018  * Fork - LGPL
12019  * <script type="text/javascript">
12020  */
12021
12022  
12023 Roo.data.Field = function(config){
12024     if(typeof config == "string"){
12025         config = {name: config};
12026     }
12027     Roo.apply(this, config);
12028     
12029     if(!this.type){
12030         this.type = "auto";
12031     }
12032     
12033     var st = Roo.data.SortTypes;
12034     // named sortTypes are supported, here we look them up
12035     if(typeof this.sortType == "string"){
12036         this.sortType = st[this.sortType];
12037     }
12038     
12039     // set default sortType for strings and dates
12040     if(!this.sortType){
12041         switch(this.type){
12042             case "string":
12043                 this.sortType = st.asUCString;
12044                 break;
12045             case "date":
12046                 this.sortType = st.asDate;
12047                 break;
12048             default:
12049                 this.sortType = st.none;
12050         }
12051     }
12052
12053     // define once
12054     var stripRe = /[\$,%]/g;
12055
12056     // prebuilt conversion function for this field, instead of
12057     // switching every time we're reading a value
12058     if(!this.convert){
12059         var cv, dateFormat = this.dateFormat;
12060         switch(this.type){
12061             case "":
12062             case "auto":
12063             case undefined:
12064                 cv = function(v){ return v; };
12065                 break;
12066             case "string":
12067                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12068                 break;
12069             case "int":
12070                 cv = function(v){
12071                     return v !== undefined && v !== null && v !== '' ?
12072                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12073                     };
12074                 break;
12075             case "float":
12076                 cv = function(v){
12077                     return v !== undefined && v !== null && v !== '' ?
12078                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12079                     };
12080                 break;
12081             case "bool":
12082             case "boolean":
12083                 cv = function(v){ return v === true || v === "true" || v == 1; };
12084                 break;
12085             case "date":
12086                 cv = function(v){
12087                     if(!v){
12088                         return '';
12089                     }
12090                     if(v instanceof Date){
12091                         return v;
12092                     }
12093                     if(dateFormat){
12094                         if(dateFormat == "timestamp"){
12095                             return new Date(v*1000);
12096                         }
12097                         return Date.parseDate(v, dateFormat);
12098                     }
12099                     var parsed = Date.parse(v);
12100                     return parsed ? new Date(parsed) : null;
12101                 };
12102              break;
12103             
12104         }
12105         this.convert = cv;
12106     }
12107 };
12108
12109 Roo.data.Field.prototype = {
12110     dateFormat: null,
12111     defaultValue: "",
12112     mapping: null,
12113     sortType : null,
12114     sortDir : "ASC"
12115 };/*
12116  * Based on:
12117  * Ext JS Library 1.1.1
12118  * Copyright(c) 2006-2007, Ext JS, LLC.
12119  *
12120  * Originally Released Under LGPL - original licence link has changed is not relivant.
12121  *
12122  * Fork - LGPL
12123  * <script type="text/javascript">
12124  */
12125  
12126 // Base class for reading structured data from a data source.  This class is intended to be
12127 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12128
12129 /**
12130  * @class Roo.data.DataReader
12131  * Base class for reading structured data from a data source.  This class is intended to be
12132  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12133  */
12134
12135 Roo.data.DataReader = function(meta, recordType){
12136     
12137     this.meta = meta;
12138     
12139     this.recordType = recordType instanceof Array ? 
12140         Roo.data.Record.create(recordType) : recordType;
12141 };
12142
12143 Roo.data.DataReader.prototype = {
12144      /**
12145      * Create an empty record
12146      * @param {Object} data (optional) - overlay some values
12147      * @return {Roo.data.Record} record created.
12148      */
12149     newRow :  function(d) {
12150         var da =  {};
12151         this.recordType.prototype.fields.each(function(c) {
12152             switch( c.type) {
12153                 case 'int' : da[c.name] = 0; break;
12154                 case 'date' : da[c.name] = new Date(); break;
12155                 case 'float' : da[c.name] = 0.0; break;
12156                 case 'boolean' : da[c.name] = false; break;
12157                 default : da[c.name] = ""; break;
12158             }
12159             
12160         });
12161         return new this.recordType(Roo.apply(da, d));
12162     }
12163     
12164 };/*
12165  * Based on:
12166  * Ext JS Library 1.1.1
12167  * Copyright(c) 2006-2007, Ext JS, LLC.
12168  *
12169  * Originally Released Under LGPL - original licence link has changed is not relivant.
12170  *
12171  * Fork - LGPL
12172  * <script type="text/javascript">
12173  */
12174
12175 /**
12176  * @class Roo.data.DataProxy
12177  * @extends Roo.data.Observable
12178  * This class is an abstract base class for implementations which provide retrieval of
12179  * unformatted data objects.<br>
12180  * <p>
12181  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12182  * (of the appropriate type which knows how to parse the data object) to provide a block of
12183  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12184  * <p>
12185  * Custom implementations must implement the load method as described in
12186  * {@link Roo.data.HttpProxy#load}.
12187  */
12188 Roo.data.DataProxy = function(){
12189     this.addEvents({
12190         /**
12191          * @event beforeload
12192          * Fires before a network request is made to retrieve a data object.
12193          * @param {Object} This DataProxy object.
12194          * @param {Object} params The params parameter to the load function.
12195          */
12196         beforeload : true,
12197         /**
12198          * @event load
12199          * Fires before the load method's callback is called.
12200          * @param {Object} This DataProxy object.
12201          * @param {Object} o The data object.
12202          * @param {Object} arg The callback argument object passed to the load function.
12203          */
12204         load : true,
12205         /**
12206          * @event loadexception
12207          * Fires if an Exception occurs during data retrieval.
12208          * @param {Object} This DataProxy object.
12209          * @param {Object} o The data object.
12210          * @param {Object} arg The callback argument object passed to the load function.
12211          * @param {Object} e The Exception.
12212          */
12213         loadexception : true
12214     });
12215     Roo.data.DataProxy.superclass.constructor.call(this);
12216 };
12217
12218 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12219
12220     /**
12221      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12222      */
12223 /*
12224  * Based on:
12225  * Ext JS Library 1.1.1
12226  * Copyright(c) 2006-2007, Ext JS, LLC.
12227  *
12228  * Originally Released Under LGPL - original licence link has changed is not relivant.
12229  *
12230  * Fork - LGPL
12231  * <script type="text/javascript">
12232  */
12233 /**
12234  * @class Roo.data.MemoryProxy
12235  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12236  * to the Reader when its load method is called.
12237  * @constructor
12238  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12239  */
12240 Roo.data.MemoryProxy = function(data){
12241     if (data.data) {
12242         data = data.data;
12243     }
12244     Roo.data.MemoryProxy.superclass.constructor.call(this);
12245     this.data = data;
12246 };
12247
12248 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12249     
12250     /**
12251      * Load data from the requested source (in this case an in-memory
12252      * data object passed to the constructor), read the data object into
12253      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12254      * process that block using the passed callback.
12255      * @param {Object} params This parameter is not used by the MemoryProxy class.
12256      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12257      * object into a block of Roo.data.Records.
12258      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12259      * The function must be passed <ul>
12260      * <li>The Record block object</li>
12261      * <li>The "arg" argument from the load function</li>
12262      * <li>A boolean success indicator</li>
12263      * </ul>
12264      * @param {Object} scope The scope in which to call the callback
12265      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12266      */
12267     load : function(params, reader, callback, scope, arg){
12268         params = params || {};
12269         var result;
12270         try {
12271             result = reader.readRecords(this.data);
12272         }catch(e){
12273             this.fireEvent("loadexception", this, arg, null, e);
12274             callback.call(scope, null, arg, false);
12275             return;
12276         }
12277         callback.call(scope, result, arg, true);
12278     },
12279     
12280     // private
12281     update : function(params, records){
12282         
12283     }
12284 });/*
12285  * Based on:
12286  * Ext JS Library 1.1.1
12287  * Copyright(c) 2006-2007, Ext JS, LLC.
12288  *
12289  * Originally Released Under LGPL - original licence link has changed is not relivant.
12290  *
12291  * Fork - LGPL
12292  * <script type="text/javascript">
12293  */
12294 /**
12295  * @class Roo.data.HttpProxy
12296  * @extends Roo.data.DataProxy
12297  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12298  * configured to reference a certain URL.<br><br>
12299  * <p>
12300  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12301  * from which the running page was served.<br><br>
12302  * <p>
12303  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12304  * <p>
12305  * Be aware that to enable the browser to parse an XML document, the server must set
12306  * the Content-Type header in the HTTP response to "text/xml".
12307  * @constructor
12308  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12309  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12310  * will be used to make the request.
12311  */
12312 Roo.data.HttpProxy = function(conn){
12313     Roo.data.HttpProxy.superclass.constructor.call(this);
12314     // is conn a conn config or a real conn?
12315     this.conn = conn;
12316     this.useAjax = !conn || !conn.events;
12317   
12318 };
12319
12320 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12321     // thse are take from connection...
12322     
12323     /**
12324      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12325      */
12326     /**
12327      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12328      * extra parameters to each request made by this object. (defaults to undefined)
12329      */
12330     /**
12331      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12332      *  to each request made by this object. (defaults to undefined)
12333      */
12334     /**
12335      * @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)
12336      */
12337     /**
12338      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12339      */
12340      /**
12341      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12342      * @type Boolean
12343      */
12344   
12345
12346     /**
12347      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12348      * @type Boolean
12349      */
12350     /**
12351      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12352      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12353      * a finer-grained basis than the DataProxy events.
12354      */
12355     getConnection : function(){
12356         return this.useAjax ? Roo.Ajax : this.conn;
12357     },
12358
12359     /**
12360      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12361      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12362      * process that block using the passed callback.
12363      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12364      * for the request to the remote server.
12365      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12366      * object into a block of Roo.data.Records.
12367      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12368      * The function must be passed <ul>
12369      * <li>The Record block object</li>
12370      * <li>The "arg" argument from the load function</li>
12371      * <li>A boolean success indicator</li>
12372      * </ul>
12373      * @param {Object} scope The scope in which to call the callback
12374      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12375      */
12376     load : function(params, reader, callback, scope, arg){
12377         if(this.fireEvent("beforeload", this, params) !== false){
12378             var  o = {
12379                 params : params || {},
12380                 request: {
12381                     callback : callback,
12382                     scope : scope,
12383                     arg : arg
12384                 },
12385                 reader: reader,
12386                 callback : this.loadResponse,
12387                 scope: this
12388             };
12389             if(this.useAjax){
12390                 Roo.applyIf(o, this.conn);
12391                 if(this.activeRequest){
12392                     Roo.Ajax.abort(this.activeRequest);
12393                 }
12394                 this.activeRequest = Roo.Ajax.request(o);
12395             }else{
12396                 this.conn.request(o);
12397             }
12398         }else{
12399             callback.call(scope||this, null, arg, false);
12400         }
12401     },
12402
12403     // private
12404     loadResponse : function(o, success, response){
12405         delete this.activeRequest;
12406         if(!success){
12407             this.fireEvent("loadexception", this, o, response);
12408             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12409             return;
12410         }
12411         var result;
12412         try {
12413             result = o.reader.read(response);
12414         }catch(e){
12415             this.fireEvent("loadexception", this, o, response, e);
12416             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12417             return;
12418         }
12419         
12420         this.fireEvent("load", this, o, o.request.arg);
12421         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12422     },
12423
12424     // private
12425     update : function(dataSet){
12426
12427     },
12428
12429     // private
12430     updateResponse : function(dataSet){
12431
12432     }
12433 });/*
12434  * Based on:
12435  * Ext JS Library 1.1.1
12436  * Copyright(c) 2006-2007, Ext JS, LLC.
12437  *
12438  * Originally Released Under LGPL - original licence link has changed is not relivant.
12439  *
12440  * Fork - LGPL
12441  * <script type="text/javascript">
12442  */
12443
12444 /**
12445  * @class Roo.data.ScriptTagProxy
12446  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12447  * other than the originating domain of the running page.<br><br>
12448  * <p>
12449  * <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
12450  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12451  * <p>
12452  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12453  * source code that is used as the source inside a &lt;script> tag.<br><br>
12454  * <p>
12455  * In order for the browser to process the returned data, the server must wrap the data object
12456  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12457  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12458  * depending on whether the callback name was passed:
12459  * <p>
12460  * <pre><code>
12461 boolean scriptTag = false;
12462 String cb = request.getParameter("callback");
12463 if (cb != null) {
12464     scriptTag = true;
12465     response.setContentType("text/javascript");
12466 } else {
12467     response.setContentType("application/x-json");
12468 }
12469 Writer out = response.getWriter();
12470 if (scriptTag) {
12471     out.write(cb + "(");
12472 }
12473 out.print(dataBlock.toJsonString());
12474 if (scriptTag) {
12475     out.write(");");
12476 }
12477 </pre></code>
12478  *
12479  * @constructor
12480  * @param {Object} config A configuration object.
12481  */
12482 Roo.data.ScriptTagProxy = function(config){
12483     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12484     Roo.apply(this, config);
12485     this.head = document.getElementsByTagName("head")[0];
12486 };
12487
12488 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12489
12490 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12491     /**
12492      * @cfg {String} url The URL from which to request the data object.
12493      */
12494     /**
12495      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12496      */
12497     timeout : 30000,
12498     /**
12499      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12500      * the server the name of the callback function set up by the load call to process the returned data object.
12501      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12502      * javascript output which calls this named function passing the data object as its only parameter.
12503      */
12504     callbackParam : "callback",
12505     /**
12506      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12507      * name to the request.
12508      */
12509     nocache : true,
12510
12511     /**
12512      * Load data from the configured URL, read the data object into
12513      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12514      * process that block using the passed callback.
12515      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12516      * for the request to the remote server.
12517      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12518      * object into a block of Roo.data.Records.
12519      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12520      * The function must be passed <ul>
12521      * <li>The Record block object</li>
12522      * <li>The "arg" argument from the load function</li>
12523      * <li>A boolean success indicator</li>
12524      * </ul>
12525      * @param {Object} scope The scope in which to call the callback
12526      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12527      */
12528     load : function(params, reader, callback, scope, arg){
12529         if(this.fireEvent("beforeload", this, params) !== false){
12530
12531             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12532
12533             var url = this.url;
12534             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12535             if(this.nocache){
12536                 url += "&_dc=" + (new Date().getTime());
12537             }
12538             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12539             var trans = {
12540                 id : transId,
12541                 cb : "stcCallback"+transId,
12542                 scriptId : "stcScript"+transId,
12543                 params : params,
12544                 arg : arg,
12545                 url : url,
12546                 callback : callback,
12547                 scope : scope,
12548                 reader : reader
12549             };
12550             var conn = this;
12551
12552             window[trans.cb] = function(o){
12553                 conn.handleResponse(o, trans);
12554             };
12555
12556             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12557
12558             if(this.autoAbort !== false){
12559                 this.abort();
12560             }
12561
12562             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12563
12564             var script = document.createElement("script");
12565             script.setAttribute("src", url);
12566             script.setAttribute("type", "text/javascript");
12567             script.setAttribute("id", trans.scriptId);
12568             this.head.appendChild(script);
12569
12570             this.trans = trans;
12571         }else{
12572             callback.call(scope||this, null, arg, false);
12573         }
12574     },
12575
12576     // private
12577     isLoading : function(){
12578         return this.trans ? true : false;
12579     },
12580
12581     /**
12582      * Abort the current server request.
12583      */
12584     abort : function(){
12585         if(this.isLoading()){
12586             this.destroyTrans(this.trans);
12587         }
12588     },
12589
12590     // private
12591     destroyTrans : function(trans, isLoaded){
12592         this.head.removeChild(document.getElementById(trans.scriptId));
12593         clearTimeout(trans.timeoutId);
12594         if(isLoaded){
12595             window[trans.cb] = undefined;
12596             try{
12597                 delete window[trans.cb];
12598             }catch(e){}
12599         }else{
12600             // if hasn't been loaded, wait for load to remove it to prevent script error
12601             window[trans.cb] = function(){
12602                 window[trans.cb] = undefined;
12603                 try{
12604                     delete window[trans.cb];
12605                 }catch(e){}
12606             };
12607         }
12608     },
12609
12610     // private
12611     handleResponse : function(o, trans){
12612         this.trans = false;
12613         this.destroyTrans(trans, true);
12614         var result;
12615         try {
12616             result = trans.reader.readRecords(o);
12617         }catch(e){
12618             this.fireEvent("loadexception", this, o, trans.arg, e);
12619             trans.callback.call(trans.scope||window, null, trans.arg, false);
12620             return;
12621         }
12622         this.fireEvent("load", this, o, trans.arg);
12623         trans.callback.call(trans.scope||window, result, trans.arg, true);
12624     },
12625
12626     // private
12627     handleFailure : function(trans){
12628         this.trans = false;
12629         this.destroyTrans(trans, false);
12630         this.fireEvent("loadexception", this, null, trans.arg);
12631         trans.callback.call(trans.scope||window, null, trans.arg, false);
12632     }
12633 });/*
12634  * Based on:
12635  * Ext JS Library 1.1.1
12636  * Copyright(c) 2006-2007, Ext JS, LLC.
12637  *
12638  * Originally Released Under LGPL - original licence link has changed is not relivant.
12639  *
12640  * Fork - LGPL
12641  * <script type="text/javascript">
12642  */
12643
12644 /**
12645  * @class Roo.data.JsonReader
12646  * @extends Roo.data.DataReader
12647  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12648  * based on mappings in a provided Roo.data.Record constructor.
12649  * 
12650  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12651  * in the reply previously. 
12652  * 
12653  * <p>
12654  * Example code:
12655  * <pre><code>
12656 var RecordDef = Roo.data.Record.create([
12657     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12658     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12659 ]);
12660 var myReader = new Roo.data.JsonReader({
12661     totalProperty: "results",    // The property which contains the total dataset size (optional)
12662     root: "rows",                // The property which contains an Array of row objects
12663     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12664 }, RecordDef);
12665 </code></pre>
12666  * <p>
12667  * This would consume a JSON file like this:
12668  * <pre><code>
12669 { 'results': 2, 'rows': [
12670     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12671     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12672 }
12673 </code></pre>
12674  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12675  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12676  * paged from the remote server.
12677  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12678  * @cfg {String} root name of the property which contains the Array of row objects.
12679  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12680  * @cfg {Array} fields Array of field definition objects
12681  * @constructor
12682  * Create a new JsonReader
12683  * @param {Object} meta Metadata configuration options
12684  * @param {Object} recordType Either an Array of field definition objects,
12685  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12686  */
12687 Roo.data.JsonReader = function(meta, recordType){
12688     
12689     meta = meta || {};
12690     // set some defaults:
12691     Roo.applyIf(meta, {
12692         totalProperty: 'total',
12693         successProperty : 'success',
12694         root : 'data',
12695         id : 'id'
12696     });
12697     
12698     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12699 };
12700 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12701     
12702     /**
12703      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12704      * Used by Store query builder to append _requestMeta to params.
12705      * 
12706      */
12707     metaFromRemote : false,
12708     /**
12709      * This method is only used by a DataProxy which has retrieved data from a remote server.
12710      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12711      * @return {Object} data A data block which is used by an Roo.data.Store object as
12712      * a cache of Roo.data.Records.
12713      */
12714     read : function(response){
12715         var json = response.responseText;
12716        
12717         var o = /* eval:var:o */ eval("("+json+")");
12718         if(!o) {
12719             throw {message: "JsonReader.read: Json object not found"};
12720         }
12721         
12722         if(o.metaData){
12723             
12724             delete this.ef;
12725             this.metaFromRemote = true;
12726             this.meta = o.metaData;
12727             this.recordType = Roo.data.Record.create(o.metaData.fields);
12728             this.onMetaChange(this.meta, this.recordType, o);
12729         }
12730         return this.readRecords(o);
12731     },
12732
12733     // private function a store will implement
12734     onMetaChange : function(meta, recordType, o){
12735
12736     },
12737
12738     /**
12739          * @ignore
12740          */
12741     simpleAccess: function(obj, subsc) {
12742         return obj[subsc];
12743     },
12744
12745         /**
12746          * @ignore
12747          */
12748     getJsonAccessor: function(){
12749         var re = /[\[\.]/;
12750         return function(expr) {
12751             try {
12752                 return(re.test(expr))
12753                     ? new Function("obj", "return obj." + expr)
12754                     : function(obj){
12755                         return obj[expr];
12756                     };
12757             } catch(e){}
12758             return Roo.emptyFn;
12759         };
12760     }(),
12761
12762     /**
12763      * Create a data block containing Roo.data.Records from an XML document.
12764      * @param {Object} o An object which contains an Array of row objects in the property specified
12765      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12766      * which contains the total size of the dataset.
12767      * @return {Object} data A data block which is used by an Roo.data.Store object as
12768      * a cache of Roo.data.Records.
12769      */
12770     readRecords : function(o){
12771         /**
12772          * After any data loads, the raw JSON data is available for further custom processing.
12773          * @type Object
12774          */
12775         this.o = o;
12776         var s = this.meta, Record = this.recordType,
12777             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12778
12779 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12780         if (!this.ef) {
12781             if(s.totalProperty) {
12782                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12783                 }
12784                 if(s.successProperty) {
12785                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12786                 }
12787                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12788                 if (s.id) {
12789                         var g = this.getJsonAccessor(s.id);
12790                         this.getId = function(rec) {
12791                                 var r = g(rec);  
12792                                 return (r === undefined || r === "") ? null : r;
12793                         };
12794                 } else {
12795                         this.getId = function(){return null;};
12796                 }
12797             this.ef = [];
12798             for(var jj = 0; jj < fl; jj++){
12799                 f = fi[jj];
12800                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12801                 this.ef[jj] = this.getJsonAccessor(map);
12802             }
12803         }
12804
12805         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12806         if(s.totalProperty){
12807             var vt = parseInt(this.getTotal(o), 10);
12808             if(!isNaN(vt)){
12809                 totalRecords = vt;
12810             }
12811         }
12812         if(s.successProperty){
12813             var vs = this.getSuccess(o);
12814             if(vs === false || vs === 'false'){
12815                 success = false;
12816             }
12817         }
12818         var records = [];
12819         for(var i = 0; i < c; i++){
12820                 var n = root[i];
12821             var values = {};
12822             var id = this.getId(n);
12823             for(var j = 0; j < fl; j++){
12824                 f = fi[j];
12825             var v = this.ef[j](n);
12826             if (!f.convert) {
12827                 Roo.log('missing convert for ' + f.name);
12828                 Roo.log(f);
12829                 continue;
12830             }
12831             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12832             }
12833             var record = new Record(values, id);
12834             record.json = n;
12835             records[i] = record;
12836         }
12837         return {
12838             raw : o,
12839             success : success,
12840             records : records,
12841             totalRecords : totalRecords
12842         };
12843     }
12844 });/*
12845  * Based on:
12846  * Ext JS Library 1.1.1
12847  * Copyright(c) 2006-2007, Ext JS, LLC.
12848  *
12849  * Originally Released Under LGPL - original licence link has changed is not relivant.
12850  *
12851  * Fork - LGPL
12852  * <script type="text/javascript">
12853  */
12854
12855 /**
12856  * @class Roo.data.ArrayReader
12857  * @extends Roo.data.DataReader
12858  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12859  * Each element of that Array represents a row of data fields. The
12860  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12861  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12862  * <p>
12863  * Example code:.
12864  * <pre><code>
12865 var RecordDef = Roo.data.Record.create([
12866     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12867     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12868 ]);
12869 var myReader = new Roo.data.ArrayReader({
12870     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12871 }, RecordDef);
12872 </code></pre>
12873  * <p>
12874  * This would consume an Array like this:
12875  * <pre><code>
12876 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12877   </code></pre>
12878  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12879  * @constructor
12880  * Create a new JsonReader
12881  * @param {Object} meta Metadata configuration options.
12882  * @param {Object} recordType Either an Array of field definition objects
12883  * as specified to {@link Roo.data.Record#create},
12884  * or an {@link Roo.data.Record} object
12885  * created using {@link Roo.data.Record#create}.
12886  */
12887 Roo.data.ArrayReader = function(meta, recordType){
12888     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12889 };
12890
12891 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12892     /**
12893      * Create a data block containing Roo.data.Records from an XML document.
12894      * @param {Object} o An Array of row objects which represents the dataset.
12895      * @return {Object} data A data block which is used by an Roo.data.Store object as
12896      * a cache of Roo.data.Records.
12897      */
12898     readRecords : function(o){
12899         var sid = this.meta ? this.meta.id : null;
12900         var recordType = this.recordType, fields = recordType.prototype.fields;
12901         var records = [];
12902         var root = o;
12903             for(var i = 0; i < root.length; i++){
12904                     var n = root[i];
12905                 var values = {};
12906                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12907                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12908                 var f = fields.items[j];
12909                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12910                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12911                 v = f.convert(v);
12912                 values[f.name] = v;
12913             }
12914                 var record = new recordType(values, id);
12915                 record.json = n;
12916                 records[records.length] = record;
12917             }
12918             return {
12919                 records : records,
12920                 totalRecords : records.length
12921             };
12922     }
12923 });/*
12924  * - LGPL
12925  * * 
12926  */
12927
12928 /**
12929  * @class Roo.bootstrap.ComboBox
12930  * @extends Roo.bootstrap.TriggerField
12931  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12932  * @cfg {Boolean} append (true|false) default false
12933  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12934  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12935  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12936  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12937  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12938  * @cfg {Boolean} animate default true
12939  * @cfg {Boolean} emptyResultText only for touch device
12940  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12941  * @cfg {String} emptyTitle default ''
12942  * @constructor
12943  * Create a new ComboBox.
12944  * @param {Object} config Configuration options
12945  */
12946 Roo.bootstrap.ComboBox = function(config){
12947     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12948     this.addEvents({
12949         /**
12950          * @event expand
12951          * Fires when the dropdown list is expanded
12952         * @param {Roo.bootstrap.ComboBox} combo This combo box
12953         */
12954         'expand' : true,
12955         /**
12956          * @event collapse
12957          * Fires when the dropdown list is collapsed
12958         * @param {Roo.bootstrap.ComboBox} combo This combo box
12959         */
12960         'collapse' : true,
12961         /**
12962          * @event beforeselect
12963          * Fires before a list item is selected. Return false to cancel the selection.
12964         * @param {Roo.bootstrap.ComboBox} combo This combo box
12965         * @param {Roo.data.Record} record The data record returned from the underlying store
12966         * @param {Number} index The index of the selected item in the dropdown list
12967         */
12968         'beforeselect' : true,
12969         /**
12970          * @event select
12971          * Fires when a list item is selected
12972         * @param {Roo.bootstrap.ComboBox} combo This combo box
12973         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12974         * @param {Number} index The index of the selected item in the dropdown list
12975         */
12976         'select' : true,
12977         /**
12978          * @event beforequery
12979          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12980          * The event object passed has these properties:
12981         * @param {Roo.bootstrap.ComboBox} combo This combo box
12982         * @param {String} query The query
12983         * @param {Boolean} forceAll true to force "all" query
12984         * @param {Boolean} cancel true to cancel the query
12985         * @param {Object} e The query event object
12986         */
12987         'beforequery': true,
12988          /**
12989          * @event add
12990          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12991         * @param {Roo.bootstrap.ComboBox} combo This combo box
12992         */
12993         'add' : true,
12994         /**
12995          * @event edit
12996          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12997         * @param {Roo.bootstrap.ComboBox} combo This combo box
12998         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12999         */
13000         'edit' : true,
13001         /**
13002          * @event remove
13003          * Fires when the remove value from the combobox array
13004         * @param {Roo.bootstrap.ComboBox} combo This combo box
13005         */
13006         'remove' : true,
13007         /**
13008          * @event afterremove
13009          * Fires when the remove value from the combobox array
13010         * @param {Roo.bootstrap.ComboBox} combo This combo box
13011         */
13012         'afterremove' : true,
13013         /**
13014          * @event specialfilter
13015          * Fires when specialfilter
13016             * @param {Roo.bootstrap.ComboBox} combo This combo box
13017             */
13018         'specialfilter' : true,
13019         /**
13020          * @event tick
13021          * Fires when tick the element
13022             * @param {Roo.bootstrap.ComboBox} combo This combo box
13023             */
13024         'tick' : true,
13025         /**
13026          * @event touchviewdisplay
13027          * Fires when touch view require special display (default is using displayField)
13028             * @param {Roo.bootstrap.ComboBox} combo This combo box
13029             * @param {Object} cfg set html .
13030             */
13031         'touchviewdisplay' : true
13032         
13033     });
13034     
13035     this.item = [];
13036     this.tickItems = [];
13037     
13038     this.selectedIndex = -1;
13039     if(this.mode == 'local'){
13040         if(config.queryDelay === undefined){
13041             this.queryDelay = 10;
13042         }
13043         if(config.minChars === undefined){
13044             this.minChars = 0;
13045         }
13046     }
13047 };
13048
13049 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13050      
13051     /**
13052      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13053      * rendering into an Roo.Editor, defaults to false)
13054      */
13055     /**
13056      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13057      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13058      */
13059     /**
13060      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13061      */
13062     /**
13063      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13064      * the dropdown list (defaults to undefined, with no header element)
13065      */
13066
13067      /**
13068      * @cfg {String/Roo.Template} tpl The template to use to render the output
13069      */
13070      
13071      /**
13072      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13073      */
13074     listWidth: undefined,
13075     /**
13076      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13077      * mode = 'remote' or 'text' if mode = 'local')
13078      */
13079     displayField: undefined,
13080     
13081     /**
13082      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13083      * mode = 'remote' or 'value' if mode = 'local'). 
13084      * Note: use of a valueField requires the user make a selection
13085      * in order for a value to be mapped.
13086      */
13087     valueField: undefined,
13088     /**
13089      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13090      */
13091     modalTitle : '',
13092     
13093     /**
13094      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13095      * field's data value (defaults to the underlying DOM element's name)
13096      */
13097     hiddenName: undefined,
13098     /**
13099      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13100      */
13101     listClass: '',
13102     /**
13103      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13104      */
13105     selectedClass: 'active',
13106     
13107     /**
13108      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13109      */
13110     shadow:'sides',
13111     /**
13112      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13113      * anchor positions (defaults to 'tl-bl')
13114      */
13115     listAlign: 'tl-bl?',
13116     /**
13117      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13118      */
13119     maxHeight: 300,
13120     /**
13121      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13122      * query specified by the allQuery config option (defaults to 'query')
13123      */
13124     triggerAction: 'query',
13125     /**
13126      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13127      * (defaults to 4, does not apply if editable = false)
13128      */
13129     minChars : 4,
13130     /**
13131      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13132      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13133      */
13134     typeAhead: false,
13135     /**
13136      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13137      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13138      */
13139     queryDelay: 500,
13140     /**
13141      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13142      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13143      */
13144     pageSize: 0,
13145     /**
13146      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13147      * when editable = true (defaults to false)
13148      */
13149     selectOnFocus:false,
13150     /**
13151      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13152      */
13153     queryParam: 'query',
13154     /**
13155      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13156      * when mode = 'remote' (defaults to 'Loading...')
13157      */
13158     loadingText: 'Loading...',
13159     /**
13160      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13161      */
13162     resizable: false,
13163     /**
13164      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13165      */
13166     handleHeight : 8,
13167     /**
13168      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13169      * traditional select (defaults to true)
13170      */
13171     editable: true,
13172     /**
13173      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13174      */
13175     allQuery: '',
13176     /**
13177      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13178      */
13179     mode: 'remote',
13180     /**
13181      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13182      * listWidth has a higher value)
13183      */
13184     minListWidth : 70,
13185     /**
13186      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13187      * allow the user to set arbitrary text into the field (defaults to false)
13188      */
13189     forceSelection:false,
13190     /**
13191      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13192      * if typeAhead = true (defaults to 250)
13193      */
13194     typeAheadDelay : 250,
13195     /**
13196      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13197      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13198      */
13199     valueNotFoundText : undefined,
13200     /**
13201      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13202      */
13203     blockFocus : false,
13204     
13205     /**
13206      * @cfg {Boolean} disableClear Disable showing of clear button.
13207      */
13208     disableClear : false,
13209     /**
13210      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13211      */
13212     alwaysQuery : false,
13213     
13214     /**
13215      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13216      */
13217     multiple : false,
13218     
13219     /**
13220      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13221      */
13222     invalidClass : "has-warning",
13223     
13224     /**
13225      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13226      */
13227     validClass : "has-success",
13228     
13229     /**
13230      * @cfg {Boolean} specialFilter (true|false) special filter default false
13231      */
13232     specialFilter : false,
13233     
13234     /**
13235      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13236      */
13237     mobileTouchView : true,
13238     
13239     /**
13240      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13241      */
13242     useNativeIOS : false,
13243     
13244     /**
13245      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13246      */
13247     mobile_restrict_height : false,
13248     
13249     ios_options : false,
13250     
13251     //private
13252     addicon : false,
13253     editicon: false,
13254     
13255     page: 0,
13256     hasQuery: false,
13257     append: false,
13258     loadNext: false,
13259     autoFocus : true,
13260     tickable : false,
13261     btnPosition : 'right',
13262     triggerList : true,
13263     showToggleBtn : true,
13264     animate : true,
13265     emptyResultText: 'Empty',
13266     triggerText : 'Select',
13267     emptyTitle : '',
13268     
13269     // element that contains real text value.. (when hidden is used..)
13270     
13271     getAutoCreate : function()
13272     {   
13273         var cfg = false;
13274         //render
13275         /*
13276          * Render classic select for iso
13277          */
13278         
13279         if(Roo.isIOS && this.useNativeIOS){
13280             cfg = this.getAutoCreateNativeIOS();
13281             return cfg;
13282         }
13283         
13284         /*
13285          * Touch Devices
13286          */
13287         
13288         if(Roo.isTouch && this.mobileTouchView){
13289             cfg = this.getAutoCreateTouchView();
13290             return cfg;;
13291         }
13292         
13293         /*
13294          *  Normal ComboBox
13295          */
13296         if(!this.tickable){
13297             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13298             return cfg;
13299         }
13300         
13301         /*
13302          *  ComboBox with tickable selections
13303          */
13304              
13305         var align = this.labelAlign || this.parentLabelAlign();
13306         
13307         cfg = {
13308             cls : 'form-group roo-combobox-tickable' //input-group
13309         };
13310         
13311         var btn_text_select = '';
13312         var btn_text_done = '';
13313         var btn_text_cancel = '';
13314         
13315         if (this.btn_text_show) {
13316             btn_text_select = 'Select';
13317             btn_text_done = 'Done';
13318             btn_text_cancel = 'Cancel'; 
13319         }
13320         
13321         var buttons = {
13322             tag : 'div',
13323             cls : 'tickable-buttons',
13324             cn : [
13325                 {
13326                     tag : 'button',
13327                     type : 'button',
13328                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13329                     //html : this.triggerText
13330                     html: btn_text_select
13331                 },
13332                 {
13333                     tag : 'button',
13334                     type : 'button',
13335                     name : 'ok',
13336                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13337                     //html : 'Done'
13338                     html: btn_text_done
13339                 },
13340                 {
13341                     tag : 'button',
13342                     type : 'button',
13343                     name : 'cancel',
13344                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13345                     //html : 'Cancel'
13346                     html: btn_text_cancel
13347                 }
13348             ]
13349         };
13350         
13351         if(this.editable){
13352             buttons.cn.unshift({
13353                 tag: 'input',
13354                 cls: 'roo-select2-search-field-input'
13355             });
13356         }
13357         
13358         var _this = this;
13359         
13360         Roo.each(buttons.cn, function(c){
13361             if (_this.size) {
13362                 c.cls += ' btn-' + _this.size;
13363             }
13364
13365             if (_this.disabled) {
13366                 c.disabled = true;
13367             }
13368         });
13369         
13370         var box = {
13371             tag: 'div',
13372             cn: [
13373                 {
13374                     tag: 'input',
13375                     type : 'hidden',
13376                     cls: 'form-hidden-field'
13377                 },
13378                 {
13379                     tag: 'ul',
13380                     cls: 'roo-select2-choices',
13381                     cn:[
13382                         {
13383                             tag: 'li',
13384                             cls: 'roo-select2-search-field',
13385                             cn: [
13386                                 buttons
13387                             ]
13388                         }
13389                     ]
13390                 }
13391             ]
13392         };
13393         
13394         var combobox = {
13395             cls: 'roo-select2-container input-group roo-select2-container-multi',
13396             cn: [
13397                 
13398                 box
13399 //                {
13400 //                    tag: 'ul',
13401 //                    cls: 'typeahead typeahead-long dropdown-menu',
13402 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13403 //                }
13404             ]
13405         };
13406         
13407         if(this.hasFeedback && !this.allowBlank){
13408             
13409             var feedback = {
13410                 tag: 'span',
13411                 cls: 'glyphicon form-control-feedback'
13412             };
13413
13414             combobox.cn.push(feedback);
13415         }
13416         
13417         var indicator = {
13418             tag : 'i',
13419             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13420             tooltip : 'This field is required'
13421         };
13422         if (Roo.bootstrap.version == 4) {
13423             indicator = {
13424                 tag : 'i',
13425                 style : 'display:none'
13426             };
13427         }
13428         if (align ==='left' && this.fieldLabel.length) {
13429             
13430             cfg.cls += ' roo-form-group-label-left row';
13431             
13432             cfg.cn = [
13433                 indicator,
13434                 {
13435                     tag: 'label',
13436                     'for' :  id,
13437                     cls : 'control-label col-form-label',
13438                     html : this.fieldLabel
13439
13440                 },
13441                 {
13442                     cls : "", 
13443                     cn: [
13444                         combobox
13445                     ]
13446                 }
13447
13448             ];
13449             
13450             var labelCfg = cfg.cn[1];
13451             var contentCfg = cfg.cn[2];
13452             
13453
13454             if(this.indicatorpos == 'right'){
13455                 
13456                 cfg.cn = [
13457                     {
13458                         tag: 'label',
13459                         'for' :  id,
13460                         cls : 'control-label col-form-label',
13461                         cn : [
13462                             {
13463                                 tag : 'span',
13464                                 html : this.fieldLabel
13465                             },
13466                             indicator
13467                         ]
13468                     },
13469                     {
13470                         cls : "",
13471                         cn: [
13472                             combobox
13473                         ]
13474                     }
13475
13476                 ];
13477                 
13478                 
13479                 
13480                 labelCfg = cfg.cn[0];
13481                 contentCfg = cfg.cn[1];
13482             
13483             }
13484             
13485             if(this.labelWidth > 12){
13486                 labelCfg.style = "width: " + this.labelWidth + 'px';
13487             }
13488             
13489             if(this.labelWidth < 13 && this.labelmd == 0){
13490                 this.labelmd = this.labelWidth;
13491             }
13492             
13493             if(this.labellg > 0){
13494                 labelCfg.cls += ' col-lg-' + this.labellg;
13495                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13496             }
13497             
13498             if(this.labelmd > 0){
13499                 labelCfg.cls += ' col-md-' + this.labelmd;
13500                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13501             }
13502             
13503             if(this.labelsm > 0){
13504                 labelCfg.cls += ' col-sm-' + this.labelsm;
13505                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13506             }
13507             
13508             if(this.labelxs > 0){
13509                 labelCfg.cls += ' col-xs-' + this.labelxs;
13510                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13511             }
13512                 
13513                 
13514         } else if ( this.fieldLabel.length) {
13515 //                Roo.log(" label");
13516                  cfg.cn = [
13517                    indicator,
13518                     {
13519                         tag: 'label',
13520                         //cls : 'input-group-addon',
13521                         html : this.fieldLabel
13522                     },
13523                     combobox
13524                 ];
13525                 
13526                 if(this.indicatorpos == 'right'){
13527                     cfg.cn = [
13528                         {
13529                             tag: 'label',
13530                             //cls : 'input-group-addon',
13531                             html : this.fieldLabel
13532                         },
13533                         indicator,
13534                         combobox
13535                     ];
13536                     
13537                 }
13538
13539         } else {
13540             
13541 //                Roo.log(" no label && no align");
13542                 cfg = combobox
13543                      
13544                 
13545         }
13546          
13547         var settings=this;
13548         ['xs','sm','md','lg'].map(function(size){
13549             if (settings[size]) {
13550                 cfg.cls += ' col-' + size + '-' + settings[size];
13551             }
13552         });
13553         
13554         return cfg;
13555         
13556     },
13557     
13558     _initEventsCalled : false,
13559     
13560     // private
13561     initEvents: function()
13562     {   
13563         if (this._initEventsCalled) { // as we call render... prevent looping...
13564             return;
13565         }
13566         this._initEventsCalled = true;
13567         
13568         if (!this.store) {
13569             throw "can not find store for combo";
13570         }
13571         
13572         this.indicator = this.indicatorEl();
13573         
13574         this.store = Roo.factory(this.store, Roo.data);
13575         this.store.parent = this;
13576         
13577         // if we are building from html. then this element is so complex, that we can not really
13578         // use the rendered HTML.
13579         // so we have to trash and replace the previous code.
13580         if (Roo.XComponent.build_from_html) {
13581             // remove this element....
13582             var e = this.el.dom, k=0;
13583             while (e ) { e = e.previousSibling;  ++k;}
13584
13585             this.el.remove();
13586             
13587             this.el=false;
13588             this.rendered = false;
13589             
13590             this.render(this.parent().getChildContainer(true), k);
13591         }
13592         
13593         if(Roo.isIOS && this.useNativeIOS){
13594             this.initIOSView();
13595             return;
13596         }
13597         
13598         /*
13599          * Touch Devices
13600          */
13601         
13602         if(Roo.isTouch && this.mobileTouchView){
13603             this.initTouchView();
13604             return;
13605         }
13606         
13607         if(this.tickable){
13608             this.initTickableEvents();
13609             return;
13610         }
13611         
13612         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13613         
13614         if(this.hiddenName){
13615             
13616             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13617             
13618             this.hiddenField.dom.value =
13619                 this.hiddenValue !== undefined ? this.hiddenValue :
13620                 this.value !== undefined ? this.value : '';
13621
13622             // prevent input submission
13623             this.el.dom.removeAttribute('name');
13624             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13625              
13626              
13627         }
13628         //if(Roo.isGecko){
13629         //    this.el.dom.setAttribute('autocomplete', 'off');
13630         //}
13631         
13632         var cls = 'x-combo-list';
13633         
13634         //this.list = new Roo.Layer({
13635         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13636         //});
13637         
13638         var _this = this;
13639         
13640         (function(){
13641             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13642             _this.list.setWidth(lw);
13643         }).defer(100);
13644         
13645         this.list.on('mouseover', this.onViewOver, this);
13646         this.list.on('mousemove', this.onViewMove, this);
13647         this.list.on('scroll', this.onViewScroll, this);
13648         
13649         /*
13650         this.list.swallowEvent('mousewheel');
13651         this.assetHeight = 0;
13652
13653         if(this.title){
13654             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13655             this.assetHeight += this.header.getHeight();
13656         }
13657
13658         this.innerList = this.list.createChild({cls:cls+'-inner'});
13659         this.innerList.on('mouseover', this.onViewOver, this);
13660         this.innerList.on('mousemove', this.onViewMove, this);
13661         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13662         
13663         if(this.allowBlank && !this.pageSize && !this.disableClear){
13664             this.footer = this.list.createChild({cls:cls+'-ft'});
13665             this.pageTb = new Roo.Toolbar(this.footer);
13666            
13667         }
13668         if(this.pageSize){
13669             this.footer = this.list.createChild({cls:cls+'-ft'});
13670             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13671                     {pageSize: this.pageSize});
13672             
13673         }
13674         
13675         if (this.pageTb && this.allowBlank && !this.disableClear) {
13676             var _this = this;
13677             this.pageTb.add(new Roo.Toolbar.Fill(), {
13678                 cls: 'x-btn-icon x-btn-clear',
13679                 text: '&#160;',
13680                 handler: function()
13681                 {
13682                     _this.collapse();
13683                     _this.clearValue();
13684                     _this.onSelect(false, -1);
13685                 }
13686             });
13687         }
13688         if (this.footer) {
13689             this.assetHeight += this.footer.getHeight();
13690         }
13691         */
13692             
13693         if(!this.tpl){
13694             this.tpl = Roo.bootstrap.version == 4 ?
13695                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13696                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13697         }
13698
13699         this.view = new Roo.View(this.list, this.tpl, {
13700             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13701         });
13702         //this.view.wrapEl.setDisplayed(false);
13703         this.view.on('click', this.onViewClick, this);
13704         
13705         
13706         this.store.on('beforeload', this.onBeforeLoad, this);
13707         this.store.on('load', this.onLoad, this);
13708         this.store.on('loadexception', this.onLoadException, this);
13709         /*
13710         if(this.resizable){
13711             this.resizer = new Roo.Resizable(this.list,  {
13712                pinned:true, handles:'se'
13713             });
13714             this.resizer.on('resize', function(r, w, h){
13715                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13716                 this.listWidth = w;
13717                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13718                 this.restrictHeight();
13719             }, this);
13720             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13721         }
13722         */
13723         if(!this.editable){
13724             this.editable = true;
13725             this.setEditable(false);
13726         }
13727         
13728         /*
13729         
13730         if (typeof(this.events.add.listeners) != 'undefined') {
13731             
13732             this.addicon = this.wrap.createChild(
13733                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13734        
13735             this.addicon.on('click', function(e) {
13736                 this.fireEvent('add', this);
13737             }, this);
13738         }
13739         if (typeof(this.events.edit.listeners) != 'undefined') {
13740             
13741             this.editicon = this.wrap.createChild(
13742                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13743             if (this.addicon) {
13744                 this.editicon.setStyle('margin-left', '40px');
13745             }
13746             this.editicon.on('click', function(e) {
13747                 
13748                 // we fire even  if inothing is selected..
13749                 this.fireEvent('edit', this, this.lastData );
13750                 
13751             }, this);
13752         }
13753         */
13754         
13755         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13756             "up" : function(e){
13757                 this.inKeyMode = true;
13758                 this.selectPrev();
13759             },
13760
13761             "down" : function(e){
13762                 if(!this.isExpanded()){
13763                     this.onTriggerClick();
13764                 }else{
13765                     this.inKeyMode = true;
13766                     this.selectNext();
13767                 }
13768             },
13769
13770             "enter" : function(e){
13771 //                this.onViewClick();
13772                 //return true;
13773                 this.collapse();
13774                 
13775                 if(this.fireEvent("specialkey", this, e)){
13776                     this.onViewClick(false);
13777                 }
13778                 
13779                 return true;
13780             },
13781
13782             "esc" : function(e){
13783                 this.collapse();
13784             },
13785
13786             "tab" : function(e){
13787                 this.collapse();
13788                 
13789                 if(this.fireEvent("specialkey", this, e)){
13790                     this.onViewClick(false);
13791                 }
13792                 
13793                 return true;
13794             },
13795
13796             scope : this,
13797
13798             doRelay : function(foo, bar, hname){
13799                 if(hname == 'down' || this.scope.isExpanded()){
13800                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13801                 }
13802                 return true;
13803             },
13804
13805             forceKeyDown: true
13806         });
13807         
13808         
13809         this.queryDelay = Math.max(this.queryDelay || 10,
13810                 this.mode == 'local' ? 10 : 250);
13811         
13812         
13813         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13814         
13815         if(this.typeAhead){
13816             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13817         }
13818         if(this.editable !== false){
13819             this.inputEl().on("keyup", this.onKeyUp, this);
13820         }
13821         if(this.forceSelection){
13822             this.inputEl().on('blur', this.doForce, this);
13823         }
13824         
13825         if(this.multiple){
13826             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13827             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13828         }
13829     },
13830     
13831     initTickableEvents: function()
13832     {   
13833         this.createList();
13834         
13835         if(this.hiddenName){
13836             
13837             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13838             
13839             this.hiddenField.dom.value =
13840                 this.hiddenValue !== undefined ? this.hiddenValue :
13841                 this.value !== undefined ? this.value : '';
13842
13843             // prevent input submission
13844             this.el.dom.removeAttribute('name');
13845             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13846              
13847              
13848         }
13849         
13850 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13851         
13852         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13853         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13854         if(this.triggerList){
13855             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13856         }
13857          
13858         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13859         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13860         
13861         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13862         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13863         
13864         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13865         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13866         
13867         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13868         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13869         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13870         
13871         this.okBtn.hide();
13872         this.cancelBtn.hide();
13873         
13874         var _this = this;
13875         
13876         (function(){
13877             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13878             _this.list.setWidth(lw);
13879         }).defer(100);
13880         
13881         this.list.on('mouseover', this.onViewOver, this);
13882         this.list.on('mousemove', this.onViewMove, this);
13883         
13884         this.list.on('scroll', this.onViewScroll, this);
13885         
13886         if(!this.tpl){
13887             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13888                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13889         }
13890
13891         this.view = new Roo.View(this.list, this.tpl, {
13892             singleSelect:true,
13893             tickable:true,
13894             parent:this,
13895             store: this.store,
13896             selectedClass: this.selectedClass
13897         });
13898         
13899         //this.view.wrapEl.setDisplayed(false);
13900         this.view.on('click', this.onViewClick, this);
13901         
13902         
13903         
13904         this.store.on('beforeload', this.onBeforeLoad, this);
13905         this.store.on('load', this.onLoad, this);
13906         this.store.on('loadexception', this.onLoadException, this);
13907         
13908         if(this.editable){
13909             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13910                 "up" : function(e){
13911                     this.inKeyMode = true;
13912                     this.selectPrev();
13913                 },
13914
13915                 "down" : function(e){
13916                     this.inKeyMode = true;
13917                     this.selectNext();
13918                 },
13919
13920                 "enter" : function(e){
13921                     if(this.fireEvent("specialkey", this, e)){
13922                         this.onViewClick(false);
13923                     }
13924                     
13925                     return true;
13926                 },
13927
13928                 "esc" : function(e){
13929                     this.onTickableFooterButtonClick(e, false, false);
13930                 },
13931
13932                 "tab" : function(e){
13933                     this.fireEvent("specialkey", this, e);
13934                     
13935                     this.onTickableFooterButtonClick(e, false, false);
13936                     
13937                     return true;
13938                 },
13939
13940                 scope : this,
13941
13942                 doRelay : function(e, fn, key){
13943                     if(this.scope.isExpanded()){
13944                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13945                     }
13946                     return true;
13947                 },
13948
13949                 forceKeyDown: true
13950             });
13951         }
13952         
13953         this.queryDelay = Math.max(this.queryDelay || 10,
13954                 this.mode == 'local' ? 10 : 250);
13955         
13956         
13957         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13958         
13959         if(this.typeAhead){
13960             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13961         }
13962         
13963         if(this.editable !== false){
13964             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13965         }
13966         
13967         this.indicator = this.indicatorEl();
13968         
13969         if(this.indicator){
13970             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13971             this.indicator.hide();
13972         }
13973         
13974     },
13975
13976     onDestroy : function(){
13977         if(this.view){
13978             this.view.setStore(null);
13979             this.view.el.removeAllListeners();
13980             this.view.el.remove();
13981             this.view.purgeListeners();
13982         }
13983         if(this.list){
13984             this.list.dom.innerHTML  = '';
13985         }
13986         
13987         if(this.store){
13988             this.store.un('beforeload', this.onBeforeLoad, this);
13989             this.store.un('load', this.onLoad, this);
13990             this.store.un('loadexception', this.onLoadException, this);
13991         }
13992         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13993     },
13994
13995     // private
13996     fireKey : function(e){
13997         if(e.isNavKeyPress() && !this.list.isVisible()){
13998             this.fireEvent("specialkey", this, e);
13999         }
14000     },
14001
14002     // private
14003     onResize: function(w, h){
14004 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14005 //        
14006 //        if(typeof w != 'number'){
14007 //            // we do not handle it!?!?
14008 //            return;
14009 //        }
14010 //        var tw = this.trigger.getWidth();
14011 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14012 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14013 //        var x = w - tw;
14014 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14015 //            
14016 //        //this.trigger.setStyle('left', x+'px');
14017 //        
14018 //        if(this.list && this.listWidth === undefined){
14019 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14020 //            this.list.setWidth(lw);
14021 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14022 //        }
14023         
14024     
14025         
14026     },
14027
14028     /**
14029      * Allow or prevent the user from directly editing the field text.  If false is passed,
14030      * the user will only be able to select from the items defined in the dropdown list.  This method
14031      * is the runtime equivalent of setting the 'editable' config option at config time.
14032      * @param {Boolean} value True to allow the user to directly edit the field text
14033      */
14034     setEditable : function(value){
14035         if(value == this.editable){
14036             return;
14037         }
14038         this.editable = value;
14039         if(!value){
14040             this.inputEl().dom.setAttribute('readOnly', true);
14041             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14042             this.inputEl().addClass('x-combo-noedit');
14043         }else{
14044             this.inputEl().dom.setAttribute('readOnly', false);
14045             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14046             this.inputEl().removeClass('x-combo-noedit');
14047         }
14048     },
14049
14050     // private
14051     
14052     onBeforeLoad : function(combo,opts){
14053         if(!this.hasFocus){
14054             return;
14055         }
14056          if (!opts.add) {
14057             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14058          }
14059         this.restrictHeight();
14060         this.selectedIndex = -1;
14061     },
14062
14063     // private
14064     onLoad : function(){
14065         
14066         this.hasQuery = false;
14067         
14068         if(!this.hasFocus){
14069             return;
14070         }
14071         
14072         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14073             this.loading.hide();
14074         }
14075         
14076         if(this.store.getCount() > 0){
14077             
14078             this.expand();
14079             this.restrictHeight();
14080             if(this.lastQuery == this.allQuery){
14081                 if(this.editable && !this.tickable){
14082                     this.inputEl().dom.select();
14083                 }
14084                 
14085                 if(
14086                     !this.selectByValue(this.value, true) &&
14087                     this.autoFocus && 
14088                     (
14089                         !this.store.lastOptions ||
14090                         typeof(this.store.lastOptions.add) == 'undefined' || 
14091                         this.store.lastOptions.add != true
14092                     )
14093                 ){
14094                     this.select(0, true);
14095                 }
14096             }else{
14097                 if(this.autoFocus){
14098                     this.selectNext();
14099                 }
14100                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14101                     this.taTask.delay(this.typeAheadDelay);
14102                 }
14103             }
14104         }else{
14105             this.onEmptyResults();
14106         }
14107         
14108         //this.el.focus();
14109     },
14110     // private
14111     onLoadException : function()
14112     {
14113         this.hasQuery = false;
14114         
14115         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14116             this.loading.hide();
14117         }
14118         
14119         if(this.tickable && this.editable){
14120             return;
14121         }
14122         
14123         this.collapse();
14124         // only causes errors at present
14125         //Roo.log(this.store.reader.jsonData);
14126         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14127             // fixme
14128             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14129         //}
14130         
14131         
14132     },
14133     // private
14134     onTypeAhead : function(){
14135         if(this.store.getCount() > 0){
14136             var r = this.store.getAt(0);
14137             var newValue = r.data[this.displayField];
14138             var len = newValue.length;
14139             var selStart = this.getRawValue().length;
14140             
14141             if(selStart != len){
14142                 this.setRawValue(newValue);
14143                 this.selectText(selStart, newValue.length);
14144             }
14145         }
14146     },
14147
14148     // private
14149     onSelect : function(record, index){
14150         
14151         if(this.fireEvent('beforeselect', this, record, index) !== false){
14152         
14153             this.setFromData(index > -1 ? record.data : false);
14154             
14155             this.collapse();
14156             this.fireEvent('select', this, record, index);
14157         }
14158     },
14159
14160     /**
14161      * Returns the currently selected field value or empty string if no value is set.
14162      * @return {String} value The selected value
14163      */
14164     getValue : function()
14165     {
14166         if(Roo.isIOS && this.useNativeIOS){
14167             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14168         }
14169         
14170         if(this.multiple){
14171             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14172         }
14173         
14174         if(this.valueField){
14175             return typeof this.value != 'undefined' ? this.value : '';
14176         }else{
14177             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14178         }
14179     },
14180     
14181     getRawValue : function()
14182     {
14183         if(Roo.isIOS && this.useNativeIOS){
14184             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14185         }
14186         
14187         var v = this.inputEl().getValue();
14188         
14189         return v;
14190     },
14191
14192     /**
14193      * Clears any text/value currently set in the field
14194      */
14195     clearValue : function(){
14196         
14197         if(this.hiddenField){
14198             this.hiddenField.dom.value = '';
14199         }
14200         this.value = '';
14201         this.setRawValue('');
14202         this.lastSelectionText = '';
14203         this.lastData = false;
14204         
14205         var close = this.closeTriggerEl();
14206         
14207         if(close){
14208             close.hide();
14209         }
14210         
14211         this.validate();
14212         
14213     },
14214
14215     /**
14216      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14217      * will be displayed in the field.  If the value does not match the data value of an existing item,
14218      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14219      * Otherwise the field will be blank (although the value will still be set).
14220      * @param {String} value The value to match
14221      */
14222     setValue : function(v)
14223     {
14224         if(Roo.isIOS && this.useNativeIOS){
14225             this.setIOSValue(v);
14226             return;
14227         }
14228         
14229         if(this.multiple){
14230             this.syncValue();
14231             return;
14232         }
14233         
14234         var text = v;
14235         if(this.valueField){
14236             var r = this.findRecord(this.valueField, v);
14237             if(r){
14238                 text = r.data[this.displayField];
14239             }else if(this.valueNotFoundText !== undefined){
14240                 text = this.valueNotFoundText;
14241             }
14242         }
14243         this.lastSelectionText = text;
14244         if(this.hiddenField){
14245             this.hiddenField.dom.value = v;
14246         }
14247         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14248         this.value = v;
14249         
14250         var close = this.closeTriggerEl();
14251         
14252         if(close){
14253             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14254         }
14255         
14256         this.validate();
14257     },
14258     /**
14259      * @property {Object} the last set data for the element
14260      */
14261     
14262     lastData : false,
14263     /**
14264      * Sets the value of the field based on a object which is related to the record format for the store.
14265      * @param {Object} value the value to set as. or false on reset?
14266      */
14267     setFromData : function(o){
14268         
14269         if(this.multiple){
14270             this.addItem(o);
14271             return;
14272         }
14273             
14274         var dv = ''; // display value
14275         var vv = ''; // value value..
14276         this.lastData = o;
14277         if (this.displayField) {
14278             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14279         } else {
14280             // this is an error condition!!!
14281             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14282         }
14283         
14284         if(this.valueField){
14285             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14286         }
14287         
14288         var close = this.closeTriggerEl();
14289         
14290         if(close){
14291             if(dv.length || vv * 1 > 0){
14292                 close.show() ;
14293                 this.blockFocus=true;
14294             } else {
14295                 close.hide();
14296             }             
14297         }
14298         
14299         if(this.hiddenField){
14300             this.hiddenField.dom.value = vv;
14301             
14302             this.lastSelectionText = dv;
14303             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14304             this.value = vv;
14305             return;
14306         }
14307         // no hidden field.. - we store the value in 'value', but still display
14308         // display field!!!!
14309         this.lastSelectionText = dv;
14310         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14311         this.value = vv;
14312         
14313         
14314         
14315     },
14316     // private
14317     reset : function(){
14318         // overridden so that last data is reset..
14319         
14320         if(this.multiple){
14321             this.clearItem();
14322             return;
14323         }
14324         
14325         this.setValue(this.originalValue);
14326         //this.clearInvalid();
14327         this.lastData = false;
14328         if (this.view) {
14329             this.view.clearSelections();
14330         }
14331         
14332         this.validate();
14333     },
14334     // private
14335     findRecord : function(prop, value){
14336         var record;
14337         if(this.store.getCount() > 0){
14338             this.store.each(function(r){
14339                 if(r.data[prop] == value){
14340                     record = r;
14341                     return false;
14342                 }
14343                 return true;
14344             });
14345         }
14346         return record;
14347     },
14348     
14349     getName: function()
14350     {
14351         // returns hidden if it's set..
14352         if (!this.rendered) {return ''};
14353         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14354         
14355     },
14356     // private
14357     onViewMove : function(e, t){
14358         this.inKeyMode = false;
14359     },
14360
14361     // private
14362     onViewOver : function(e, t){
14363         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14364             return;
14365         }
14366         var item = this.view.findItemFromChild(t);
14367         
14368         if(item){
14369             var index = this.view.indexOf(item);
14370             this.select(index, false);
14371         }
14372     },
14373
14374     // private
14375     onViewClick : function(view, doFocus, el, e)
14376     {
14377         var index = this.view.getSelectedIndexes()[0];
14378         
14379         var r = this.store.getAt(index);
14380         
14381         if(this.tickable){
14382             
14383             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14384                 return;
14385             }
14386             
14387             var rm = false;
14388             var _this = this;
14389             
14390             Roo.each(this.tickItems, function(v,k){
14391                 
14392                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14393                     Roo.log(v);
14394                     _this.tickItems.splice(k, 1);
14395                     
14396                     if(typeof(e) == 'undefined' && view == false){
14397                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14398                     }
14399                     
14400                     rm = true;
14401                     return;
14402                 }
14403             });
14404             
14405             if(rm){
14406                 return;
14407             }
14408             
14409             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14410                 this.tickItems.push(r.data);
14411             }
14412             
14413             if(typeof(e) == 'undefined' && view == false){
14414                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14415             }
14416                     
14417             return;
14418         }
14419         
14420         if(r){
14421             this.onSelect(r, index);
14422         }
14423         if(doFocus !== false && !this.blockFocus){
14424             this.inputEl().focus();
14425         }
14426     },
14427
14428     // private
14429     restrictHeight : function(){
14430         //this.innerList.dom.style.height = '';
14431         //var inner = this.innerList.dom;
14432         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14433         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14434         //this.list.beginUpdate();
14435         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14436         this.list.alignTo(this.inputEl(), this.listAlign);
14437         this.list.alignTo(this.inputEl(), this.listAlign);
14438         //this.list.endUpdate();
14439     },
14440
14441     // private
14442     onEmptyResults : function(){
14443         
14444         if(this.tickable && this.editable){
14445             this.hasFocus = false;
14446             this.restrictHeight();
14447             return;
14448         }
14449         
14450         this.collapse();
14451     },
14452
14453     /**
14454      * Returns true if the dropdown list is expanded, else false.
14455      */
14456     isExpanded : function(){
14457         return this.list.isVisible();
14458     },
14459
14460     /**
14461      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14462      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14463      * @param {String} value The data value of the item to select
14464      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14465      * selected item if it is not currently in view (defaults to true)
14466      * @return {Boolean} True if the value matched an item in the list, else false
14467      */
14468     selectByValue : function(v, scrollIntoView){
14469         if(v !== undefined && v !== null){
14470             var r = this.findRecord(this.valueField || this.displayField, v);
14471             if(r){
14472                 this.select(this.store.indexOf(r), scrollIntoView);
14473                 return true;
14474             }
14475         }
14476         return false;
14477     },
14478
14479     /**
14480      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14481      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14482      * @param {Number} index The zero-based index of the list item to select
14483      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14484      * selected item if it is not currently in view (defaults to true)
14485      */
14486     select : function(index, scrollIntoView){
14487         this.selectedIndex = index;
14488         this.view.select(index);
14489         if(scrollIntoView !== false){
14490             var el = this.view.getNode(index);
14491             /*
14492              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14493              */
14494             if(el){
14495                 this.list.scrollChildIntoView(el, false);
14496             }
14497         }
14498     },
14499
14500     // private
14501     selectNext : function(){
14502         var ct = this.store.getCount();
14503         if(ct > 0){
14504             if(this.selectedIndex == -1){
14505                 this.select(0);
14506             }else if(this.selectedIndex < ct-1){
14507                 this.select(this.selectedIndex+1);
14508             }
14509         }
14510     },
14511
14512     // private
14513     selectPrev : function(){
14514         var ct = this.store.getCount();
14515         if(ct > 0){
14516             if(this.selectedIndex == -1){
14517                 this.select(0);
14518             }else if(this.selectedIndex != 0){
14519                 this.select(this.selectedIndex-1);
14520             }
14521         }
14522     },
14523
14524     // private
14525     onKeyUp : function(e){
14526         if(this.editable !== false && !e.isSpecialKey()){
14527             this.lastKey = e.getKey();
14528             this.dqTask.delay(this.queryDelay);
14529         }
14530     },
14531
14532     // private
14533     validateBlur : function(){
14534         return !this.list || !this.list.isVisible();   
14535     },
14536
14537     // private
14538     initQuery : function(){
14539         
14540         var v = this.getRawValue();
14541         
14542         if(this.tickable && this.editable){
14543             v = this.tickableInputEl().getValue();
14544         }
14545         
14546         this.doQuery(v);
14547     },
14548
14549     // private
14550     doForce : function(){
14551         if(this.inputEl().dom.value.length > 0){
14552             this.inputEl().dom.value =
14553                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14554              
14555         }
14556     },
14557
14558     /**
14559      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14560      * query allowing the query action to be canceled if needed.
14561      * @param {String} query The SQL query to execute
14562      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14563      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14564      * saved in the current store (defaults to false)
14565      */
14566     doQuery : function(q, forceAll){
14567         
14568         if(q === undefined || q === null){
14569             q = '';
14570         }
14571         var qe = {
14572             query: q,
14573             forceAll: forceAll,
14574             combo: this,
14575             cancel:false
14576         };
14577         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14578             return false;
14579         }
14580         q = qe.query;
14581         
14582         forceAll = qe.forceAll;
14583         if(forceAll === true || (q.length >= this.minChars)){
14584             
14585             this.hasQuery = true;
14586             
14587             if(this.lastQuery != q || this.alwaysQuery){
14588                 this.lastQuery = q;
14589                 if(this.mode == 'local'){
14590                     this.selectedIndex = -1;
14591                     if(forceAll){
14592                         this.store.clearFilter();
14593                     }else{
14594                         
14595                         if(this.specialFilter){
14596                             this.fireEvent('specialfilter', this);
14597                             this.onLoad();
14598                             return;
14599                         }
14600                         
14601                         this.store.filter(this.displayField, q);
14602                     }
14603                     
14604                     this.store.fireEvent("datachanged", this.store);
14605                     
14606                     this.onLoad();
14607                     
14608                     
14609                 }else{
14610                     
14611                     this.store.baseParams[this.queryParam] = q;
14612                     
14613                     var options = {params : this.getParams(q)};
14614                     
14615                     if(this.loadNext){
14616                         options.add = true;
14617                         options.params.start = this.page * this.pageSize;
14618                     }
14619                     
14620                     this.store.load(options);
14621                     
14622                     /*
14623                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14624                      *  we should expand the list on onLoad
14625                      *  so command out it
14626                      */
14627 //                    this.expand();
14628                 }
14629             }else{
14630                 this.selectedIndex = -1;
14631                 this.onLoad();   
14632             }
14633         }
14634         
14635         this.loadNext = false;
14636     },
14637     
14638     // private
14639     getParams : function(q){
14640         var p = {};
14641         //p[this.queryParam] = q;
14642         
14643         if(this.pageSize){
14644             p.start = 0;
14645             p.limit = this.pageSize;
14646         }
14647         return p;
14648     },
14649
14650     /**
14651      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14652      */
14653     collapse : function(){
14654         if(!this.isExpanded()){
14655             return;
14656         }
14657         
14658         this.list.hide();
14659         
14660         this.hasFocus = false;
14661         
14662         if(this.tickable){
14663             this.okBtn.hide();
14664             this.cancelBtn.hide();
14665             this.trigger.show();
14666             
14667             if(this.editable){
14668                 this.tickableInputEl().dom.value = '';
14669                 this.tickableInputEl().blur();
14670             }
14671             
14672         }
14673         
14674         Roo.get(document).un('mousedown', this.collapseIf, this);
14675         Roo.get(document).un('mousewheel', this.collapseIf, this);
14676         if (!this.editable) {
14677             Roo.get(document).un('keydown', this.listKeyPress, this);
14678         }
14679         this.fireEvent('collapse', this);
14680         
14681         this.validate();
14682     },
14683
14684     // private
14685     collapseIf : function(e){
14686         var in_combo  = e.within(this.el);
14687         var in_list =  e.within(this.list);
14688         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14689         
14690         if (in_combo || in_list || is_list) {
14691             //e.stopPropagation();
14692             return;
14693         }
14694         
14695         if(this.tickable){
14696             this.onTickableFooterButtonClick(e, false, false);
14697         }
14698
14699         this.collapse();
14700         
14701     },
14702
14703     /**
14704      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14705      */
14706     expand : function(){
14707        
14708         if(this.isExpanded() || !this.hasFocus){
14709             return;
14710         }
14711         
14712         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14713         this.list.setWidth(lw);
14714         
14715         Roo.log('expand');
14716         
14717         this.list.show();
14718         
14719         this.restrictHeight();
14720         
14721         if(this.tickable){
14722             
14723             this.tickItems = Roo.apply([], this.item);
14724             
14725             this.okBtn.show();
14726             this.cancelBtn.show();
14727             this.trigger.hide();
14728             
14729             if(this.editable){
14730                 this.tickableInputEl().focus();
14731             }
14732             
14733         }
14734         
14735         Roo.get(document).on('mousedown', this.collapseIf, this);
14736         Roo.get(document).on('mousewheel', this.collapseIf, this);
14737         if (!this.editable) {
14738             Roo.get(document).on('keydown', this.listKeyPress, this);
14739         }
14740         
14741         this.fireEvent('expand', this);
14742     },
14743
14744     // private
14745     // Implements the default empty TriggerField.onTriggerClick function
14746     onTriggerClick : function(e)
14747     {
14748         Roo.log('trigger click');
14749         
14750         if(this.disabled || !this.triggerList){
14751             return;
14752         }
14753         
14754         this.page = 0;
14755         this.loadNext = false;
14756         
14757         if(this.isExpanded()){
14758             this.collapse();
14759             if (!this.blockFocus) {
14760                 this.inputEl().focus();
14761             }
14762             
14763         }else {
14764             this.hasFocus = true;
14765             if(this.triggerAction == 'all') {
14766                 this.doQuery(this.allQuery, true);
14767             } else {
14768                 this.doQuery(this.getRawValue());
14769             }
14770             if (!this.blockFocus) {
14771                 this.inputEl().focus();
14772             }
14773         }
14774     },
14775     
14776     onTickableTriggerClick : function(e)
14777     {
14778         if(this.disabled){
14779             return;
14780         }
14781         
14782         this.page = 0;
14783         this.loadNext = false;
14784         this.hasFocus = true;
14785         
14786         if(this.triggerAction == 'all') {
14787             this.doQuery(this.allQuery, true);
14788         } else {
14789             this.doQuery(this.getRawValue());
14790         }
14791     },
14792     
14793     onSearchFieldClick : function(e)
14794     {
14795         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14796             this.onTickableFooterButtonClick(e, false, false);
14797             return;
14798         }
14799         
14800         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14801             return;
14802         }
14803         
14804         this.page = 0;
14805         this.loadNext = false;
14806         this.hasFocus = true;
14807         
14808         if(this.triggerAction == 'all') {
14809             this.doQuery(this.allQuery, true);
14810         } else {
14811             this.doQuery(this.getRawValue());
14812         }
14813     },
14814     
14815     listKeyPress : function(e)
14816     {
14817         //Roo.log('listkeypress');
14818         // scroll to first matching element based on key pres..
14819         if (e.isSpecialKey()) {
14820             return false;
14821         }
14822         var k = String.fromCharCode(e.getKey()).toUpperCase();
14823         //Roo.log(k);
14824         var match  = false;
14825         var csel = this.view.getSelectedNodes();
14826         var cselitem = false;
14827         if (csel.length) {
14828             var ix = this.view.indexOf(csel[0]);
14829             cselitem  = this.store.getAt(ix);
14830             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14831                 cselitem = false;
14832             }
14833             
14834         }
14835         
14836         this.store.each(function(v) { 
14837             if (cselitem) {
14838                 // start at existing selection.
14839                 if (cselitem.id == v.id) {
14840                     cselitem = false;
14841                 }
14842                 return true;
14843             }
14844                 
14845             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14846                 match = this.store.indexOf(v);
14847                 return false;
14848             }
14849             return true;
14850         }, this);
14851         
14852         if (match === false) {
14853             return true; // no more action?
14854         }
14855         // scroll to?
14856         this.view.select(match);
14857         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14858         sn.scrollIntoView(sn.dom.parentNode, false);
14859     },
14860     
14861     onViewScroll : function(e, t){
14862         
14863         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){
14864             return;
14865         }
14866         
14867         this.hasQuery = true;
14868         
14869         this.loading = this.list.select('.loading', true).first();
14870         
14871         if(this.loading === null){
14872             this.list.createChild({
14873                 tag: 'div',
14874                 cls: 'loading roo-select2-more-results roo-select2-active',
14875                 html: 'Loading more results...'
14876             });
14877             
14878             this.loading = this.list.select('.loading', true).first();
14879             
14880             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14881             
14882             this.loading.hide();
14883         }
14884         
14885         this.loading.show();
14886         
14887         var _combo = this;
14888         
14889         this.page++;
14890         this.loadNext = true;
14891         
14892         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14893         
14894         return;
14895     },
14896     
14897     addItem : function(o)
14898     {   
14899         var dv = ''; // display value
14900         
14901         if (this.displayField) {
14902             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14903         } else {
14904             // this is an error condition!!!
14905             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14906         }
14907         
14908         if(!dv.length){
14909             return;
14910         }
14911         
14912         var choice = this.choices.createChild({
14913             tag: 'li',
14914             cls: 'roo-select2-search-choice',
14915             cn: [
14916                 {
14917                     tag: 'div',
14918                     html: dv
14919                 },
14920                 {
14921                     tag: 'a',
14922                     href: '#',
14923                     cls: 'roo-select2-search-choice-close fa fa-times',
14924                     tabindex: '-1'
14925                 }
14926             ]
14927             
14928         }, this.searchField);
14929         
14930         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14931         
14932         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14933         
14934         this.item.push(o);
14935         
14936         this.lastData = o;
14937         
14938         this.syncValue();
14939         
14940         this.inputEl().dom.value = '';
14941         
14942         this.validate();
14943     },
14944     
14945     onRemoveItem : function(e, _self, o)
14946     {
14947         e.preventDefault();
14948         
14949         this.lastItem = Roo.apply([], this.item);
14950         
14951         var index = this.item.indexOf(o.data) * 1;
14952         
14953         if( index < 0){
14954             Roo.log('not this item?!');
14955             return;
14956         }
14957         
14958         this.item.splice(index, 1);
14959         o.item.remove();
14960         
14961         this.syncValue();
14962         
14963         this.fireEvent('remove', this, e);
14964         
14965         this.validate();
14966         
14967     },
14968     
14969     syncValue : function()
14970     {
14971         if(!this.item.length){
14972             this.clearValue();
14973             return;
14974         }
14975             
14976         var value = [];
14977         var _this = this;
14978         Roo.each(this.item, function(i){
14979             if(_this.valueField){
14980                 value.push(i[_this.valueField]);
14981                 return;
14982             }
14983
14984             value.push(i);
14985         });
14986
14987         this.value = value.join(',');
14988
14989         if(this.hiddenField){
14990             this.hiddenField.dom.value = this.value;
14991         }
14992         
14993         this.store.fireEvent("datachanged", this.store);
14994         
14995         this.validate();
14996     },
14997     
14998     clearItem : function()
14999     {
15000         if(!this.multiple){
15001             return;
15002         }
15003         
15004         this.item = [];
15005         
15006         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15007            c.remove();
15008         });
15009         
15010         this.syncValue();
15011         
15012         this.validate();
15013         
15014         if(this.tickable && !Roo.isTouch){
15015             this.view.refresh();
15016         }
15017     },
15018     
15019     inputEl: function ()
15020     {
15021         if(Roo.isIOS && this.useNativeIOS){
15022             return this.el.select('select.roo-ios-select', true).first();
15023         }
15024         
15025         if(Roo.isTouch && this.mobileTouchView){
15026             return this.el.select('input.form-control',true).first();
15027         }
15028         
15029         if(this.tickable){
15030             return this.searchField;
15031         }
15032         
15033         return this.el.select('input.form-control',true).first();
15034     },
15035     
15036     onTickableFooterButtonClick : function(e, btn, el)
15037     {
15038         e.preventDefault();
15039         
15040         this.lastItem = Roo.apply([], this.item);
15041         
15042         if(btn && btn.name == 'cancel'){
15043             this.tickItems = Roo.apply([], this.item);
15044             this.collapse();
15045             return;
15046         }
15047         
15048         this.clearItem();
15049         
15050         var _this = this;
15051         
15052         Roo.each(this.tickItems, function(o){
15053             _this.addItem(o);
15054         });
15055         
15056         this.collapse();
15057         
15058     },
15059     
15060     validate : function()
15061     {
15062         if(this.getVisibilityEl().hasClass('hidden')){
15063             return true;
15064         }
15065         
15066         var v = this.getRawValue();
15067         
15068         if(this.multiple){
15069             v = this.getValue();
15070         }
15071         
15072         if(this.disabled || this.allowBlank || v.length){
15073             this.markValid();
15074             return true;
15075         }
15076         
15077         this.markInvalid();
15078         return false;
15079     },
15080     
15081     tickableInputEl : function()
15082     {
15083         if(!this.tickable || !this.editable){
15084             return this.inputEl();
15085         }
15086         
15087         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15088     },
15089     
15090     
15091     getAutoCreateTouchView : function()
15092     {
15093         var id = Roo.id();
15094         
15095         var cfg = {
15096             cls: 'form-group' //input-group
15097         };
15098         
15099         var input =  {
15100             tag: 'input',
15101             id : id,
15102             type : this.inputType,
15103             cls : 'form-control x-combo-noedit',
15104             autocomplete: 'new-password',
15105             placeholder : this.placeholder || '',
15106             readonly : true
15107         };
15108         
15109         if (this.name) {
15110             input.name = this.name;
15111         }
15112         
15113         if (this.size) {
15114             input.cls += ' input-' + this.size;
15115         }
15116         
15117         if (this.disabled) {
15118             input.disabled = true;
15119         }
15120         
15121         var inputblock = {
15122             cls : '',
15123             cn : [
15124                 input
15125             ]
15126         };
15127         
15128         if(this.before){
15129             inputblock.cls += ' input-group';
15130             
15131             inputblock.cn.unshift({
15132                 tag :'span',
15133                 cls : 'input-group-addon input-group-prepend input-group-text',
15134                 html : this.before
15135             });
15136         }
15137         
15138         if(this.removable && !this.multiple){
15139             inputblock.cls += ' roo-removable';
15140             
15141             inputblock.cn.push({
15142                 tag: 'button',
15143                 html : 'x',
15144                 cls : 'roo-combo-removable-btn close'
15145             });
15146         }
15147
15148         if(this.hasFeedback && !this.allowBlank){
15149             
15150             inputblock.cls += ' has-feedback';
15151             
15152             inputblock.cn.push({
15153                 tag: 'span',
15154                 cls: 'glyphicon form-control-feedback'
15155             });
15156             
15157         }
15158         
15159         if (this.after) {
15160             
15161             inputblock.cls += (this.before) ? '' : ' input-group';
15162             
15163             inputblock.cn.push({
15164                 tag :'span',
15165                 cls : 'input-group-addon input-group-append input-group-text',
15166                 html : this.after
15167             });
15168         }
15169
15170         
15171         var ibwrap = inputblock;
15172         
15173         if(this.multiple){
15174             ibwrap = {
15175                 tag: 'ul',
15176                 cls: 'roo-select2-choices',
15177                 cn:[
15178                     {
15179                         tag: 'li',
15180                         cls: 'roo-select2-search-field',
15181                         cn: [
15182
15183                             inputblock
15184                         ]
15185                     }
15186                 ]
15187             };
15188         
15189             
15190         }
15191         
15192         var combobox = {
15193             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15194             cn: [
15195                 {
15196                     tag: 'input',
15197                     type : 'hidden',
15198                     cls: 'form-hidden-field'
15199                 },
15200                 ibwrap
15201             ]
15202         };
15203         
15204         if(!this.multiple && this.showToggleBtn){
15205             
15206             var caret = {
15207                         tag: 'span',
15208                         cls: 'caret'
15209             };
15210             
15211             if (this.caret != false) {
15212                 caret = {
15213                      tag: 'i',
15214                      cls: 'fa fa-' + this.caret
15215                 };
15216                 
15217             }
15218             
15219             combobox.cn.push({
15220                 tag :'span',
15221                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15222                 cn : [
15223                     caret,
15224                     {
15225                         tag: 'span',
15226                         cls: 'combobox-clear',
15227                         cn  : [
15228                             {
15229                                 tag : 'i',
15230                                 cls: 'icon-remove'
15231                             }
15232                         ]
15233                     }
15234                 ]
15235
15236             })
15237         }
15238         
15239         if(this.multiple){
15240             combobox.cls += ' roo-select2-container-multi';
15241         }
15242         
15243         var align = this.labelAlign || this.parentLabelAlign();
15244         
15245         if (align ==='left' && this.fieldLabel.length) {
15246
15247             cfg.cn = [
15248                 {
15249                    tag : 'i',
15250                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15251                    tooltip : 'This field is required'
15252                 },
15253                 {
15254                     tag: 'label',
15255                     cls : 'control-label col-form-label',
15256                     html : this.fieldLabel
15257
15258                 },
15259                 {
15260                     cls : '', 
15261                     cn: [
15262                         combobox
15263                     ]
15264                 }
15265             ];
15266             
15267             var labelCfg = cfg.cn[1];
15268             var contentCfg = cfg.cn[2];
15269             
15270
15271             if(this.indicatorpos == 'right'){
15272                 cfg.cn = [
15273                     {
15274                         tag: 'label',
15275                         'for' :  id,
15276                         cls : 'control-label col-form-label',
15277                         cn : [
15278                             {
15279                                 tag : 'span',
15280                                 html : this.fieldLabel
15281                             },
15282                             {
15283                                 tag : 'i',
15284                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15285                                 tooltip : 'This field is required'
15286                             }
15287                         ]
15288                     },
15289                     {
15290                         cls : "",
15291                         cn: [
15292                             combobox
15293                         ]
15294                     }
15295
15296                 ];
15297                 
15298                 labelCfg = cfg.cn[0];
15299                 contentCfg = cfg.cn[1];
15300             }
15301             
15302            
15303             
15304             if(this.labelWidth > 12){
15305                 labelCfg.style = "width: " + this.labelWidth + 'px';
15306             }
15307             
15308             if(this.labelWidth < 13 && this.labelmd == 0){
15309                 this.labelmd = this.labelWidth;
15310             }
15311             
15312             if(this.labellg > 0){
15313                 labelCfg.cls += ' col-lg-' + this.labellg;
15314                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15315             }
15316             
15317             if(this.labelmd > 0){
15318                 labelCfg.cls += ' col-md-' + this.labelmd;
15319                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15320             }
15321             
15322             if(this.labelsm > 0){
15323                 labelCfg.cls += ' col-sm-' + this.labelsm;
15324                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15325             }
15326             
15327             if(this.labelxs > 0){
15328                 labelCfg.cls += ' col-xs-' + this.labelxs;
15329                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15330             }
15331                 
15332                 
15333         } else if ( this.fieldLabel.length) {
15334             cfg.cn = [
15335                 {
15336                    tag : 'i',
15337                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15338                    tooltip : 'This field is required'
15339                 },
15340                 {
15341                     tag: 'label',
15342                     cls : 'control-label',
15343                     html : this.fieldLabel
15344
15345                 },
15346                 {
15347                     cls : '', 
15348                     cn: [
15349                         combobox
15350                     ]
15351                 }
15352             ];
15353             
15354             if(this.indicatorpos == 'right'){
15355                 cfg.cn = [
15356                     {
15357                         tag: 'label',
15358                         cls : 'control-label',
15359                         html : this.fieldLabel,
15360                         cn : [
15361                             {
15362                                tag : 'i',
15363                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15364                                tooltip : 'This field is required'
15365                             }
15366                         ]
15367                     },
15368                     {
15369                         cls : '', 
15370                         cn: [
15371                             combobox
15372                         ]
15373                     }
15374                 ];
15375             }
15376         } else {
15377             cfg.cn = combobox;    
15378         }
15379         
15380         
15381         var settings = this;
15382         
15383         ['xs','sm','md','lg'].map(function(size){
15384             if (settings[size]) {
15385                 cfg.cls += ' col-' + size + '-' + settings[size];
15386             }
15387         });
15388         
15389         return cfg;
15390     },
15391     
15392     initTouchView : function()
15393     {
15394         this.renderTouchView();
15395         
15396         this.touchViewEl.on('scroll', function(){
15397             this.el.dom.scrollTop = 0;
15398         }, this);
15399         
15400         this.originalValue = this.getValue();
15401         
15402         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15403         
15404         this.inputEl().on("click", this.showTouchView, this);
15405         if (this.triggerEl) {
15406             this.triggerEl.on("click", this.showTouchView, this);
15407         }
15408         
15409         
15410         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15411         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15412         
15413         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15414         
15415         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15416         this.store.on('load', this.onTouchViewLoad, this);
15417         this.store.on('loadexception', this.onTouchViewLoadException, this);
15418         
15419         if(this.hiddenName){
15420             
15421             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15422             
15423             this.hiddenField.dom.value =
15424                 this.hiddenValue !== undefined ? this.hiddenValue :
15425                 this.value !== undefined ? this.value : '';
15426         
15427             this.el.dom.removeAttribute('name');
15428             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15429         }
15430         
15431         if(this.multiple){
15432             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15433             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15434         }
15435         
15436         if(this.removable && !this.multiple){
15437             var close = this.closeTriggerEl();
15438             if(close){
15439                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15440                 close.on('click', this.removeBtnClick, this, close);
15441             }
15442         }
15443         /*
15444          * fix the bug in Safari iOS8
15445          */
15446         this.inputEl().on("focus", function(e){
15447             document.activeElement.blur();
15448         }, this);
15449         
15450         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15451         
15452         return;
15453         
15454         
15455     },
15456     
15457     renderTouchView : function()
15458     {
15459         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15460         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15461         
15462         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15463         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15464         
15465         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15466         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15467         this.touchViewBodyEl.setStyle('overflow', 'auto');
15468         
15469         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15470         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15471         
15472         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15473         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15474         
15475     },
15476     
15477     showTouchView : function()
15478     {
15479         if(this.disabled){
15480             return;
15481         }
15482         
15483         this.touchViewHeaderEl.hide();
15484
15485         if(this.modalTitle.length){
15486             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15487             this.touchViewHeaderEl.show();
15488         }
15489
15490         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15491         this.touchViewEl.show();
15492
15493         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15494         
15495         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15496         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15497
15498         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15499
15500         if(this.modalTitle.length){
15501             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15502         }
15503         
15504         this.touchViewBodyEl.setHeight(bodyHeight);
15505
15506         if(this.animate){
15507             var _this = this;
15508             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15509         }else{
15510             this.touchViewEl.addClass('in');
15511         }
15512         
15513         if(this._touchViewMask){
15514             Roo.get(document.body).addClass("x-body-masked");
15515             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15516             this._touchViewMask.setStyle('z-index', 10000);
15517             this._touchViewMask.addClass('show');
15518         }
15519         
15520         this.doTouchViewQuery();
15521         
15522     },
15523     
15524     hideTouchView : function()
15525     {
15526         this.touchViewEl.removeClass('in');
15527
15528         if(this.animate){
15529             var _this = this;
15530             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15531         }else{
15532             this.touchViewEl.setStyle('display', 'none');
15533         }
15534         
15535         if(this._touchViewMask){
15536             this._touchViewMask.removeClass('show');
15537             Roo.get(document.body).removeClass("x-body-masked");
15538         }
15539     },
15540     
15541     setTouchViewValue : function()
15542     {
15543         if(this.multiple){
15544             this.clearItem();
15545         
15546             var _this = this;
15547
15548             Roo.each(this.tickItems, function(o){
15549                 this.addItem(o);
15550             }, this);
15551         }
15552         
15553         this.hideTouchView();
15554     },
15555     
15556     doTouchViewQuery : function()
15557     {
15558         var qe = {
15559             query: '',
15560             forceAll: true,
15561             combo: this,
15562             cancel:false
15563         };
15564         
15565         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15566             return false;
15567         }
15568         
15569         if(!this.alwaysQuery || this.mode == 'local'){
15570             this.onTouchViewLoad();
15571             return;
15572         }
15573         
15574         this.store.load();
15575     },
15576     
15577     onTouchViewBeforeLoad : function(combo,opts)
15578     {
15579         return;
15580     },
15581
15582     // private
15583     onTouchViewLoad : function()
15584     {
15585         if(this.store.getCount() < 1){
15586             this.onTouchViewEmptyResults();
15587             return;
15588         }
15589         
15590         this.clearTouchView();
15591         
15592         var rawValue = this.getRawValue();
15593         
15594         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15595         
15596         this.tickItems = [];
15597         
15598         this.store.data.each(function(d, rowIndex){
15599             var row = this.touchViewListGroup.createChild(template);
15600             
15601             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15602                 row.addClass(d.data.cls);
15603             }
15604             
15605             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15606                 var cfg = {
15607                     data : d.data,
15608                     html : d.data[this.displayField]
15609                 };
15610                 
15611                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15612                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15613                 }
15614             }
15615             row.removeClass('selected');
15616             if(!this.multiple && this.valueField &&
15617                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15618             {
15619                 // radio buttons..
15620                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15621                 row.addClass('selected');
15622             }
15623             
15624             if(this.multiple && this.valueField &&
15625                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15626             {
15627                 
15628                 // checkboxes...
15629                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15630                 this.tickItems.push(d.data);
15631             }
15632             
15633             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15634             
15635         }, this);
15636         
15637         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15638         
15639         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15640
15641         if(this.modalTitle.length){
15642             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15643         }
15644
15645         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15646         
15647         if(this.mobile_restrict_height && listHeight < bodyHeight){
15648             this.touchViewBodyEl.setHeight(listHeight);
15649         }
15650         
15651         var _this = this;
15652         
15653         if(firstChecked && listHeight > bodyHeight){
15654             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15655         }
15656         
15657     },
15658     
15659     onTouchViewLoadException : function()
15660     {
15661         this.hideTouchView();
15662     },
15663     
15664     onTouchViewEmptyResults : function()
15665     {
15666         this.clearTouchView();
15667         
15668         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15669         
15670         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15671         
15672     },
15673     
15674     clearTouchView : function()
15675     {
15676         this.touchViewListGroup.dom.innerHTML = '';
15677     },
15678     
15679     onTouchViewClick : function(e, el, o)
15680     {
15681         e.preventDefault();
15682         
15683         var row = o.row;
15684         var rowIndex = o.rowIndex;
15685         
15686         var r = this.store.getAt(rowIndex);
15687         
15688         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15689             
15690             if(!this.multiple){
15691                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15692                     c.dom.removeAttribute('checked');
15693                 }, this);
15694
15695                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15696
15697                 this.setFromData(r.data);
15698
15699                 var close = this.closeTriggerEl();
15700
15701                 if(close){
15702                     close.show();
15703                 }
15704
15705                 this.hideTouchView();
15706
15707                 this.fireEvent('select', this, r, rowIndex);
15708
15709                 return;
15710             }
15711
15712             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15713                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15714                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15715                 return;
15716             }
15717
15718             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15719             this.addItem(r.data);
15720             this.tickItems.push(r.data);
15721         }
15722     },
15723     
15724     getAutoCreateNativeIOS : function()
15725     {
15726         var cfg = {
15727             cls: 'form-group' //input-group,
15728         };
15729         
15730         var combobox =  {
15731             tag: 'select',
15732             cls : 'roo-ios-select'
15733         };
15734         
15735         if (this.name) {
15736             combobox.name = this.name;
15737         }
15738         
15739         if (this.disabled) {
15740             combobox.disabled = true;
15741         }
15742         
15743         var settings = this;
15744         
15745         ['xs','sm','md','lg'].map(function(size){
15746             if (settings[size]) {
15747                 cfg.cls += ' col-' + size + '-' + settings[size];
15748             }
15749         });
15750         
15751         cfg.cn = combobox;
15752         
15753         return cfg;
15754         
15755     },
15756     
15757     initIOSView : function()
15758     {
15759         this.store.on('load', this.onIOSViewLoad, this);
15760         
15761         return;
15762     },
15763     
15764     onIOSViewLoad : function()
15765     {
15766         if(this.store.getCount() < 1){
15767             return;
15768         }
15769         
15770         this.clearIOSView();
15771         
15772         if(this.allowBlank) {
15773             
15774             var default_text = '-- SELECT --';
15775             
15776             if(this.placeholder.length){
15777                 default_text = this.placeholder;
15778             }
15779             
15780             if(this.emptyTitle.length){
15781                 default_text += ' - ' + this.emptyTitle + ' -';
15782             }
15783             
15784             var opt = this.inputEl().createChild({
15785                 tag: 'option',
15786                 value : 0,
15787                 html : default_text
15788             });
15789             
15790             var o = {};
15791             o[this.valueField] = 0;
15792             o[this.displayField] = default_text;
15793             
15794             this.ios_options.push({
15795                 data : o,
15796                 el : opt
15797             });
15798             
15799         }
15800         
15801         this.store.data.each(function(d, rowIndex){
15802             
15803             var html = '';
15804             
15805             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15806                 html = d.data[this.displayField];
15807             }
15808             
15809             var value = '';
15810             
15811             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15812                 value = d.data[this.valueField];
15813             }
15814             
15815             var option = {
15816                 tag: 'option',
15817                 value : value,
15818                 html : html
15819             };
15820             
15821             if(this.value == d.data[this.valueField]){
15822                 option['selected'] = true;
15823             }
15824             
15825             var opt = this.inputEl().createChild(option);
15826             
15827             this.ios_options.push({
15828                 data : d.data,
15829                 el : opt
15830             });
15831             
15832         }, this);
15833         
15834         this.inputEl().on('change', function(){
15835            this.fireEvent('select', this);
15836         }, this);
15837         
15838     },
15839     
15840     clearIOSView: function()
15841     {
15842         this.inputEl().dom.innerHTML = '';
15843         
15844         this.ios_options = [];
15845     },
15846     
15847     setIOSValue: function(v)
15848     {
15849         this.value = v;
15850         
15851         if(!this.ios_options){
15852             return;
15853         }
15854         
15855         Roo.each(this.ios_options, function(opts){
15856            
15857            opts.el.dom.removeAttribute('selected');
15858            
15859            if(opts.data[this.valueField] != v){
15860                return;
15861            }
15862            
15863            opts.el.dom.setAttribute('selected', true);
15864            
15865         }, this);
15866     }
15867
15868     /** 
15869     * @cfg {Boolean} grow 
15870     * @hide 
15871     */
15872     /** 
15873     * @cfg {Number} growMin 
15874     * @hide 
15875     */
15876     /** 
15877     * @cfg {Number} growMax 
15878     * @hide 
15879     */
15880     /**
15881      * @hide
15882      * @method autoSize
15883      */
15884 });
15885
15886 Roo.apply(Roo.bootstrap.ComboBox,  {
15887     
15888     header : {
15889         tag: 'div',
15890         cls: 'modal-header',
15891         cn: [
15892             {
15893                 tag: 'h4',
15894                 cls: 'modal-title'
15895             }
15896         ]
15897     },
15898     
15899     body : {
15900         tag: 'div',
15901         cls: 'modal-body',
15902         cn: [
15903             {
15904                 tag: 'ul',
15905                 cls: 'list-group'
15906             }
15907         ]
15908     },
15909     
15910     listItemRadio : {
15911         tag: 'li',
15912         cls: 'list-group-item',
15913         cn: [
15914             {
15915                 tag: 'span',
15916                 cls: 'roo-combobox-list-group-item-value'
15917             },
15918             {
15919                 tag: 'div',
15920                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15921                 cn: [
15922                     {
15923                         tag: 'input',
15924                         type: 'radio'
15925                     },
15926                     {
15927                         tag: 'label'
15928                     }
15929                 ]
15930             }
15931         ]
15932     },
15933     
15934     listItemCheckbox : {
15935         tag: 'li',
15936         cls: 'list-group-item',
15937         cn: [
15938             {
15939                 tag: 'span',
15940                 cls: 'roo-combobox-list-group-item-value'
15941             },
15942             {
15943                 tag: 'div',
15944                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15945                 cn: [
15946                     {
15947                         tag: 'input',
15948                         type: 'checkbox'
15949                     },
15950                     {
15951                         tag: 'label'
15952                     }
15953                 ]
15954             }
15955         ]
15956     },
15957     
15958     emptyResult : {
15959         tag: 'div',
15960         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15961     },
15962     
15963     footer : {
15964         tag: 'div',
15965         cls: 'modal-footer',
15966         cn: [
15967             {
15968                 tag: 'div',
15969                 cls: 'row',
15970                 cn: [
15971                     {
15972                         tag: 'div',
15973                         cls: 'col-xs-6 text-left',
15974                         cn: {
15975                             tag: 'button',
15976                             cls: 'btn btn-danger roo-touch-view-cancel',
15977                             html: 'Cancel'
15978                         }
15979                     },
15980                     {
15981                         tag: 'div',
15982                         cls: 'col-xs-6 text-right',
15983                         cn: {
15984                             tag: 'button',
15985                             cls: 'btn btn-success roo-touch-view-ok',
15986                             html: 'OK'
15987                         }
15988                     }
15989                 ]
15990             }
15991         ]
15992         
15993     }
15994 });
15995
15996 Roo.apply(Roo.bootstrap.ComboBox,  {
15997     
15998     touchViewTemplate : {
15999         tag: 'div',
16000         cls: 'modal fade roo-combobox-touch-view',
16001         cn: [
16002             {
16003                 tag: 'div',
16004                 cls: 'modal-dialog',
16005                 style : 'position:fixed', // we have to fix position....
16006                 cn: [
16007                     {
16008                         tag: 'div',
16009                         cls: 'modal-content',
16010                         cn: [
16011                             Roo.bootstrap.ComboBox.header,
16012                             Roo.bootstrap.ComboBox.body,
16013                             Roo.bootstrap.ComboBox.footer
16014                         ]
16015                     }
16016                 ]
16017             }
16018         ]
16019     }
16020 });/*
16021  * Based on:
16022  * Ext JS Library 1.1.1
16023  * Copyright(c) 2006-2007, Ext JS, LLC.
16024  *
16025  * Originally Released Under LGPL - original licence link has changed is not relivant.
16026  *
16027  * Fork - LGPL
16028  * <script type="text/javascript">
16029  */
16030
16031 /**
16032  * @class Roo.View
16033  * @extends Roo.util.Observable
16034  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16035  * This class also supports single and multi selection modes. <br>
16036  * Create a data model bound view:
16037  <pre><code>
16038  var store = new Roo.data.Store(...);
16039
16040  var view = new Roo.View({
16041     el : "my-element",
16042     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16043  
16044     singleSelect: true,
16045     selectedClass: "ydataview-selected",
16046     store: store
16047  });
16048
16049  // listen for node click?
16050  view.on("click", function(vw, index, node, e){
16051  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16052  });
16053
16054  // load XML data
16055  dataModel.load("foobar.xml");
16056  </code></pre>
16057  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16058  * <br><br>
16059  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16060  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16061  * 
16062  * Note: old style constructor is still suported (container, template, config)
16063  * 
16064  * @constructor
16065  * Create a new View
16066  * @param {Object} config The config object
16067  * 
16068  */
16069 Roo.View = function(config, depreciated_tpl, depreciated_config){
16070     
16071     this.parent = false;
16072     
16073     if (typeof(depreciated_tpl) == 'undefined') {
16074         // new way.. - universal constructor.
16075         Roo.apply(this, config);
16076         this.el  = Roo.get(this.el);
16077     } else {
16078         // old format..
16079         this.el  = Roo.get(config);
16080         this.tpl = depreciated_tpl;
16081         Roo.apply(this, depreciated_config);
16082     }
16083     this.wrapEl  = this.el.wrap().wrap();
16084     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16085     
16086     
16087     if(typeof(this.tpl) == "string"){
16088         this.tpl = new Roo.Template(this.tpl);
16089     } else {
16090         // support xtype ctors..
16091         this.tpl = new Roo.factory(this.tpl, Roo);
16092     }
16093     
16094     
16095     this.tpl.compile();
16096     
16097     /** @private */
16098     this.addEvents({
16099         /**
16100          * @event beforeclick
16101          * Fires before a click is processed. Returns false to cancel the default action.
16102          * @param {Roo.View} this
16103          * @param {Number} index The index of the target node
16104          * @param {HTMLElement} node The target node
16105          * @param {Roo.EventObject} e The raw event object
16106          */
16107             "beforeclick" : true,
16108         /**
16109          * @event click
16110          * Fires when a template node is clicked.
16111          * @param {Roo.View} this
16112          * @param {Number} index The index of the target node
16113          * @param {HTMLElement} node The target node
16114          * @param {Roo.EventObject} e The raw event object
16115          */
16116             "click" : true,
16117         /**
16118          * @event dblclick
16119          * Fires when a template node is double clicked.
16120          * @param {Roo.View} this
16121          * @param {Number} index The index of the target node
16122          * @param {HTMLElement} node The target node
16123          * @param {Roo.EventObject} e The raw event object
16124          */
16125             "dblclick" : true,
16126         /**
16127          * @event contextmenu
16128          * Fires when a template node is right clicked.
16129          * @param {Roo.View} this
16130          * @param {Number} index The index of the target node
16131          * @param {HTMLElement} node The target node
16132          * @param {Roo.EventObject} e The raw event object
16133          */
16134             "contextmenu" : true,
16135         /**
16136          * @event selectionchange
16137          * Fires when the selected nodes change.
16138          * @param {Roo.View} this
16139          * @param {Array} selections Array of the selected nodes
16140          */
16141             "selectionchange" : true,
16142     
16143         /**
16144          * @event beforeselect
16145          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16146          * @param {Roo.View} this
16147          * @param {HTMLElement} node The node to be selected
16148          * @param {Array} selections Array of currently selected nodes
16149          */
16150             "beforeselect" : true,
16151         /**
16152          * @event preparedata
16153          * Fires on every row to render, to allow you to change the data.
16154          * @param {Roo.View} this
16155          * @param {Object} data to be rendered (change this)
16156          */
16157           "preparedata" : true
16158           
16159           
16160         });
16161
16162
16163
16164     this.el.on({
16165         "click": this.onClick,
16166         "dblclick": this.onDblClick,
16167         "contextmenu": this.onContextMenu,
16168         scope:this
16169     });
16170
16171     this.selections = [];
16172     this.nodes = [];
16173     this.cmp = new Roo.CompositeElementLite([]);
16174     if(this.store){
16175         this.store = Roo.factory(this.store, Roo.data);
16176         this.setStore(this.store, true);
16177     }
16178     
16179     if ( this.footer && this.footer.xtype) {
16180            
16181          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16182         
16183         this.footer.dataSource = this.store;
16184         this.footer.container = fctr;
16185         this.footer = Roo.factory(this.footer, Roo);
16186         fctr.insertFirst(this.el);
16187         
16188         // this is a bit insane - as the paging toolbar seems to detach the el..
16189 //        dom.parentNode.parentNode.parentNode
16190          // they get detached?
16191     }
16192     
16193     
16194     Roo.View.superclass.constructor.call(this);
16195     
16196     
16197 };
16198
16199 Roo.extend(Roo.View, Roo.util.Observable, {
16200     
16201      /**
16202      * @cfg {Roo.data.Store} store Data store to load data from.
16203      */
16204     store : false,
16205     
16206     /**
16207      * @cfg {String|Roo.Element} el The container element.
16208      */
16209     el : '',
16210     
16211     /**
16212      * @cfg {String|Roo.Template} tpl The template used by this View 
16213      */
16214     tpl : false,
16215     /**
16216      * @cfg {String} dataName the named area of the template to use as the data area
16217      *                          Works with domtemplates roo-name="name"
16218      */
16219     dataName: false,
16220     /**
16221      * @cfg {String} selectedClass The css class to add to selected nodes
16222      */
16223     selectedClass : "x-view-selected",
16224      /**
16225      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16226      */
16227     emptyText : "",
16228     
16229     /**
16230      * @cfg {String} text to display on mask (default Loading)
16231      */
16232     mask : false,
16233     /**
16234      * @cfg {Boolean} multiSelect Allow multiple selection
16235      */
16236     multiSelect : false,
16237     /**
16238      * @cfg {Boolean} singleSelect Allow single selection
16239      */
16240     singleSelect:  false,
16241     
16242     /**
16243      * @cfg {Boolean} toggleSelect - selecting 
16244      */
16245     toggleSelect : false,
16246     
16247     /**
16248      * @cfg {Boolean} tickable - selecting 
16249      */
16250     tickable : false,
16251     
16252     /**
16253      * Returns the element this view is bound to.
16254      * @return {Roo.Element}
16255      */
16256     getEl : function(){
16257         return this.wrapEl;
16258     },
16259     
16260     
16261
16262     /**
16263      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16264      */
16265     refresh : function(){
16266         //Roo.log('refresh');
16267         var t = this.tpl;
16268         
16269         // if we are using something like 'domtemplate', then
16270         // the what gets used is:
16271         // t.applySubtemplate(NAME, data, wrapping data..)
16272         // the outer template then get' applied with
16273         //     the store 'extra data'
16274         // and the body get's added to the
16275         //      roo-name="data" node?
16276         //      <span class='roo-tpl-{name}'></span> ?????
16277         
16278         
16279         
16280         this.clearSelections();
16281         this.el.update("");
16282         var html = [];
16283         var records = this.store.getRange();
16284         if(records.length < 1) {
16285             
16286             // is this valid??  = should it render a template??
16287             
16288             this.el.update(this.emptyText);
16289             return;
16290         }
16291         var el = this.el;
16292         if (this.dataName) {
16293             this.el.update(t.apply(this.store.meta)); //????
16294             el = this.el.child('.roo-tpl-' + this.dataName);
16295         }
16296         
16297         for(var i = 0, len = records.length; i < len; i++){
16298             var data = this.prepareData(records[i].data, i, records[i]);
16299             this.fireEvent("preparedata", this, data, i, records[i]);
16300             
16301             var d = Roo.apply({}, data);
16302             
16303             if(this.tickable){
16304                 Roo.apply(d, {'roo-id' : Roo.id()});
16305                 
16306                 var _this = this;
16307             
16308                 Roo.each(this.parent.item, function(item){
16309                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16310                         return;
16311                     }
16312                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16313                 });
16314             }
16315             
16316             html[html.length] = Roo.util.Format.trim(
16317                 this.dataName ?
16318                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16319                     t.apply(d)
16320             );
16321         }
16322         
16323         
16324         
16325         el.update(html.join(""));
16326         this.nodes = el.dom.childNodes;
16327         this.updateIndexes(0);
16328     },
16329     
16330
16331     /**
16332      * Function to override to reformat the data that is sent to
16333      * the template for each node.
16334      * DEPRICATED - use the preparedata event handler.
16335      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16336      * a JSON object for an UpdateManager bound view).
16337      */
16338     prepareData : function(data, index, record)
16339     {
16340         this.fireEvent("preparedata", this, data, index, record);
16341         return data;
16342     },
16343
16344     onUpdate : function(ds, record){
16345         // Roo.log('on update');   
16346         this.clearSelections();
16347         var index = this.store.indexOf(record);
16348         var n = this.nodes[index];
16349         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16350         n.parentNode.removeChild(n);
16351         this.updateIndexes(index, index);
16352     },
16353
16354     
16355     
16356 // --------- FIXME     
16357     onAdd : function(ds, records, index)
16358     {
16359         //Roo.log(['on Add', ds, records, index] );        
16360         this.clearSelections();
16361         if(this.nodes.length == 0){
16362             this.refresh();
16363             return;
16364         }
16365         var n = this.nodes[index];
16366         for(var i = 0, len = records.length; i < len; i++){
16367             var d = this.prepareData(records[i].data, i, records[i]);
16368             if(n){
16369                 this.tpl.insertBefore(n, d);
16370             }else{
16371                 
16372                 this.tpl.append(this.el, d);
16373             }
16374         }
16375         this.updateIndexes(index);
16376     },
16377
16378     onRemove : function(ds, record, index){
16379        // Roo.log('onRemove');
16380         this.clearSelections();
16381         var el = this.dataName  ?
16382             this.el.child('.roo-tpl-' + this.dataName) :
16383             this.el; 
16384         
16385         el.dom.removeChild(this.nodes[index]);
16386         this.updateIndexes(index);
16387     },
16388
16389     /**
16390      * Refresh an individual node.
16391      * @param {Number} index
16392      */
16393     refreshNode : function(index){
16394         this.onUpdate(this.store, this.store.getAt(index));
16395     },
16396
16397     updateIndexes : function(startIndex, endIndex){
16398         var ns = this.nodes;
16399         startIndex = startIndex || 0;
16400         endIndex = endIndex || ns.length - 1;
16401         for(var i = startIndex; i <= endIndex; i++){
16402             ns[i].nodeIndex = i;
16403         }
16404     },
16405
16406     /**
16407      * Changes the data store this view uses and refresh the view.
16408      * @param {Store} store
16409      */
16410     setStore : function(store, initial){
16411         if(!initial && this.store){
16412             this.store.un("datachanged", this.refresh);
16413             this.store.un("add", this.onAdd);
16414             this.store.un("remove", this.onRemove);
16415             this.store.un("update", this.onUpdate);
16416             this.store.un("clear", this.refresh);
16417             this.store.un("beforeload", this.onBeforeLoad);
16418             this.store.un("load", this.onLoad);
16419             this.store.un("loadexception", this.onLoad);
16420         }
16421         if(store){
16422           
16423             store.on("datachanged", this.refresh, this);
16424             store.on("add", this.onAdd, this);
16425             store.on("remove", this.onRemove, this);
16426             store.on("update", this.onUpdate, this);
16427             store.on("clear", this.refresh, this);
16428             store.on("beforeload", this.onBeforeLoad, this);
16429             store.on("load", this.onLoad, this);
16430             store.on("loadexception", this.onLoad, this);
16431         }
16432         
16433         if(store){
16434             this.refresh();
16435         }
16436     },
16437     /**
16438      * onbeforeLoad - masks the loading area.
16439      *
16440      */
16441     onBeforeLoad : function(store,opts)
16442     {
16443          //Roo.log('onBeforeLoad');   
16444         if (!opts.add) {
16445             this.el.update("");
16446         }
16447         this.el.mask(this.mask ? this.mask : "Loading" ); 
16448     },
16449     onLoad : function ()
16450     {
16451         this.el.unmask();
16452     },
16453     
16454
16455     /**
16456      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16457      * @param {HTMLElement} node
16458      * @return {HTMLElement} The template node
16459      */
16460     findItemFromChild : function(node){
16461         var el = this.dataName  ?
16462             this.el.child('.roo-tpl-' + this.dataName,true) :
16463             this.el.dom; 
16464         
16465         if(!node || node.parentNode == el){
16466                     return node;
16467             }
16468             var p = node.parentNode;
16469             while(p && p != el){
16470             if(p.parentNode == el){
16471                 return p;
16472             }
16473             p = p.parentNode;
16474         }
16475             return null;
16476     },
16477
16478     /** @ignore */
16479     onClick : function(e){
16480         var item = this.findItemFromChild(e.getTarget());
16481         if(item){
16482             var index = this.indexOf(item);
16483             if(this.onItemClick(item, index, e) !== false){
16484                 this.fireEvent("click", this, index, item, e);
16485             }
16486         }else{
16487             this.clearSelections();
16488         }
16489     },
16490
16491     /** @ignore */
16492     onContextMenu : function(e){
16493         var item = this.findItemFromChild(e.getTarget());
16494         if(item){
16495             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16496         }
16497     },
16498
16499     /** @ignore */
16500     onDblClick : function(e){
16501         var item = this.findItemFromChild(e.getTarget());
16502         if(item){
16503             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16504         }
16505     },
16506
16507     onItemClick : function(item, index, e)
16508     {
16509         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16510             return false;
16511         }
16512         if (this.toggleSelect) {
16513             var m = this.isSelected(item) ? 'unselect' : 'select';
16514             //Roo.log(m);
16515             var _t = this;
16516             _t[m](item, true, false);
16517             return true;
16518         }
16519         if(this.multiSelect || this.singleSelect){
16520             if(this.multiSelect && e.shiftKey && this.lastSelection){
16521                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16522             }else{
16523                 this.select(item, this.multiSelect && e.ctrlKey);
16524                 this.lastSelection = item;
16525             }
16526             
16527             if(!this.tickable){
16528                 e.preventDefault();
16529             }
16530             
16531         }
16532         return true;
16533     },
16534
16535     /**
16536      * Get the number of selected nodes.
16537      * @return {Number}
16538      */
16539     getSelectionCount : function(){
16540         return this.selections.length;
16541     },
16542
16543     /**
16544      * Get the currently selected nodes.
16545      * @return {Array} An array of HTMLElements
16546      */
16547     getSelectedNodes : function(){
16548         return this.selections;
16549     },
16550
16551     /**
16552      * Get the indexes of the selected nodes.
16553      * @return {Array}
16554      */
16555     getSelectedIndexes : function(){
16556         var indexes = [], s = this.selections;
16557         for(var i = 0, len = s.length; i < len; i++){
16558             indexes.push(s[i].nodeIndex);
16559         }
16560         return indexes;
16561     },
16562
16563     /**
16564      * Clear all selections
16565      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16566      */
16567     clearSelections : function(suppressEvent){
16568         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16569             this.cmp.elements = this.selections;
16570             this.cmp.removeClass(this.selectedClass);
16571             this.selections = [];
16572             if(!suppressEvent){
16573                 this.fireEvent("selectionchange", this, this.selections);
16574             }
16575         }
16576     },
16577
16578     /**
16579      * Returns true if the passed node is selected
16580      * @param {HTMLElement/Number} node The node or node index
16581      * @return {Boolean}
16582      */
16583     isSelected : function(node){
16584         var s = this.selections;
16585         if(s.length < 1){
16586             return false;
16587         }
16588         node = this.getNode(node);
16589         return s.indexOf(node) !== -1;
16590     },
16591
16592     /**
16593      * Selects nodes.
16594      * @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
16595      * @param {Boolean} keepExisting (optional) true to keep existing selections
16596      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16597      */
16598     select : function(nodeInfo, keepExisting, suppressEvent){
16599         if(nodeInfo instanceof Array){
16600             if(!keepExisting){
16601                 this.clearSelections(true);
16602             }
16603             for(var i = 0, len = nodeInfo.length; i < len; i++){
16604                 this.select(nodeInfo[i], true, true);
16605             }
16606             return;
16607         } 
16608         var node = this.getNode(nodeInfo);
16609         if(!node || this.isSelected(node)){
16610             return; // already selected.
16611         }
16612         if(!keepExisting){
16613             this.clearSelections(true);
16614         }
16615         
16616         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16617             Roo.fly(node).addClass(this.selectedClass);
16618             this.selections.push(node);
16619             if(!suppressEvent){
16620                 this.fireEvent("selectionchange", this, this.selections);
16621             }
16622         }
16623         
16624         
16625     },
16626       /**
16627      * Unselects nodes.
16628      * @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
16629      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16630      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16631      */
16632     unselect : function(nodeInfo, keepExisting, suppressEvent)
16633     {
16634         if(nodeInfo instanceof Array){
16635             Roo.each(this.selections, function(s) {
16636                 this.unselect(s, nodeInfo);
16637             }, this);
16638             return;
16639         }
16640         var node = this.getNode(nodeInfo);
16641         if(!node || !this.isSelected(node)){
16642             //Roo.log("not selected");
16643             return; // not selected.
16644         }
16645         // fireevent???
16646         var ns = [];
16647         Roo.each(this.selections, function(s) {
16648             if (s == node ) {
16649                 Roo.fly(node).removeClass(this.selectedClass);
16650
16651                 return;
16652             }
16653             ns.push(s);
16654         },this);
16655         
16656         this.selections= ns;
16657         this.fireEvent("selectionchange", this, this.selections);
16658     },
16659
16660     /**
16661      * Gets a template node.
16662      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16663      * @return {HTMLElement} The node or null if it wasn't found
16664      */
16665     getNode : function(nodeInfo){
16666         if(typeof nodeInfo == "string"){
16667             return document.getElementById(nodeInfo);
16668         }else if(typeof nodeInfo == "number"){
16669             return this.nodes[nodeInfo];
16670         }
16671         return nodeInfo;
16672     },
16673
16674     /**
16675      * Gets a range template nodes.
16676      * @param {Number} startIndex
16677      * @param {Number} endIndex
16678      * @return {Array} An array of nodes
16679      */
16680     getNodes : function(start, end){
16681         var ns = this.nodes;
16682         start = start || 0;
16683         end = typeof end == "undefined" ? ns.length - 1 : end;
16684         var nodes = [];
16685         if(start <= end){
16686             for(var i = start; i <= end; i++){
16687                 nodes.push(ns[i]);
16688             }
16689         } else{
16690             for(var i = start; i >= end; i--){
16691                 nodes.push(ns[i]);
16692             }
16693         }
16694         return nodes;
16695     },
16696
16697     /**
16698      * Finds the index of the passed node
16699      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16700      * @return {Number} The index of the node or -1
16701      */
16702     indexOf : function(node){
16703         node = this.getNode(node);
16704         if(typeof node.nodeIndex == "number"){
16705             return node.nodeIndex;
16706         }
16707         var ns = this.nodes;
16708         for(var i = 0, len = ns.length; i < len; i++){
16709             if(ns[i] == node){
16710                 return i;
16711             }
16712         }
16713         return -1;
16714     }
16715 });
16716 /*
16717  * - LGPL
16718  *
16719  * based on jquery fullcalendar
16720  * 
16721  */
16722
16723 Roo.bootstrap = Roo.bootstrap || {};
16724 /**
16725  * @class Roo.bootstrap.Calendar
16726  * @extends Roo.bootstrap.Component
16727  * Bootstrap Calendar class
16728  * @cfg {Boolean} loadMask (true|false) default false
16729  * @cfg {Object} header generate the user specific header of the calendar, default false
16730
16731  * @constructor
16732  * Create a new Container
16733  * @param {Object} config The config object
16734  */
16735
16736
16737
16738 Roo.bootstrap.Calendar = function(config){
16739     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16740      this.addEvents({
16741         /**
16742              * @event select
16743              * Fires when a date is selected
16744              * @param {DatePicker} this
16745              * @param {Date} date The selected date
16746              */
16747         'select': true,
16748         /**
16749              * @event monthchange
16750              * Fires when the displayed month changes 
16751              * @param {DatePicker} this
16752              * @param {Date} date The selected month
16753              */
16754         'monthchange': true,
16755         /**
16756              * @event evententer
16757              * Fires when mouse over an event
16758              * @param {Calendar} this
16759              * @param {event} Event
16760              */
16761         'evententer': true,
16762         /**
16763              * @event eventleave
16764              * Fires when the mouse leaves an
16765              * @param {Calendar} this
16766              * @param {event}
16767              */
16768         'eventleave': true,
16769         /**
16770              * @event eventclick
16771              * Fires when the mouse click an
16772              * @param {Calendar} this
16773              * @param {event}
16774              */
16775         'eventclick': true
16776         
16777     });
16778
16779 };
16780
16781 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16782     
16783      /**
16784      * @cfg {Number} startDay
16785      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16786      */
16787     startDay : 0,
16788     
16789     loadMask : false,
16790     
16791     header : false,
16792       
16793     getAutoCreate : function(){
16794         
16795         
16796         var fc_button = function(name, corner, style, content ) {
16797             return Roo.apply({},{
16798                 tag : 'span',
16799                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16800                          (corner.length ?
16801                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16802                             ''
16803                         ),
16804                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16805                 unselectable: 'on'
16806             });
16807         };
16808         
16809         var header = {};
16810         
16811         if(!this.header){
16812             header = {
16813                 tag : 'table',
16814                 cls : 'fc-header',
16815                 style : 'width:100%',
16816                 cn : [
16817                     {
16818                         tag: 'tr',
16819                         cn : [
16820                             {
16821                                 tag : 'td',
16822                                 cls : 'fc-header-left',
16823                                 cn : [
16824                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16825                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16826                                     { tag: 'span', cls: 'fc-header-space' },
16827                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16828
16829
16830                                 ]
16831                             },
16832
16833                             {
16834                                 tag : 'td',
16835                                 cls : 'fc-header-center',
16836                                 cn : [
16837                                     {
16838                                         tag: 'span',
16839                                         cls: 'fc-header-title',
16840                                         cn : {
16841                                             tag: 'H2',
16842                                             html : 'month / year'
16843                                         }
16844                                     }
16845
16846                                 ]
16847                             },
16848                             {
16849                                 tag : 'td',
16850                                 cls : 'fc-header-right',
16851                                 cn : [
16852                               /*      fc_button('month', 'left', '', 'month' ),
16853                                     fc_button('week', '', '', 'week' ),
16854                                     fc_button('day', 'right', '', 'day' )
16855                                 */    
16856
16857                                 ]
16858                             }
16859
16860                         ]
16861                     }
16862                 ]
16863             };
16864         }
16865         
16866         header = this.header;
16867         
16868        
16869         var cal_heads = function() {
16870             var ret = [];
16871             // fixme - handle this.
16872             
16873             for (var i =0; i < Date.dayNames.length; i++) {
16874                 var d = Date.dayNames[i];
16875                 ret.push({
16876                     tag: 'th',
16877                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16878                     html : d.substring(0,3)
16879                 });
16880                 
16881             }
16882             ret[0].cls += ' fc-first';
16883             ret[6].cls += ' fc-last';
16884             return ret;
16885         };
16886         var cal_cell = function(n) {
16887             return  {
16888                 tag: 'td',
16889                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16890                 cn : [
16891                     {
16892                         cn : [
16893                             {
16894                                 cls: 'fc-day-number',
16895                                 html: 'D'
16896                             },
16897                             {
16898                                 cls: 'fc-day-content',
16899                              
16900                                 cn : [
16901                                      {
16902                                         style: 'position: relative;' // height: 17px;
16903                                     }
16904                                 ]
16905                             }
16906                             
16907                             
16908                         ]
16909                     }
16910                 ]
16911                 
16912             }
16913         };
16914         var cal_rows = function() {
16915             
16916             var ret = [];
16917             for (var r = 0; r < 6; r++) {
16918                 var row= {
16919                     tag : 'tr',
16920                     cls : 'fc-week',
16921                     cn : []
16922                 };
16923                 
16924                 for (var i =0; i < Date.dayNames.length; i++) {
16925                     var d = Date.dayNames[i];
16926                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16927
16928                 }
16929                 row.cn[0].cls+=' fc-first';
16930                 row.cn[0].cn[0].style = 'min-height:90px';
16931                 row.cn[6].cls+=' fc-last';
16932                 ret.push(row);
16933                 
16934             }
16935             ret[0].cls += ' fc-first';
16936             ret[4].cls += ' fc-prev-last';
16937             ret[5].cls += ' fc-last';
16938             return ret;
16939             
16940         };
16941         
16942         var cal_table = {
16943             tag: 'table',
16944             cls: 'fc-border-separate',
16945             style : 'width:100%',
16946             cellspacing  : 0,
16947             cn : [
16948                 { 
16949                     tag: 'thead',
16950                     cn : [
16951                         { 
16952                             tag: 'tr',
16953                             cls : 'fc-first fc-last',
16954                             cn : cal_heads()
16955                         }
16956                     ]
16957                 },
16958                 { 
16959                     tag: 'tbody',
16960                     cn : cal_rows()
16961                 }
16962                   
16963             ]
16964         };
16965          
16966          var cfg = {
16967             cls : 'fc fc-ltr',
16968             cn : [
16969                 header,
16970                 {
16971                     cls : 'fc-content',
16972                     style : "position: relative;",
16973                     cn : [
16974                         {
16975                             cls : 'fc-view fc-view-month fc-grid',
16976                             style : 'position: relative',
16977                             unselectable : 'on',
16978                             cn : [
16979                                 {
16980                                     cls : 'fc-event-container',
16981                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16982                                 },
16983                                 cal_table
16984                             ]
16985                         }
16986                     ]
16987     
16988                 }
16989            ] 
16990             
16991         };
16992         
16993          
16994         
16995         return cfg;
16996     },
16997     
16998     
16999     initEvents : function()
17000     {
17001         if(!this.store){
17002             throw "can not find store for calendar";
17003         }
17004         
17005         var mark = {
17006             tag: "div",
17007             cls:"x-dlg-mask",
17008             style: "text-align:center",
17009             cn: [
17010                 {
17011                     tag: "div",
17012                     style: "background-color:white;width:50%;margin:250 auto",
17013                     cn: [
17014                         {
17015                             tag: "img",
17016                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17017                         },
17018                         {
17019                             tag: "span",
17020                             html: "Loading"
17021                         }
17022                         
17023                     ]
17024                 }
17025             ]
17026         };
17027         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17028         
17029         var size = this.el.select('.fc-content', true).first().getSize();
17030         this.maskEl.setSize(size.width, size.height);
17031         this.maskEl.enableDisplayMode("block");
17032         if(!this.loadMask){
17033             this.maskEl.hide();
17034         }
17035         
17036         this.store = Roo.factory(this.store, Roo.data);
17037         this.store.on('load', this.onLoad, this);
17038         this.store.on('beforeload', this.onBeforeLoad, this);
17039         
17040         this.resize();
17041         
17042         this.cells = this.el.select('.fc-day',true);
17043         //Roo.log(this.cells);
17044         this.textNodes = this.el.query('.fc-day-number');
17045         this.cells.addClassOnOver('fc-state-hover');
17046         
17047         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17048         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17049         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17050         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17051         
17052         this.on('monthchange', this.onMonthChange, this);
17053         
17054         this.update(new Date().clearTime());
17055     },
17056     
17057     resize : function() {
17058         var sz  = this.el.getSize();
17059         
17060         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17061         this.el.select('.fc-day-content div',true).setHeight(34);
17062     },
17063     
17064     
17065     // private
17066     showPrevMonth : function(e){
17067         this.update(this.activeDate.add("mo", -1));
17068     },
17069     showToday : function(e){
17070         this.update(new Date().clearTime());
17071     },
17072     // private
17073     showNextMonth : function(e){
17074         this.update(this.activeDate.add("mo", 1));
17075     },
17076
17077     // private
17078     showPrevYear : function(){
17079         this.update(this.activeDate.add("y", -1));
17080     },
17081
17082     // private
17083     showNextYear : function(){
17084         this.update(this.activeDate.add("y", 1));
17085     },
17086
17087     
17088    // private
17089     update : function(date)
17090     {
17091         var vd = this.activeDate;
17092         this.activeDate = date;
17093 //        if(vd && this.el){
17094 //            var t = date.getTime();
17095 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17096 //                Roo.log('using add remove');
17097 //                
17098 //                this.fireEvent('monthchange', this, date);
17099 //                
17100 //                this.cells.removeClass("fc-state-highlight");
17101 //                this.cells.each(function(c){
17102 //                   if(c.dateValue == t){
17103 //                       c.addClass("fc-state-highlight");
17104 //                       setTimeout(function(){
17105 //                            try{c.dom.firstChild.focus();}catch(e){}
17106 //                       }, 50);
17107 //                       return false;
17108 //                   }
17109 //                   return true;
17110 //                });
17111 //                return;
17112 //            }
17113 //        }
17114         
17115         var days = date.getDaysInMonth();
17116         
17117         var firstOfMonth = date.getFirstDateOfMonth();
17118         var startingPos = firstOfMonth.getDay()-this.startDay;
17119         
17120         if(startingPos < this.startDay){
17121             startingPos += 7;
17122         }
17123         
17124         var pm = date.add(Date.MONTH, -1);
17125         var prevStart = pm.getDaysInMonth()-startingPos;
17126 //        
17127         this.cells = this.el.select('.fc-day',true);
17128         this.textNodes = this.el.query('.fc-day-number');
17129         this.cells.addClassOnOver('fc-state-hover');
17130         
17131         var cells = this.cells.elements;
17132         var textEls = this.textNodes;
17133         
17134         Roo.each(cells, function(cell){
17135             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17136         });
17137         
17138         days += startingPos;
17139
17140         // convert everything to numbers so it's fast
17141         var day = 86400000;
17142         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17143         //Roo.log(d);
17144         //Roo.log(pm);
17145         //Roo.log(prevStart);
17146         
17147         var today = new Date().clearTime().getTime();
17148         var sel = date.clearTime().getTime();
17149         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17150         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17151         var ddMatch = this.disabledDatesRE;
17152         var ddText = this.disabledDatesText;
17153         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17154         var ddaysText = this.disabledDaysText;
17155         var format = this.format;
17156         
17157         var setCellClass = function(cal, cell){
17158             cell.row = 0;
17159             cell.events = [];
17160             cell.more = [];
17161             //Roo.log('set Cell Class');
17162             cell.title = "";
17163             var t = d.getTime();
17164             
17165             //Roo.log(d);
17166             
17167             cell.dateValue = t;
17168             if(t == today){
17169                 cell.className += " fc-today";
17170                 cell.className += " fc-state-highlight";
17171                 cell.title = cal.todayText;
17172             }
17173             if(t == sel){
17174                 // disable highlight in other month..
17175                 //cell.className += " fc-state-highlight";
17176                 
17177             }
17178             // disabling
17179             if(t < min) {
17180                 cell.className = " fc-state-disabled";
17181                 cell.title = cal.minText;
17182                 return;
17183             }
17184             if(t > max) {
17185                 cell.className = " fc-state-disabled";
17186                 cell.title = cal.maxText;
17187                 return;
17188             }
17189             if(ddays){
17190                 if(ddays.indexOf(d.getDay()) != -1){
17191                     cell.title = ddaysText;
17192                     cell.className = " fc-state-disabled";
17193                 }
17194             }
17195             if(ddMatch && format){
17196                 var fvalue = d.dateFormat(format);
17197                 if(ddMatch.test(fvalue)){
17198                     cell.title = ddText.replace("%0", fvalue);
17199                     cell.className = " fc-state-disabled";
17200                 }
17201             }
17202             
17203             if (!cell.initialClassName) {
17204                 cell.initialClassName = cell.dom.className;
17205             }
17206             
17207             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17208         };
17209
17210         var i = 0;
17211         
17212         for(; i < startingPos; i++) {
17213             textEls[i].innerHTML = (++prevStart);
17214             d.setDate(d.getDate()+1);
17215             
17216             cells[i].className = "fc-past fc-other-month";
17217             setCellClass(this, cells[i]);
17218         }
17219         
17220         var intDay = 0;
17221         
17222         for(; i < days; i++){
17223             intDay = i - startingPos + 1;
17224             textEls[i].innerHTML = (intDay);
17225             d.setDate(d.getDate()+1);
17226             
17227             cells[i].className = ''; // "x-date-active";
17228             setCellClass(this, cells[i]);
17229         }
17230         var extraDays = 0;
17231         
17232         for(; i < 42; i++) {
17233             textEls[i].innerHTML = (++extraDays);
17234             d.setDate(d.getDate()+1);
17235             
17236             cells[i].className = "fc-future fc-other-month";
17237             setCellClass(this, cells[i]);
17238         }
17239         
17240         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17241         
17242         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17243         
17244         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17245         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17246         
17247         if(totalRows != 6){
17248             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17249             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17250         }
17251         
17252         this.fireEvent('monthchange', this, date);
17253         
17254         
17255         /*
17256         if(!this.internalRender){
17257             var main = this.el.dom.firstChild;
17258             var w = main.offsetWidth;
17259             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17260             Roo.fly(main).setWidth(w);
17261             this.internalRender = true;
17262             // opera does not respect the auto grow header center column
17263             // then, after it gets a width opera refuses to recalculate
17264             // without a second pass
17265             if(Roo.isOpera && !this.secondPass){
17266                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17267                 this.secondPass = true;
17268                 this.update.defer(10, this, [date]);
17269             }
17270         }
17271         */
17272         
17273     },
17274     
17275     findCell : function(dt) {
17276         dt = dt.clearTime().getTime();
17277         var ret = false;
17278         this.cells.each(function(c){
17279             //Roo.log("check " +c.dateValue + '?=' + dt);
17280             if(c.dateValue == dt){
17281                 ret = c;
17282                 return false;
17283             }
17284             return true;
17285         });
17286         
17287         return ret;
17288     },
17289     
17290     findCells : function(ev) {
17291         var s = ev.start.clone().clearTime().getTime();
17292        // Roo.log(s);
17293         var e= ev.end.clone().clearTime().getTime();
17294        // Roo.log(e);
17295         var ret = [];
17296         this.cells.each(function(c){
17297              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17298             
17299             if(c.dateValue > e){
17300                 return ;
17301             }
17302             if(c.dateValue < s){
17303                 return ;
17304             }
17305             ret.push(c);
17306         });
17307         
17308         return ret;    
17309     },
17310     
17311 //    findBestRow: function(cells)
17312 //    {
17313 //        var ret = 0;
17314 //        
17315 //        for (var i =0 ; i < cells.length;i++) {
17316 //            ret  = Math.max(cells[i].rows || 0,ret);
17317 //        }
17318 //        return ret;
17319 //        
17320 //    },
17321     
17322     
17323     addItem : function(ev)
17324     {
17325         // look for vertical location slot in
17326         var cells = this.findCells(ev);
17327         
17328 //        ev.row = this.findBestRow(cells);
17329         
17330         // work out the location.
17331         
17332         var crow = false;
17333         var rows = [];
17334         for(var i =0; i < cells.length; i++) {
17335             
17336             cells[i].row = cells[0].row;
17337             
17338             if(i == 0){
17339                 cells[i].row = cells[i].row + 1;
17340             }
17341             
17342             if (!crow) {
17343                 crow = {
17344                     start : cells[i],
17345                     end :  cells[i]
17346                 };
17347                 continue;
17348             }
17349             if (crow.start.getY() == cells[i].getY()) {
17350                 // on same row.
17351                 crow.end = cells[i];
17352                 continue;
17353             }
17354             // different row.
17355             rows.push(crow);
17356             crow = {
17357                 start: cells[i],
17358                 end : cells[i]
17359             };
17360             
17361         }
17362         
17363         rows.push(crow);
17364         ev.els = [];
17365         ev.rows = rows;
17366         ev.cells = cells;
17367         
17368         cells[0].events.push(ev);
17369         
17370         this.calevents.push(ev);
17371     },
17372     
17373     clearEvents: function() {
17374         
17375         if(!this.calevents){
17376             return;
17377         }
17378         
17379         Roo.each(this.cells.elements, function(c){
17380             c.row = 0;
17381             c.events = [];
17382             c.more = [];
17383         });
17384         
17385         Roo.each(this.calevents, function(e) {
17386             Roo.each(e.els, function(el) {
17387                 el.un('mouseenter' ,this.onEventEnter, this);
17388                 el.un('mouseleave' ,this.onEventLeave, this);
17389                 el.remove();
17390             },this);
17391         },this);
17392         
17393         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17394             e.remove();
17395         });
17396         
17397     },
17398     
17399     renderEvents: function()
17400     {   
17401         var _this = this;
17402         
17403         this.cells.each(function(c) {
17404             
17405             if(c.row < 5){
17406                 return;
17407             }
17408             
17409             var ev = c.events;
17410             
17411             var r = 4;
17412             if(c.row != c.events.length){
17413                 r = 4 - (4 - (c.row - c.events.length));
17414             }
17415             
17416             c.events = ev.slice(0, r);
17417             c.more = ev.slice(r);
17418             
17419             if(c.more.length && c.more.length == 1){
17420                 c.events.push(c.more.pop());
17421             }
17422             
17423             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17424             
17425         });
17426             
17427         this.cells.each(function(c) {
17428             
17429             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17430             
17431             
17432             for (var e = 0; e < c.events.length; e++){
17433                 var ev = c.events[e];
17434                 var rows = ev.rows;
17435                 
17436                 for(var i = 0; i < rows.length; i++) {
17437                 
17438                     // how many rows should it span..
17439
17440                     var  cfg = {
17441                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17442                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17443
17444                         unselectable : "on",
17445                         cn : [
17446                             {
17447                                 cls: 'fc-event-inner',
17448                                 cn : [
17449     //                                {
17450     //                                  tag:'span',
17451     //                                  cls: 'fc-event-time',
17452     //                                  html : cells.length > 1 ? '' : ev.time
17453     //                                },
17454                                     {
17455                                       tag:'span',
17456                                       cls: 'fc-event-title',
17457                                       html : String.format('{0}', ev.title)
17458                                     }
17459
17460
17461                                 ]
17462                             },
17463                             {
17464                                 cls: 'ui-resizable-handle ui-resizable-e',
17465                                 html : '&nbsp;&nbsp;&nbsp'
17466                             }
17467
17468                         ]
17469                     };
17470
17471                     if (i == 0) {
17472                         cfg.cls += ' fc-event-start';
17473                     }
17474                     if ((i+1) == rows.length) {
17475                         cfg.cls += ' fc-event-end';
17476                     }
17477
17478                     var ctr = _this.el.select('.fc-event-container',true).first();
17479                     var cg = ctr.createChild(cfg);
17480
17481                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17482                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17483
17484                     var r = (c.more.length) ? 1 : 0;
17485                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17486                     cg.setWidth(ebox.right - sbox.x -2);
17487
17488                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17489                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17490                     cg.on('click', _this.onEventClick, _this, ev);
17491
17492                     ev.els.push(cg);
17493                     
17494                 }
17495                 
17496             }
17497             
17498             
17499             if(c.more.length){
17500                 var  cfg = {
17501                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17502                     style : 'position: absolute',
17503                     unselectable : "on",
17504                     cn : [
17505                         {
17506                             cls: 'fc-event-inner',
17507                             cn : [
17508                                 {
17509                                   tag:'span',
17510                                   cls: 'fc-event-title',
17511                                   html : 'More'
17512                                 }
17513
17514
17515                             ]
17516                         },
17517                         {
17518                             cls: 'ui-resizable-handle ui-resizable-e',
17519                             html : '&nbsp;&nbsp;&nbsp'
17520                         }
17521
17522                     ]
17523                 };
17524
17525                 var ctr = _this.el.select('.fc-event-container',true).first();
17526                 var cg = ctr.createChild(cfg);
17527
17528                 var sbox = c.select('.fc-day-content',true).first().getBox();
17529                 var ebox = c.select('.fc-day-content',true).first().getBox();
17530                 //Roo.log(cg);
17531                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17532                 cg.setWidth(ebox.right - sbox.x -2);
17533
17534                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17535                 
17536             }
17537             
17538         });
17539         
17540         
17541         
17542     },
17543     
17544     onEventEnter: function (e, el,event,d) {
17545         this.fireEvent('evententer', this, el, event);
17546     },
17547     
17548     onEventLeave: function (e, el,event,d) {
17549         this.fireEvent('eventleave', this, el, event);
17550     },
17551     
17552     onEventClick: function (e, el,event,d) {
17553         this.fireEvent('eventclick', this, el, event);
17554     },
17555     
17556     onMonthChange: function () {
17557         this.store.load();
17558     },
17559     
17560     onMoreEventClick: function(e, el, more)
17561     {
17562         var _this = this;
17563         
17564         this.calpopover.placement = 'right';
17565         this.calpopover.setTitle('More');
17566         
17567         this.calpopover.setContent('');
17568         
17569         var ctr = this.calpopover.el.select('.popover-content', true).first();
17570         
17571         Roo.each(more, function(m){
17572             var cfg = {
17573                 cls : 'fc-event-hori fc-event-draggable',
17574                 html : m.title
17575             };
17576             var cg = ctr.createChild(cfg);
17577             
17578             cg.on('click', _this.onEventClick, _this, m);
17579         });
17580         
17581         this.calpopover.show(el);
17582         
17583         
17584     },
17585     
17586     onLoad: function () 
17587     {   
17588         this.calevents = [];
17589         var cal = this;
17590         
17591         if(this.store.getCount() > 0){
17592             this.store.data.each(function(d){
17593                cal.addItem({
17594                     id : d.data.id,
17595                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17596                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17597                     time : d.data.start_time,
17598                     title : d.data.title,
17599                     description : d.data.description,
17600                     venue : d.data.venue
17601                 });
17602             });
17603         }
17604         
17605         this.renderEvents();
17606         
17607         if(this.calevents.length && this.loadMask){
17608             this.maskEl.hide();
17609         }
17610     },
17611     
17612     onBeforeLoad: function()
17613     {
17614         this.clearEvents();
17615         if(this.loadMask){
17616             this.maskEl.show();
17617         }
17618     }
17619 });
17620
17621  
17622  /*
17623  * - LGPL
17624  *
17625  * element
17626  * 
17627  */
17628
17629 /**
17630  * @class Roo.bootstrap.Popover
17631  * @extends Roo.bootstrap.Component
17632  * Bootstrap Popover class
17633  * @cfg {String} html contents of the popover   (or false to use children..)
17634  * @cfg {String} title of popover (or false to hide)
17635  * @cfg {String} placement how it is placed
17636  * @cfg {String} trigger click || hover (or false to trigger manually)
17637  * @cfg {String} over what (parent or false to trigger manually.)
17638  * @cfg {Number} delay - delay before showing
17639  
17640  * @constructor
17641  * Create a new Popover
17642  * @param {Object} config The config object
17643  */
17644
17645 Roo.bootstrap.Popover = function(config){
17646     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17647     
17648     this.addEvents({
17649         // raw events
17650          /**
17651          * @event show
17652          * After the popover show
17653          * 
17654          * @param {Roo.bootstrap.Popover} this
17655          */
17656         "show" : true,
17657         /**
17658          * @event hide
17659          * After the popover hide
17660          * 
17661          * @param {Roo.bootstrap.Popover} this
17662          */
17663         "hide" : true
17664     });
17665 };
17666
17667 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17668     
17669     title: 'Fill in a title',
17670     html: false,
17671     
17672     placement : 'right',
17673     trigger : 'hover', // hover
17674     
17675     delay : 0,
17676     
17677     over: 'parent',
17678     
17679     can_build_overlaid : false,
17680     
17681     getChildContainer : function()
17682     {
17683         return this.el.select('.popover-content',true).first();
17684     },
17685     
17686     getAutoCreate : function(){
17687          
17688         var cfg = {
17689            cls : 'popover roo-dynamic',
17690            style: 'display:block',
17691            cn : [
17692                 {
17693                     cls : 'arrow'
17694                 },
17695                 {
17696                     cls : 'popover-inner',
17697                     cn : [
17698                         {
17699                             tag: 'h3',
17700                             cls: 'popover-title popover-header',
17701                             html : this.title
17702                         },
17703                         {
17704                             cls : 'popover-content popover-body',
17705                             html : this.html
17706                         }
17707                     ]
17708                     
17709                 }
17710            ]
17711         };
17712         
17713         return cfg;
17714     },
17715     setTitle: function(str)
17716     {
17717         this.title = str;
17718         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17719     },
17720     setContent: function(str)
17721     {
17722         this.html = str;
17723         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17724     },
17725     // as it get's added to the bottom of the page.
17726     onRender : function(ct, position)
17727     {
17728         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17729         if(!this.el){
17730             var cfg = Roo.apply({},  this.getAutoCreate());
17731             cfg.id = Roo.id();
17732             
17733             if (this.cls) {
17734                 cfg.cls += ' ' + this.cls;
17735             }
17736             if (this.style) {
17737                 cfg.style = this.style;
17738             }
17739             //Roo.log("adding to ");
17740             this.el = Roo.get(document.body).createChild(cfg, position);
17741 //            Roo.log(this.el);
17742         }
17743         this.initEvents();
17744     },
17745     
17746     initEvents : function()
17747     {
17748         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17749         this.el.enableDisplayMode('block');
17750         this.el.hide();
17751         if (this.over === false) {
17752             return; 
17753         }
17754         if (this.triggers === false) {
17755             return;
17756         }
17757         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17758         var triggers = this.trigger ? this.trigger.split(' ') : [];
17759         Roo.each(triggers, function(trigger) {
17760         
17761             if (trigger == 'click') {
17762                 on_el.on('click', this.toggle, this);
17763             } else if (trigger != 'manual') {
17764                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17765                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17766       
17767                 on_el.on(eventIn  ,this.enter, this);
17768                 on_el.on(eventOut, this.leave, this);
17769             }
17770         }, this);
17771         
17772     },
17773     
17774     
17775     // private
17776     timeout : null,
17777     hoverState : null,
17778     
17779     toggle : function () {
17780         this.hoverState == 'in' ? this.leave() : this.enter();
17781     },
17782     
17783     enter : function () {
17784         
17785         clearTimeout(this.timeout);
17786     
17787         this.hoverState = 'in';
17788     
17789         if (!this.delay || !this.delay.show) {
17790             this.show();
17791             return;
17792         }
17793         var _t = this;
17794         this.timeout = setTimeout(function () {
17795             if (_t.hoverState == 'in') {
17796                 _t.show();
17797             }
17798         }, this.delay.show)
17799     },
17800     
17801     leave : function() {
17802         clearTimeout(this.timeout);
17803     
17804         this.hoverState = 'out';
17805     
17806         if (!this.delay || !this.delay.hide) {
17807             this.hide();
17808             return;
17809         }
17810         var _t = this;
17811         this.timeout = setTimeout(function () {
17812             if (_t.hoverState == 'out') {
17813                 _t.hide();
17814             }
17815         }, this.delay.hide)
17816     },
17817     
17818     show : function (on_el)
17819     {
17820         if (!on_el) {
17821             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17822         }
17823         
17824         // set content.
17825         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17826         if (this.html !== false) {
17827             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17828         }
17829         this.el.removeClass([
17830             'fade','top','bottom', 'left', 'right','in',
17831             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17832         ]);
17833         if (!this.title.length) {
17834             this.el.select('.popover-title',true).hide();
17835         }
17836         
17837         var placement = typeof this.placement == 'function' ?
17838             this.placement.call(this, this.el, on_el) :
17839             this.placement;
17840             
17841         var autoToken = /\s?auto?\s?/i;
17842         var autoPlace = autoToken.test(placement);
17843         if (autoPlace) {
17844             placement = placement.replace(autoToken, '') || 'top';
17845         }
17846         
17847         //this.el.detach()
17848         //this.el.setXY([0,0]);
17849         this.el.show();
17850         this.el.dom.style.display='block';
17851         this.el.addClass(placement);
17852         
17853         //this.el.appendTo(on_el);
17854         
17855         var p = this.getPosition();
17856         var box = this.el.getBox();
17857         
17858         if (autoPlace) {
17859             // fixme..
17860         }
17861         var align = Roo.bootstrap.Popover.alignment[placement];
17862         
17863 //        Roo.log(align);
17864         this.el.alignTo(on_el, align[0],align[1]);
17865         //var arrow = this.el.select('.arrow',true).first();
17866         //arrow.set(align[2], 
17867         
17868         this.el.addClass('in');
17869         
17870         
17871         if (this.el.hasClass('fade')) {
17872             // fade it?
17873         }
17874         
17875         this.hoverState = 'in';
17876         
17877         this.fireEvent('show', this);
17878         
17879     },
17880     hide : function()
17881     {
17882         this.el.setXY([0,0]);
17883         this.el.removeClass('in');
17884         this.el.hide();
17885         this.hoverState = null;
17886         
17887         this.fireEvent('hide', this);
17888     }
17889     
17890 });
17891
17892 Roo.bootstrap.Popover.alignment = {
17893     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17894     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17895     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17896     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17897 };
17898
17899  /*
17900  * - LGPL
17901  *
17902  * Progress
17903  * 
17904  */
17905
17906 /**
17907  * @class Roo.bootstrap.Progress
17908  * @extends Roo.bootstrap.Component
17909  * Bootstrap Progress class
17910  * @cfg {Boolean} striped striped of the progress bar
17911  * @cfg {Boolean} active animated of the progress bar
17912  * 
17913  * 
17914  * @constructor
17915  * Create a new Progress
17916  * @param {Object} config The config object
17917  */
17918
17919 Roo.bootstrap.Progress = function(config){
17920     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17921 };
17922
17923 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17924     
17925     striped : false,
17926     active: false,
17927     
17928     getAutoCreate : function(){
17929         var cfg = {
17930             tag: 'div',
17931             cls: 'progress'
17932         };
17933         
17934         
17935         if(this.striped){
17936             cfg.cls += ' progress-striped';
17937         }
17938       
17939         if(this.active){
17940             cfg.cls += ' active';
17941         }
17942         
17943         
17944         return cfg;
17945     }
17946    
17947 });
17948
17949  
17950
17951  /*
17952  * - LGPL
17953  *
17954  * ProgressBar
17955  * 
17956  */
17957
17958 /**
17959  * @class Roo.bootstrap.ProgressBar
17960  * @extends Roo.bootstrap.Component
17961  * Bootstrap ProgressBar class
17962  * @cfg {Number} aria_valuenow aria-value now
17963  * @cfg {Number} aria_valuemin aria-value min
17964  * @cfg {Number} aria_valuemax aria-value max
17965  * @cfg {String} label label for the progress bar
17966  * @cfg {String} panel (success | info | warning | danger )
17967  * @cfg {String} role role of the progress bar
17968  * @cfg {String} sr_only text
17969  * 
17970  * 
17971  * @constructor
17972  * Create a new ProgressBar
17973  * @param {Object} config The config object
17974  */
17975
17976 Roo.bootstrap.ProgressBar = function(config){
17977     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17978 };
17979
17980 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17981     
17982     aria_valuenow : 0,
17983     aria_valuemin : 0,
17984     aria_valuemax : 100,
17985     label : false,
17986     panel : false,
17987     role : false,
17988     sr_only: false,
17989     
17990     getAutoCreate : function()
17991     {
17992         
17993         var cfg = {
17994             tag: 'div',
17995             cls: 'progress-bar',
17996             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17997         };
17998         
17999         if(this.sr_only){
18000             cfg.cn = {
18001                 tag: 'span',
18002                 cls: 'sr-only',
18003                 html: this.sr_only
18004             }
18005         }
18006         
18007         if(this.role){
18008             cfg.role = this.role;
18009         }
18010         
18011         if(this.aria_valuenow){
18012             cfg['aria-valuenow'] = this.aria_valuenow;
18013         }
18014         
18015         if(this.aria_valuemin){
18016             cfg['aria-valuemin'] = this.aria_valuemin;
18017         }
18018         
18019         if(this.aria_valuemax){
18020             cfg['aria-valuemax'] = this.aria_valuemax;
18021         }
18022         
18023         if(this.label && !this.sr_only){
18024             cfg.html = this.label;
18025         }
18026         
18027         if(this.panel){
18028             cfg.cls += ' progress-bar-' + this.panel;
18029         }
18030         
18031         return cfg;
18032     },
18033     
18034     update : function(aria_valuenow)
18035     {
18036         this.aria_valuenow = aria_valuenow;
18037         
18038         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18039     }
18040    
18041 });
18042
18043  
18044
18045  /*
18046  * - LGPL
18047  *
18048  * column
18049  * 
18050  */
18051
18052 /**
18053  * @class Roo.bootstrap.TabGroup
18054  * @extends Roo.bootstrap.Column
18055  * Bootstrap Column class
18056  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18057  * @cfg {Boolean} carousel true to make the group behave like a carousel
18058  * @cfg {Boolean} bullets show bullets for the panels
18059  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18060  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18061  * @cfg {Boolean} showarrow (true|false) show arrow default true
18062  * 
18063  * @constructor
18064  * Create a new TabGroup
18065  * @param {Object} config The config object
18066  */
18067
18068 Roo.bootstrap.TabGroup = function(config){
18069     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18070     if (!this.navId) {
18071         this.navId = Roo.id();
18072     }
18073     this.tabs = [];
18074     Roo.bootstrap.TabGroup.register(this);
18075     
18076 };
18077
18078 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18079     
18080     carousel : false,
18081     transition : false,
18082     bullets : 0,
18083     timer : 0,
18084     autoslide : false,
18085     slideFn : false,
18086     slideOnTouch : false,
18087     showarrow : true,
18088     
18089     getAutoCreate : function()
18090     {
18091         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18092         
18093         cfg.cls += ' tab-content';
18094         
18095         if (this.carousel) {
18096             cfg.cls += ' carousel slide';
18097             
18098             cfg.cn = [{
18099                cls : 'carousel-inner',
18100                cn : []
18101             }];
18102         
18103             if(this.bullets  && !Roo.isTouch){
18104                 
18105                 var bullets = {
18106                     cls : 'carousel-bullets',
18107                     cn : []
18108                 };
18109                
18110                 if(this.bullets_cls){
18111                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18112                 }
18113                 
18114                 bullets.cn.push({
18115                     cls : 'clear'
18116                 });
18117                 
18118                 cfg.cn[0].cn.push(bullets);
18119             }
18120             
18121             if(this.showarrow){
18122                 cfg.cn[0].cn.push({
18123                     tag : 'div',
18124                     class : 'carousel-arrow',
18125                     cn : [
18126                         {
18127                             tag : 'div',
18128                             class : 'carousel-prev',
18129                             cn : [
18130                                 {
18131                                     tag : 'i',
18132                                     class : 'fa fa-chevron-left'
18133                                 }
18134                             ]
18135                         },
18136                         {
18137                             tag : 'div',
18138                             class : 'carousel-next',
18139                             cn : [
18140                                 {
18141                                     tag : 'i',
18142                                     class : 'fa fa-chevron-right'
18143                                 }
18144                             ]
18145                         }
18146                     ]
18147                 });
18148             }
18149             
18150         }
18151         
18152         return cfg;
18153     },
18154     
18155     initEvents:  function()
18156     {
18157 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18158 //            this.el.on("touchstart", this.onTouchStart, this);
18159 //        }
18160         
18161         if(this.autoslide){
18162             var _this = this;
18163             
18164             this.slideFn = window.setInterval(function() {
18165                 _this.showPanelNext();
18166             }, this.timer);
18167         }
18168         
18169         if(this.showarrow){
18170             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18171             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18172         }
18173         
18174         
18175     },
18176     
18177 //    onTouchStart : function(e, el, o)
18178 //    {
18179 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18180 //            return;
18181 //        }
18182 //        
18183 //        this.showPanelNext();
18184 //    },
18185     
18186     
18187     getChildContainer : function()
18188     {
18189         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18190     },
18191     
18192     /**
18193     * register a Navigation item
18194     * @param {Roo.bootstrap.NavItem} the navitem to add
18195     */
18196     register : function(item)
18197     {
18198         this.tabs.push( item);
18199         item.navId = this.navId; // not really needed..
18200         this.addBullet();
18201     
18202     },
18203     
18204     getActivePanel : function()
18205     {
18206         var r = false;
18207         Roo.each(this.tabs, function(t) {
18208             if (t.active) {
18209                 r = t;
18210                 return false;
18211             }
18212             return null;
18213         });
18214         return r;
18215         
18216     },
18217     getPanelByName : function(n)
18218     {
18219         var r = false;
18220         Roo.each(this.tabs, function(t) {
18221             if (t.tabId == n) {
18222                 r = t;
18223                 return false;
18224             }
18225             return null;
18226         });
18227         return r;
18228     },
18229     indexOfPanel : function(p)
18230     {
18231         var r = false;
18232         Roo.each(this.tabs, function(t,i) {
18233             if (t.tabId == p.tabId) {
18234                 r = i;
18235                 return false;
18236             }
18237             return null;
18238         });
18239         return r;
18240     },
18241     /**
18242      * show a specific panel
18243      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18244      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18245      */
18246     showPanel : function (pan)
18247     {
18248         if(this.transition || typeof(pan) == 'undefined'){
18249             Roo.log("waiting for the transitionend");
18250             return;
18251         }
18252         
18253         if (typeof(pan) == 'number') {
18254             pan = this.tabs[pan];
18255         }
18256         
18257         if (typeof(pan) == 'string') {
18258             pan = this.getPanelByName(pan);
18259         }
18260         
18261         var cur = this.getActivePanel();
18262         
18263         if(!pan || !cur){
18264             Roo.log('pan or acitve pan is undefined');
18265             return false;
18266         }
18267         
18268         if (pan.tabId == this.getActivePanel().tabId) {
18269             return true;
18270         }
18271         
18272         if (false === cur.fireEvent('beforedeactivate')) {
18273             return false;
18274         }
18275         
18276         if(this.bullets > 0 && !Roo.isTouch){
18277             this.setActiveBullet(this.indexOfPanel(pan));
18278         }
18279         
18280         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18281             
18282             this.transition = true;
18283             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18284             var lr = dir == 'next' ? 'left' : 'right';
18285             pan.el.addClass(dir); // or prev
18286             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18287             cur.el.addClass(lr); // or right
18288             pan.el.addClass(lr);
18289             
18290             var _this = this;
18291             cur.el.on('transitionend', function() {
18292                 Roo.log("trans end?");
18293                 
18294                 pan.el.removeClass([lr,dir]);
18295                 pan.setActive(true);
18296                 
18297                 cur.el.removeClass([lr]);
18298                 cur.setActive(false);
18299                 
18300                 _this.transition = false;
18301                 
18302             }, this, { single:  true } );
18303             
18304             return true;
18305         }
18306         
18307         cur.setActive(false);
18308         pan.setActive(true);
18309         
18310         return true;
18311         
18312     },
18313     showPanelNext : function()
18314     {
18315         var i = this.indexOfPanel(this.getActivePanel());
18316         
18317         if (i >= this.tabs.length - 1 && !this.autoslide) {
18318             return;
18319         }
18320         
18321         if (i >= this.tabs.length - 1 && this.autoslide) {
18322             i = -1;
18323         }
18324         
18325         this.showPanel(this.tabs[i+1]);
18326     },
18327     
18328     showPanelPrev : function()
18329     {
18330         var i = this.indexOfPanel(this.getActivePanel());
18331         
18332         if (i  < 1 && !this.autoslide) {
18333             return;
18334         }
18335         
18336         if (i < 1 && this.autoslide) {
18337             i = this.tabs.length;
18338         }
18339         
18340         this.showPanel(this.tabs[i-1]);
18341     },
18342     
18343     
18344     addBullet: function()
18345     {
18346         if(!this.bullets || Roo.isTouch){
18347             return;
18348         }
18349         var ctr = this.el.select('.carousel-bullets',true).first();
18350         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18351         var bullet = ctr.createChild({
18352             cls : 'bullet bullet-' + i
18353         },ctr.dom.lastChild);
18354         
18355         
18356         var _this = this;
18357         
18358         bullet.on('click', (function(e, el, o, ii, t){
18359
18360             e.preventDefault();
18361
18362             this.showPanel(ii);
18363
18364             if(this.autoslide && this.slideFn){
18365                 clearInterval(this.slideFn);
18366                 this.slideFn = window.setInterval(function() {
18367                     _this.showPanelNext();
18368                 }, this.timer);
18369             }
18370
18371         }).createDelegate(this, [i, bullet], true));
18372                 
18373         
18374     },
18375      
18376     setActiveBullet : function(i)
18377     {
18378         if(Roo.isTouch){
18379             return;
18380         }
18381         
18382         Roo.each(this.el.select('.bullet', true).elements, function(el){
18383             el.removeClass('selected');
18384         });
18385
18386         var bullet = this.el.select('.bullet-' + i, true).first();
18387         
18388         if(!bullet){
18389             return;
18390         }
18391         
18392         bullet.addClass('selected');
18393     }
18394     
18395     
18396   
18397 });
18398
18399  
18400
18401  
18402  
18403 Roo.apply(Roo.bootstrap.TabGroup, {
18404     
18405     groups: {},
18406      /**
18407     * register a Navigation Group
18408     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18409     */
18410     register : function(navgrp)
18411     {
18412         this.groups[navgrp.navId] = navgrp;
18413         
18414     },
18415     /**
18416     * fetch a Navigation Group based on the navigation ID
18417     * if one does not exist , it will get created.
18418     * @param {string} the navgroup to add
18419     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18420     */
18421     get: function(navId) {
18422         if (typeof(this.groups[navId]) == 'undefined') {
18423             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18424         }
18425         return this.groups[navId] ;
18426     }
18427     
18428     
18429     
18430 });
18431
18432  /*
18433  * - LGPL
18434  *
18435  * TabPanel
18436  * 
18437  */
18438
18439 /**
18440  * @class Roo.bootstrap.TabPanel
18441  * @extends Roo.bootstrap.Component
18442  * Bootstrap TabPanel class
18443  * @cfg {Boolean} active panel active
18444  * @cfg {String} html panel content
18445  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18446  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18447  * @cfg {String} href click to link..
18448  * 
18449  * 
18450  * @constructor
18451  * Create a new TabPanel
18452  * @param {Object} config The config object
18453  */
18454
18455 Roo.bootstrap.TabPanel = function(config){
18456     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18457     this.addEvents({
18458         /**
18459              * @event changed
18460              * Fires when the active status changes
18461              * @param {Roo.bootstrap.TabPanel} this
18462              * @param {Boolean} state the new state
18463             
18464          */
18465         'changed': true,
18466         /**
18467              * @event beforedeactivate
18468              * Fires before a tab is de-activated - can be used to do validation on a form.
18469              * @param {Roo.bootstrap.TabPanel} this
18470              * @return {Boolean} false if there is an error
18471             
18472          */
18473         'beforedeactivate': true
18474      });
18475     
18476     this.tabId = this.tabId || Roo.id();
18477   
18478 };
18479
18480 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18481     
18482     active: false,
18483     html: false,
18484     tabId: false,
18485     navId : false,
18486     href : '',
18487     
18488     getAutoCreate : function(){
18489         var cfg = {
18490             tag: 'div',
18491             // item is needed for carousel - not sure if it has any effect otherwise
18492             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18493             html: this.html || ''
18494         };
18495         
18496         if(this.active){
18497             cfg.cls += ' active';
18498         }
18499         
18500         if(this.tabId){
18501             cfg.tabId = this.tabId;
18502         }
18503         
18504         
18505         return cfg;
18506     },
18507     
18508     initEvents:  function()
18509     {
18510         var p = this.parent();
18511         
18512         this.navId = this.navId || p.navId;
18513         
18514         if (typeof(this.navId) != 'undefined') {
18515             // not really needed.. but just in case.. parent should be a NavGroup.
18516             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18517             
18518             tg.register(this);
18519             
18520             var i = tg.tabs.length - 1;
18521             
18522             if(this.active && tg.bullets > 0 && i < tg.bullets){
18523                 tg.setActiveBullet(i);
18524             }
18525         }
18526         
18527         this.el.on('click', this.onClick, this);
18528         
18529         if(Roo.isTouch){
18530             this.el.on("touchstart", this.onTouchStart, this);
18531             this.el.on("touchmove", this.onTouchMove, this);
18532             this.el.on("touchend", this.onTouchEnd, this);
18533         }
18534         
18535     },
18536     
18537     onRender : function(ct, position)
18538     {
18539         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18540     },
18541     
18542     setActive : function(state)
18543     {
18544         Roo.log("panel - set active " + this.tabId + "=" + state);
18545         
18546         this.active = state;
18547         if (!state) {
18548             this.el.removeClass('active');
18549             
18550         } else  if (!this.el.hasClass('active')) {
18551             this.el.addClass('active');
18552         }
18553         
18554         this.fireEvent('changed', this, state);
18555     },
18556     
18557     onClick : function(e)
18558     {
18559         e.preventDefault();
18560         
18561         if(!this.href.length){
18562             return;
18563         }
18564         
18565         window.location.href = this.href;
18566     },
18567     
18568     startX : 0,
18569     startY : 0,
18570     endX : 0,
18571     endY : 0,
18572     swiping : false,
18573     
18574     onTouchStart : function(e)
18575     {
18576         this.swiping = false;
18577         
18578         this.startX = e.browserEvent.touches[0].clientX;
18579         this.startY = e.browserEvent.touches[0].clientY;
18580     },
18581     
18582     onTouchMove : function(e)
18583     {
18584         this.swiping = true;
18585         
18586         this.endX = e.browserEvent.touches[0].clientX;
18587         this.endY = e.browserEvent.touches[0].clientY;
18588     },
18589     
18590     onTouchEnd : function(e)
18591     {
18592         if(!this.swiping){
18593             this.onClick(e);
18594             return;
18595         }
18596         
18597         var tabGroup = this.parent();
18598         
18599         if(this.endX > this.startX){ // swiping right
18600             tabGroup.showPanelPrev();
18601             return;
18602         }
18603         
18604         if(this.startX > this.endX){ // swiping left
18605             tabGroup.showPanelNext();
18606             return;
18607         }
18608     }
18609     
18610     
18611 });
18612  
18613
18614  
18615
18616  /*
18617  * - LGPL
18618  *
18619  * DateField
18620  * 
18621  */
18622
18623 /**
18624  * @class Roo.bootstrap.DateField
18625  * @extends Roo.bootstrap.Input
18626  * Bootstrap DateField class
18627  * @cfg {Number} weekStart default 0
18628  * @cfg {String} viewMode default empty, (months|years)
18629  * @cfg {String} minViewMode default empty, (months|years)
18630  * @cfg {Number} startDate default -Infinity
18631  * @cfg {Number} endDate default Infinity
18632  * @cfg {Boolean} todayHighlight default false
18633  * @cfg {Boolean} todayBtn default false
18634  * @cfg {Boolean} calendarWeeks default false
18635  * @cfg {Object} daysOfWeekDisabled default empty
18636  * @cfg {Boolean} singleMode default false (true | false)
18637  * 
18638  * @cfg {Boolean} keyboardNavigation default true
18639  * @cfg {String} language default en
18640  * 
18641  * @constructor
18642  * Create a new DateField
18643  * @param {Object} config The config object
18644  */
18645
18646 Roo.bootstrap.DateField = function(config){
18647     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18648      this.addEvents({
18649             /**
18650              * @event show
18651              * Fires when this field show.
18652              * @param {Roo.bootstrap.DateField} this
18653              * @param {Mixed} date The date value
18654              */
18655             show : true,
18656             /**
18657              * @event show
18658              * Fires when this field hide.
18659              * @param {Roo.bootstrap.DateField} this
18660              * @param {Mixed} date The date value
18661              */
18662             hide : true,
18663             /**
18664              * @event select
18665              * Fires when select a date.
18666              * @param {Roo.bootstrap.DateField} this
18667              * @param {Mixed} date The date value
18668              */
18669             select : true,
18670             /**
18671              * @event beforeselect
18672              * Fires when before select a date.
18673              * @param {Roo.bootstrap.DateField} this
18674              * @param {Mixed} date The date value
18675              */
18676             beforeselect : true
18677         });
18678 };
18679
18680 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18681     
18682     /**
18683      * @cfg {String} format
18684      * The default date format string which can be overriden for localization support.  The format must be
18685      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18686      */
18687     format : "m/d/y",
18688     /**
18689      * @cfg {String} altFormats
18690      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18691      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18692      */
18693     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18694     
18695     weekStart : 0,
18696     
18697     viewMode : '',
18698     
18699     minViewMode : '',
18700     
18701     todayHighlight : false,
18702     
18703     todayBtn: false,
18704     
18705     language: 'en',
18706     
18707     keyboardNavigation: true,
18708     
18709     calendarWeeks: false,
18710     
18711     startDate: -Infinity,
18712     
18713     endDate: Infinity,
18714     
18715     daysOfWeekDisabled: [],
18716     
18717     _events: [],
18718     
18719     singleMode : false,
18720     
18721     UTCDate: function()
18722     {
18723         return new Date(Date.UTC.apply(Date, arguments));
18724     },
18725     
18726     UTCToday: function()
18727     {
18728         var today = new Date();
18729         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18730     },
18731     
18732     getDate: function() {
18733             var d = this.getUTCDate();
18734             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18735     },
18736     
18737     getUTCDate: function() {
18738             return this.date;
18739     },
18740     
18741     setDate: function(d) {
18742             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18743     },
18744     
18745     setUTCDate: function(d) {
18746             this.date = d;
18747             this.setValue(this.formatDate(this.date));
18748     },
18749         
18750     onRender: function(ct, position)
18751     {
18752         
18753         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18754         
18755         this.language = this.language || 'en';
18756         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18757         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18758         
18759         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18760         this.format = this.format || 'm/d/y';
18761         this.isInline = false;
18762         this.isInput = true;
18763         this.component = this.el.select('.add-on', true).first() || false;
18764         this.component = (this.component && this.component.length === 0) ? false : this.component;
18765         this.hasInput = this.component && this.inputEl().length;
18766         
18767         if (typeof(this.minViewMode === 'string')) {
18768             switch (this.minViewMode) {
18769                 case 'months':
18770                     this.minViewMode = 1;
18771                     break;
18772                 case 'years':
18773                     this.minViewMode = 2;
18774                     break;
18775                 default:
18776                     this.minViewMode = 0;
18777                     break;
18778             }
18779         }
18780         
18781         if (typeof(this.viewMode === 'string')) {
18782             switch (this.viewMode) {
18783                 case 'months':
18784                     this.viewMode = 1;
18785                     break;
18786                 case 'years':
18787                     this.viewMode = 2;
18788                     break;
18789                 default:
18790                     this.viewMode = 0;
18791                     break;
18792             }
18793         }
18794                 
18795         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18796         
18797 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18798         
18799         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18800         
18801         this.picker().on('mousedown', this.onMousedown, this);
18802         this.picker().on('click', this.onClick, this);
18803         
18804         this.picker().addClass('datepicker-dropdown');
18805         
18806         this.startViewMode = this.viewMode;
18807         
18808         if(this.singleMode){
18809             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18810                 v.setVisibilityMode(Roo.Element.DISPLAY);
18811                 v.hide();
18812             });
18813             
18814             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18815                 v.setStyle('width', '189px');
18816             });
18817         }
18818         
18819         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18820             if(!this.calendarWeeks){
18821                 v.remove();
18822                 return;
18823             }
18824             
18825             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18826             v.attr('colspan', function(i, val){
18827                 return parseInt(val) + 1;
18828             });
18829         });
18830                         
18831         
18832         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18833         
18834         this.setStartDate(this.startDate);
18835         this.setEndDate(this.endDate);
18836         
18837         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18838         
18839         this.fillDow();
18840         this.fillMonths();
18841         this.update();
18842         this.showMode();
18843         
18844         if(this.isInline) {
18845             this.showPopup();
18846         }
18847     },
18848     
18849     picker : function()
18850     {
18851         return this.pickerEl;
18852 //        return this.el.select('.datepicker', true).first();
18853     },
18854     
18855     fillDow: function()
18856     {
18857         var dowCnt = this.weekStart;
18858         
18859         var dow = {
18860             tag: 'tr',
18861             cn: [
18862                 
18863             ]
18864         };
18865         
18866         if(this.calendarWeeks){
18867             dow.cn.push({
18868                 tag: 'th',
18869                 cls: 'cw',
18870                 html: '&nbsp;'
18871             })
18872         }
18873         
18874         while (dowCnt < this.weekStart + 7) {
18875             dow.cn.push({
18876                 tag: 'th',
18877                 cls: 'dow',
18878                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18879             });
18880         }
18881         
18882         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18883     },
18884     
18885     fillMonths: function()
18886     {    
18887         var i = 0;
18888         var months = this.picker().select('>.datepicker-months td', true).first();
18889         
18890         months.dom.innerHTML = '';
18891         
18892         while (i < 12) {
18893             var month = {
18894                 tag: 'span',
18895                 cls: 'month',
18896                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18897             };
18898             
18899             months.createChild(month);
18900         }
18901         
18902     },
18903     
18904     update: function()
18905     {
18906         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;
18907         
18908         if (this.date < this.startDate) {
18909             this.viewDate = new Date(this.startDate);
18910         } else if (this.date > this.endDate) {
18911             this.viewDate = new Date(this.endDate);
18912         } else {
18913             this.viewDate = new Date(this.date);
18914         }
18915         
18916         this.fill();
18917     },
18918     
18919     fill: function() 
18920     {
18921         var d = new Date(this.viewDate),
18922                 year = d.getUTCFullYear(),
18923                 month = d.getUTCMonth(),
18924                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18925                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18926                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18927                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18928                 currentDate = this.date && this.date.valueOf(),
18929                 today = this.UTCToday();
18930         
18931         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18932         
18933 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18934         
18935 //        this.picker.select('>tfoot th.today').
18936 //                                              .text(dates[this.language].today)
18937 //                                              .toggle(this.todayBtn !== false);
18938     
18939         this.updateNavArrows();
18940         this.fillMonths();
18941                                                 
18942         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18943         
18944         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18945          
18946         prevMonth.setUTCDate(day);
18947         
18948         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18949         
18950         var nextMonth = new Date(prevMonth);
18951         
18952         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18953         
18954         nextMonth = nextMonth.valueOf();
18955         
18956         var fillMonths = false;
18957         
18958         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18959         
18960         while(prevMonth.valueOf() <= nextMonth) {
18961             var clsName = '';
18962             
18963             if (prevMonth.getUTCDay() === this.weekStart) {
18964                 if(fillMonths){
18965                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18966                 }
18967                     
18968                 fillMonths = {
18969                     tag: 'tr',
18970                     cn: []
18971                 };
18972                 
18973                 if(this.calendarWeeks){
18974                     // ISO 8601: First week contains first thursday.
18975                     // ISO also states week starts on Monday, but we can be more abstract here.
18976                     var
18977                     // Start of current week: based on weekstart/current date
18978                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18979                     // Thursday of this week
18980                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18981                     // First Thursday of year, year from thursday
18982                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18983                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18984                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18985                     
18986                     fillMonths.cn.push({
18987                         tag: 'td',
18988                         cls: 'cw',
18989                         html: calWeek
18990                     });
18991                 }
18992             }
18993             
18994             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18995                 clsName += ' old';
18996             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18997                 clsName += ' new';
18998             }
18999             if (this.todayHighlight &&
19000                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19001                 prevMonth.getUTCMonth() == today.getMonth() &&
19002                 prevMonth.getUTCDate() == today.getDate()) {
19003                 clsName += ' today';
19004             }
19005             
19006             if (currentDate && prevMonth.valueOf() === currentDate) {
19007                 clsName += ' active';
19008             }
19009             
19010             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19011                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19012                     clsName += ' disabled';
19013             }
19014             
19015             fillMonths.cn.push({
19016                 tag: 'td',
19017                 cls: 'day ' + clsName,
19018                 html: prevMonth.getDate()
19019             });
19020             
19021             prevMonth.setDate(prevMonth.getDate()+1);
19022         }
19023           
19024         var currentYear = this.date && this.date.getUTCFullYear();
19025         var currentMonth = this.date && this.date.getUTCMonth();
19026         
19027         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19028         
19029         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19030             v.removeClass('active');
19031             
19032             if(currentYear === year && k === currentMonth){
19033                 v.addClass('active');
19034             }
19035             
19036             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19037                 v.addClass('disabled');
19038             }
19039             
19040         });
19041         
19042         
19043         year = parseInt(year/10, 10) * 10;
19044         
19045         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19046         
19047         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19048         
19049         year -= 1;
19050         for (var i = -1; i < 11; i++) {
19051             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19052                 tag: 'span',
19053                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19054                 html: year
19055             });
19056             
19057             year += 1;
19058         }
19059     },
19060     
19061     showMode: function(dir) 
19062     {
19063         if (dir) {
19064             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19065         }
19066         
19067         Roo.each(this.picker().select('>div',true).elements, function(v){
19068             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19069             v.hide();
19070         });
19071         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19072     },
19073     
19074     place: function()
19075     {
19076         if(this.isInline) {
19077             return;
19078         }
19079         
19080         this.picker().removeClass(['bottom', 'top']);
19081         
19082         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19083             /*
19084              * place to the top of element!
19085              *
19086              */
19087             
19088             this.picker().addClass('top');
19089             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19090             
19091             return;
19092         }
19093         
19094         this.picker().addClass('bottom');
19095         
19096         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19097     },
19098     
19099     parseDate : function(value)
19100     {
19101         if(!value || value instanceof Date){
19102             return value;
19103         }
19104         var v = Date.parseDate(value, this.format);
19105         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19106             v = Date.parseDate(value, 'Y-m-d');
19107         }
19108         if(!v && this.altFormats){
19109             if(!this.altFormatsArray){
19110                 this.altFormatsArray = this.altFormats.split("|");
19111             }
19112             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19113                 v = Date.parseDate(value, this.altFormatsArray[i]);
19114             }
19115         }
19116         return v;
19117     },
19118     
19119     formatDate : function(date, fmt)
19120     {   
19121         return (!date || !(date instanceof Date)) ?
19122         date : date.dateFormat(fmt || this.format);
19123     },
19124     
19125     onFocus : function()
19126     {
19127         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19128         this.showPopup();
19129     },
19130     
19131     onBlur : function()
19132     {
19133         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19134         
19135         var d = this.inputEl().getValue();
19136         
19137         this.setValue(d);
19138                 
19139         this.hidePopup();
19140     },
19141     
19142     showPopup : function()
19143     {
19144         this.picker().show();
19145         this.update();
19146         this.place();
19147         
19148         this.fireEvent('showpopup', this, this.date);
19149     },
19150     
19151     hidePopup : function()
19152     {
19153         if(this.isInline) {
19154             return;
19155         }
19156         this.picker().hide();
19157         this.viewMode = this.startViewMode;
19158         this.showMode();
19159         
19160         this.fireEvent('hidepopup', this, this.date);
19161         
19162     },
19163     
19164     onMousedown: function(e)
19165     {
19166         e.stopPropagation();
19167         e.preventDefault();
19168     },
19169     
19170     keyup: function(e)
19171     {
19172         Roo.bootstrap.DateField.superclass.keyup.call(this);
19173         this.update();
19174     },
19175
19176     setValue: function(v)
19177     {
19178         if(this.fireEvent('beforeselect', this, v) !== false){
19179             var d = new Date(this.parseDate(v) ).clearTime();
19180         
19181             if(isNaN(d.getTime())){
19182                 this.date = this.viewDate = '';
19183                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19184                 return;
19185             }
19186
19187             v = this.formatDate(d);
19188
19189             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19190
19191             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19192
19193             this.update();
19194
19195             this.fireEvent('select', this, this.date);
19196         }
19197     },
19198     
19199     getValue: function()
19200     {
19201         return this.formatDate(this.date);
19202     },
19203     
19204     fireKey: function(e)
19205     {
19206         if (!this.picker().isVisible()){
19207             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19208                 this.showPopup();
19209             }
19210             return;
19211         }
19212         
19213         var dateChanged = false,
19214         dir, day, month,
19215         newDate, newViewDate;
19216         
19217         switch(e.keyCode){
19218             case 27: // escape
19219                 this.hidePopup();
19220                 e.preventDefault();
19221                 break;
19222             case 37: // left
19223             case 39: // right
19224                 if (!this.keyboardNavigation) {
19225                     break;
19226                 }
19227                 dir = e.keyCode == 37 ? -1 : 1;
19228                 
19229                 if (e.ctrlKey){
19230                     newDate = this.moveYear(this.date, dir);
19231                     newViewDate = this.moveYear(this.viewDate, dir);
19232                 } else if (e.shiftKey){
19233                     newDate = this.moveMonth(this.date, dir);
19234                     newViewDate = this.moveMonth(this.viewDate, dir);
19235                 } else {
19236                     newDate = new Date(this.date);
19237                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19238                     newViewDate = new Date(this.viewDate);
19239                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19240                 }
19241                 if (this.dateWithinRange(newDate)){
19242                     this.date = newDate;
19243                     this.viewDate = newViewDate;
19244                     this.setValue(this.formatDate(this.date));
19245 //                    this.update();
19246                     e.preventDefault();
19247                     dateChanged = true;
19248                 }
19249                 break;
19250             case 38: // up
19251             case 40: // down
19252                 if (!this.keyboardNavigation) {
19253                     break;
19254                 }
19255                 dir = e.keyCode == 38 ? -1 : 1;
19256                 if (e.ctrlKey){
19257                     newDate = this.moveYear(this.date, dir);
19258                     newViewDate = this.moveYear(this.viewDate, dir);
19259                 } else if (e.shiftKey){
19260                     newDate = this.moveMonth(this.date, dir);
19261                     newViewDate = this.moveMonth(this.viewDate, dir);
19262                 } else {
19263                     newDate = new Date(this.date);
19264                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19265                     newViewDate = new Date(this.viewDate);
19266                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19267                 }
19268                 if (this.dateWithinRange(newDate)){
19269                     this.date = newDate;
19270                     this.viewDate = newViewDate;
19271                     this.setValue(this.formatDate(this.date));
19272 //                    this.update();
19273                     e.preventDefault();
19274                     dateChanged = true;
19275                 }
19276                 break;
19277             case 13: // enter
19278                 this.setValue(this.formatDate(this.date));
19279                 this.hidePopup();
19280                 e.preventDefault();
19281                 break;
19282             case 9: // tab
19283                 this.setValue(this.formatDate(this.date));
19284                 this.hidePopup();
19285                 break;
19286             case 16: // shift
19287             case 17: // ctrl
19288             case 18: // alt
19289                 break;
19290             default :
19291                 this.hidePopup();
19292                 
19293         }
19294     },
19295     
19296     
19297     onClick: function(e) 
19298     {
19299         e.stopPropagation();
19300         e.preventDefault();
19301         
19302         var target = e.getTarget();
19303         
19304         if(target.nodeName.toLowerCase() === 'i'){
19305             target = Roo.get(target).dom.parentNode;
19306         }
19307         
19308         var nodeName = target.nodeName;
19309         var className = target.className;
19310         var html = target.innerHTML;
19311         //Roo.log(nodeName);
19312         
19313         switch(nodeName.toLowerCase()) {
19314             case 'th':
19315                 switch(className) {
19316                     case 'switch':
19317                         this.showMode(1);
19318                         break;
19319                     case 'prev':
19320                     case 'next':
19321                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19322                         switch(this.viewMode){
19323                                 case 0:
19324                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19325                                         break;
19326                                 case 1:
19327                                 case 2:
19328                                         this.viewDate = this.moveYear(this.viewDate, dir);
19329                                         break;
19330                         }
19331                         this.fill();
19332                         break;
19333                     case 'today':
19334                         var date = new Date();
19335                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19336 //                        this.fill()
19337                         this.setValue(this.formatDate(this.date));
19338                         
19339                         this.hidePopup();
19340                         break;
19341                 }
19342                 break;
19343             case 'span':
19344                 if (className.indexOf('disabled') < 0) {
19345                     this.viewDate.setUTCDate(1);
19346                     if (className.indexOf('month') > -1) {
19347                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19348                     } else {
19349                         var year = parseInt(html, 10) || 0;
19350                         this.viewDate.setUTCFullYear(year);
19351                         
19352                     }
19353                     
19354                     if(this.singleMode){
19355                         this.setValue(this.formatDate(this.viewDate));
19356                         this.hidePopup();
19357                         return;
19358                     }
19359                     
19360                     this.showMode(-1);
19361                     this.fill();
19362                 }
19363                 break;
19364                 
19365             case 'td':
19366                 //Roo.log(className);
19367                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19368                     var day = parseInt(html, 10) || 1;
19369                     var year = this.viewDate.getUTCFullYear(),
19370                         month = this.viewDate.getUTCMonth();
19371
19372                     if (className.indexOf('old') > -1) {
19373                         if(month === 0 ){
19374                             month = 11;
19375                             year -= 1;
19376                         }else{
19377                             month -= 1;
19378                         }
19379                     } else if (className.indexOf('new') > -1) {
19380                         if (month == 11) {
19381                             month = 0;
19382                             year += 1;
19383                         } else {
19384                             month += 1;
19385                         }
19386                     }
19387                     //Roo.log([year,month,day]);
19388                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19389                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19390 //                    this.fill();
19391                     //Roo.log(this.formatDate(this.date));
19392                     this.setValue(this.formatDate(this.date));
19393                     this.hidePopup();
19394                 }
19395                 break;
19396         }
19397     },
19398     
19399     setStartDate: function(startDate)
19400     {
19401         this.startDate = startDate || -Infinity;
19402         if (this.startDate !== -Infinity) {
19403             this.startDate = this.parseDate(this.startDate);
19404         }
19405         this.update();
19406         this.updateNavArrows();
19407     },
19408
19409     setEndDate: function(endDate)
19410     {
19411         this.endDate = endDate || Infinity;
19412         if (this.endDate !== Infinity) {
19413             this.endDate = this.parseDate(this.endDate);
19414         }
19415         this.update();
19416         this.updateNavArrows();
19417     },
19418     
19419     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19420     {
19421         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19422         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19423             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19424         }
19425         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19426             return parseInt(d, 10);
19427         });
19428         this.update();
19429         this.updateNavArrows();
19430     },
19431     
19432     updateNavArrows: function() 
19433     {
19434         if(this.singleMode){
19435             return;
19436         }
19437         
19438         var d = new Date(this.viewDate),
19439         year = d.getUTCFullYear(),
19440         month = d.getUTCMonth();
19441         
19442         Roo.each(this.picker().select('.prev', true).elements, function(v){
19443             v.show();
19444             switch (this.viewMode) {
19445                 case 0:
19446
19447                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19448                         v.hide();
19449                     }
19450                     break;
19451                 case 1:
19452                 case 2:
19453                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19454                         v.hide();
19455                     }
19456                     break;
19457             }
19458         });
19459         
19460         Roo.each(this.picker().select('.next', true).elements, function(v){
19461             v.show();
19462             switch (this.viewMode) {
19463                 case 0:
19464
19465                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19466                         v.hide();
19467                     }
19468                     break;
19469                 case 1:
19470                 case 2:
19471                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19472                         v.hide();
19473                     }
19474                     break;
19475             }
19476         })
19477     },
19478     
19479     moveMonth: function(date, dir)
19480     {
19481         if (!dir) {
19482             return date;
19483         }
19484         var new_date = new Date(date.valueOf()),
19485         day = new_date.getUTCDate(),
19486         month = new_date.getUTCMonth(),
19487         mag = Math.abs(dir),
19488         new_month, test;
19489         dir = dir > 0 ? 1 : -1;
19490         if (mag == 1){
19491             test = dir == -1
19492             // If going back one month, make sure month is not current month
19493             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19494             ? function(){
19495                 return new_date.getUTCMonth() == month;
19496             }
19497             // If going forward one month, make sure month is as expected
19498             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19499             : function(){
19500                 return new_date.getUTCMonth() != new_month;
19501             };
19502             new_month = month + dir;
19503             new_date.setUTCMonth(new_month);
19504             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19505             if (new_month < 0 || new_month > 11) {
19506                 new_month = (new_month + 12) % 12;
19507             }
19508         } else {
19509             // For magnitudes >1, move one month at a time...
19510             for (var i=0; i<mag; i++) {
19511                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19512                 new_date = this.moveMonth(new_date, dir);
19513             }
19514             // ...then reset the day, keeping it in the new month
19515             new_month = new_date.getUTCMonth();
19516             new_date.setUTCDate(day);
19517             test = function(){
19518                 return new_month != new_date.getUTCMonth();
19519             };
19520         }
19521         // Common date-resetting loop -- if date is beyond end of month, make it
19522         // end of month
19523         while (test()){
19524             new_date.setUTCDate(--day);
19525             new_date.setUTCMonth(new_month);
19526         }
19527         return new_date;
19528     },
19529
19530     moveYear: function(date, dir)
19531     {
19532         return this.moveMonth(date, dir*12);
19533     },
19534
19535     dateWithinRange: function(date)
19536     {
19537         return date >= this.startDate && date <= this.endDate;
19538     },
19539
19540     
19541     remove: function() 
19542     {
19543         this.picker().remove();
19544     },
19545     
19546     validateValue : function(value)
19547     {
19548         if(this.getVisibilityEl().hasClass('hidden')){
19549             return true;
19550         }
19551         
19552         if(value.length < 1)  {
19553             if(this.allowBlank){
19554                 return true;
19555             }
19556             return false;
19557         }
19558         
19559         if(value.length < this.minLength){
19560             return false;
19561         }
19562         if(value.length > this.maxLength){
19563             return false;
19564         }
19565         if(this.vtype){
19566             var vt = Roo.form.VTypes;
19567             if(!vt[this.vtype](value, this)){
19568                 return false;
19569             }
19570         }
19571         if(typeof this.validator == "function"){
19572             var msg = this.validator(value);
19573             if(msg !== true){
19574                 return false;
19575             }
19576         }
19577         
19578         if(this.regex && !this.regex.test(value)){
19579             return false;
19580         }
19581         
19582         if(typeof(this.parseDate(value)) == 'undefined'){
19583             return false;
19584         }
19585         
19586         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19587             return false;
19588         }      
19589         
19590         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19591             return false;
19592         } 
19593         
19594         
19595         return true;
19596     },
19597     
19598     reset : function()
19599     {
19600         this.date = this.viewDate = '';
19601         
19602         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19603     }
19604    
19605 });
19606
19607 Roo.apply(Roo.bootstrap.DateField,  {
19608     
19609     head : {
19610         tag: 'thead',
19611         cn: [
19612         {
19613             tag: 'tr',
19614             cn: [
19615             {
19616                 tag: 'th',
19617                 cls: 'prev',
19618                 html: '<i class="fa fa-arrow-left"/>'
19619             },
19620             {
19621                 tag: 'th',
19622                 cls: 'switch',
19623                 colspan: '5'
19624             },
19625             {
19626                 tag: 'th',
19627                 cls: 'next',
19628                 html: '<i class="fa fa-arrow-right"/>'
19629             }
19630
19631             ]
19632         }
19633         ]
19634     },
19635     
19636     content : {
19637         tag: 'tbody',
19638         cn: [
19639         {
19640             tag: 'tr',
19641             cn: [
19642             {
19643                 tag: 'td',
19644                 colspan: '7'
19645             }
19646             ]
19647         }
19648         ]
19649     },
19650     
19651     footer : {
19652         tag: 'tfoot',
19653         cn: [
19654         {
19655             tag: 'tr',
19656             cn: [
19657             {
19658                 tag: 'th',
19659                 colspan: '7',
19660                 cls: 'today'
19661             }
19662                     
19663             ]
19664         }
19665         ]
19666     },
19667     
19668     dates:{
19669         en: {
19670             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19671             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19672             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19673             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19674             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19675             today: "Today"
19676         }
19677     },
19678     
19679     modes: [
19680     {
19681         clsName: 'days',
19682         navFnc: 'Month',
19683         navStep: 1
19684     },
19685     {
19686         clsName: 'months',
19687         navFnc: 'FullYear',
19688         navStep: 1
19689     },
19690     {
19691         clsName: 'years',
19692         navFnc: 'FullYear',
19693         navStep: 10
19694     }]
19695 });
19696
19697 Roo.apply(Roo.bootstrap.DateField,  {
19698   
19699     template : {
19700         tag: 'div',
19701         cls: 'datepicker dropdown-menu roo-dynamic',
19702         cn: [
19703         {
19704             tag: 'div',
19705             cls: 'datepicker-days',
19706             cn: [
19707             {
19708                 tag: 'table',
19709                 cls: 'table-condensed',
19710                 cn:[
19711                 Roo.bootstrap.DateField.head,
19712                 {
19713                     tag: 'tbody'
19714                 },
19715                 Roo.bootstrap.DateField.footer
19716                 ]
19717             }
19718             ]
19719         },
19720         {
19721             tag: 'div',
19722             cls: 'datepicker-months',
19723             cn: [
19724             {
19725                 tag: 'table',
19726                 cls: 'table-condensed',
19727                 cn:[
19728                 Roo.bootstrap.DateField.head,
19729                 Roo.bootstrap.DateField.content,
19730                 Roo.bootstrap.DateField.footer
19731                 ]
19732             }
19733             ]
19734         },
19735         {
19736             tag: 'div',
19737             cls: 'datepicker-years',
19738             cn: [
19739             {
19740                 tag: 'table',
19741                 cls: 'table-condensed',
19742                 cn:[
19743                 Roo.bootstrap.DateField.head,
19744                 Roo.bootstrap.DateField.content,
19745                 Roo.bootstrap.DateField.footer
19746                 ]
19747             }
19748             ]
19749         }
19750         ]
19751     }
19752 });
19753
19754  
19755
19756  /*
19757  * - LGPL
19758  *
19759  * TimeField
19760  * 
19761  */
19762
19763 /**
19764  * @class Roo.bootstrap.TimeField
19765  * @extends Roo.bootstrap.Input
19766  * Bootstrap DateField class
19767  * 
19768  * 
19769  * @constructor
19770  * Create a new TimeField
19771  * @param {Object} config The config object
19772  */
19773
19774 Roo.bootstrap.TimeField = function(config){
19775     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19776     this.addEvents({
19777             /**
19778              * @event show
19779              * Fires when this field show.
19780              * @param {Roo.bootstrap.DateField} thisthis
19781              * @param {Mixed} date The date value
19782              */
19783             show : true,
19784             /**
19785              * @event show
19786              * Fires when this field hide.
19787              * @param {Roo.bootstrap.DateField} this
19788              * @param {Mixed} date The date value
19789              */
19790             hide : true,
19791             /**
19792              * @event select
19793              * Fires when select a date.
19794              * @param {Roo.bootstrap.DateField} this
19795              * @param {Mixed} date The date value
19796              */
19797             select : true
19798         });
19799 };
19800
19801 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19802     
19803     /**
19804      * @cfg {String} format
19805      * The default time format string which can be overriden for localization support.  The format must be
19806      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19807      */
19808     format : "H:i",
19809        
19810     onRender: function(ct, position)
19811     {
19812         
19813         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19814                 
19815         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19816         
19817         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19818         
19819         this.pop = this.picker().select('>.datepicker-time',true).first();
19820         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19821         
19822         this.picker().on('mousedown', this.onMousedown, this);
19823         this.picker().on('click', this.onClick, this);
19824         
19825         this.picker().addClass('datepicker-dropdown');
19826     
19827         this.fillTime();
19828         this.update();
19829             
19830         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19831         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19832         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19833         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19834         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19835         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19836
19837     },
19838     
19839     fireKey: function(e){
19840         if (!this.picker().isVisible()){
19841             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19842                 this.show();
19843             }
19844             return;
19845         }
19846
19847         e.preventDefault();
19848         
19849         switch(e.keyCode){
19850             case 27: // escape
19851                 this.hide();
19852                 break;
19853             case 37: // left
19854             case 39: // right
19855                 this.onTogglePeriod();
19856                 break;
19857             case 38: // up
19858                 this.onIncrementMinutes();
19859                 break;
19860             case 40: // down
19861                 this.onDecrementMinutes();
19862                 break;
19863             case 13: // enter
19864             case 9: // tab
19865                 this.setTime();
19866                 break;
19867         }
19868     },
19869     
19870     onClick: function(e) {
19871         e.stopPropagation();
19872         e.preventDefault();
19873     },
19874     
19875     picker : function()
19876     {
19877         return this.el.select('.datepicker', true).first();
19878     },
19879     
19880     fillTime: function()
19881     {    
19882         var time = this.pop.select('tbody', true).first();
19883         
19884         time.dom.innerHTML = '';
19885         
19886         time.createChild({
19887             tag: 'tr',
19888             cn: [
19889                 {
19890                     tag: 'td',
19891                     cn: [
19892                         {
19893                             tag: 'a',
19894                             href: '#',
19895                             cls: 'btn',
19896                             cn: [
19897                                 {
19898                                     tag: 'span',
19899                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19900                                 }
19901                             ]
19902                         } 
19903                     ]
19904                 },
19905                 {
19906                     tag: 'td',
19907                     cls: 'separator'
19908                 },
19909                 {
19910                     tag: 'td',
19911                     cn: [
19912                         {
19913                             tag: 'a',
19914                             href: '#',
19915                             cls: 'btn',
19916                             cn: [
19917                                 {
19918                                     tag: 'span',
19919                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19920                                 }
19921                             ]
19922                         }
19923                     ]
19924                 },
19925                 {
19926                     tag: 'td',
19927                     cls: 'separator'
19928                 }
19929             ]
19930         });
19931         
19932         time.createChild({
19933             tag: 'tr',
19934             cn: [
19935                 {
19936                     tag: 'td',
19937                     cn: [
19938                         {
19939                             tag: 'span',
19940                             cls: 'timepicker-hour',
19941                             html: '00'
19942                         }  
19943                     ]
19944                 },
19945                 {
19946                     tag: 'td',
19947                     cls: 'separator',
19948                     html: ':'
19949                 },
19950                 {
19951                     tag: 'td',
19952                     cn: [
19953                         {
19954                             tag: 'span',
19955                             cls: 'timepicker-minute',
19956                             html: '00'
19957                         }  
19958                     ]
19959                 },
19960                 {
19961                     tag: 'td',
19962                     cls: 'separator'
19963                 },
19964                 {
19965                     tag: 'td',
19966                     cn: [
19967                         {
19968                             tag: 'button',
19969                             type: 'button',
19970                             cls: 'btn btn-primary period',
19971                             html: 'AM'
19972                             
19973                         }
19974                     ]
19975                 }
19976             ]
19977         });
19978         
19979         time.createChild({
19980             tag: 'tr',
19981             cn: [
19982                 {
19983                     tag: 'td',
19984                     cn: [
19985                         {
19986                             tag: 'a',
19987                             href: '#',
19988                             cls: 'btn',
19989                             cn: [
19990                                 {
19991                                     tag: 'span',
19992                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19993                                 }
19994                             ]
19995                         }
19996                     ]
19997                 },
19998                 {
19999                     tag: 'td',
20000                     cls: 'separator'
20001                 },
20002                 {
20003                     tag: 'td',
20004                     cn: [
20005                         {
20006                             tag: 'a',
20007                             href: '#',
20008                             cls: 'btn',
20009                             cn: [
20010                                 {
20011                                     tag: 'span',
20012                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20013                                 }
20014                             ]
20015                         }
20016                     ]
20017                 },
20018                 {
20019                     tag: 'td',
20020                     cls: 'separator'
20021                 }
20022             ]
20023         });
20024         
20025     },
20026     
20027     update: function()
20028     {
20029         
20030         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20031         
20032         this.fill();
20033     },
20034     
20035     fill: function() 
20036     {
20037         var hours = this.time.getHours();
20038         var minutes = this.time.getMinutes();
20039         var period = 'AM';
20040         
20041         if(hours > 11){
20042             period = 'PM';
20043         }
20044         
20045         if(hours == 0){
20046             hours = 12;
20047         }
20048         
20049         
20050         if(hours > 12){
20051             hours = hours - 12;
20052         }
20053         
20054         if(hours < 10){
20055             hours = '0' + hours;
20056         }
20057         
20058         if(minutes < 10){
20059             minutes = '0' + minutes;
20060         }
20061         
20062         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20063         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20064         this.pop.select('button', true).first().dom.innerHTML = period;
20065         
20066     },
20067     
20068     place: function()
20069     {   
20070         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20071         
20072         var cls = ['bottom'];
20073         
20074         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20075             cls.pop();
20076             cls.push('top');
20077         }
20078         
20079         cls.push('right');
20080         
20081         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20082             cls.pop();
20083             cls.push('left');
20084         }
20085         
20086         this.picker().addClass(cls.join('-'));
20087         
20088         var _this = this;
20089         
20090         Roo.each(cls, function(c){
20091             if(c == 'bottom'){
20092                 _this.picker().setTop(_this.inputEl().getHeight());
20093                 return;
20094             }
20095             if(c == 'top'){
20096                 _this.picker().setTop(0 - _this.picker().getHeight());
20097                 return;
20098             }
20099             
20100             if(c == 'left'){
20101                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20102                 return;
20103             }
20104             if(c == 'right'){
20105                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20106                 return;
20107             }
20108         });
20109         
20110     },
20111   
20112     onFocus : function()
20113     {
20114         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20115         this.show();
20116     },
20117     
20118     onBlur : function()
20119     {
20120         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20121         this.hide();
20122     },
20123     
20124     show : function()
20125     {
20126         this.picker().show();
20127         this.pop.show();
20128         this.update();
20129         this.place();
20130         
20131         this.fireEvent('show', this, this.date);
20132     },
20133     
20134     hide : function()
20135     {
20136         this.picker().hide();
20137         this.pop.hide();
20138         
20139         this.fireEvent('hide', this, this.date);
20140     },
20141     
20142     setTime : function()
20143     {
20144         this.hide();
20145         this.setValue(this.time.format(this.format));
20146         
20147         this.fireEvent('select', this, this.date);
20148         
20149         
20150     },
20151     
20152     onMousedown: function(e){
20153         e.stopPropagation();
20154         e.preventDefault();
20155     },
20156     
20157     onIncrementHours: function()
20158     {
20159         Roo.log('onIncrementHours');
20160         this.time = this.time.add(Date.HOUR, 1);
20161         this.update();
20162         
20163     },
20164     
20165     onDecrementHours: function()
20166     {
20167         Roo.log('onDecrementHours');
20168         this.time = this.time.add(Date.HOUR, -1);
20169         this.update();
20170     },
20171     
20172     onIncrementMinutes: function()
20173     {
20174         Roo.log('onIncrementMinutes');
20175         this.time = this.time.add(Date.MINUTE, 1);
20176         this.update();
20177     },
20178     
20179     onDecrementMinutes: function()
20180     {
20181         Roo.log('onDecrementMinutes');
20182         this.time = this.time.add(Date.MINUTE, -1);
20183         this.update();
20184     },
20185     
20186     onTogglePeriod: function()
20187     {
20188         Roo.log('onTogglePeriod');
20189         this.time = this.time.add(Date.HOUR, 12);
20190         this.update();
20191     }
20192     
20193    
20194 });
20195
20196 Roo.apply(Roo.bootstrap.TimeField,  {
20197     
20198     content : {
20199         tag: 'tbody',
20200         cn: [
20201             {
20202                 tag: 'tr',
20203                 cn: [
20204                 {
20205                     tag: 'td',
20206                     colspan: '7'
20207                 }
20208                 ]
20209             }
20210         ]
20211     },
20212     
20213     footer : {
20214         tag: 'tfoot',
20215         cn: [
20216             {
20217                 tag: 'tr',
20218                 cn: [
20219                 {
20220                     tag: 'th',
20221                     colspan: '7',
20222                     cls: '',
20223                     cn: [
20224                         {
20225                             tag: 'button',
20226                             cls: 'btn btn-info ok',
20227                             html: 'OK'
20228                         }
20229                     ]
20230                 }
20231
20232                 ]
20233             }
20234         ]
20235     }
20236 });
20237
20238 Roo.apply(Roo.bootstrap.TimeField,  {
20239   
20240     template : {
20241         tag: 'div',
20242         cls: 'datepicker dropdown-menu',
20243         cn: [
20244             {
20245                 tag: 'div',
20246                 cls: 'datepicker-time',
20247                 cn: [
20248                 {
20249                     tag: 'table',
20250                     cls: 'table-condensed',
20251                     cn:[
20252                     Roo.bootstrap.TimeField.content,
20253                     Roo.bootstrap.TimeField.footer
20254                     ]
20255                 }
20256                 ]
20257             }
20258         ]
20259     }
20260 });
20261
20262  
20263
20264  /*
20265  * - LGPL
20266  *
20267  * MonthField
20268  * 
20269  */
20270
20271 /**
20272  * @class Roo.bootstrap.MonthField
20273  * @extends Roo.bootstrap.Input
20274  * Bootstrap MonthField class
20275  * 
20276  * @cfg {String} language default en
20277  * 
20278  * @constructor
20279  * Create a new MonthField
20280  * @param {Object} config The config object
20281  */
20282
20283 Roo.bootstrap.MonthField = function(config){
20284     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20285     
20286     this.addEvents({
20287         /**
20288          * @event show
20289          * Fires when this field show.
20290          * @param {Roo.bootstrap.MonthField} this
20291          * @param {Mixed} date The date value
20292          */
20293         show : true,
20294         /**
20295          * @event show
20296          * Fires when this field hide.
20297          * @param {Roo.bootstrap.MonthField} this
20298          * @param {Mixed} date The date value
20299          */
20300         hide : true,
20301         /**
20302          * @event select
20303          * Fires when select a date.
20304          * @param {Roo.bootstrap.MonthField} this
20305          * @param {String} oldvalue The old value
20306          * @param {String} newvalue The new value
20307          */
20308         select : true
20309     });
20310 };
20311
20312 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20313     
20314     onRender: function(ct, position)
20315     {
20316         
20317         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20318         
20319         this.language = this.language || 'en';
20320         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20321         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20322         
20323         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20324         this.isInline = false;
20325         this.isInput = true;
20326         this.component = this.el.select('.add-on', true).first() || false;
20327         this.component = (this.component && this.component.length === 0) ? false : this.component;
20328         this.hasInput = this.component && this.inputEL().length;
20329         
20330         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20331         
20332         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20333         
20334         this.picker().on('mousedown', this.onMousedown, this);
20335         this.picker().on('click', this.onClick, this);
20336         
20337         this.picker().addClass('datepicker-dropdown');
20338         
20339         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20340             v.setStyle('width', '189px');
20341         });
20342         
20343         this.fillMonths();
20344         
20345         this.update();
20346         
20347         if(this.isInline) {
20348             this.show();
20349         }
20350         
20351     },
20352     
20353     setValue: function(v, suppressEvent)
20354     {   
20355         var o = this.getValue();
20356         
20357         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20358         
20359         this.update();
20360
20361         if(suppressEvent !== true){
20362             this.fireEvent('select', this, o, v);
20363         }
20364         
20365     },
20366     
20367     getValue: function()
20368     {
20369         return this.value;
20370     },
20371     
20372     onClick: function(e) 
20373     {
20374         e.stopPropagation();
20375         e.preventDefault();
20376         
20377         var target = e.getTarget();
20378         
20379         if(target.nodeName.toLowerCase() === 'i'){
20380             target = Roo.get(target).dom.parentNode;
20381         }
20382         
20383         var nodeName = target.nodeName;
20384         var className = target.className;
20385         var html = target.innerHTML;
20386         
20387         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20388             return;
20389         }
20390         
20391         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20392         
20393         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20394         
20395         this.hide();
20396                         
20397     },
20398     
20399     picker : function()
20400     {
20401         return this.pickerEl;
20402     },
20403     
20404     fillMonths: function()
20405     {    
20406         var i = 0;
20407         var months = this.picker().select('>.datepicker-months td', true).first();
20408         
20409         months.dom.innerHTML = '';
20410         
20411         while (i < 12) {
20412             var month = {
20413                 tag: 'span',
20414                 cls: 'month',
20415                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20416             };
20417             
20418             months.createChild(month);
20419         }
20420         
20421     },
20422     
20423     update: function()
20424     {
20425         var _this = this;
20426         
20427         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20428             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20429         }
20430         
20431         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20432             e.removeClass('active');
20433             
20434             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20435                 e.addClass('active');
20436             }
20437         })
20438     },
20439     
20440     place: function()
20441     {
20442         if(this.isInline) {
20443             return;
20444         }
20445         
20446         this.picker().removeClass(['bottom', 'top']);
20447         
20448         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20449             /*
20450              * place to the top of element!
20451              *
20452              */
20453             
20454             this.picker().addClass('top');
20455             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20456             
20457             return;
20458         }
20459         
20460         this.picker().addClass('bottom');
20461         
20462         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20463     },
20464     
20465     onFocus : function()
20466     {
20467         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20468         this.show();
20469     },
20470     
20471     onBlur : function()
20472     {
20473         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20474         
20475         var d = this.inputEl().getValue();
20476         
20477         this.setValue(d);
20478                 
20479         this.hide();
20480     },
20481     
20482     show : function()
20483     {
20484         this.picker().show();
20485         this.picker().select('>.datepicker-months', true).first().show();
20486         this.update();
20487         this.place();
20488         
20489         this.fireEvent('show', this, this.date);
20490     },
20491     
20492     hide : function()
20493     {
20494         if(this.isInline) {
20495             return;
20496         }
20497         this.picker().hide();
20498         this.fireEvent('hide', this, this.date);
20499         
20500     },
20501     
20502     onMousedown: function(e)
20503     {
20504         e.stopPropagation();
20505         e.preventDefault();
20506     },
20507     
20508     keyup: function(e)
20509     {
20510         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20511         this.update();
20512     },
20513
20514     fireKey: function(e)
20515     {
20516         if (!this.picker().isVisible()){
20517             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20518                 this.show();
20519             }
20520             return;
20521         }
20522         
20523         var dir;
20524         
20525         switch(e.keyCode){
20526             case 27: // escape
20527                 this.hide();
20528                 e.preventDefault();
20529                 break;
20530             case 37: // left
20531             case 39: // right
20532                 dir = e.keyCode == 37 ? -1 : 1;
20533                 
20534                 this.vIndex = this.vIndex + dir;
20535                 
20536                 if(this.vIndex < 0){
20537                     this.vIndex = 0;
20538                 }
20539                 
20540                 if(this.vIndex > 11){
20541                     this.vIndex = 11;
20542                 }
20543                 
20544                 if(isNaN(this.vIndex)){
20545                     this.vIndex = 0;
20546                 }
20547                 
20548                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20549                 
20550                 break;
20551             case 38: // up
20552             case 40: // down
20553                 
20554                 dir = e.keyCode == 38 ? -1 : 1;
20555                 
20556                 this.vIndex = this.vIndex + dir * 4;
20557                 
20558                 if(this.vIndex < 0){
20559                     this.vIndex = 0;
20560                 }
20561                 
20562                 if(this.vIndex > 11){
20563                     this.vIndex = 11;
20564                 }
20565                 
20566                 if(isNaN(this.vIndex)){
20567                     this.vIndex = 0;
20568                 }
20569                 
20570                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20571                 break;
20572                 
20573             case 13: // enter
20574                 
20575                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20576                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20577                 }
20578                 
20579                 this.hide();
20580                 e.preventDefault();
20581                 break;
20582             case 9: // tab
20583                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20584                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20585                 }
20586                 this.hide();
20587                 break;
20588             case 16: // shift
20589             case 17: // ctrl
20590             case 18: // alt
20591                 break;
20592             default :
20593                 this.hide();
20594                 
20595         }
20596     },
20597     
20598     remove: function() 
20599     {
20600         this.picker().remove();
20601     }
20602    
20603 });
20604
20605 Roo.apply(Roo.bootstrap.MonthField,  {
20606     
20607     content : {
20608         tag: 'tbody',
20609         cn: [
20610         {
20611             tag: 'tr',
20612             cn: [
20613             {
20614                 tag: 'td',
20615                 colspan: '7'
20616             }
20617             ]
20618         }
20619         ]
20620     },
20621     
20622     dates:{
20623         en: {
20624             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20625             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20626         }
20627     }
20628 });
20629
20630 Roo.apply(Roo.bootstrap.MonthField,  {
20631   
20632     template : {
20633         tag: 'div',
20634         cls: 'datepicker dropdown-menu roo-dynamic',
20635         cn: [
20636             {
20637                 tag: 'div',
20638                 cls: 'datepicker-months',
20639                 cn: [
20640                 {
20641                     tag: 'table',
20642                     cls: 'table-condensed',
20643                     cn:[
20644                         Roo.bootstrap.DateField.content
20645                     ]
20646                 }
20647                 ]
20648             }
20649         ]
20650     }
20651 });
20652
20653  
20654
20655  
20656  /*
20657  * - LGPL
20658  *
20659  * CheckBox
20660  * 
20661  */
20662
20663 /**
20664  * @class Roo.bootstrap.CheckBox
20665  * @extends Roo.bootstrap.Input
20666  * Bootstrap CheckBox class
20667  * 
20668  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20669  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20670  * @cfg {String} boxLabel The text that appears beside the checkbox
20671  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20672  * @cfg {Boolean} checked initnal the element
20673  * @cfg {Boolean} inline inline the element (default false)
20674  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20675  * @cfg {String} tooltip label tooltip
20676  * 
20677  * @constructor
20678  * Create a new CheckBox
20679  * @param {Object} config The config object
20680  */
20681
20682 Roo.bootstrap.CheckBox = function(config){
20683     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20684    
20685     this.addEvents({
20686         /**
20687         * @event check
20688         * Fires when the element is checked or unchecked.
20689         * @param {Roo.bootstrap.CheckBox} this This input
20690         * @param {Boolean} checked The new checked value
20691         */
20692        check : true,
20693        /**
20694         * @event click
20695         * Fires when the element is click.
20696         * @param {Roo.bootstrap.CheckBox} this This input
20697         */
20698        click : true
20699     });
20700     
20701 };
20702
20703 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20704   
20705     inputType: 'checkbox',
20706     inputValue: 1,
20707     valueOff: 0,
20708     boxLabel: false,
20709     checked: false,
20710     weight : false,
20711     inline: false,
20712     tooltip : '',
20713     
20714     getAutoCreate : function()
20715     {
20716         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20717         
20718         var id = Roo.id();
20719         
20720         var cfg = {};
20721         
20722         cfg.cls = 'form-group ' + this.inputType; //input-group
20723         
20724         if(this.inline){
20725             cfg.cls += ' ' + this.inputType + '-inline';
20726         }
20727         
20728         var input =  {
20729             tag: 'input',
20730             id : id,
20731             type : this.inputType,
20732             value : this.inputValue,
20733             cls : 'roo-' + this.inputType, //'form-box',
20734             placeholder : this.placeholder || ''
20735             
20736         };
20737         
20738         if(this.inputType != 'radio'){
20739             var hidden =  {
20740                 tag: 'input',
20741                 type : 'hidden',
20742                 cls : 'roo-hidden-value',
20743                 value : this.checked ? this.inputValue : this.valueOff
20744             };
20745         }
20746         
20747             
20748         if (this.weight) { // Validity check?
20749             cfg.cls += " " + this.inputType + "-" + this.weight;
20750         }
20751         
20752         if (this.disabled) {
20753             input.disabled=true;
20754         }
20755         
20756         if(this.checked){
20757             input.checked = this.checked;
20758         }
20759         
20760         if (this.name) {
20761             
20762             input.name = this.name;
20763             
20764             if(this.inputType != 'radio'){
20765                 hidden.name = this.name;
20766                 input.name = '_hidden_' + this.name;
20767             }
20768         }
20769         
20770         if (this.size) {
20771             input.cls += ' input-' + this.size;
20772         }
20773         
20774         var settings=this;
20775         
20776         ['xs','sm','md','lg'].map(function(size){
20777             if (settings[size]) {
20778                 cfg.cls += ' col-' + size + '-' + settings[size];
20779             }
20780         });
20781         
20782         var inputblock = input;
20783          
20784         if (this.before || this.after) {
20785             
20786             inputblock = {
20787                 cls : 'input-group',
20788                 cn :  [] 
20789             };
20790             
20791             if (this.before) {
20792                 inputblock.cn.push({
20793                     tag :'span',
20794                     cls : 'input-group-addon',
20795                     html : this.before
20796                 });
20797             }
20798             
20799             inputblock.cn.push(input);
20800             
20801             if(this.inputType != 'radio'){
20802                 inputblock.cn.push(hidden);
20803             }
20804             
20805             if (this.after) {
20806                 inputblock.cn.push({
20807                     tag :'span',
20808                     cls : 'input-group-addon',
20809                     html : this.after
20810                 });
20811             }
20812             
20813         }
20814         
20815         if (align ==='left' && this.fieldLabel.length) {
20816 //                Roo.log("left and has label");
20817             cfg.cn = [
20818                 {
20819                     tag: 'label',
20820                     'for' :  id,
20821                     cls : 'control-label',
20822                     html : this.fieldLabel
20823                 },
20824                 {
20825                     cls : "", 
20826                     cn: [
20827                         inputblock
20828                     ]
20829                 }
20830             ];
20831             
20832             if(this.labelWidth > 12){
20833                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20834             }
20835             
20836             if(this.labelWidth < 13 && this.labelmd == 0){
20837                 this.labelmd = this.labelWidth;
20838             }
20839             
20840             if(this.labellg > 0){
20841                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20842                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20843             }
20844             
20845             if(this.labelmd > 0){
20846                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20847                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20848             }
20849             
20850             if(this.labelsm > 0){
20851                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20852                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20853             }
20854             
20855             if(this.labelxs > 0){
20856                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20857                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20858             }
20859             
20860         } else if ( this.fieldLabel.length) {
20861 //                Roo.log(" label");
20862                 cfg.cn = [
20863                    
20864                     {
20865                         tag: this.boxLabel ? 'span' : 'label',
20866                         'for': id,
20867                         cls: 'control-label box-input-label',
20868                         //cls : 'input-group-addon',
20869                         html : this.fieldLabel
20870                     },
20871                     
20872                     inputblock
20873                     
20874                 ];
20875
20876         } else {
20877             
20878 //                Roo.log(" no label && no align");
20879                 cfg.cn = [  inputblock ] ;
20880                 
20881                 
20882         }
20883         
20884         if(this.boxLabel){
20885              var boxLabelCfg = {
20886                 tag: 'label',
20887                 //'for': id, // box label is handled by onclick - so no for...
20888                 cls: 'box-label',
20889                 html: this.boxLabel
20890             };
20891             
20892             if(this.tooltip){
20893                 boxLabelCfg.tooltip = this.tooltip;
20894             }
20895              
20896             cfg.cn.push(boxLabelCfg);
20897         }
20898         
20899         if(this.inputType != 'radio'){
20900             cfg.cn.push(hidden);
20901         }
20902         
20903         return cfg;
20904         
20905     },
20906     
20907     /**
20908      * return the real input element.
20909      */
20910     inputEl: function ()
20911     {
20912         return this.el.select('input.roo-' + this.inputType,true).first();
20913     },
20914     hiddenEl: function ()
20915     {
20916         return this.el.select('input.roo-hidden-value',true).first();
20917     },
20918     
20919     labelEl: function()
20920     {
20921         return this.el.select('label.control-label',true).first();
20922     },
20923     /* depricated... */
20924     
20925     label: function()
20926     {
20927         return this.labelEl();
20928     },
20929     
20930     boxLabelEl: function()
20931     {
20932         return this.el.select('label.box-label',true).first();
20933     },
20934     
20935     initEvents : function()
20936     {
20937 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20938         
20939         this.inputEl().on('click', this.onClick,  this);
20940         
20941         if (this.boxLabel) { 
20942             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20943         }
20944         
20945         this.startValue = this.getValue();
20946         
20947         if(this.groupId){
20948             Roo.bootstrap.CheckBox.register(this);
20949         }
20950     },
20951     
20952     onClick : function(e)
20953     {   
20954         if(this.fireEvent('click', this, e) !== false){
20955             this.setChecked(!this.checked);
20956         }
20957         
20958     },
20959     
20960     setChecked : function(state,suppressEvent)
20961     {
20962         this.startValue = this.getValue();
20963
20964         if(this.inputType == 'radio'){
20965             
20966             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20967                 e.dom.checked = false;
20968             });
20969             
20970             this.inputEl().dom.checked = true;
20971             
20972             this.inputEl().dom.value = this.inputValue;
20973             
20974             if(suppressEvent !== true){
20975                 this.fireEvent('check', this, true);
20976             }
20977             
20978             this.validate();
20979             
20980             return;
20981         }
20982         
20983         this.checked = state;
20984         
20985         this.inputEl().dom.checked = state;
20986         
20987         
20988         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20989         
20990         if(suppressEvent !== true){
20991             this.fireEvent('check', this, state);
20992         }
20993         
20994         this.validate();
20995     },
20996     
20997     getValue : function()
20998     {
20999         if(this.inputType == 'radio'){
21000             return this.getGroupValue();
21001         }
21002         
21003         return this.hiddenEl().dom.value;
21004         
21005     },
21006     
21007     getGroupValue : function()
21008     {
21009         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21010             return '';
21011         }
21012         
21013         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21014     },
21015     
21016     setValue : function(v,suppressEvent)
21017     {
21018         if(this.inputType == 'radio'){
21019             this.setGroupValue(v, suppressEvent);
21020             return;
21021         }
21022         
21023         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21024         
21025         this.validate();
21026     },
21027     
21028     setGroupValue : function(v, suppressEvent)
21029     {
21030         this.startValue = this.getValue();
21031         
21032         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21033             e.dom.checked = false;
21034             
21035             if(e.dom.value == v){
21036                 e.dom.checked = true;
21037             }
21038         });
21039         
21040         if(suppressEvent !== true){
21041             this.fireEvent('check', this, true);
21042         }
21043
21044         this.validate();
21045         
21046         return;
21047     },
21048     
21049     validate : function()
21050     {
21051         if(this.getVisibilityEl().hasClass('hidden')){
21052             return true;
21053         }
21054         
21055         if(
21056                 this.disabled || 
21057                 (this.inputType == 'radio' && this.validateRadio()) ||
21058                 (this.inputType == 'checkbox' && this.validateCheckbox())
21059         ){
21060             this.markValid();
21061             return true;
21062         }
21063         
21064         this.markInvalid();
21065         return false;
21066     },
21067     
21068     validateRadio : function()
21069     {
21070         if(this.getVisibilityEl().hasClass('hidden')){
21071             return true;
21072         }
21073         
21074         if(this.allowBlank){
21075             return true;
21076         }
21077         
21078         var valid = false;
21079         
21080         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21081             if(!e.dom.checked){
21082                 return;
21083             }
21084             
21085             valid = true;
21086             
21087             return false;
21088         });
21089         
21090         return valid;
21091     },
21092     
21093     validateCheckbox : function()
21094     {
21095         if(!this.groupId){
21096             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21097             //return (this.getValue() == this.inputValue) ? true : false;
21098         }
21099         
21100         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21101         
21102         if(!group){
21103             return false;
21104         }
21105         
21106         var r = false;
21107         
21108         for(var i in group){
21109             if(group[i].el.isVisible(true)){
21110                 r = false;
21111                 break;
21112             }
21113             
21114             r = true;
21115         }
21116         
21117         for(var i in group){
21118             if(r){
21119                 break;
21120             }
21121             
21122             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21123         }
21124         
21125         return r;
21126     },
21127     
21128     /**
21129      * Mark this field as valid
21130      */
21131     markValid : function()
21132     {
21133         var _this = this;
21134         
21135         this.fireEvent('valid', this);
21136         
21137         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21138         
21139         if(this.groupId){
21140             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21141         }
21142         
21143         if(label){
21144             label.markValid();
21145         }
21146
21147         if(this.inputType == 'radio'){
21148             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21149                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21150                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21151             });
21152             
21153             return;
21154         }
21155
21156         if(!this.groupId){
21157             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21158             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21159             return;
21160         }
21161         
21162         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21163         
21164         if(!group){
21165             return;
21166         }
21167         
21168         for(var i in group){
21169             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21170             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21171         }
21172     },
21173     
21174      /**
21175      * Mark this field as invalid
21176      * @param {String} msg The validation message
21177      */
21178     markInvalid : function(msg)
21179     {
21180         if(this.allowBlank){
21181             return;
21182         }
21183         
21184         var _this = this;
21185         
21186         this.fireEvent('invalid', this, msg);
21187         
21188         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21189         
21190         if(this.groupId){
21191             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21192         }
21193         
21194         if(label){
21195             label.markInvalid();
21196         }
21197             
21198         if(this.inputType == 'radio'){
21199             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21200                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21201                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21202             });
21203             
21204             return;
21205         }
21206         
21207         if(!this.groupId){
21208             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21209             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21210             return;
21211         }
21212         
21213         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21214         
21215         if(!group){
21216             return;
21217         }
21218         
21219         for(var i in group){
21220             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21221             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21222         }
21223         
21224     },
21225     
21226     clearInvalid : function()
21227     {
21228         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21229         
21230         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21231         
21232         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21233         
21234         if (label && label.iconEl) {
21235             label.iconEl.removeClass(label.validClass);
21236             label.iconEl.removeClass(label.invalidClass);
21237         }
21238     },
21239     
21240     disable : function()
21241     {
21242         if(this.inputType != 'radio'){
21243             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21244             return;
21245         }
21246         
21247         var _this = this;
21248         
21249         if(this.rendered){
21250             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21251                 _this.getActionEl().addClass(this.disabledClass);
21252                 e.dom.disabled = true;
21253             });
21254         }
21255         
21256         this.disabled = true;
21257         this.fireEvent("disable", this);
21258         return this;
21259     },
21260
21261     enable : function()
21262     {
21263         if(this.inputType != 'radio'){
21264             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21265             return;
21266         }
21267         
21268         var _this = this;
21269         
21270         if(this.rendered){
21271             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21272                 _this.getActionEl().removeClass(this.disabledClass);
21273                 e.dom.disabled = false;
21274             });
21275         }
21276         
21277         this.disabled = false;
21278         this.fireEvent("enable", this);
21279         return this;
21280     },
21281     
21282     setBoxLabel : function(v)
21283     {
21284         this.boxLabel = v;
21285         
21286         if(this.rendered){
21287             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21288         }
21289     }
21290
21291 });
21292
21293 Roo.apply(Roo.bootstrap.CheckBox, {
21294     
21295     groups: {},
21296     
21297      /**
21298     * register a CheckBox Group
21299     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21300     */
21301     register : function(checkbox)
21302     {
21303         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21304             this.groups[checkbox.groupId] = {};
21305         }
21306         
21307         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21308             return;
21309         }
21310         
21311         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21312         
21313     },
21314     /**
21315     * fetch a CheckBox Group based on the group ID
21316     * @param {string} the group ID
21317     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21318     */
21319     get: function(groupId) {
21320         if (typeof(this.groups[groupId]) == 'undefined') {
21321             return false;
21322         }
21323         
21324         return this.groups[groupId] ;
21325     }
21326     
21327     
21328 });
21329 /*
21330  * - LGPL
21331  *
21332  * RadioItem
21333  * 
21334  */
21335
21336 /**
21337  * @class Roo.bootstrap.Radio
21338  * @extends Roo.bootstrap.Component
21339  * Bootstrap Radio class
21340  * @cfg {String} boxLabel - the label associated
21341  * @cfg {String} value - the value of radio
21342  * 
21343  * @constructor
21344  * Create a new Radio
21345  * @param {Object} config The config object
21346  */
21347 Roo.bootstrap.Radio = function(config){
21348     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21349     
21350 };
21351
21352 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21353     
21354     boxLabel : '',
21355     
21356     value : '',
21357     
21358     getAutoCreate : function()
21359     {
21360         var cfg = {
21361             tag : 'div',
21362             cls : 'form-group radio',
21363             cn : [
21364                 {
21365                     tag : 'label',
21366                     cls : 'box-label',
21367                     html : this.boxLabel
21368                 }
21369             ]
21370         };
21371         
21372         return cfg;
21373     },
21374     
21375     initEvents : function() 
21376     {
21377         this.parent().register(this);
21378         
21379         this.el.on('click', this.onClick, this);
21380         
21381     },
21382     
21383     onClick : function(e)
21384     {
21385         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21386             this.setChecked(true);
21387         }
21388     },
21389     
21390     setChecked : function(state, suppressEvent)
21391     {
21392         this.parent().setValue(this.value, suppressEvent);
21393         
21394     },
21395     
21396     setBoxLabel : function(v)
21397     {
21398         this.boxLabel = v;
21399         
21400         if(this.rendered){
21401             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21402         }
21403     }
21404     
21405 });
21406  
21407
21408  /*
21409  * - LGPL
21410  *
21411  * Input
21412  * 
21413  */
21414
21415 /**
21416  * @class Roo.bootstrap.SecurePass
21417  * @extends Roo.bootstrap.Input
21418  * Bootstrap SecurePass class
21419  *
21420  * 
21421  * @constructor
21422  * Create a new SecurePass
21423  * @param {Object} config The config object
21424  */
21425  
21426 Roo.bootstrap.SecurePass = function (config) {
21427     // these go here, so the translation tool can replace them..
21428     this.errors = {
21429         PwdEmpty: "Please type a password, and then retype it to confirm.",
21430         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21431         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21432         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21433         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21434         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21435         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21436         TooWeak: "Your password is Too Weak."
21437     },
21438     this.meterLabel = "Password strength:";
21439     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21440     this.meterClass = [
21441         "roo-password-meter-tooweak", 
21442         "roo-password-meter-weak", 
21443         "roo-password-meter-medium", 
21444         "roo-password-meter-strong", 
21445         "roo-password-meter-grey"
21446     ];
21447     
21448     this.errors = {};
21449     
21450     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21451 }
21452
21453 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21454     /**
21455      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21456      * {
21457      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21458      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21459      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21460      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21461      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21462      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21463      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21464      * })
21465      */
21466     // private
21467     
21468     meterWidth: 300,
21469     errorMsg :'',    
21470     errors: false,
21471     imageRoot: '/',
21472     /**
21473      * @cfg {String/Object} Label for the strength meter (defaults to
21474      * 'Password strength:')
21475      */
21476     // private
21477     meterLabel: '',
21478     /**
21479      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21480      * ['Weak', 'Medium', 'Strong'])
21481      */
21482     // private    
21483     pwdStrengths: false,    
21484     // private
21485     strength: 0,
21486     // private
21487     _lastPwd: null,
21488     // private
21489     kCapitalLetter: 0,
21490     kSmallLetter: 1,
21491     kDigit: 2,
21492     kPunctuation: 3,
21493     
21494     insecure: false,
21495     // private
21496     initEvents: function ()
21497     {
21498         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21499
21500         if (this.el.is('input[type=password]') && Roo.isSafari) {
21501             this.el.on('keydown', this.SafariOnKeyDown, this);
21502         }
21503
21504         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21505     },
21506     // private
21507     onRender: function (ct, position)
21508     {
21509         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21510         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21511         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21512
21513         this.trigger.createChild({
21514                    cn: [
21515                     {
21516                     //id: 'PwdMeter',
21517                     tag: 'div',
21518                     cls: 'roo-password-meter-grey col-xs-12',
21519                     style: {
21520                         //width: 0,
21521                         //width: this.meterWidth + 'px'                                                
21522                         }
21523                     },
21524                     {                            
21525                          cls: 'roo-password-meter-text'                          
21526                     }
21527                 ]            
21528         });
21529
21530          
21531         if (this.hideTrigger) {
21532             this.trigger.setDisplayed(false);
21533         }
21534         this.setSize(this.width || '', this.height || '');
21535     },
21536     // private
21537     onDestroy: function ()
21538     {
21539         if (this.trigger) {
21540             this.trigger.removeAllListeners();
21541             this.trigger.remove();
21542         }
21543         if (this.wrap) {
21544             this.wrap.remove();
21545         }
21546         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21547     },
21548     // private
21549     checkStrength: function ()
21550     {
21551         var pwd = this.inputEl().getValue();
21552         if (pwd == this._lastPwd) {
21553             return;
21554         }
21555
21556         var strength;
21557         if (this.ClientSideStrongPassword(pwd)) {
21558             strength = 3;
21559         } else if (this.ClientSideMediumPassword(pwd)) {
21560             strength = 2;
21561         } else if (this.ClientSideWeakPassword(pwd)) {
21562             strength = 1;
21563         } else {
21564             strength = 0;
21565         }
21566         
21567         Roo.log('strength1: ' + strength);
21568         
21569         //var pm = this.trigger.child('div/div/div').dom;
21570         var pm = this.trigger.child('div/div');
21571         pm.removeClass(this.meterClass);
21572         pm.addClass(this.meterClass[strength]);
21573                 
21574         
21575         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21576                 
21577         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21578         
21579         this._lastPwd = pwd;
21580     },
21581     reset: function ()
21582     {
21583         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21584         
21585         this._lastPwd = '';
21586         
21587         var pm = this.trigger.child('div/div');
21588         pm.removeClass(this.meterClass);
21589         pm.addClass('roo-password-meter-grey');        
21590         
21591         
21592         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21593         
21594         pt.innerHTML = '';
21595         this.inputEl().dom.type='password';
21596     },
21597     // private
21598     validateValue: function (value)
21599     {
21600         
21601         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21602             return false;
21603         }
21604         if (value.length == 0) {
21605             if (this.allowBlank) {
21606                 this.clearInvalid();
21607                 return true;
21608             }
21609
21610             this.markInvalid(this.errors.PwdEmpty);
21611             this.errorMsg = this.errors.PwdEmpty;
21612             return false;
21613         }
21614         
21615         if(this.insecure){
21616             return true;
21617         }
21618         
21619         if ('[\x21-\x7e]*'.match(value)) {
21620             this.markInvalid(this.errors.PwdBadChar);
21621             this.errorMsg = this.errors.PwdBadChar;
21622             return false;
21623         }
21624         if (value.length < 6) {
21625             this.markInvalid(this.errors.PwdShort);
21626             this.errorMsg = this.errors.PwdShort;
21627             return false;
21628         }
21629         if (value.length > 16) {
21630             this.markInvalid(this.errors.PwdLong);
21631             this.errorMsg = this.errors.PwdLong;
21632             return false;
21633         }
21634         var strength;
21635         if (this.ClientSideStrongPassword(value)) {
21636             strength = 3;
21637         } else if (this.ClientSideMediumPassword(value)) {
21638             strength = 2;
21639         } else if (this.ClientSideWeakPassword(value)) {
21640             strength = 1;
21641         } else {
21642             strength = 0;
21643         }
21644
21645         
21646         if (strength < 2) {
21647             //this.markInvalid(this.errors.TooWeak);
21648             this.errorMsg = this.errors.TooWeak;
21649             //return false;
21650         }
21651         
21652         
21653         console.log('strength2: ' + strength);
21654         
21655         //var pm = this.trigger.child('div/div/div').dom;
21656         
21657         var pm = this.trigger.child('div/div');
21658         pm.removeClass(this.meterClass);
21659         pm.addClass(this.meterClass[strength]);
21660                 
21661         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21662                 
21663         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21664         
21665         this.errorMsg = ''; 
21666         return true;
21667     },
21668     // private
21669     CharacterSetChecks: function (type)
21670     {
21671         this.type = type;
21672         this.fResult = false;
21673     },
21674     // private
21675     isctype: function (character, type)
21676     {
21677         switch (type) {  
21678             case this.kCapitalLetter:
21679                 if (character >= 'A' && character <= 'Z') {
21680                     return true;
21681                 }
21682                 break;
21683             
21684             case this.kSmallLetter:
21685                 if (character >= 'a' && character <= 'z') {
21686                     return true;
21687                 }
21688                 break;
21689             
21690             case this.kDigit:
21691                 if (character >= '0' && character <= '9') {
21692                     return true;
21693                 }
21694                 break;
21695             
21696             case this.kPunctuation:
21697                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21698                     return true;
21699                 }
21700                 break;
21701             
21702             default:
21703                 return false;
21704         }
21705
21706     },
21707     // private
21708     IsLongEnough: function (pwd, size)
21709     {
21710         return !(pwd == null || isNaN(size) || pwd.length < size);
21711     },
21712     // private
21713     SpansEnoughCharacterSets: function (word, nb)
21714     {
21715         if (!this.IsLongEnough(word, nb))
21716         {
21717             return false;
21718         }
21719
21720         var characterSetChecks = new Array(
21721             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21722             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21723         );
21724         
21725         for (var index = 0; index < word.length; ++index) {
21726             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21727                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21728                     characterSetChecks[nCharSet].fResult = true;
21729                     break;
21730                 }
21731             }
21732         }
21733
21734         var nCharSets = 0;
21735         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21736             if (characterSetChecks[nCharSet].fResult) {
21737                 ++nCharSets;
21738             }
21739         }
21740
21741         if (nCharSets < nb) {
21742             return false;
21743         }
21744         return true;
21745     },
21746     // private
21747     ClientSideStrongPassword: function (pwd)
21748     {
21749         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21750     },
21751     // private
21752     ClientSideMediumPassword: function (pwd)
21753     {
21754         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21755     },
21756     // private
21757     ClientSideWeakPassword: function (pwd)
21758     {
21759         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21760     }
21761           
21762 })//<script type="text/javascript">
21763
21764 /*
21765  * Based  Ext JS Library 1.1.1
21766  * Copyright(c) 2006-2007, Ext JS, LLC.
21767  * LGPL
21768  *
21769  */
21770  
21771 /**
21772  * @class Roo.HtmlEditorCore
21773  * @extends Roo.Component
21774  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21775  *
21776  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21777  */
21778
21779 Roo.HtmlEditorCore = function(config){
21780     
21781     
21782     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21783     
21784     
21785     this.addEvents({
21786         /**
21787          * @event initialize
21788          * Fires when the editor is fully initialized (including the iframe)
21789          * @param {Roo.HtmlEditorCore} this
21790          */
21791         initialize: true,
21792         /**
21793          * @event activate
21794          * Fires when the editor is first receives the focus. Any insertion must wait
21795          * until after this event.
21796          * @param {Roo.HtmlEditorCore} this
21797          */
21798         activate: true,
21799          /**
21800          * @event beforesync
21801          * Fires before the textarea is updated with content from the editor iframe. Return false
21802          * to cancel the sync.
21803          * @param {Roo.HtmlEditorCore} this
21804          * @param {String} html
21805          */
21806         beforesync: true,
21807          /**
21808          * @event beforepush
21809          * Fires before the iframe editor is updated with content from the textarea. Return false
21810          * to cancel the push.
21811          * @param {Roo.HtmlEditorCore} this
21812          * @param {String} html
21813          */
21814         beforepush: true,
21815          /**
21816          * @event sync
21817          * Fires when the textarea is updated with content from the editor iframe.
21818          * @param {Roo.HtmlEditorCore} this
21819          * @param {String} html
21820          */
21821         sync: true,
21822          /**
21823          * @event push
21824          * Fires when the iframe editor is updated with content from the textarea.
21825          * @param {Roo.HtmlEditorCore} this
21826          * @param {String} html
21827          */
21828         push: true,
21829         
21830         /**
21831          * @event editorevent
21832          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21833          * @param {Roo.HtmlEditorCore} this
21834          */
21835         editorevent: true
21836         
21837     });
21838     
21839     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21840     
21841     // defaults : white / black...
21842     this.applyBlacklists();
21843     
21844     
21845     
21846 };
21847
21848
21849 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21850
21851
21852      /**
21853      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21854      */
21855     
21856     owner : false,
21857     
21858      /**
21859      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21860      *                        Roo.resizable.
21861      */
21862     resizable : false,
21863      /**
21864      * @cfg {Number} height (in pixels)
21865      */   
21866     height: 300,
21867    /**
21868      * @cfg {Number} width (in pixels)
21869      */   
21870     width: 500,
21871     
21872     /**
21873      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21874      * 
21875      */
21876     stylesheets: false,
21877     
21878     // id of frame..
21879     frameId: false,
21880     
21881     // private properties
21882     validationEvent : false,
21883     deferHeight: true,
21884     initialized : false,
21885     activated : false,
21886     sourceEditMode : false,
21887     onFocus : Roo.emptyFn,
21888     iframePad:3,
21889     hideMode:'offsets',
21890     
21891     clearUp: true,
21892     
21893     // blacklist + whitelisted elements..
21894     black: false,
21895     white: false,
21896      
21897     bodyCls : '',
21898
21899     /**
21900      * Protected method that will not generally be called directly. It
21901      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21902      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21903      */
21904     getDocMarkup : function(){
21905         // body styles..
21906         var st = '';
21907         
21908         // inherit styels from page...?? 
21909         if (this.stylesheets === false) {
21910             
21911             Roo.get(document.head).select('style').each(function(node) {
21912                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21913             });
21914             
21915             Roo.get(document.head).select('link').each(function(node) { 
21916                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21917             });
21918             
21919         } else if (!this.stylesheets.length) {
21920                 // simple..
21921                 st = '<style type="text/css">' +
21922                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21923                    '</style>';
21924         } else { 
21925             st = '<style type="text/css">' +
21926                     this.stylesheets +
21927                 '</style>';
21928         }
21929         
21930         st +=  '<style type="text/css">' +
21931             'IMG { cursor: pointer } ' +
21932         '</style>';
21933
21934         var cls = 'roo-htmleditor-body';
21935         
21936         if(this.bodyCls.length){
21937             cls += ' ' + this.bodyCls;
21938         }
21939         
21940         return '<html><head>' + st  +
21941             //<style type="text/css">' +
21942             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21943             //'</style>' +
21944             ' </head><body class="' +  cls + '"></body></html>';
21945     },
21946
21947     // private
21948     onRender : function(ct, position)
21949     {
21950         var _t = this;
21951         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21952         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21953         
21954         
21955         this.el.dom.style.border = '0 none';
21956         this.el.dom.setAttribute('tabIndex', -1);
21957         this.el.addClass('x-hidden hide');
21958         
21959         
21960         
21961         if(Roo.isIE){ // fix IE 1px bogus margin
21962             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21963         }
21964        
21965         
21966         this.frameId = Roo.id();
21967         
21968          
21969         
21970         var iframe = this.owner.wrap.createChild({
21971             tag: 'iframe',
21972             cls: 'form-control', // bootstrap..
21973             id: this.frameId,
21974             name: this.frameId,
21975             frameBorder : 'no',
21976             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21977         }, this.el
21978         );
21979         
21980         
21981         this.iframe = iframe.dom;
21982
21983          this.assignDocWin();
21984         
21985         this.doc.designMode = 'on';
21986        
21987         this.doc.open();
21988         this.doc.write(this.getDocMarkup());
21989         this.doc.close();
21990
21991         
21992         var task = { // must defer to wait for browser to be ready
21993             run : function(){
21994                 //console.log("run task?" + this.doc.readyState);
21995                 this.assignDocWin();
21996                 if(this.doc.body || this.doc.readyState == 'complete'){
21997                     try {
21998                         this.doc.designMode="on";
21999                     } catch (e) {
22000                         return;
22001                     }
22002                     Roo.TaskMgr.stop(task);
22003                     this.initEditor.defer(10, this);
22004                 }
22005             },
22006             interval : 10,
22007             duration: 10000,
22008             scope: this
22009         };
22010         Roo.TaskMgr.start(task);
22011
22012     },
22013
22014     // private
22015     onResize : function(w, h)
22016     {
22017          Roo.log('resize: ' +w + ',' + h );
22018         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22019         if(!this.iframe){
22020             return;
22021         }
22022         if(typeof w == 'number'){
22023             
22024             this.iframe.style.width = w + 'px';
22025         }
22026         if(typeof h == 'number'){
22027             
22028             this.iframe.style.height = h + 'px';
22029             if(this.doc){
22030                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22031             }
22032         }
22033         
22034     },
22035
22036     /**
22037      * Toggles the editor between standard and source edit mode.
22038      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22039      */
22040     toggleSourceEdit : function(sourceEditMode){
22041         
22042         this.sourceEditMode = sourceEditMode === true;
22043         
22044         if(this.sourceEditMode){
22045  
22046             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22047             
22048         }else{
22049             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22050             //this.iframe.className = '';
22051             this.deferFocus();
22052         }
22053         //this.setSize(this.owner.wrap.getSize());
22054         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22055     },
22056
22057     
22058   
22059
22060     /**
22061      * Protected method that will not generally be called directly. If you need/want
22062      * custom HTML cleanup, this is the method you should override.
22063      * @param {String} html The HTML to be cleaned
22064      * return {String} The cleaned HTML
22065      */
22066     cleanHtml : function(html){
22067         html = String(html);
22068         if(html.length > 5){
22069             if(Roo.isSafari){ // strip safari nonsense
22070                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22071             }
22072         }
22073         if(html == '&nbsp;'){
22074             html = '';
22075         }
22076         return html;
22077     },
22078
22079     /**
22080      * HTML Editor -> Textarea
22081      * Protected method that will not generally be called directly. Syncs the contents
22082      * of the editor iframe with the textarea.
22083      */
22084     syncValue : function(){
22085         if(this.initialized){
22086             var bd = (this.doc.body || this.doc.documentElement);
22087             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22088             var html = bd.innerHTML;
22089             if(Roo.isSafari){
22090                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22091                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22092                 if(m && m[1]){
22093                     html = '<div style="'+m[0]+'">' + html + '</div>';
22094                 }
22095             }
22096             html = this.cleanHtml(html);
22097             // fix up the special chars.. normaly like back quotes in word...
22098             // however we do not want to do this with chinese..
22099             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22100                 var cc = b.charCodeAt();
22101                 if (
22102                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22103                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22104                     (cc >= 0xf900 && cc < 0xfb00 )
22105                 ) {
22106                         return b;
22107                 }
22108                 return "&#"+cc+";" 
22109             });
22110             if(this.owner.fireEvent('beforesync', this, html) !== false){
22111                 this.el.dom.value = html;
22112                 this.owner.fireEvent('sync', this, html);
22113             }
22114         }
22115     },
22116
22117     /**
22118      * Protected method that will not generally be called directly. Pushes the value of the textarea
22119      * into the iframe editor.
22120      */
22121     pushValue : function(){
22122         if(this.initialized){
22123             var v = this.el.dom.value.trim();
22124             
22125 //            if(v.length < 1){
22126 //                v = '&#160;';
22127 //            }
22128             
22129             if(this.owner.fireEvent('beforepush', this, v) !== false){
22130                 var d = (this.doc.body || this.doc.documentElement);
22131                 d.innerHTML = v;
22132                 this.cleanUpPaste();
22133                 this.el.dom.value = d.innerHTML;
22134                 this.owner.fireEvent('push', this, v);
22135             }
22136         }
22137     },
22138
22139     // private
22140     deferFocus : function(){
22141         this.focus.defer(10, this);
22142     },
22143
22144     // doc'ed in Field
22145     focus : function(){
22146         if(this.win && !this.sourceEditMode){
22147             this.win.focus();
22148         }else{
22149             this.el.focus();
22150         }
22151     },
22152     
22153     assignDocWin: function()
22154     {
22155         var iframe = this.iframe;
22156         
22157          if(Roo.isIE){
22158             this.doc = iframe.contentWindow.document;
22159             this.win = iframe.contentWindow;
22160         } else {
22161 //            if (!Roo.get(this.frameId)) {
22162 //                return;
22163 //            }
22164 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22165 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22166             
22167             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22168                 return;
22169             }
22170             
22171             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22172             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22173         }
22174     },
22175     
22176     // private
22177     initEditor : function(){
22178         //console.log("INIT EDITOR");
22179         this.assignDocWin();
22180         
22181         
22182         
22183         this.doc.designMode="on";
22184         this.doc.open();
22185         this.doc.write(this.getDocMarkup());
22186         this.doc.close();
22187         
22188         var dbody = (this.doc.body || this.doc.documentElement);
22189         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22190         // this copies styles from the containing element into thsi one..
22191         // not sure why we need all of this..
22192         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22193         
22194         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22195         //ss['background-attachment'] = 'fixed'; // w3c
22196         dbody.bgProperties = 'fixed'; // ie
22197         //Roo.DomHelper.applyStyles(dbody, ss);
22198         Roo.EventManager.on(this.doc, {
22199             //'mousedown': this.onEditorEvent,
22200             'mouseup': this.onEditorEvent,
22201             'dblclick': this.onEditorEvent,
22202             'click': this.onEditorEvent,
22203             'keyup': this.onEditorEvent,
22204             buffer:100,
22205             scope: this
22206         });
22207         if(Roo.isGecko){
22208             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22209         }
22210         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22211             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22212         }
22213         this.initialized = true;
22214
22215         this.owner.fireEvent('initialize', this);
22216         this.pushValue();
22217     },
22218
22219     // private
22220     onDestroy : function(){
22221         
22222         
22223         
22224         if(this.rendered){
22225             
22226             //for (var i =0; i < this.toolbars.length;i++) {
22227             //    // fixme - ask toolbars for heights?
22228             //    this.toolbars[i].onDestroy();
22229            // }
22230             
22231             //this.wrap.dom.innerHTML = '';
22232             //this.wrap.remove();
22233         }
22234     },
22235
22236     // private
22237     onFirstFocus : function(){
22238         
22239         this.assignDocWin();
22240         
22241         
22242         this.activated = true;
22243          
22244     
22245         if(Roo.isGecko){ // prevent silly gecko errors
22246             this.win.focus();
22247             var s = this.win.getSelection();
22248             if(!s.focusNode || s.focusNode.nodeType != 3){
22249                 var r = s.getRangeAt(0);
22250                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22251                 r.collapse(true);
22252                 this.deferFocus();
22253             }
22254             try{
22255                 this.execCmd('useCSS', true);
22256                 this.execCmd('styleWithCSS', false);
22257             }catch(e){}
22258         }
22259         this.owner.fireEvent('activate', this);
22260     },
22261
22262     // private
22263     adjustFont: function(btn){
22264         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22265         //if(Roo.isSafari){ // safari
22266         //    adjust *= 2;
22267        // }
22268         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22269         if(Roo.isSafari){ // safari
22270             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22271             v =  (v < 10) ? 10 : v;
22272             v =  (v > 48) ? 48 : v;
22273             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22274             
22275         }
22276         
22277         
22278         v = Math.max(1, v+adjust);
22279         
22280         this.execCmd('FontSize', v  );
22281     },
22282
22283     onEditorEvent : function(e)
22284     {
22285         this.owner.fireEvent('editorevent', this, e);
22286       //  this.updateToolbar();
22287         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22288     },
22289
22290     insertTag : function(tg)
22291     {
22292         // could be a bit smarter... -> wrap the current selected tRoo..
22293         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22294             
22295             range = this.createRange(this.getSelection());
22296             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22297             wrappingNode.appendChild(range.extractContents());
22298             range.insertNode(wrappingNode);
22299
22300             return;
22301             
22302             
22303             
22304         }
22305         this.execCmd("formatblock",   tg);
22306         
22307     },
22308     
22309     insertText : function(txt)
22310     {
22311         
22312         
22313         var range = this.createRange();
22314         range.deleteContents();
22315                //alert(Sender.getAttribute('label'));
22316                
22317         range.insertNode(this.doc.createTextNode(txt));
22318     } ,
22319     
22320      
22321
22322     /**
22323      * Executes a Midas editor command on the editor document and performs necessary focus and
22324      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22325      * @param {String} cmd The Midas command
22326      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22327      */
22328     relayCmd : function(cmd, value){
22329         this.win.focus();
22330         this.execCmd(cmd, value);
22331         this.owner.fireEvent('editorevent', this);
22332         //this.updateToolbar();
22333         this.owner.deferFocus();
22334     },
22335
22336     /**
22337      * Executes a Midas editor command directly on the editor document.
22338      * For visual commands, you should use {@link #relayCmd} instead.
22339      * <b>This should only be called after the editor is initialized.</b>
22340      * @param {String} cmd The Midas command
22341      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22342      */
22343     execCmd : function(cmd, value){
22344         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22345         this.syncValue();
22346     },
22347  
22348  
22349    
22350     /**
22351      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22352      * to insert tRoo.
22353      * @param {String} text | dom node.. 
22354      */
22355     insertAtCursor : function(text)
22356     {
22357         
22358         if(!this.activated){
22359             return;
22360         }
22361         /*
22362         if(Roo.isIE){
22363             this.win.focus();
22364             var r = this.doc.selection.createRange();
22365             if(r){
22366                 r.collapse(true);
22367                 r.pasteHTML(text);
22368                 this.syncValue();
22369                 this.deferFocus();
22370             
22371             }
22372             return;
22373         }
22374         */
22375         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22376             this.win.focus();
22377             
22378             
22379             // from jquery ui (MIT licenced)
22380             var range, node;
22381             var win = this.win;
22382             
22383             if (win.getSelection && win.getSelection().getRangeAt) {
22384                 range = win.getSelection().getRangeAt(0);
22385                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22386                 range.insertNode(node);
22387             } else if (win.document.selection && win.document.selection.createRange) {
22388                 // no firefox support
22389                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22390                 win.document.selection.createRange().pasteHTML(txt);
22391             } else {
22392                 // no firefox support
22393                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22394                 this.execCmd('InsertHTML', txt);
22395             } 
22396             
22397             this.syncValue();
22398             
22399             this.deferFocus();
22400         }
22401     },
22402  // private
22403     mozKeyPress : function(e){
22404         if(e.ctrlKey){
22405             var c = e.getCharCode(), cmd;
22406           
22407             if(c > 0){
22408                 c = String.fromCharCode(c).toLowerCase();
22409                 switch(c){
22410                     case 'b':
22411                         cmd = 'bold';
22412                         break;
22413                     case 'i':
22414                         cmd = 'italic';
22415                         break;
22416                     
22417                     case 'u':
22418                         cmd = 'underline';
22419                         break;
22420                     
22421                     case 'v':
22422                         this.cleanUpPaste.defer(100, this);
22423                         return;
22424                         
22425                 }
22426                 if(cmd){
22427                     this.win.focus();
22428                     this.execCmd(cmd);
22429                     this.deferFocus();
22430                     e.preventDefault();
22431                 }
22432                 
22433             }
22434         }
22435     },
22436
22437     // private
22438     fixKeys : function(){ // load time branching for fastest keydown performance
22439         if(Roo.isIE){
22440             return function(e){
22441                 var k = e.getKey(), r;
22442                 if(k == e.TAB){
22443                     e.stopEvent();
22444                     r = this.doc.selection.createRange();
22445                     if(r){
22446                         r.collapse(true);
22447                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22448                         this.deferFocus();
22449                     }
22450                     return;
22451                 }
22452                 
22453                 if(k == e.ENTER){
22454                     r = this.doc.selection.createRange();
22455                     if(r){
22456                         var target = r.parentElement();
22457                         if(!target || target.tagName.toLowerCase() != 'li'){
22458                             e.stopEvent();
22459                             r.pasteHTML('<br />');
22460                             r.collapse(false);
22461                             r.select();
22462                         }
22463                     }
22464                 }
22465                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22466                     this.cleanUpPaste.defer(100, this);
22467                     return;
22468                 }
22469                 
22470                 
22471             };
22472         }else if(Roo.isOpera){
22473             return function(e){
22474                 var k = e.getKey();
22475                 if(k == e.TAB){
22476                     e.stopEvent();
22477                     this.win.focus();
22478                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22479                     this.deferFocus();
22480                 }
22481                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22482                     this.cleanUpPaste.defer(100, this);
22483                     return;
22484                 }
22485                 
22486             };
22487         }else if(Roo.isSafari){
22488             return function(e){
22489                 var k = e.getKey();
22490                 
22491                 if(k == e.TAB){
22492                     e.stopEvent();
22493                     this.execCmd('InsertText','\t');
22494                     this.deferFocus();
22495                     return;
22496                 }
22497                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22498                     this.cleanUpPaste.defer(100, this);
22499                     return;
22500                 }
22501                 
22502              };
22503         }
22504     }(),
22505     
22506     getAllAncestors: function()
22507     {
22508         var p = this.getSelectedNode();
22509         var a = [];
22510         if (!p) {
22511             a.push(p); // push blank onto stack..
22512             p = this.getParentElement();
22513         }
22514         
22515         
22516         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22517             a.push(p);
22518             p = p.parentNode;
22519         }
22520         a.push(this.doc.body);
22521         return a;
22522     },
22523     lastSel : false,
22524     lastSelNode : false,
22525     
22526     
22527     getSelection : function() 
22528     {
22529         this.assignDocWin();
22530         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22531     },
22532     
22533     getSelectedNode: function() 
22534     {
22535         // this may only work on Gecko!!!
22536         
22537         // should we cache this!!!!
22538         
22539         
22540         
22541          
22542         var range = this.createRange(this.getSelection()).cloneRange();
22543         
22544         if (Roo.isIE) {
22545             var parent = range.parentElement();
22546             while (true) {
22547                 var testRange = range.duplicate();
22548                 testRange.moveToElementText(parent);
22549                 if (testRange.inRange(range)) {
22550                     break;
22551                 }
22552                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22553                     break;
22554                 }
22555                 parent = parent.parentElement;
22556             }
22557             return parent;
22558         }
22559         
22560         // is ancestor a text element.
22561         var ac =  range.commonAncestorContainer;
22562         if (ac.nodeType == 3) {
22563             ac = ac.parentNode;
22564         }
22565         
22566         var ar = ac.childNodes;
22567          
22568         var nodes = [];
22569         var other_nodes = [];
22570         var has_other_nodes = false;
22571         for (var i=0;i<ar.length;i++) {
22572             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22573                 continue;
22574             }
22575             // fullly contained node.
22576             
22577             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22578                 nodes.push(ar[i]);
22579                 continue;
22580             }
22581             
22582             // probably selected..
22583             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22584                 other_nodes.push(ar[i]);
22585                 continue;
22586             }
22587             // outer..
22588             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22589                 continue;
22590             }
22591             
22592             
22593             has_other_nodes = true;
22594         }
22595         if (!nodes.length && other_nodes.length) {
22596             nodes= other_nodes;
22597         }
22598         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22599             return false;
22600         }
22601         
22602         return nodes[0];
22603     },
22604     createRange: function(sel)
22605     {
22606         // this has strange effects when using with 
22607         // top toolbar - not sure if it's a great idea.
22608         //this.editor.contentWindow.focus();
22609         if (typeof sel != "undefined") {
22610             try {
22611                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22612             } catch(e) {
22613                 return this.doc.createRange();
22614             }
22615         } else {
22616             return this.doc.createRange();
22617         }
22618     },
22619     getParentElement: function()
22620     {
22621         
22622         this.assignDocWin();
22623         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22624         
22625         var range = this.createRange(sel);
22626          
22627         try {
22628             var p = range.commonAncestorContainer;
22629             while (p.nodeType == 3) { // text node
22630                 p = p.parentNode;
22631             }
22632             return p;
22633         } catch (e) {
22634             return null;
22635         }
22636     
22637     },
22638     /***
22639      *
22640      * Range intersection.. the hard stuff...
22641      *  '-1' = before
22642      *  '0' = hits..
22643      *  '1' = after.
22644      *         [ -- selected range --- ]
22645      *   [fail]                        [fail]
22646      *
22647      *    basically..
22648      *      if end is before start or  hits it. fail.
22649      *      if start is after end or hits it fail.
22650      *
22651      *   if either hits (but other is outside. - then it's not 
22652      *   
22653      *    
22654      **/
22655     
22656     
22657     // @see http://www.thismuchiknow.co.uk/?p=64.
22658     rangeIntersectsNode : function(range, node)
22659     {
22660         var nodeRange = node.ownerDocument.createRange();
22661         try {
22662             nodeRange.selectNode(node);
22663         } catch (e) {
22664             nodeRange.selectNodeContents(node);
22665         }
22666     
22667         var rangeStartRange = range.cloneRange();
22668         rangeStartRange.collapse(true);
22669     
22670         var rangeEndRange = range.cloneRange();
22671         rangeEndRange.collapse(false);
22672     
22673         var nodeStartRange = nodeRange.cloneRange();
22674         nodeStartRange.collapse(true);
22675     
22676         var nodeEndRange = nodeRange.cloneRange();
22677         nodeEndRange.collapse(false);
22678     
22679         return rangeStartRange.compareBoundaryPoints(
22680                  Range.START_TO_START, nodeEndRange) == -1 &&
22681                rangeEndRange.compareBoundaryPoints(
22682                  Range.START_TO_START, nodeStartRange) == 1;
22683         
22684          
22685     },
22686     rangeCompareNode : function(range, node)
22687     {
22688         var nodeRange = node.ownerDocument.createRange();
22689         try {
22690             nodeRange.selectNode(node);
22691         } catch (e) {
22692             nodeRange.selectNodeContents(node);
22693         }
22694         
22695         
22696         range.collapse(true);
22697     
22698         nodeRange.collapse(true);
22699      
22700         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22701         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22702          
22703         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22704         
22705         var nodeIsBefore   =  ss == 1;
22706         var nodeIsAfter    = ee == -1;
22707         
22708         if (nodeIsBefore && nodeIsAfter) {
22709             return 0; // outer
22710         }
22711         if (!nodeIsBefore && nodeIsAfter) {
22712             return 1; //right trailed.
22713         }
22714         
22715         if (nodeIsBefore && !nodeIsAfter) {
22716             return 2;  // left trailed.
22717         }
22718         // fully contined.
22719         return 3;
22720     },
22721
22722     // private? - in a new class?
22723     cleanUpPaste :  function()
22724     {
22725         // cleans up the whole document..
22726         Roo.log('cleanuppaste');
22727         
22728         this.cleanUpChildren(this.doc.body);
22729         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22730         if (clean != this.doc.body.innerHTML) {
22731             this.doc.body.innerHTML = clean;
22732         }
22733         
22734     },
22735     
22736     cleanWordChars : function(input) {// change the chars to hex code
22737         var he = Roo.HtmlEditorCore;
22738         
22739         var output = input;
22740         Roo.each(he.swapCodes, function(sw) { 
22741             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22742             
22743             output = output.replace(swapper, sw[1]);
22744         });
22745         
22746         return output;
22747     },
22748     
22749     
22750     cleanUpChildren : function (n)
22751     {
22752         if (!n.childNodes.length) {
22753             return;
22754         }
22755         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22756            this.cleanUpChild(n.childNodes[i]);
22757         }
22758     },
22759     
22760     
22761         
22762     
22763     cleanUpChild : function (node)
22764     {
22765         var ed = this;
22766         //console.log(node);
22767         if (node.nodeName == "#text") {
22768             // clean up silly Windows -- stuff?
22769             return; 
22770         }
22771         if (node.nodeName == "#comment") {
22772             node.parentNode.removeChild(node);
22773             // clean up silly Windows -- stuff?
22774             return; 
22775         }
22776         var lcname = node.tagName.toLowerCase();
22777         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22778         // whitelist of tags..
22779         
22780         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22781             // remove node.
22782             node.parentNode.removeChild(node);
22783             return;
22784             
22785         }
22786         
22787         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22788         
22789         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22790         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22791         
22792         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22793         //    remove_keep_children = true;
22794         //}
22795         
22796         if (remove_keep_children) {
22797             this.cleanUpChildren(node);
22798             // inserts everything just before this node...
22799             while (node.childNodes.length) {
22800                 var cn = node.childNodes[0];
22801                 node.removeChild(cn);
22802                 node.parentNode.insertBefore(cn, node);
22803             }
22804             node.parentNode.removeChild(node);
22805             return;
22806         }
22807         
22808         if (!node.attributes || !node.attributes.length) {
22809             this.cleanUpChildren(node);
22810             return;
22811         }
22812         
22813         function cleanAttr(n,v)
22814         {
22815             
22816             if (v.match(/^\./) || v.match(/^\//)) {
22817                 return;
22818             }
22819             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22820                 return;
22821             }
22822             if (v.match(/^#/)) {
22823                 return;
22824             }
22825 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22826             node.removeAttribute(n);
22827             
22828         }
22829         
22830         var cwhite = this.cwhite;
22831         var cblack = this.cblack;
22832             
22833         function cleanStyle(n,v)
22834         {
22835             if (v.match(/expression/)) { //XSS?? should we even bother..
22836                 node.removeAttribute(n);
22837                 return;
22838             }
22839             
22840             var parts = v.split(/;/);
22841             var clean = [];
22842             
22843             Roo.each(parts, function(p) {
22844                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22845                 if (!p.length) {
22846                     return true;
22847                 }
22848                 var l = p.split(':').shift().replace(/\s+/g,'');
22849                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22850                 
22851                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22852 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22853                     //node.removeAttribute(n);
22854                     return true;
22855                 }
22856                 //Roo.log()
22857                 // only allow 'c whitelisted system attributes'
22858                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22859 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22860                     //node.removeAttribute(n);
22861                     return true;
22862                 }
22863                 
22864                 
22865                  
22866                 
22867                 clean.push(p);
22868                 return true;
22869             });
22870             if (clean.length) { 
22871                 node.setAttribute(n, clean.join(';'));
22872             } else {
22873                 node.removeAttribute(n);
22874             }
22875             
22876         }
22877         
22878         
22879         for (var i = node.attributes.length-1; i > -1 ; i--) {
22880             var a = node.attributes[i];
22881             //console.log(a);
22882             
22883             if (a.name.toLowerCase().substr(0,2)=='on')  {
22884                 node.removeAttribute(a.name);
22885                 continue;
22886             }
22887             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22888                 node.removeAttribute(a.name);
22889                 continue;
22890             }
22891             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22892                 cleanAttr(a.name,a.value); // fixme..
22893                 continue;
22894             }
22895             if (a.name == 'style') {
22896                 cleanStyle(a.name,a.value);
22897                 continue;
22898             }
22899             /// clean up MS crap..
22900             // tecnically this should be a list of valid class'es..
22901             
22902             
22903             if (a.name == 'class') {
22904                 if (a.value.match(/^Mso/)) {
22905                     node.className = '';
22906                 }
22907                 
22908                 if (a.value.match(/^body$/)) {
22909                     node.className = '';
22910                 }
22911                 continue;
22912             }
22913             
22914             // style cleanup!?
22915             // class cleanup?
22916             
22917         }
22918         
22919         
22920         this.cleanUpChildren(node);
22921         
22922         
22923     },
22924     
22925     /**
22926      * Clean up MS wordisms...
22927      */
22928     cleanWord : function(node)
22929     {
22930         
22931         
22932         if (!node) {
22933             this.cleanWord(this.doc.body);
22934             return;
22935         }
22936         if (node.nodeName == "#text") {
22937             // clean up silly Windows -- stuff?
22938             return; 
22939         }
22940         if (node.nodeName == "#comment") {
22941             node.parentNode.removeChild(node);
22942             // clean up silly Windows -- stuff?
22943             return; 
22944         }
22945         
22946         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22947             node.parentNode.removeChild(node);
22948             return;
22949         }
22950         
22951         // remove - but keep children..
22952         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22953             while (node.childNodes.length) {
22954                 var cn = node.childNodes[0];
22955                 node.removeChild(cn);
22956                 node.parentNode.insertBefore(cn, node);
22957             }
22958             node.parentNode.removeChild(node);
22959             this.iterateChildren(node, this.cleanWord);
22960             return;
22961         }
22962         // clean styles
22963         if (node.className.length) {
22964             
22965             var cn = node.className.split(/\W+/);
22966             var cna = [];
22967             Roo.each(cn, function(cls) {
22968                 if (cls.match(/Mso[a-zA-Z]+/)) {
22969                     return;
22970                 }
22971                 cna.push(cls);
22972             });
22973             node.className = cna.length ? cna.join(' ') : '';
22974             if (!cna.length) {
22975                 node.removeAttribute("class");
22976             }
22977         }
22978         
22979         if (node.hasAttribute("lang")) {
22980             node.removeAttribute("lang");
22981         }
22982         
22983         if (node.hasAttribute("style")) {
22984             
22985             var styles = node.getAttribute("style").split(";");
22986             var nstyle = [];
22987             Roo.each(styles, function(s) {
22988                 if (!s.match(/:/)) {
22989                     return;
22990                 }
22991                 var kv = s.split(":");
22992                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22993                     return;
22994                 }
22995                 // what ever is left... we allow.
22996                 nstyle.push(s);
22997             });
22998             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22999             if (!nstyle.length) {
23000                 node.removeAttribute('style');
23001             }
23002         }
23003         this.iterateChildren(node, this.cleanWord);
23004         
23005         
23006         
23007     },
23008     /**
23009      * iterateChildren of a Node, calling fn each time, using this as the scole..
23010      * @param {DomNode} node node to iterate children of.
23011      * @param {Function} fn method of this class to call on each item.
23012      */
23013     iterateChildren : function(node, fn)
23014     {
23015         if (!node.childNodes.length) {
23016                 return;
23017         }
23018         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23019            fn.call(this, node.childNodes[i])
23020         }
23021     },
23022     
23023     
23024     /**
23025      * cleanTableWidths.
23026      *
23027      * Quite often pasting from word etc.. results in tables with column and widths.
23028      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23029      *
23030      */
23031     cleanTableWidths : function(node)
23032     {
23033          
23034          
23035         if (!node) {
23036             this.cleanTableWidths(this.doc.body);
23037             return;
23038         }
23039         
23040         // ignore list...
23041         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23042             return; 
23043         }
23044         Roo.log(node.tagName);
23045         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23046             this.iterateChildren(node, this.cleanTableWidths);
23047             return;
23048         }
23049         if (node.hasAttribute('width')) {
23050             node.removeAttribute('width');
23051         }
23052         
23053          
23054         if (node.hasAttribute("style")) {
23055             // pretty basic...
23056             
23057             var styles = node.getAttribute("style").split(";");
23058             var nstyle = [];
23059             Roo.each(styles, function(s) {
23060                 if (!s.match(/:/)) {
23061                     return;
23062                 }
23063                 var kv = s.split(":");
23064                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23065                     return;
23066                 }
23067                 // what ever is left... we allow.
23068                 nstyle.push(s);
23069             });
23070             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23071             if (!nstyle.length) {
23072                 node.removeAttribute('style');
23073             }
23074         }
23075         
23076         this.iterateChildren(node, this.cleanTableWidths);
23077         
23078         
23079     },
23080     
23081     
23082     
23083     
23084     domToHTML : function(currentElement, depth, nopadtext) {
23085         
23086         depth = depth || 0;
23087         nopadtext = nopadtext || false;
23088     
23089         if (!currentElement) {
23090             return this.domToHTML(this.doc.body);
23091         }
23092         
23093         //Roo.log(currentElement);
23094         var j;
23095         var allText = false;
23096         var nodeName = currentElement.nodeName;
23097         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23098         
23099         if  (nodeName == '#text') {
23100             
23101             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23102         }
23103         
23104         
23105         var ret = '';
23106         if (nodeName != 'BODY') {
23107              
23108             var i = 0;
23109             // Prints the node tagName, such as <A>, <IMG>, etc
23110             if (tagName) {
23111                 var attr = [];
23112                 for(i = 0; i < currentElement.attributes.length;i++) {
23113                     // quoting?
23114                     var aname = currentElement.attributes.item(i).name;
23115                     if (!currentElement.attributes.item(i).value.length) {
23116                         continue;
23117                     }
23118                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23119                 }
23120                 
23121                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23122             } 
23123             else {
23124                 
23125                 // eack
23126             }
23127         } else {
23128             tagName = false;
23129         }
23130         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23131             return ret;
23132         }
23133         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23134             nopadtext = true;
23135         }
23136         
23137         
23138         // Traverse the tree
23139         i = 0;
23140         var currentElementChild = currentElement.childNodes.item(i);
23141         var allText = true;
23142         var innerHTML  = '';
23143         lastnode = '';
23144         while (currentElementChild) {
23145             // Formatting code (indent the tree so it looks nice on the screen)
23146             var nopad = nopadtext;
23147             if (lastnode == 'SPAN') {
23148                 nopad  = true;
23149             }
23150             // text
23151             if  (currentElementChild.nodeName == '#text') {
23152                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23153                 toadd = nopadtext ? toadd : toadd.trim();
23154                 if (!nopad && toadd.length > 80) {
23155                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23156                 }
23157                 innerHTML  += toadd;
23158                 
23159                 i++;
23160                 currentElementChild = currentElement.childNodes.item(i);
23161                 lastNode = '';
23162                 continue;
23163             }
23164             allText = false;
23165             
23166             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23167                 
23168             // Recursively traverse the tree structure of the child node
23169             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23170             lastnode = currentElementChild.nodeName;
23171             i++;
23172             currentElementChild=currentElement.childNodes.item(i);
23173         }
23174         
23175         ret += innerHTML;
23176         
23177         if (!allText) {
23178                 // The remaining code is mostly for formatting the tree
23179             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23180         }
23181         
23182         
23183         if (tagName) {
23184             ret+= "</"+tagName+">";
23185         }
23186         return ret;
23187         
23188     },
23189         
23190     applyBlacklists : function()
23191     {
23192         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23193         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23194         
23195         this.white = [];
23196         this.black = [];
23197         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23198             if (b.indexOf(tag) > -1) {
23199                 return;
23200             }
23201             this.white.push(tag);
23202             
23203         }, this);
23204         
23205         Roo.each(w, function(tag) {
23206             if (b.indexOf(tag) > -1) {
23207                 return;
23208             }
23209             if (this.white.indexOf(tag) > -1) {
23210                 return;
23211             }
23212             this.white.push(tag);
23213             
23214         }, this);
23215         
23216         
23217         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23218             if (w.indexOf(tag) > -1) {
23219                 return;
23220             }
23221             this.black.push(tag);
23222             
23223         }, this);
23224         
23225         Roo.each(b, function(tag) {
23226             if (w.indexOf(tag) > -1) {
23227                 return;
23228             }
23229             if (this.black.indexOf(tag) > -1) {
23230                 return;
23231             }
23232             this.black.push(tag);
23233             
23234         }, this);
23235         
23236         
23237         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23238         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23239         
23240         this.cwhite = [];
23241         this.cblack = [];
23242         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23243             if (b.indexOf(tag) > -1) {
23244                 return;
23245             }
23246             this.cwhite.push(tag);
23247             
23248         }, this);
23249         
23250         Roo.each(w, function(tag) {
23251             if (b.indexOf(tag) > -1) {
23252                 return;
23253             }
23254             if (this.cwhite.indexOf(tag) > -1) {
23255                 return;
23256             }
23257             this.cwhite.push(tag);
23258             
23259         }, this);
23260         
23261         
23262         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23263             if (w.indexOf(tag) > -1) {
23264                 return;
23265             }
23266             this.cblack.push(tag);
23267             
23268         }, this);
23269         
23270         Roo.each(b, function(tag) {
23271             if (w.indexOf(tag) > -1) {
23272                 return;
23273             }
23274             if (this.cblack.indexOf(tag) > -1) {
23275                 return;
23276             }
23277             this.cblack.push(tag);
23278             
23279         }, this);
23280     },
23281     
23282     setStylesheets : function(stylesheets)
23283     {
23284         if(typeof(stylesheets) == 'string'){
23285             Roo.get(this.iframe.contentDocument.head).createChild({
23286                 tag : 'link',
23287                 rel : 'stylesheet',
23288                 type : 'text/css',
23289                 href : stylesheets
23290             });
23291             
23292             return;
23293         }
23294         var _this = this;
23295      
23296         Roo.each(stylesheets, function(s) {
23297             if(!s.length){
23298                 return;
23299             }
23300             
23301             Roo.get(_this.iframe.contentDocument.head).createChild({
23302                 tag : 'link',
23303                 rel : 'stylesheet',
23304                 type : 'text/css',
23305                 href : s
23306             });
23307         });
23308
23309         
23310     },
23311     
23312     removeStylesheets : function()
23313     {
23314         var _this = this;
23315         
23316         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23317             s.remove();
23318         });
23319     },
23320     
23321     setStyle : function(style)
23322     {
23323         Roo.get(this.iframe.contentDocument.head).createChild({
23324             tag : 'style',
23325             type : 'text/css',
23326             html : style
23327         });
23328
23329         return;
23330     }
23331     
23332     // hide stuff that is not compatible
23333     /**
23334      * @event blur
23335      * @hide
23336      */
23337     /**
23338      * @event change
23339      * @hide
23340      */
23341     /**
23342      * @event focus
23343      * @hide
23344      */
23345     /**
23346      * @event specialkey
23347      * @hide
23348      */
23349     /**
23350      * @cfg {String} fieldClass @hide
23351      */
23352     /**
23353      * @cfg {String} focusClass @hide
23354      */
23355     /**
23356      * @cfg {String} autoCreate @hide
23357      */
23358     /**
23359      * @cfg {String} inputType @hide
23360      */
23361     /**
23362      * @cfg {String} invalidClass @hide
23363      */
23364     /**
23365      * @cfg {String} invalidText @hide
23366      */
23367     /**
23368      * @cfg {String} msgFx @hide
23369      */
23370     /**
23371      * @cfg {String} validateOnBlur @hide
23372      */
23373 });
23374
23375 Roo.HtmlEditorCore.white = [
23376         'area', 'br', 'img', 'input', 'hr', 'wbr',
23377         
23378        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23379        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23380        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23381        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23382        'table',   'ul',         'xmp', 
23383        
23384        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23385       'thead',   'tr', 
23386      
23387       'dir', 'menu', 'ol', 'ul', 'dl',
23388        
23389       'embed',  'object'
23390 ];
23391
23392
23393 Roo.HtmlEditorCore.black = [
23394     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23395         'applet', // 
23396         'base',   'basefont', 'bgsound', 'blink',  'body', 
23397         'frame',  'frameset', 'head',    'html',   'ilayer', 
23398         'iframe', 'layer',  'link',     'meta',    'object',   
23399         'script', 'style' ,'title',  'xml' // clean later..
23400 ];
23401 Roo.HtmlEditorCore.clean = [
23402     'script', 'style', 'title', 'xml'
23403 ];
23404 Roo.HtmlEditorCore.remove = [
23405     'font'
23406 ];
23407 // attributes..
23408
23409 Roo.HtmlEditorCore.ablack = [
23410     'on'
23411 ];
23412     
23413 Roo.HtmlEditorCore.aclean = [ 
23414     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23415 ];
23416
23417 // protocols..
23418 Roo.HtmlEditorCore.pwhite= [
23419         'http',  'https',  'mailto'
23420 ];
23421
23422 // white listed style attributes.
23423 Roo.HtmlEditorCore.cwhite= [
23424       //  'text-align', /// default is to allow most things..
23425       
23426          
23427 //        'font-size'//??
23428 ];
23429
23430 // black listed style attributes.
23431 Roo.HtmlEditorCore.cblack= [
23432       //  'font-size' -- this can be set by the project 
23433 ];
23434
23435
23436 Roo.HtmlEditorCore.swapCodes   =[ 
23437     [    8211, "--" ], 
23438     [    8212, "--" ], 
23439     [    8216,  "'" ],  
23440     [    8217, "'" ],  
23441     [    8220, '"' ],  
23442     [    8221, '"' ],  
23443     [    8226, "*" ],  
23444     [    8230, "..." ]
23445 ]; 
23446
23447     /*
23448  * - LGPL
23449  *
23450  * HtmlEditor
23451  * 
23452  */
23453
23454 /**
23455  * @class Roo.bootstrap.HtmlEditor
23456  * @extends Roo.bootstrap.TextArea
23457  * Bootstrap HtmlEditor class
23458
23459  * @constructor
23460  * Create a new HtmlEditor
23461  * @param {Object} config The config object
23462  */
23463
23464 Roo.bootstrap.HtmlEditor = function(config){
23465     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23466     if (!this.toolbars) {
23467         this.toolbars = [];
23468     }
23469     
23470     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23471     this.addEvents({
23472             /**
23473              * @event initialize
23474              * Fires when the editor is fully initialized (including the iframe)
23475              * @param {HtmlEditor} this
23476              */
23477             initialize: true,
23478             /**
23479              * @event activate
23480              * Fires when the editor is first receives the focus. Any insertion must wait
23481              * until after this event.
23482              * @param {HtmlEditor} this
23483              */
23484             activate: true,
23485              /**
23486              * @event beforesync
23487              * Fires before the textarea is updated with content from the editor iframe. Return false
23488              * to cancel the sync.
23489              * @param {HtmlEditor} this
23490              * @param {String} html
23491              */
23492             beforesync: true,
23493              /**
23494              * @event beforepush
23495              * Fires before the iframe editor is updated with content from the textarea. Return false
23496              * to cancel the push.
23497              * @param {HtmlEditor} this
23498              * @param {String} html
23499              */
23500             beforepush: true,
23501              /**
23502              * @event sync
23503              * Fires when the textarea is updated with content from the editor iframe.
23504              * @param {HtmlEditor} this
23505              * @param {String} html
23506              */
23507             sync: true,
23508              /**
23509              * @event push
23510              * Fires when the iframe editor is updated with content from the textarea.
23511              * @param {HtmlEditor} this
23512              * @param {String} html
23513              */
23514             push: true,
23515              /**
23516              * @event editmodechange
23517              * Fires when the editor switches edit modes
23518              * @param {HtmlEditor} this
23519              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23520              */
23521             editmodechange: true,
23522             /**
23523              * @event editorevent
23524              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23525              * @param {HtmlEditor} this
23526              */
23527             editorevent: true,
23528             /**
23529              * @event firstfocus
23530              * Fires when on first focus - needed by toolbars..
23531              * @param {HtmlEditor} this
23532              */
23533             firstfocus: true,
23534             /**
23535              * @event autosave
23536              * Auto save the htmlEditor value as a file into Events
23537              * @param {HtmlEditor} this
23538              */
23539             autosave: true,
23540             /**
23541              * @event savedpreview
23542              * preview the saved version of htmlEditor
23543              * @param {HtmlEditor} this
23544              */
23545             savedpreview: true
23546         });
23547 };
23548
23549
23550 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23551     
23552     
23553       /**
23554      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23555      */
23556     toolbars : false,
23557     
23558      /**
23559     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23560     */
23561     btns : [],
23562    
23563      /**
23564      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23565      *                        Roo.resizable.
23566      */
23567     resizable : false,
23568      /**
23569      * @cfg {Number} height (in pixels)
23570      */   
23571     height: 300,
23572    /**
23573      * @cfg {Number} width (in pixels)
23574      */   
23575     width: false,
23576     
23577     /**
23578      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23579      * 
23580      */
23581     stylesheets: false,
23582     
23583     // id of frame..
23584     frameId: false,
23585     
23586     // private properties
23587     validationEvent : false,
23588     deferHeight: true,
23589     initialized : false,
23590     activated : false,
23591     
23592     onFocus : Roo.emptyFn,
23593     iframePad:3,
23594     hideMode:'offsets',
23595     
23596     tbContainer : false,
23597     
23598     bodyCls : '',
23599     
23600     toolbarContainer :function() {
23601         return this.wrap.select('.x-html-editor-tb',true).first();
23602     },
23603
23604     /**
23605      * Protected method that will not generally be called directly. It
23606      * is called when the editor creates its toolbar. Override this method if you need to
23607      * add custom toolbar buttons.
23608      * @param {HtmlEditor} editor
23609      */
23610     createToolbar : function(){
23611         Roo.log('renewing');
23612         Roo.log("create toolbars");
23613         
23614         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23615         this.toolbars[0].render(this.toolbarContainer());
23616         
23617         return;
23618         
23619 //        if (!editor.toolbars || !editor.toolbars.length) {
23620 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23621 //        }
23622 //        
23623 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23624 //            editor.toolbars[i] = Roo.factory(
23625 //                    typeof(editor.toolbars[i]) == 'string' ?
23626 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23627 //                Roo.bootstrap.HtmlEditor);
23628 //            editor.toolbars[i].init(editor);
23629 //        }
23630     },
23631
23632      
23633     // private
23634     onRender : function(ct, position)
23635     {
23636        // Roo.log("Call onRender: " + this.xtype);
23637         var _t = this;
23638         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23639       
23640         this.wrap = this.inputEl().wrap({
23641             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23642         });
23643         
23644         this.editorcore.onRender(ct, position);
23645          
23646         if (this.resizable) {
23647             this.resizeEl = new Roo.Resizable(this.wrap, {
23648                 pinned : true,
23649                 wrap: true,
23650                 dynamic : true,
23651                 minHeight : this.height,
23652                 height: this.height,
23653                 handles : this.resizable,
23654                 width: this.width,
23655                 listeners : {
23656                     resize : function(r, w, h) {
23657                         _t.onResize(w,h); // -something
23658                     }
23659                 }
23660             });
23661             
23662         }
23663         this.createToolbar(this);
23664        
23665         
23666         if(!this.width && this.resizable){
23667             this.setSize(this.wrap.getSize());
23668         }
23669         if (this.resizeEl) {
23670             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23671             // should trigger onReize..
23672         }
23673         
23674     },
23675
23676     // private
23677     onResize : function(w, h)
23678     {
23679         Roo.log('resize: ' +w + ',' + h );
23680         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23681         var ew = false;
23682         var eh = false;
23683         
23684         if(this.inputEl() ){
23685             if(typeof w == 'number'){
23686                 var aw = w - this.wrap.getFrameWidth('lr');
23687                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23688                 ew = aw;
23689             }
23690             if(typeof h == 'number'){
23691                  var tbh = -11;  // fixme it needs to tool bar size!
23692                 for (var i =0; i < this.toolbars.length;i++) {
23693                     // fixme - ask toolbars for heights?
23694                     tbh += this.toolbars[i].el.getHeight();
23695                     //if (this.toolbars[i].footer) {
23696                     //    tbh += this.toolbars[i].footer.el.getHeight();
23697                     //}
23698                 }
23699               
23700                 
23701                 
23702                 
23703                 
23704                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23705                 ah -= 5; // knock a few pixes off for look..
23706                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23707                 var eh = ah;
23708             }
23709         }
23710         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23711         this.editorcore.onResize(ew,eh);
23712         
23713     },
23714
23715     /**
23716      * Toggles the editor between standard and source edit mode.
23717      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23718      */
23719     toggleSourceEdit : function(sourceEditMode)
23720     {
23721         this.editorcore.toggleSourceEdit(sourceEditMode);
23722         
23723         if(this.editorcore.sourceEditMode){
23724             Roo.log('editor - showing textarea');
23725             
23726 //            Roo.log('in');
23727 //            Roo.log(this.syncValue());
23728             this.syncValue();
23729             this.inputEl().removeClass(['hide', 'x-hidden']);
23730             this.inputEl().dom.removeAttribute('tabIndex');
23731             this.inputEl().focus();
23732         }else{
23733             Roo.log('editor - hiding textarea');
23734 //            Roo.log('out')
23735 //            Roo.log(this.pushValue()); 
23736             this.pushValue();
23737             
23738             this.inputEl().addClass(['hide', 'x-hidden']);
23739             this.inputEl().dom.setAttribute('tabIndex', -1);
23740             //this.deferFocus();
23741         }
23742          
23743         if(this.resizable){
23744             this.setSize(this.wrap.getSize());
23745         }
23746         
23747         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23748     },
23749  
23750     // private (for BoxComponent)
23751     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23752
23753     // private (for BoxComponent)
23754     getResizeEl : function(){
23755         return this.wrap;
23756     },
23757
23758     // private (for BoxComponent)
23759     getPositionEl : function(){
23760         return this.wrap;
23761     },
23762
23763     // private
23764     initEvents : function(){
23765         this.originalValue = this.getValue();
23766     },
23767
23768 //    /**
23769 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23770 //     * @method
23771 //     */
23772 //    markInvalid : Roo.emptyFn,
23773 //    /**
23774 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23775 //     * @method
23776 //     */
23777 //    clearInvalid : Roo.emptyFn,
23778
23779     setValue : function(v){
23780         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23781         this.editorcore.pushValue();
23782     },
23783
23784      
23785     // private
23786     deferFocus : function(){
23787         this.focus.defer(10, this);
23788     },
23789
23790     // doc'ed in Field
23791     focus : function(){
23792         this.editorcore.focus();
23793         
23794     },
23795       
23796
23797     // private
23798     onDestroy : function(){
23799         
23800         
23801         
23802         if(this.rendered){
23803             
23804             for (var i =0; i < this.toolbars.length;i++) {
23805                 // fixme - ask toolbars for heights?
23806                 this.toolbars[i].onDestroy();
23807             }
23808             
23809             this.wrap.dom.innerHTML = '';
23810             this.wrap.remove();
23811         }
23812     },
23813
23814     // private
23815     onFirstFocus : function(){
23816         //Roo.log("onFirstFocus");
23817         this.editorcore.onFirstFocus();
23818          for (var i =0; i < this.toolbars.length;i++) {
23819             this.toolbars[i].onFirstFocus();
23820         }
23821         
23822     },
23823     
23824     // private
23825     syncValue : function()
23826     {   
23827         this.editorcore.syncValue();
23828     },
23829     
23830     pushValue : function()
23831     {   
23832         this.editorcore.pushValue();
23833     }
23834      
23835     
23836     // hide stuff that is not compatible
23837     /**
23838      * @event blur
23839      * @hide
23840      */
23841     /**
23842      * @event change
23843      * @hide
23844      */
23845     /**
23846      * @event focus
23847      * @hide
23848      */
23849     /**
23850      * @event specialkey
23851      * @hide
23852      */
23853     /**
23854      * @cfg {String} fieldClass @hide
23855      */
23856     /**
23857      * @cfg {String} focusClass @hide
23858      */
23859     /**
23860      * @cfg {String} autoCreate @hide
23861      */
23862     /**
23863      * @cfg {String} inputType @hide
23864      */
23865     /**
23866      * @cfg {String} invalidClass @hide
23867      */
23868     /**
23869      * @cfg {String} invalidText @hide
23870      */
23871     /**
23872      * @cfg {String} msgFx @hide
23873      */
23874     /**
23875      * @cfg {String} validateOnBlur @hide
23876      */
23877 });
23878  
23879     
23880    
23881    
23882    
23883       
23884 Roo.namespace('Roo.bootstrap.htmleditor');
23885 /**
23886  * @class Roo.bootstrap.HtmlEditorToolbar1
23887  * Basic Toolbar
23888  * 
23889  * Usage:
23890  *
23891  new Roo.bootstrap.HtmlEditor({
23892     ....
23893     toolbars : [
23894         new Roo.bootstrap.HtmlEditorToolbar1({
23895             disable : { fonts: 1 , format: 1, ..., ... , ...],
23896             btns : [ .... ]
23897         })
23898     }
23899      
23900  * 
23901  * @cfg {Object} disable List of elements to disable..
23902  * @cfg {Array} btns List of additional buttons.
23903  * 
23904  * 
23905  * NEEDS Extra CSS? 
23906  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23907  */
23908  
23909 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23910 {
23911     
23912     Roo.apply(this, config);
23913     
23914     // default disabled, based on 'good practice'..
23915     this.disable = this.disable || {};
23916     Roo.applyIf(this.disable, {
23917         fontSize : true,
23918         colors : true,
23919         specialElements : true
23920     });
23921     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23922     
23923     this.editor = config.editor;
23924     this.editorcore = config.editor.editorcore;
23925     
23926     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23927     
23928     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23929     // dont call parent... till later.
23930 }
23931 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23932      
23933     bar : true,
23934     
23935     editor : false,
23936     editorcore : false,
23937     
23938     
23939     formats : [
23940         "p" ,  
23941         "h1","h2","h3","h4","h5","h6", 
23942         "pre", "code", 
23943         "abbr", "acronym", "address", "cite", "samp", "var",
23944         'div','span'
23945     ],
23946     
23947     onRender : function(ct, position)
23948     {
23949        // Roo.log("Call onRender: " + this.xtype);
23950         
23951        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23952        Roo.log(this.el);
23953        this.el.dom.style.marginBottom = '0';
23954        var _this = this;
23955        var editorcore = this.editorcore;
23956        var editor= this.editor;
23957        
23958        var children = [];
23959        var btn = function(id,cmd , toggle, handler, html){
23960        
23961             var  event = toggle ? 'toggle' : 'click';
23962        
23963             var a = {
23964                 size : 'sm',
23965                 xtype: 'Button',
23966                 xns: Roo.bootstrap,
23967                 glyphicon : id,
23968                 cmd : id || cmd,
23969                 enableToggle:toggle !== false,
23970                 html : html || '',
23971                 pressed : toggle ? false : null,
23972                 listeners : {}
23973             };
23974             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23975                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23976             };
23977             children.push(a);
23978             return a;
23979        }
23980        
23981     //    var cb_box = function...
23982         
23983         var style = {
23984                 xtype: 'Button',
23985                 size : 'sm',
23986                 xns: Roo.bootstrap,
23987                 glyphicon : 'font',
23988                 //html : 'submit'
23989                 menu : {
23990                     xtype: 'Menu',
23991                     xns: Roo.bootstrap,
23992                     items:  []
23993                 }
23994         };
23995         Roo.each(this.formats, function(f) {
23996             style.menu.items.push({
23997                 xtype :'MenuItem',
23998                 xns: Roo.bootstrap,
23999                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24000                 tagname : f,
24001                 listeners : {
24002                     click : function()
24003                     {
24004                         editorcore.insertTag(this.tagname);
24005                         editor.focus();
24006                     }
24007                 }
24008                 
24009             });
24010         });
24011         children.push(style);   
24012         
24013         btn('bold',false,true);
24014         btn('italic',false,true);
24015         btn('align-left', 'justifyleft',true);
24016         btn('align-center', 'justifycenter',true);
24017         btn('align-right' , 'justifyright',true);
24018         btn('link', false, false, function(btn) {
24019             //Roo.log("create link?");
24020             var url = prompt(this.createLinkText, this.defaultLinkValue);
24021             if(url && url != 'http:/'+'/'){
24022                 this.editorcore.relayCmd('createlink', url);
24023             }
24024         }),
24025         btn('list','insertunorderedlist',true);
24026         btn('pencil', false,true, function(btn){
24027                 Roo.log(this);
24028                 this.toggleSourceEdit(btn.pressed);
24029         });
24030         
24031         if (this.editor.btns.length > 0) {
24032             for (var i = 0; i<this.editor.btns.length; i++) {
24033                 children.push(this.editor.btns[i]);
24034             }
24035         }
24036         
24037         /*
24038         var cog = {
24039                 xtype: 'Button',
24040                 size : 'sm',
24041                 xns: Roo.bootstrap,
24042                 glyphicon : 'cog',
24043                 //html : 'submit'
24044                 menu : {
24045                     xtype: 'Menu',
24046                     xns: Roo.bootstrap,
24047                     items:  []
24048                 }
24049         };
24050         
24051         cog.menu.items.push({
24052             xtype :'MenuItem',
24053             xns: Roo.bootstrap,
24054             html : Clean styles,
24055             tagname : f,
24056             listeners : {
24057                 click : function()
24058                 {
24059                     editorcore.insertTag(this.tagname);
24060                     editor.focus();
24061                 }
24062             }
24063             
24064         });
24065        */
24066         
24067          
24068        this.xtype = 'NavSimplebar';
24069         
24070         for(var i=0;i< children.length;i++) {
24071             
24072             this.buttons.add(this.addxtypeChild(children[i]));
24073             
24074         }
24075         
24076         editor.on('editorevent', this.updateToolbar, this);
24077     },
24078     onBtnClick : function(id)
24079     {
24080        this.editorcore.relayCmd(id);
24081        this.editorcore.focus();
24082     },
24083     
24084     /**
24085      * Protected method that will not generally be called directly. It triggers
24086      * a toolbar update by reading the markup state of the current selection in the editor.
24087      */
24088     updateToolbar: function(){
24089
24090         if(!this.editorcore.activated){
24091             this.editor.onFirstFocus(); // is this neeed?
24092             return;
24093         }
24094
24095         var btns = this.buttons; 
24096         var doc = this.editorcore.doc;
24097         btns.get('bold').setActive(doc.queryCommandState('bold'));
24098         btns.get('italic').setActive(doc.queryCommandState('italic'));
24099         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24100         
24101         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24102         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24103         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24104         
24105         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24106         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24107          /*
24108         
24109         var ans = this.editorcore.getAllAncestors();
24110         if (this.formatCombo) {
24111             
24112             
24113             var store = this.formatCombo.store;
24114             this.formatCombo.setValue("");
24115             for (var i =0; i < ans.length;i++) {
24116                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24117                     // select it..
24118                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24119                     break;
24120                 }
24121             }
24122         }
24123         
24124         
24125         
24126         // hides menus... - so this cant be on a menu...
24127         Roo.bootstrap.MenuMgr.hideAll();
24128         */
24129         Roo.bootstrap.MenuMgr.hideAll();
24130         //this.editorsyncValue();
24131     },
24132     onFirstFocus: function() {
24133         this.buttons.each(function(item){
24134            item.enable();
24135         });
24136     },
24137     toggleSourceEdit : function(sourceEditMode){
24138         
24139           
24140         if(sourceEditMode){
24141             Roo.log("disabling buttons");
24142            this.buttons.each( function(item){
24143                 if(item.cmd != 'pencil'){
24144                     item.disable();
24145                 }
24146             });
24147           
24148         }else{
24149             Roo.log("enabling buttons");
24150             if(this.editorcore.initialized){
24151                 this.buttons.each( function(item){
24152                     item.enable();
24153                 });
24154             }
24155             
24156         }
24157         Roo.log("calling toggole on editor");
24158         // tell the editor that it's been pressed..
24159         this.editor.toggleSourceEdit(sourceEditMode);
24160        
24161     }
24162 });
24163
24164
24165
24166
24167
24168 /**
24169  * @class Roo.bootstrap.Table.AbstractSelectionModel
24170  * @extends Roo.util.Observable
24171  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24172  * implemented by descendant classes.  This class should not be directly instantiated.
24173  * @constructor
24174  */
24175 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24176     this.locked = false;
24177     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24178 };
24179
24180
24181 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24182     /** @ignore Called by the grid automatically. Do not call directly. */
24183     init : function(grid){
24184         this.grid = grid;
24185         this.initEvents();
24186     },
24187
24188     /**
24189      * Locks the selections.
24190      */
24191     lock : function(){
24192         this.locked = true;
24193     },
24194
24195     /**
24196      * Unlocks the selections.
24197      */
24198     unlock : function(){
24199         this.locked = false;
24200     },
24201
24202     /**
24203      * Returns true if the selections are locked.
24204      * @return {Boolean}
24205      */
24206     isLocked : function(){
24207         return this.locked;
24208     }
24209 });
24210 /**
24211  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24212  * @class Roo.bootstrap.Table.RowSelectionModel
24213  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24214  * It supports multiple selections and keyboard selection/navigation. 
24215  * @constructor
24216  * @param {Object} config
24217  */
24218
24219 Roo.bootstrap.Table.RowSelectionModel = function(config){
24220     Roo.apply(this, config);
24221     this.selections = new Roo.util.MixedCollection(false, function(o){
24222         return o.id;
24223     });
24224
24225     this.last = false;
24226     this.lastActive = false;
24227
24228     this.addEvents({
24229         /**
24230              * @event selectionchange
24231              * Fires when the selection changes
24232              * @param {SelectionModel} this
24233              */
24234             "selectionchange" : true,
24235         /**
24236              * @event afterselectionchange
24237              * Fires after the selection changes (eg. by key press or clicking)
24238              * @param {SelectionModel} this
24239              */
24240             "afterselectionchange" : true,
24241         /**
24242              * @event beforerowselect
24243              * Fires when a row is selected being selected, return false to cancel.
24244              * @param {SelectionModel} this
24245              * @param {Number} rowIndex The selected index
24246              * @param {Boolean} keepExisting False if other selections will be cleared
24247              */
24248             "beforerowselect" : true,
24249         /**
24250              * @event rowselect
24251              * Fires when a row is selected.
24252              * @param {SelectionModel} this
24253              * @param {Number} rowIndex The selected index
24254              * @param {Roo.data.Record} r The record
24255              */
24256             "rowselect" : true,
24257         /**
24258              * @event rowdeselect
24259              * Fires when a row is deselected.
24260              * @param {SelectionModel} this
24261              * @param {Number} rowIndex The selected index
24262              */
24263         "rowdeselect" : true
24264     });
24265     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24266     this.locked = false;
24267  };
24268
24269 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24270     /**
24271      * @cfg {Boolean} singleSelect
24272      * True to allow selection of only one row at a time (defaults to false)
24273      */
24274     singleSelect : false,
24275
24276     // private
24277     initEvents : function()
24278     {
24279
24280         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24281         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24282         //}else{ // allow click to work like normal
24283          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24284         //}
24285         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24286         this.grid.on("rowclick", this.handleMouseDown, this);
24287         
24288         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24289             "up" : function(e){
24290                 if(!e.shiftKey){
24291                     this.selectPrevious(e.shiftKey);
24292                 }else if(this.last !== false && this.lastActive !== false){
24293                     var last = this.last;
24294                     this.selectRange(this.last,  this.lastActive-1);
24295                     this.grid.getView().focusRow(this.lastActive);
24296                     if(last !== false){
24297                         this.last = last;
24298                     }
24299                 }else{
24300                     this.selectFirstRow();
24301                 }
24302                 this.fireEvent("afterselectionchange", this);
24303             },
24304             "down" : function(e){
24305                 if(!e.shiftKey){
24306                     this.selectNext(e.shiftKey);
24307                 }else if(this.last !== false && this.lastActive !== false){
24308                     var last = this.last;
24309                     this.selectRange(this.last,  this.lastActive+1);
24310                     this.grid.getView().focusRow(this.lastActive);
24311                     if(last !== false){
24312                         this.last = last;
24313                     }
24314                 }else{
24315                     this.selectFirstRow();
24316                 }
24317                 this.fireEvent("afterselectionchange", this);
24318             },
24319             scope: this
24320         });
24321         this.grid.store.on('load', function(){
24322             this.selections.clear();
24323         },this);
24324         /*
24325         var view = this.grid.view;
24326         view.on("refresh", this.onRefresh, this);
24327         view.on("rowupdated", this.onRowUpdated, this);
24328         view.on("rowremoved", this.onRemove, this);
24329         */
24330     },
24331
24332     // private
24333     onRefresh : function()
24334     {
24335         var ds = this.grid.store, i, v = this.grid.view;
24336         var s = this.selections;
24337         s.each(function(r){
24338             if((i = ds.indexOfId(r.id)) != -1){
24339                 v.onRowSelect(i);
24340             }else{
24341                 s.remove(r);
24342             }
24343         });
24344     },
24345
24346     // private
24347     onRemove : function(v, index, r){
24348         this.selections.remove(r);
24349     },
24350
24351     // private
24352     onRowUpdated : function(v, index, r){
24353         if(this.isSelected(r)){
24354             v.onRowSelect(index);
24355         }
24356     },
24357
24358     /**
24359      * Select records.
24360      * @param {Array} records The records to select
24361      * @param {Boolean} keepExisting (optional) True to keep existing selections
24362      */
24363     selectRecords : function(records, keepExisting)
24364     {
24365         if(!keepExisting){
24366             this.clearSelections();
24367         }
24368             var ds = this.grid.store;
24369         for(var i = 0, len = records.length; i < len; i++){
24370             this.selectRow(ds.indexOf(records[i]), true);
24371         }
24372     },
24373
24374     /**
24375      * Gets the number of selected rows.
24376      * @return {Number}
24377      */
24378     getCount : function(){
24379         return this.selections.length;
24380     },
24381
24382     /**
24383      * Selects the first row in the grid.
24384      */
24385     selectFirstRow : function(){
24386         this.selectRow(0);
24387     },
24388
24389     /**
24390      * Select the last row.
24391      * @param {Boolean} keepExisting (optional) True to keep existing selections
24392      */
24393     selectLastRow : function(keepExisting){
24394         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24395         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24396     },
24397
24398     /**
24399      * Selects the row immediately following the last selected row.
24400      * @param {Boolean} keepExisting (optional) True to keep existing selections
24401      */
24402     selectNext : function(keepExisting)
24403     {
24404             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24405             this.selectRow(this.last+1, keepExisting);
24406             this.grid.getView().focusRow(this.last);
24407         }
24408     },
24409
24410     /**
24411      * Selects the row that precedes the last selected row.
24412      * @param {Boolean} keepExisting (optional) True to keep existing selections
24413      */
24414     selectPrevious : function(keepExisting){
24415         if(this.last){
24416             this.selectRow(this.last-1, keepExisting);
24417             this.grid.getView().focusRow(this.last);
24418         }
24419     },
24420
24421     /**
24422      * Returns the selected records
24423      * @return {Array} Array of selected records
24424      */
24425     getSelections : function(){
24426         return [].concat(this.selections.items);
24427     },
24428
24429     /**
24430      * Returns the first selected record.
24431      * @return {Record}
24432      */
24433     getSelected : function(){
24434         return this.selections.itemAt(0);
24435     },
24436
24437
24438     /**
24439      * Clears all selections.
24440      */
24441     clearSelections : function(fast)
24442     {
24443         if(this.locked) {
24444             return;
24445         }
24446         if(fast !== true){
24447                 var ds = this.grid.store;
24448             var s = this.selections;
24449             s.each(function(r){
24450                 this.deselectRow(ds.indexOfId(r.id));
24451             }, this);
24452             s.clear();
24453         }else{
24454             this.selections.clear();
24455         }
24456         this.last = false;
24457     },
24458
24459
24460     /**
24461      * Selects all rows.
24462      */
24463     selectAll : function(){
24464         if(this.locked) {
24465             return;
24466         }
24467         this.selections.clear();
24468         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24469             this.selectRow(i, true);
24470         }
24471     },
24472
24473     /**
24474      * Returns True if there is a selection.
24475      * @return {Boolean}
24476      */
24477     hasSelection : function(){
24478         return this.selections.length > 0;
24479     },
24480
24481     /**
24482      * Returns True if the specified row is selected.
24483      * @param {Number/Record} record The record or index of the record to check
24484      * @return {Boolean}
24485      */
24486     isSelected : function(index){
24487             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24488         return (r && this.selections.key(r.id) ? true : false);
24489     },
24490
24491     /**
24492      * Returns True if the specified record id is selected.
24493      * @param {String} id The id of record to check
24494      * @return {Boolean}
24495      */
24496     isIdSelected : function(id){
24497         return (this.selections.key(id) ? true : false);
24498     },
24499
24500
24501     // private
24502     handleMouseDBClick : function(e, t){
24503         
24504     },
24505     // private
24506     handleMouseDown : function(e, t)
24507     {
24508             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24509         if(this.isLocked() || rowIndex < 0 ){
24510             return;
24511         };
24512         if(e.shiftKey && this.last !== false){
24513             var last = this.last;
24514             this.selectRange(last, rowIndex, e.ctrlKey);
24515             this.last = last; // reset the last
24516             t.focus();
24517     
24518         }else{
24519             var isSelected = this.isSelected(rowIndex);
24520             //Roo.log("select row:" + rowIndex);
24521             if(isSelected){
24522                 this.deselectRow(rowIndex);
24523             } else {
24524                         this.selectRow(rowIndex, true);
24525             }
24526     
24527             /*
24528                 if(e.button !== 0 && isSelected){
24529                 alert('rowIndex 2: ' + rowIndex);
24530                     view.focusRow(rowIndex);
24531                 }else if(e.ctrlKey && isSelected){
24532                     this.deselectRow(rowIndex);
24533                 }else if(!isSelected){
24534                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24535                     view.focusRow(rowIndex);
24536                 }
24537             */
24538         }
24539         this.fireEvent("afterselectionchange", this);
24540     },
24541     // private
24542     handleDragableRowClick :  function(grid, rowIndex, e) 
24543     {
24544         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24545             this.selectRow(rowIndex, false);
24546             grid.view.focusRow(rowIndex);
24547              this.fireEvent("afterselectionchange", this);
24548         }
24549     },
24550     
24551     /**
24552      * Selects multiple rows.
24553      * @param {Array} rows Array of the indexes of the row to select
24554      * @param {Boolean} keepExisting (optional) True to keep existing selections
24555      */
24556     selectRows : function(rows, keepExisting){
24557         if(!keepExisting){
24558             this.clearSelections();
24559         }
24560         for(var i = 0, len = rows.length; i < len; i++){
24561             this.selectRow(rows[i], true);
24562         }
24563     },
24564
24565     /**
24566      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24567      * @param {Number} startRow The index of the first row in the range
24568      * @param {Number} endRow The index of the last row in the range
24569      * @param {Boolean} keepExisting (optional) True to retain existing selections
24570      */
24571     selectRange : function(startRow, endRow, keepExisting){
24572         if(this.locked) {
24573             return;
24574         }
24575         if(!keepExisting){
24576             this.clearSelections();
24577         }
24578         if(startRow <= endRow){
24579             for(var i = startRow; i <= endRow; i++){
24580                 this.selectRow(i, true);
24581             }
24582         }else{
24583             for(var i = startRow; i >= endRow; i--){
24584                 this.selectRow(i, true);
24585             }
24586         }
24587     },
24588
24589     /**
24590      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24591      * @param {Number} startRow The index of the first row in the range
24592      * @param {Number} endRow The index of the last row in the range
24593      */
24594     deselectRange : function(startRow, endRow, preventViewNotify){
24595         if(this.locked) {
24596             return;
24597         }
24598         for(var i = startRow; i <= endRow; i++){
24599             this.deselectRow(i, preventViewNotify);
24600         }
24601     },
24602
24603     /**
24604      * Selects a row.
24605      * @param {Number} row The index of the row to select
24606      * @param {Boolean} keepExisting (optional) True to keep existing selections
24607      */
24608     selectRow : function(index, keepExisting, preventViewNotify)
24609     {
24610             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24611             return;
24612         }
24613         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24614             if(!keepExisting || this.singleSelect){
24615                 this.clearSelections();
24616             }
24617             
24618             var r = this.grid.store.getAt(index);
24619             //console.log('selectRow - record id :' + r.id);
24620             
24621             this.selections.add(r);
24622             this.last = this.lastActive = index;
24623             if(!preventViewNotify){
24624                 var proxy = new Roo.Element(
24625                                 this.grid.getRowDom(index)
24626                 );
24627                 proxy.addClass('bg-info info');
24628             }
24629             this.fireEvent("rowselect", this, index, r);
24630             this.fireEvent("selectionchange", this);
24631         }
24632     },
24633
24634     /**
24635      * Deselects a row.
24636      * @param {Number} row The index of the row to deselect
24637      */
24638     deselectRow : function(index, preventViewNotify)
24639     {
24640         if(this.locked) {
24641             return;
24642         }
24643         if(this.last == index){
24644             this.last = false;
24645         }
24646         if(this.lastActive == index){
24647             this.lastActive = false;
24648         }
24649         
24650         var r = this.grid.store.getAt(index);
24651         if (!r) {
24652             return;
24653         }
24654         
24655         this.selections.remove(r);
24656         //.console.log('deselectRow - record id :' + r.id);
24657         if(!preventViewNotify){
24658         
24659             var proxy = new Roo.Element(
24660                 this.grid.getRowDom(index)
24661             );
24662             proxy.removeClass('bg-info info');
24663         }
24664         this.fireEvent("rowdeselect", this, index);
24665         this.fireEvent("selectionchange", this);
24666     },
24667
24668     // private
24669     restoreLast : function(){
24670         if(this._last){
24671             this.last = this._last;
24672         }
24673     },
24674
24675     // private
24676     acceptsNav : function(row, col, cm){
24677         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24678     },
24679
24680     // private
24681     onEditorKey : function(field, e){
24682         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24683         if(k == e.TAB){
24684             e.stopEvent();
24685             ed.completeEdit();
24686             if(e.shiftKey){
24687                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24688             }else{
24689                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24690             }
24691         }else if(k == e.ENTER && !e.ctrlKey){
24692             e.stopEvent();
24693             ed.completeEdit();
24694             if(e.shiftKey){
24695                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24696             }else{
24697                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24698             }
24699         }else if(k == e.ESC){
24700             ed.cancelEdit();
24701         }
24702         if(newCell){
24703             g.startEditing(newCell[0], newCell[1]);
24704         }
24705     }
24706 });
24707 /*
24708  * Based on:
24709  * Ext JS Library 1.1.1
24710  * Copyright(c) 2006-2007, Ext JS, LLC.
24711  *
24712  * Originally Released Under LGPL - original licence link has changed is not relivant.
24713  *
24714  * Fork - LGPL
24715  * <script type="text/javascript">
24716  */
24717  
24718 /**
24719  * @class Roo.bootstrap.PagingToolbar
24720  * @extends Roo.bootstrap.NavSimplebar
24721  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24722  * @constructor
24723  * Create a new PagingToolbar
24724  * @param {Object} config The config object
24725  * @param {Roo.data.Store} store
24726  */
24727 Roo.bootstrap.PagingToolbar = function(config)
24728 {
24729     // old args format still supported... - xtype is prefered..
24730         // created from xtype...
24731     
24732     this.ds = config.dataSource;
24733     
24734     if (config.store && !this.ds) {
24735         this.store= Roo.factory(config.store, Roo.data);
24736         this.ds = this.store;
24737         this.ds.xmodule = this.xmodule || false;
24738     }
24739     
24740     this.toolbarItems = [];
24741     if (config.items) {
24742         this.toolbarItems = config.items;
24743     }
24744     
24745     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24746     
24747     this.cursor = 0;
24748     
24749     if (this.ds) { 
24750         this.bind(this.ds);
24751     }
24752     
24753     if (Roo.bootstrap.version == 4) {
24754         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24755     } else {
24756         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24757     }
24758     
24759 };
24760
24761 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24762     /**
24763      * @cfg {Roo.data.Store} dataSource
24764      * The underlying data store providing the paged data
24765      */
24766     /**
24767      * @cfg {String/HTMLElement/Element} container
24768      * container The id or element that will contain the toolbar
24769      */
24770     /**
24771      * @cfg {Boolean} displayInfo
24772      * True to display the displayMsg (defaults to false)
24773      */
24774     /**
24775      * @cfg {Number} pageSize
24776      * The number of records to display per page (defaults to 20)
24777      */
24778     pageSize: 20,
24779     /**
24780      * @cfg {String} displayMsg
24781      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24782      */
24783     displayMsg : 'Displaying {0} - {1} of {2}',
24784     /**
24785      * @cfg {String} emptyMsg
24786      * The message to display when no records are found (defaults to "No data to display")
24787      */
24788     emptyMsg : 'No data to display',
24789     /**
24790      * Customizable piece of the default paging text (defaults to "Page")
24791      * @type String
24792      */
24793     beforePageText : "Page",
24794     /**
24795      * Customizable piece of the default paging text (defaults to "of %0")
24796      * @type String
24797      */
24798     afterPageText : "of {0}",
24799     /**
24800      * Customizable piece of the default paging text (defaults to "First Page")
24801      * @type String
24802      */
24803     firstText : "First Page",
24804     /**
24805      * Customizable piece of the default paging text (defaults to "Previous Page")
24806      * @type String
24807      */
24808     prevText : "Previous Page",
24809     /**
24810      * Customizable piece of the default paging text (defaults to "Next Page")
24811      * @type String
24812      */
24813     nextText : "Next Page",
24814     /**
24815      * Customizable piece of the default paging text (defaults to "Last Page")
24816      * @type String
24817      */
24818     lastText : "Last Page",
24819     /**
24820      * Customizable piece of the default paging text (defaults to "Refresh")
24821      * @type String
24822      */
24823     refreshText : "Refresh",
24824
24825     buttons : false,
24826     // private
24827     onRender : function(ct, position) 
24828     {
24829         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24830         this.navgroup.parentId = this.id;
24831         this.navgroup.onRender(this.el, null);
24832         // add the buttons to the navgroup
24833         
24834         if(this.displayInfo){
24835             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24836             this.displayEl = this.el.select('.x-paging-info', true).first();
24837 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24838 //            this.displayEl = navel.el.select('span',true).first();
24839         }
24840         
24841         var _this = this;
24842         
24843         if(this.buttons){
24844             Roo.each(_this.buttons, function(e){ // this might need to use render????
24845                Roo.factory(e).render(_this.el);
24846             });
24847         }
24848             
24849         Roo.each(_this.toolbarItems, function(e) {
24850             _this.navgroup.addItem(e);
24851         });
24852         
24853         
24854         this.first = this.navgroup.addItem({
24855             tooltip: this.firstText,
24856             cls: "prev btn-outline-secondary",
24857             html : ' <i class="fa fa-step-backward"></i>',
24858             disabled: true,
24859             preventDefault: true,
24860             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24861         });
24862         
24863         this.prev =  this.navgroup.addItem({
24864             tooltip: this.prevText,
24865             cls: "prev btn-outline-secondary",
24866             html : ' <i class="fa fa-backward"></i>',
24867             disabled: true,
24868             preventDefault: true,
24869             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24870         });
24871     //this.addSeparator();
24872         
24873         
24874         var field = this.navgroup.addItem( {
24875             tagtype : 'span',
24876             cls : 'x-paging-position  btn-outline-secondary',
24877              disabled: true,
24878             html : this.beforePageText  +
24879                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24880                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24881          } ); //?? escaped?
24882         
24883         this.field = field.el.select('input', true).first();
24884         this.field.on("keydown", this.onPagingKeydown, this);
24885         this.field.on("focus", function(){this.dom.select();});
24886     
24887     
24888         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24889         //this.field.setHeight(18);
24890         //this.addSeparator();
24891         this.next = this.navgroup.addItem({
24892             tooltip: this.nextText,
24893             cls: "next btn-outline-secondary",
24894             html : ' <i class="fa fa-forward"></i>',
24895             disabled: true,
24896             preventDefault: true,
24897             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24898         });
24899         this.last = this.navgroup.addItem({
24900             tooltip: this.lastText,
24901             html : ' <i class="fa fa-step-forward"></i>',
24902             cls: "next btn-outline-secondary",
24903             disabled: true,
24904             preventDefault: true,
24905             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24906         });
24907     //this.addSeparator();
24908         this.loading = this.navgroup.addItem({
24909             tooltip: this.refreshText,
24910             cls: "btn-outline-secondary",
24911             html : ' <i class="fa fa-refresh"></i>',
24912             preventDefault: true,
24913             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24914         });
24915         
24916     },
24917
24918     // private
24919     updateInfo : function(){
24920         if(this.displayEl){
24921             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24922             var msg = count == 0 ?
24923                 this.emptyMsg :
24924                 String.format(
24925                     this.displayMsg,
24926                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24927                 );
24928             this.displayEl.update(msg);
24929         }
24930     },
24931
24932     // private
24933     onLoad : function(ds, r, o)
24934     {
24935         this.cursor = o.params.start ? o.params.start : 0;
24936         
24937         var d = this.getPageData(),
24938             ap = d.activePage,
24939             ps = d.pages;
24940         
24941         
24942         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24943         this.field.dom.value = ap;
24944         this.first.setDisabled(ap == 1);
24945         this.prev.setDisabled(ap == 1);
24946         this.next.setDisabled(ap == ps);
24947         this.last.setDisabled(ap == ps);
24948         this.loading.enable();
24949         this.updateInfo();
24950     },
24951
24952     // private
24953     getPageData : function(){
24954         var total = this.ds.getTotalCount();
24955         return {
24956             total : total,
24957             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24958             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24959         };
24960     },
24961
24962     // private
24963     onLoadError : function(){
24964         this.loading.enable();
24965     },
24966
24967     // private
24968     onPagingKeydown : function(e){
24969         var k = e.getKey();
24970         var d = this.getPageData();
24971         if(k == e.RETURN){
24972             var v = this.field.dom.value, pageNum;
24973             if(!v || isNaN(pageNum = parseInt(v, 10))){
24974                 this.field.dom.value = d.activePage;
24975                 return;
24976             }
24977             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24978             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24979             e.stopEvent();
24980         }
24981         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))
24982         {
24983           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24984           this.field.dom.value = pageNum;
24985           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24986           e.stopEvent();
24987         }
24988         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24989         {
24990           var v = this.field.dom.value, pageNum; 
24991           var increment = (e.shiftKey) ? 10 : 1;
24992           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24993                 increment *= -1;
24994           }
24995           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24996             this.field.dom.value = d.activePage;
24997             return;
24998           }
24999           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25000           {
25001             this.field.dom.value = parseInt(v, 10) + increment;
25002             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25003             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25004           }
25005           e.stopEvent();
25006         }
25007     },
25008
25009     // private
25010     beforeLoad : function(){
25011         if(this.loading){
25012             this.loading.disable();
25013         }
25014     },
25015
25016     // private
25017     onClick : function(which){
25018         
25019         var ds = this.ds;
25020         if (!ds) {
25021             return;
25022         }
25023         
25024         switch(which){
25025             case "first":
25026                 ds.load({params:{start: 0, limit: this.pageSize}});
25027             break;
25028             case "prev":
25029                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25030             break;
25031             case "next":
25032                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25033             break;
25034             case "last":
25035                 var total = ds.getTotalCount();
25036                 var extra = total % this.pageSize;
25037                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25038                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25039             break;
25040             case "refresh":
25041                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25042             break;
25043         }
25044     },
25045
25046     /**
25047      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25048      * @param {Roo.data.Store} store The data store to unbind
25049      */
25050     unbind : function(ds){
25051         ds.un("beforeload", this.beforeLoad, this);
25052         ds.un("load", this.onLoad, this);
25053         ds.un("loadexception", this.onLoadError, this);
25054         ds.un("remove", this.updateInfo, this);
25055         ds.un("add", this.updateInfo, this);
25056         this.ds = undefined;
25057     },
25058
25059     /**
25060      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25061      * @param {Roo.data.Store} store The data store to bind
25062      */
25063     bind : function(ds){
25064         ds.on("beforeload", this.beforeLoad, this);
25065         ds.on("load", this.onLoad, this);
25066         ds.on("loadexception", this.onLoadError, this);
25067         ds.on("remove", this.updateInfo, this);
25068         ds.on("add", this.updateInfo, this);
25069         this.ds = ds;
25070     }
25071 });/*
25072  * - LGPL
25073  *
25074  * element
25075  * 
25076  */
25077
25078 /**
25079  * @class Roo.bootstrap.MessageBar
25080  * @extends Roo.bootstrap.Component
25081  * Bootstrap MessageBar class
25082  * @cfg {String} html contents of the MessageBar
25083  * @cfg {String} weight (info | success | warning | danger) default info
25084  * @cfg {String} beforeClass insert the bar before the given class
25085  * @cfg {Boolean} closable (true | false) default false
25086  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25087  * 
25088  * @constructor
25089  * Create a new Element
25090  * @param {Object} config The config object
25091  */
25092
25093 Roo.bootstrap.MessageBar = function(config){
25094     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25095 };
25096
25097 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25098     
25099     html: '',
25100     weight: 'info',
25101     closable: false,
25102     fixed: false,
25103     beforeClass: 'bootstrap-sticky-wrap',
25104     
25105     getAutoCreate : function(){
25106         
25107         var cfg = {
25108             tag: 'div',
25109             cls: 'alert alert-dismissable alert-' + this.weight,
25110             cn: [
25111                 {
25112                     tag: 'span',
25113                     cls: 'message',
25114                     html: this.html || ''
25115                 }
25116             ]
25117         };
25118         
25119         if(this.fixed){
25120             cfg.cls += ' alert-messages-fixed';
25121         }
25122         
25123         if(this.closable){
25124             cfg.cn.push({
25125                 tag: 'button',
25126                 cls: 'close',
25127                 html: 'x'
25128             });
25129         }
25130         
25131         return cfg;
25132     },
25133     
25134     onRender : function(ct, position)
25135     {
25136         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25137         
25138         if(!this.el){
25139             var cfg = Roo.apply({},  this.getAutoCreate());
25140             cfg.id = Roo.id();
25141             
25142             if (this.cls) {
25143                 cfg.cls += ' ' + this.cls;
25144             }
25145             if (this.style) {
25146                 cfg.style = this.style;
25147             }
25148             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25149             
25150             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25151         }
25152         
25153         this.el.select('>button.close').on('click', this.hide, this);
25154         
25155     },
25156     
25157     show : function()
25158     {
25159         if (!this.rendered) {
25160             this.render();
25161         }
25162         
25163         this.el.show();
25164         
25165         this.fireEvent('show', this);
25166         
25167     },
25168     
25169     hide : function()
25170     {
25171         if (!this.rendered) {
25172             this.render();
25173         }
25174         
25175         this.el.hide();
25176         
25177         this.fireEvent('hide', this);
25178     },
25179     
25180     update : function()
25181     {
25182 //        var e = this.el.dom.firstChild;
25183 //        
25184 //        if(this.closable){
25185 //            e = e.nextSibling;
25186 //        }
25187 //        
25188 //        e.data = this.html || '';
25189
25190         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25191     }
25192    
25193 });
25194
25195  
25196
25197      /*
25198  * - LGPL
25199  *
25200  * Graph
25201  * 
25202  */
25203
25204
25205 /**
25206  * @class Roo.bootstrap.Graph
25207  * @extends Roo.bootstrap.Component
25208  * Bootstrap Graph class
25209 > Prameters
25210  -sm {number} sm 4
25211  -md {number} md 5
25212  @cfg {String} graphtype  bar | vbar | pie
25213  @cfg {number} g_x coodinator | centre x (pie)
25214  @cfg {number} g_y coodinator | centre y (pie)
25215  @cfg {number} g_r radius (pie)
25216  @cfg {number} g_height height of the chart (respected by all elements in the set)
25217  @cfg {number} g_width width of the chart (respected by all elements in the set)
25218  @cfg {Object} title The title of the chart
25219     
25220  -{Array}  values
25221  -opts (object) options for the chart 
25222      o {
25223      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25224      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25225      o vgutter (number)
25226      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.
25227      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25228      o to
25229      o stretch (boolean)
25230      o }
25231  -opts (object) options for the pie
25232      o{
25233      o cut
25234      o startAngle (number)
25235      o endAngle (number)
25236      } 
25237  *
25238  * @constructor
25239  * Create a new Input
25240  * @param {Object} config The config object
25241  */
25242
25243 Roo.bootstrap.Graph = function(config){
25244     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25245     
25246     this.addEvents({
25247         // img events
25248         /**
25249          * @event click
25250          * The img click event for the img.
25251          * @param {Roo.EventObject} e
25252          */
25253         "click" : true
25254     });
25255 };
25256
25257 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25258     
25259     sm: 4,
25260     md: 5,
25261     graphtype: 'bar',
25262     g_height: 250,
25263     g_width: 400,
25264     g_x: 50,
25265     g_y: 50,
25266     g_r: 30,
25267     opts:{
25268         //g_colors: this.colors,
25269         g_type: 'soft',
25270         g_gutter: '20%'
25271
25272     },
25273     title : false,
25274
25275     getAutoCreate : function(){
25276         
25277         var cfg = {
25278             tag: 'div',
25279             html : null
25280         };
25281         
25282         
25283         return  cfg;
25284     },
25285
25286     onRender : function(ct,position){
25287         
25288         
25289         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25290         
25291         if (typeof(Raphael) == 'undefined') {
25292             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25293             return;
25294         }
25295         
25296         this.raphael = Raphael(this.el.dom);
25297         
25298                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25299                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25300                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25301                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25302                 /*
25303                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25304                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25305                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25306                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25307                 
25308                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25309                 r.barchart(330, 10, 300, 220, data1);
25310                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25311                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25312                 */
25313                 
25314                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25315                 // r.barchart(30, 30, 560, 250,  xdata, {
25316                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25317                 //     axis : "0 0 1 1",
25318                 //     axisxlabels :  xdata
25319                 //     //yvalues : cols,
25320                    
25321                 // });
25322 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25323 //        
25324 //        this.load(null,xdata,{
25325 //                axis : "0 0 1 1",
25326 //                axisxlabels :  xdata
25327 //                });
25328
25329     },
25330
25331     load : function(graphtype,xdata,opts)
25332     {
25333         this.raphael.clear();
25334         if(!graphtype) {
25335             graphtype = this.graphtype;
25336         }
25337         if(!opts){
25338             opts = this.opts;
25339         }
25340         var r = this.raphael,
25341             fin = function () {
25342                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25343             },
25344             fout = function () {
25345                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25346             },
25347             pfin = function() {
25348                 this.sector.stop();
25349                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25350
25351                 if (this.label) {
25352                     this.label[0].stop();
25353                     this.label[0].attr({ r: 7.5 });
25354                     this.label[1].attr({ "font-weight": 800 });
25355                 }
25356             },
25357             pfout = function() {
25358                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25359
25360                 if (this.label) {
25361                     this.label[0].animate({ r: 5 }, 500, "bounce");
25362                     this.label[1].attr({ "font-weight": 400 });
25363                 }
25364             };
25365
25366         switch(graphtype){
25367             case 'bar':
25368                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25369                 break;
25370             case 'hbar':
25371                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25372                 break;
25373             case 'pie':
25374 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25375 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25376 //            
25377                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25378                 
25379                 break;
25380
25381         }
25382         
25383         if(this.title){
25384             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25385         }
25386         
25387     },
25388     
25389     setTitle: function(o)
25390     {
25391         this.title = o;
25392     },
25393     
25394     initEvents: function() {
25395         
25396         if(!this.href){
25397             this.el.on('click', this.onClick, this);
25398         }
25399     },
25400     
25401     onClick : function(e)
25402     {
25403         Roo.log('img onclick');
25404         this.fireEvent('click', this, e);
25405     }
25406    
25407 });
25408
25409  
25410 /*
25411  * - LGPL
25412  *
25413  * numberBox
25414  * 
25415  */
25416 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25417
25418 /**
25419  * @class Roo.bootstrap.dash.NumberBox
25420  * @extends Roo.bootstrap.Component
25421  * Bootstrap NumberBox class
25422  * @cfg {String} headline Box headline
25423  * @cfg {String} content Box content
25424  * @cfg {String} icon Box icon
25425  * @cfg {String} footer Footer text
25426  * @cfg {String} fhref Footer href
25427  * 
25428  * @constructor
25429  * Create a new NumberBox
25430  * @param {Object} config The config object
25431  */
25432
25433
25434 Roo.bootstrap.dash.NumberBox = function(config){
25435     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25436     
25437 };
25438
25439 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25440     
25441     headline : '',
25442     content : '',
25443     icon : '',
25444     footer : '',
25445     fhref : '',
25446     ficon : '',
25447     
25448     getAutoCreate : function(){
25449         
25450         var cfg = {
25451             tag : 'div',
25452             cls : 'small-box ',
25453             cn : [
25454                 {
25455                     tag : 'div',
25456                     cls : 'inner',
25457                     cn :[
25458                         {
25459                             tag : 'h3',
25460                             cls : 'roo-headline',
25461                             html : this.headline
25462                         },
25463                         {
25464                             tag : 'p',
25465                             cls : 'roo-content',
25466                             html : this.content
25467                         }
25468                     ]
25469                 }
25470             ]
25471         };
25472         
25473         if(this.icon){
25474             cfg.cn.push({
25475                 tag : 'div',
25476                 cls : 'icon',
25477                 cn :[
25478                     {
25479                         tag : 'i',
25480                         cls : 'ion ' + this.icon
25481                     }
25482                 ]
25483             });
25484         }
25485         
25486         if(this.footer){
25487             var footer = {
25488                 tag : 'a',
25489                 cls : 'small-box-footer',
25490                 href : this.fhref || '#',
25491                 html : this.footer
25492             };
25493             
25494             cfg.cn.push(footer);
25495             
25496         }
25497         
25498         return  cfg;
25499     },
25500
25501     onRender : function(ct,position){
25502         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25503
25504
25505        
25506                 
25507     },
25508
25509     setHeadline: function (value)
25510     {
25511         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25512     },
25513     
25514     setFooter: function (value, href)
25515     {
25516         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25517         
25518         if(href){
25519             this.el.select('a.small-box-footer',true).first().attr('href', href);
25520         }
25521         
25522     },
25523
25524     setContent: function (value)
25525     {
25526         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25527     },
25528
25529     initEvents: function() 
25530     {   
25531         
25532     }
25533     
25534 });
25535
25536  
25537 /*
25538  * - LGPL
25539  *
25540  * TabBox
25541  * 
25542  */
25543 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25544
25545 /**
25546  * @class Roo.bootstrap.dash.TabBox
25547  * @extends Roo.bootstrap.Component
25548  * Bootstrap TabBox class
25549  * @cfg {String} title Title of the TabBox
25550  * @cfg {String} icon Icon of the TabBox
25551  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25552  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25553  * 
25554  * @constructor
25555  * Create a new TabBox
25556  * @param {Object} config The config object
25557  */
25558
25559
25560 Roo.bootstrap.dash.TabBox = function(config){
25561     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25562     this.addEvents({
25563         // raw events
25564         /**
25565          * @event addpane
25566          * When a pane is added
25567          * @param {Roo.bootstrap.dash.TabPane} pane
25568          */
25569         "addpane" : true,
25570         /**
25571          * @event activatepane
25572          * When a pane is activated
25573          * @param {Roo.bootstrap.dash.TabPane} pane
25574          */
25575         "activatepane" : true
25576         
25577          
25578     });
25579     
25580     this.panes = [];
25581 };
25582
25583 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25584
25585     title : '',
25586     icon : false,
25587     showtabs : true,
25588     tabScrollable : false,
25589     
25590     getChildContainer : function()
25591     {
25592         return this.el.select('.tab-content', true).first();
25593     },
25594     
25595     getAutoCreate : function(){
25596         
25597         var header = {
25598             tag: 'li',
25599             cls: 'pull-left header',
25600             html: this.title,
25601             cn : []
25602         };
25603         
25604         if(this.icon){
25605             header.cn.push({
25606                 tag: 'i',
25607                 cls: 'fa ' + this.icon
25608             });
25609         }
25610         
25611         var h = {
25612             tag: 'ul',
25613             cls: 'nav nav-tabs pull-right',
25614             cn: [
25615                 header
25616             ]
25617         };
25618         
25619         if(this.tabScrollable){
25620             h = {
25621                 tag: 'div',
25622                 cls: 'tab-header',
25623                 cn: [
25624                     {
25625                         tag: 'ul',
25626                         cls: 'nav nav-tabs pull-right',
25627                         cn: [
25628                             header
25629                         ]
25630                     }
25631                 ]
25632             };
25633         }
25634         
25635         var cfg = {
25636             tag: 'div',
25637             cls: 'nav-tabs-custom',
25638             cn: [
25639                 h,
25640                 {
25641                     tag: 'div',
25642                     cls: 'tab-content no-padding',
25643                     cn: []
25644                 }
25645             ]
25646         };
25647
25648         return  cfg;
25649     },
25650     initEvents : function()
25651     {
25652         //Roo.log('add add pane handler');
25653         this.on('addpane', this.onAddPane, this);
25654     },
25655      /**
25656      * Updates the box title
25657      * @param {String} html to set the title to.
25658      */
25659     setTitle : function(value)
25660     {
25661         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25662     },
25663     onAddPane : function(pane)
25664     {
25665         this.panes.push(pane);
25666         //Roo.log('addpane');
25667         //Roo.log(pane);
25668         // tabs are rendere left to right..
25669         if(!this.showtabs){
25670             return;
25671         }
25672         
25673         var ctr = this.el.select('.nav-tabs', true).first();
25674          
25675          
25676         var existing = ctr.select('.nav-tab',true);
25677         var qty = existing.getCount();;
25678         
25679         
25680         var tab = ctr.createChild({
25681             tag : 'li',
25682             cls : 'nav-tab' + (qty ? '' : ' active'),
25683             cn : [
25684                 {
25685                     tag : 'a',
25686                     href:'#',
25687                     html : pane.title
25688                 }
25689             ]
25690         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25691         pane.tab = tab;
25692         
25693         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25694         if (!qty) {
25695             pane.el.addClass('active');
25696         }
25697         
25698                 
25699     },
25700     onTabClick : function(ev,un,ob,pane)
25701     {
25702         //Roo.log('tab - prev default');
25703         ev.preventDefault();
25704         
25705         
25706         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25707         pane.tab.addClass('active');
25708         //Roo.log(pane.title);
25709         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25710         // technically we should have a deactivate event.. but maybe add later.
25711         // and it should not de-activate the selected tab...
25712         this.fireEvent('activatepane', pane);
25713         pane.el.addClass('active');
25714         pane.fireEvent('activate');
25715         
25716         
25717     },
25718     
25719     getActivePane : function()
25720     {
25721         var r = false;
25722         Roo.each(this.panes, function(p) {
25723             if(p.el.hasClass('active')){
25724                 r = p;
25725                 return false;
25726             }
25727             
25728             return;
25729         });
25730         
25731         return r;
25732     }
25733     
25734     
25735 });
25736
25737  
25738 /*
25739  * - LGPL
25740  *
25741  * Tab pane
25742  * 
25743  */
25744 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25745 /**
25746  * @class Roo.bootstrap.TabPane
25747  * @extends Roo.bootstrap.Component
25748  * Bootstrap TabPane class
25749  * @cfg {Boolean} active (false | true) Default false
25750  * @cfg {String} title title of panel
25751
25752  * 
25753  * @constructor
25754  * Create a new TabPane
25755  * @param {Object} config The config object
25756  */
25757
25758 Roo.bootstrap.dash.TabPane = function(config){
25759     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25760     
25761     this.addEvents({
25762         // raw events
25763         /**
25764          * @event activate
25765          * When a pane is activated
25766          * @param {Roo.bootstrap.dash.TabPane} pane
25767          */
25768         "activate" : true
25769          
25770     });
25771 };
25772
25773 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25774     
25775     active : false,
25776     title : '',
25777     
25778     // the tabBox that this is attached to.
25779     tab : false,
25780      
25781     getAutoCreate : function() 
25782     {
25783         var cfg = {
25784             tag: 'div',
25785             cls: 'tab-pane'
25786         };
25787         
25788         if(this.active){
25789             cfg.cls += ' active';
25790         }
25791         
25792         return cfg;
25793     },
25794     initEvents  : function()
25795     {
25796         //Roo.log('trigger add pane handler');
25797         this.parent().fireEvent('addpane', this)
25798     },
25799     
25800      /**
25801      * Updates the tab title 
25802      * @param {String} html to set the title to.
25803      */
25804     setTitle: function(str)
25805     {
25806         if (!this.tab) {
25807             return;
25808         }
25809         this.title = str;
25810         this.tab.select('a', true).first().dom.innerHTML = str;
25811         
25812     }
25813     
25814     
25815     
25816 });
25817
25818  
25819
25820
25821  /*
25822  * - LGPL
25823  *
25824  * menu
25825  * 
25826  */
25827 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25828
25829 /**
25830  * @class Roo.bootstrap.menu.Menu
25831  * @extends Roo.bootstrap.Component
25832  * Bootstrap Menu class - container for Menu
25833  * @cfg {String} html Text of the menu
25834  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25835  * @cfg {String} icon Font awesome icon
25836  * @cfg {String} pos Menu align to (top | bottom) default bottom
25837  * 
25838  * 
25839  * @constructor
25840  * Create a new Menu
25841  * @param {Object} config The config object
25842  */
25843
25844
25845 Roo.bootstrap.menu.Menu = function(config){
25846     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25847     
25848     this.addEvents({
25849         /**
25850          * @event beforeshow
25851          * Fires before this menu is displayed
25852          * @param {Roo.bootstrap.menu.Menu} this
25853          */
25854         beforeshow : true,
25855         /**
25856          * @event beforehide
25857          * Fires before this menu is hidden
25858          * @param {Roo.bootstrap.menu.Menu} this
25859          */
25860         beforehide : true,
25861         /**
25862          * @event show
25863          * Fires after this menu is displayed
25864          * @param {Roo.bootstrap.menu.Menu} this
25865          */
25866         show : true,
25867         /**
25868          * @event hide
25869          * Fires after this menu is hidden
25870          * @param {Roo.bootstrap.menu.Menu} this
25871          */
25872         hide : true,
25873         /**
25874          * @event click
25875          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25876          * @param {Roo.bootstrap.menu.Menu} this
25877          * @param {Roo.EventObject} e
25878          */
25879         click : true
25880     });
25881     
25882 };
25883
25884 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25885     
25886     submenu : false,
25887     html : '',
25888     weight : 'default',
25889     icon : false,
25890     pos : 'bottom',
25891     
25892     
25893     getChildContainer : function() {
25894         if(this.isSubMenu){
25895             return this.el;
25896         }
25897         
25898         return this.el.select('ul.dropdown-menu', true).first();  
25899     },
25900     
25901     getAutoCreate : function()
25902     {
25903         var text = [
25904             {
25905                 tag : 'span',
25906                 cls : 'roo-menu-text',
25907                 html : this.html
25908             }
25909         ];
25910         
25911         if(this.icon){
25912             text.unshift({
25913                 tag : 'i',
25914                 cls : 'fa ' + this.icon
25915             })
25916         }
25917         
25918         
25919         var cfg = {
25920             tag : 'div',
25921             cls : 'btn-group',
25922             cn : [
25923                 {
25924                     tag : 'button',
25925                     cls : 'dropdown-button btn btn-' + this.weight,
25926                     cn : text
25927                 },
25928                 {
25929                     tag : 'button',
25930                     cls : 'dropdown-toggle btn btn-' + this.weight,
25931                     cn : [
25932                         {
25933                             tag : 'span',
25934                             cls : 'caret'
25935                         }
25936                     ]
25937                 },
25938                 {
25939                     tag : 'ul',
25940                     cls : 'dropdown-menu'
25941                 }
25942             ]
25943             
25944         };
25945         
25946         if(this.pos == 'top'){
25947             cfg.cls += ' dropup';
25948         }
25949         
25950         if(this.isSubMenu){
25951             cfg = {
25952                 tag : 'ul',
25953                 cls : 'dropdown-menu'
25954             }
25955         }
25956         
25957         return cfg;
25958     },
25959     
25960     onRender : function(ct, position)
25961     {
25962         this.isSubMenu = ct.hasClass('dropdown-submenu');
25963         
25964         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25965     },
25966     
25967     initEvents : function() 
25968     {
25969         if(this.isSubMenu){
25970             return;
25971         }
25972         
25973         this.hidden = true;
25974         
25975         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25976         this.triggerEl.on('click', this.onTriggerPress, this);
25977         
25978         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25979         this.buttonEl.on('click', this.onClick, this);
25980         
25981     },
25982     
25983     list : function()
25984     {
25985         if(this.isSubMenu){
25986             return this.el;
25987         }
25988         
25989         return this.el.select('ul.dropdown-menu', true).first();
25990     },
25991     
25992     onClick : function(e)
25993     {
25994         this.fireEvent("click", this, e);
25995     },
25996     
25997     onTriggerPress  : function(e)
25998     {   
25999         if (this.isVisible()) {
26000             this.hide();
26001         } else {
26002             this.show();
26003         }
26004     },
26005     
26006     isVisible : function(){
26007         return !this.hidden;
26008     },
26009     
26010     show : function()
26011     {
26012         this.fireEvent("beforeshow", this);
26013         
26014         this.hidden = false;
26015         this.el.addClass('open');
26016         
26017         Roo.get(document).on("mouseup", this.onMouseUp, this);
26018         
26019         this.fireEvent("show", this);
26020         
26021         
26022     },
26023     
26024     hide : function()
26025     {
26026         this.fireEvent("beforehide", this);
26027         
26028         this.hidden = true;
26029         this.el.removeClass('open');
26030         
26031         Roo.get(document).un("mouseup", this.onMouseUp);
26032         
26033         this.fireEvent("hide", this);
26034     },
26035     
26036     onMouseUp : function()
26037     {
26038         this.hide();
26039     }
26040     
26041 });
26042
26043  
26044  /*
26045  * - LGPL
26046  *
26047  * menu item
26048  * 
26049  */
26050 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26051
26052 /**
26053  * @class Roo.bootstrap.menu.Item
26054  * @extends Roo.bootstrap.Component
26055  * Bootstrap MenuItem class
26056  * @cfg {Boolean} submenu (true | false) default false
26057  * @cfg {String} html text of the item
26058  * @cfg {String} href the link
26059  * @cfg {Boolean} disable (true | false) default false
26060  * @cfg {Boolean} preventDefault (true | false) default true
26061  * @cfg {String} icon Font awesome icon
26062  * @cfg {String} pos Submenu align to (left | right) default right 
26063  * 
26064  * 
26065  * @constructor
26066  * Create a new Item
26067  * @param {Object} config The config object
26068  */
26069
26070
26071 Roo.bootstrap.menu.Item = function(config){
26072     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26073     this.addEvents({
26074         /**
26075          * @event mouseover
26076          * Fires when the mouse is hovering over this menu
26077          * @param {Roo.bootstrap.menu.Item} this
26078          * @param {Roo.EventObject} e
26079          */
26080         mouseover : true,
26081         /**
26082          * @event mouseout
26083          * Fires when the mouse exits this menu
26084          * @param {Roo.bootstrap.menu.Item} this
26085          * @param {Roo.EventObject} e
26086          */
26087         mouseout : true,
26088         // raw events
26089         /**
26090          * @event click
26091          * The raw click event for the entire grid.
26092          * @param {Roo.EventObject} e
26093          */
26094         click : true
26095     });
26096 };
26097
26098 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26099     
26100     submenu : false,
26101     href : '',
26102     html : '',
26103     preventDefault: true,
26104     disable : false,
26105     icon : false,
26106     pos : 'right',
26107     
26108     getAutoCreate : function()
26109     {
26110         var text = [
26111             {
26112                 tag : 'span',
26113                 cls : 'roo-menu-item-text',
26114                 html : this.html
26115             }
26116         ];
26117         
26118         if(this.icon){
26119             text.unshift({
26120                 tag : 'i',
26121                 cls : 'fa ' + this.icon
26122             })
26123         }
26124         
26125         var cfg = {
26126             tag : 'li',
26127             cn : [
26128                 {
26129                     tag : 'a',
26130                     href : this.href || '#',
26131                     cn : text
26132                 }
26133             ]
26134         };
26135         
26136         if(this.disable){
26137             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26138         }
26139         
26140         if(this.submenu){
26141             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26142             
26143             if(this.pos == 'left'){
26144                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26145             }
26146         }
26147         
26148         return cfg;
26149     },
26150     
26151     initEvents : function() 
26152     {
26153         this.el.on('mouseover', this.onMouseOver, this);
26154         this.el.on('mouseout', this.onMouseOut, this);
26155         
26156         this.el.select('a', true).first().on('click', this.onClick, this);
26157         
26158     },
26159     
26160     onClick : function(e)
26161     {
26162         if(this.preventDefault){
26163             e.preventDefault();
26164         }
26165         
26166         this.fireEvent("click", this, e);
26167     },
26168     
26169     onMouseOver : function(e)
26170     {
26171         if(this.submenu && this.pos == 'left'){
26172             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26173         }
26174         
26175         this.fireEvent("mouseover", this, e);
26176     },
26177     
26178     onMouseOut : function(e)
26179     {
26180         this.fireEvent("mouseout", this, e);
26181     }
26182 });
26183
26184  
26185
26186  /*
26187  * - LGPL
26188  *
26189  * menu separator
26190  * 
26191  */
26192 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26193
26194 /**
26195  * @class Roo.bootstrap.menu.Separator
26196  * @extends Roo.bootstrap.Component
26197  * Bootstrap Separator class
26198  * 
26199  * @constructor
26200  * Create a new Separator
26201  * @param {Object} config The config object
26202  */
26203
26204
26205 Roo.bootstrap.menu.Separator = function(config){
26206     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26207 };
26208
26209 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26210     
26211     getAutoCreate : function(){
26212         var cfg = {
26213             tag : 'li',
26214             cls: 'divider'
26215         };
26216         
26217         return cfg;
26218     }
26219    
26220 });
26221
26222  
26223
26224  /*
26225  * - LGPL
26226  *
26227  * Tooltip
26228  * 
26229  */
26230
26231 /**
26232  * @class Roo.bootstrap.Tooltip
26233  * Bootstrap Tooltip class
26234  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26235  * to determine which dom element triggers the tooltip.
26236  * 
26237  * It needs to add support for additional attributes like tooltip-position
26238  * 
26239  * @constructor
26240  * Create a new Toolti
26241  * @param {Object} config The config object
26242  */
26243
26244 Roo.bootstrap.Tooltip = function(config){
26245     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26246     
26247     this.alignment = Roo.bootstrap.Tooltip.alignment;
26248     
26249     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26250         this.alignment = config.alignment;
26251     }
26252     
26253 };
26254
26255 Roo.apply(Roo.bootstrap.Tooltip, {
26256     /**
26257      * @function init initialize tooltip monitoring.
26258      * @static
26259      */
26260     currentEl : false,
26261     currentTip : false,
26262     currentRegion : false,
26263     
26264     //  init : delay?
26265     
26266     init : function()
26267     {
26268         Roo.get(document).on('mouseover', this.enter ,this);
26269         Roo.get(document).on('mouseout', this.leave, this);
26270          
26271         
26272         this.currentTip = new Roo.bootstrap.Tooltip();
26273     },
26274     
26275     enter : function(ev)
26276     {
26277         var dom = ev.getTarget();
26278         
26279         //Roo.log(['enter',dom]);
26280         var el = Roo.fly(dom);
26281         if (this.currentEl) {
26282             //Roo.log(dom);
26283             //Roo.log(this.currentEl);
26284             //Roo.log(this.currentEl.contains(dom));
26285             if (this.currentEl == el) {
26286                 return;
26287             }
26288             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26289                 return;
26290             }
26291
26292         }
26293         
26294         if (this.currentTip.el) {
26295             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26296         }    
26297         //Roo.log(ev);
26298         
26299         if(!el || el.dom == document){
26300             return;
26301         }
26302         
26303         var bindEl = el;
26304         
26305         // you can not look for children, as if el is the body.. then everythign is the child..
26306         if (!el.attr('tooltip')) { //
26307             if (!el.select("[tooltip]").elements.length) {
26308                 return;
26309             }
26310             // is the mouse over this child...?
26311             bindEl = el.select("[tooltip]").first();
26312             var xy = ev.getXY();
26313             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26314                 //Roo.log("not in region.");
26315                 return;
26316             }
26317             //Roo.log("child element over..");
26318             
26319         }
26320         this.currentEl = bindEl;
26321         this.currentTip.bind(bindEl);
26322         this.currentRegion = Roo.lib.Region.getRegion(dom);
26323         this.currentTip.enter();
26324         
26325     },
26326     leave : function(ev)
26327     {
26328         var dom = ev.getTarget();
26329         //Roo.log(['leave',dom]);
26330         if (!this.currentEl) {
26331             return;
26332         }
26333         
26334         
26335         if (dom != this.currentEl.dom) {
26336             return;
26337         }
26338         var xy = ev.getXY();
26339         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26340             return;
26341         }
26342         // only activate leave if mouse cursor is outside... bounding box..
26343         
26344         
26345         
26346         
26347         if (this.currentTip) {
26348             this.currentTip.leave();
26349         }
26350         //Roo.log('clear currentEl');
26351         this.currentEl = false;
26352         
26353         
26354     },
26355     alignment : {
26356         'left' : ['r-l', [-2,0], 'right'],
26357         'right' : ['l-r', [2,0], 'left'],
26358         'bottom' : ['t-b', [0,2], 'top'],
26359         'top' : [ 'b-t', [0,-2], 'bottom']
26360     }
26361     
26362 });
26363
26364
26365 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26366     
26367     
26368     bindEl : false,
26369     
26370     delay : null, // can be { show : 300 , hide: 500}
26371     
26372     timeout : null,
26373     
26374     hoverState : null, //???
26375     
26376     placement : 'bottom', 
26377     
26378     alignment : false,
26379     
26380     getAutoCreate : function(){
26381     
26382         var cfg = {
26383            cls : 'tooltip',
26384            role : 'tooltip',
26385            cn : [
26386                 {
26387                     cls : 'tooltip-arrow'
26388                 },
26389                 {
26390                     cls : 'tooltip-inner'
26391                 }
26392            ]
26393         };
26394         
26395         return cfg;
26396     },
26397     bind : function(el)
26398     {
26399         this.bindEl = el;
26400     },
26401       
26402     
26403     enter : function () {
26404        
26405         if (this.timeout != null) {
26406             clearTimeout(this.timeout);
26407         }
26408         
26409         this.hoverState = 'in';
26410          //Roo.log("enter - show");
26411         if (!this.delay || !this.delay.show) {
26412             this.show();
26413             return;
26414         }
26415         var _t = this;
26416         this.timeout = setTimeout(function () {
26417             if (_t.hoverState == 'in') {
26418                 _t.show();
26419             }
26420         }, this.delay.show);
26421     },
26422     leave : function()
26423     {
26424         clearTimeout(this.timeout);
26425     
26426         this.hoverState = 'out';
26427          if (!this.delay || !this.delay.hide) {
26428             this.hide();
26429             return;
26430         }
26431        
26432         var _t = this;
26433         this.timeout = setTimeout(function () {
26434             //Roo.log("leave - timeout");
26435             
26436             if (_t.hoverState == 'out') {
26437                 _t.hide();
26438                 Roo.bootstrap.Tooltip.currentEl = false;
26439             }
26440         }, delay);
26441     },
26442     
26443     show : function (msg)
26444     {
26445         if (!this.el) {
26446             this.render(document.body);
26447         }
26448         // set content.
26449         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26450         
26451         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26452         
26453         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26454         
26455         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26456         
26457         var placement = typeof this.placement == 'function' ?
26458             this.placement.call(this, this.el, on_el) :
26459             this.placement;
26460             
26461         var autoToken = /\s?auto?\s?/i;
26462         var autoPlace = autoToken.test(placement);
26463         if (autoPlace) {
26464             placement = placement.replace(autoToken, '') || 'top';
26465         }
26466         
26467         //this.el.detach()
26468         //this.el.setXY([0,0]);
26469         this.el.show();
26470         //this.el.dom.style.display='block';
26471         
26472         //this.el.appendTo(on_el);
26473         
26474         var p = this.getPosition();
26475         var box = this.el.getBox();
26476         
26477         if (autoPlace) {
26478             // fixme..
26479         }
26480         
26481         var align = this.alignment[placement];
26482         
26483         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26484         
26485         if(placement == 'top' || placement == 'bottom'){
26486             if(xy[0] < 0){
26487                 placement = 'right';
26488             }
26489             
26490             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26491                 placement = 'left';
26492             }
26493             
26494             var scroll = Roo.select('body', true).first().getScroll();
26495             
26496             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26497                 placement = 'top';
26498             }
26499             
26500             align = this.alignment[placement];
26501         }
26502         
26503         this.el.alignTo(this.bindEl, align[0],align[1]);
26504         //var arrow = this.el.select('.arrow',true).first();
26505         //arrow.set(align[2], 
26506         
26507         this.el.addClass(placement);
26508         
26509         this.el.addClass('in fade');
26510         
26511         this.hoverState = null;
26512         
26513         if (this.el.hasClass('fade')) {
26514             // fade it?
26515         }
26516         
26517     },
26518     hide : function()
26519     {
26520          
26521         if (!this.el) {
26522             return;
26523         }
26524         //this.el.setXY([0,0]);
26525         this.el.removeClass('in');
26526         //this.el.hide();
26527         
26528     }
26529     
26530 });
26531  
26532
26533  /*
26534  * - LGPL
26535  *
26536  * Location Picker
26537  * 
26538  */
26539
26540 /**
26541  * @class Roo.bootstrap.LocationPicker
26542  * @extends Roo.bootstrap.Component
26543  * Bootstrap LocationPicker class
26544  * @cfg {Number} latitude Position when init default 0
26545  * @cfg {Number} longitude Position when init default 0
26546  * @cfg {Number} zoom default 15
26547  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26548  * @cfg {Boolean} mapTypeControl default false
26549  * @cfg {Boolean} disableDoubleClickZoom default false
26550  * @cfg {Boolean} scrollwheel default true
26551  * @cfg {Boolean} streetViewControl default false
26552  * @cfg {Number} radius default 0
26553  * @cfg {String} locationName
26554  * @cfg {Boolean} draggable default true
26555  * @cfg {Boolean} enableAutocomplete default false
26556  * @cfg {Boolean} enableReverseGeocode default true
26557  * @cfg {String} markerTitle
26558  * 
26559  * @constructor
26560  * Create a new LocationPicker
26561  * @param {Object} config The config object
26562  */
26563
26564
26565 Roo.bootstrap.LocationPicker = function(config){
26566     
26567     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26568     
26569     this.addEvents({
26570         /**
26571          * @event initial
26572          * Fires when the picker initialized.
26573          * @param {Roo.bootstrap.LocationPicker} this
26574          * @param {Google Location} location
26575          */
26576         initial : true,
26577         /**
26578          * @event positionchanged
26579          * Fires when the picker position changed.
26580          * @param {Roo.bootstrap.LocationPicker} this
26581          * @param {Google Location} location
26582          */
26583         positionchanged : true,
26584         /**
26585          * @event resize
26586          * Fires when the map resize.
26587          * @param {Roo.bootstrap.LocationPicker} this
26588          */
26589         resize : true,
26590         /**
26591          * @event show
26592          * Fires when the map show.
26593          * @param {Roo.bootstrap.LocationPicker} this
26594          */
26595         show : true,
26596         /**
26597          * @event hide
26598          * Fires when the map hide.
26599          * @param {Roo.bootstrap.LocationPicker} this
26600          */
26601         hide : true,
26602         /**
26603          * @event mapClick
26604          * Fires when click the map.
26605          * @param {Roo.bootstrap.LocationPicker} this
26606          * @param {Map event} e
26607          */
26608         mapClick : true,
26609         /**
26610          * @event mapRightClick
26611          * Fires when right click the map.
26612          * @param {Roo.bootstrap.LocationPicker} this
26613          * @param {Map event} e
26614          */
26615         mapRightClick : true,
26616         /**
26617          * @event markerClick
26618          * Fires when click the marker.
26619          * @param {Roo.bootstrap.LocationPicker} this
26620          * @param {Map event} e
26621          */
26622         markerClick : true,
26623         /**
26624          * @event markerRightClick
26625          * Fires when right click the marker.
26626          * @param {Roo.bootstrap.LocationPicker} this
26627          * @param {Map event} e
26628          */
26629         markerRightClick : true,
26630         /**
26631          * @event OverlayViewDraw
26632          * Fires when OverlayView Draw
26633          * @param {Roo.bootstrap.LocationPicker} this
26634          */
26635         OverlayViewDraw : true,
26636         /**
26637          * @event OverlayViewOnAdd
26638          * Fires when OverlayView Draw
26639          * @param {Roo.bootstrap.LocationPicker} this
26640          */
26641         OverlayViewOnAdd : true,
26642         /**
26643          * @event OverlayViewOnRemove
26644          * Fires when OverlayView Draw
26645          * @param {Roo.bootstrap.LocationPicker} this
26646          */
26647         OverlayViewOnRemove : true,
26648         /**
26649          * @event OverlayViewShow
26650          * Fires when OverlayView Draw
26651          * @param {Roo.bootstrap.LocationPicker} this
26652          * @param {Pixel} cpx
26653          */
26654         OverlayViewShow : true,
26655         /**
26656          * @event OverlayViewHide
26657          * Fires when OverlayView Draw
26658          * @param {Roo.bootstrap.LocationPicker} this
26659          */
26660         OverlayViewHide : true,
26661         /**
26662          * @event loadexception
26663          * Fires when load google lib failed.
26664          * @param {Roo.bootstrap.LocationPicker} this
26665          */
26666         loadexception : true
26667     });
26668         
26669 };
26670
26671 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26672     
26673     gMapContext: false,
26674     
26675     latitude: 0,
26676     longitude: 0,
26677     zoom: 15,
26678     mapTypeId: false,
26679     mapTypeControl: false,
26680     disableDoubleClickZoom: false,
26681     scrollwheel: true,
26682     streetViewControl: false,
26683     radius: 0,
26684     locationName: '',
26685     draggable: true,
26686     enableAutocomplete: false,
26687     enableReverseGeocode: true,
26688     markerTitle: '',
26689     
26690     getAutoCreate: function()
26691     {
26692
26693         var cfg = {
26694             tag: 'div',
26695             cls: 'roo-location-picker'
26696         };
26697         
26698         return cfg
26699     },
26700     
26701     initEvents: function(ct, position)
26702     {       
26703         if(!this.el.getWidth() || this.isApplied()){
26704             return;
26705         }
26706         
26707         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26708         
26709         this.initial();
26710     },
26711     
26712     initial: function()
26713     {
26714         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26715             this.fireEvent('loadexception', this);
26716             return;
26717         }
26718         
26719         if(!this.mapTypeId){
26720             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26721         }
26722         
26723         this.gMapContext = this.GMapContext();
26724         
26725         this.initOverlayView();
26726         
26727         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26728         
26729         var _this = this;
26730                 
26731         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26732             _this.setPosition(_this.gMapContext.marker.position);
26733         });
26734         
26735         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26736             _this.fireEvent('mapClick', this, event);
26737             
26738         });
26739
26740         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26741             _this.fireEvent('mapRightClick', this, event);
26742             
26743         });
26744         
26745         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26746             _this.fireEvent('markerClick', this, event);
26747             
26748         });
26749
26750         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26751             _this.fireEvent('markerRightClick', this, event);
26752             
26753         });
26754         
26755         this.setPosition(this.gMapContext.location);
26756         
26757         this.fireEvent('initial', this, this.gMapContext.location);
26758     },
26759     
26760     initOverlayView: function()
26761     {
26762         var _this = this;
26763         
26764         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26765             
26766             draw: function()
26767             {
26768                 _this.fireEvent('OverlayViewDraw', _this);
26769             },
26770             
26771             onAdd: function()
26772             {
26773                 _this.fireEvent('OverlayViewOnAdd', _this);
26774             },
26775             
26776             onRemove: function()
26777             {
26778                 _this.fireEvent('OverlayViewOnRemove', _this);
26779             },
26780             
26781             show: function(cpx)
26782             {
26783                 _this.fireEvent('OverlayViewShow', _this, cpx);
26784             },
26785             
26786             hide: function()
26787             {
26788                 _this.fireEvent('OverlayViewHide', _this);
26789             }
26790             
26791         });
26792     },
26793     
26794     fromLatLngToContainerPixel: function(event)
26795     {
26796         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26797     },
26798     
26799     isApplied: function() 
26800     {
26801         return this.getGmapContext() == false ? false : true;
26802     },
26803     
26804     getGmapContext: function() 
26805     {
26806         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26807     },
26808     
26809     GMapContext: function() 
26810     {
26811         var position = new google.maps.LatLng(this.latitude, this.longitude);
26812         
26813         var _map = new google.maps.Map(this.el.dom, {
26814             center: position,
26815             zoom: this.zoom,
26816             mapTypeId: this.mapTypeId,
26817             mapTypeControl: this.mapTypeControl,
26818             disableDoubleClickZoom: this.disableDoubleClickZoom,
26819             scrollwheel: this.scrollwheel,
26820             streetViewControl: this.streetViewControl,
26821             locationName: this.locationName,
26822             draggable: this.draggable,
26823             enableAutocomplete: this.enableAutocomplete,
26824             enableReverseGeocode: this.enableReverseGeocode
26825         });
26826         
26827         var _marker = new google.maps.Marker({
26828             position: position,
26829             map: _map,
26830             title: this.markerTitle,
26831             draggable: this.draggable
26832         });
26833         
26834         return {
26835             map: _map,
26836             marker: _marker,
26837             circle: null,
26838             location: position,
26839             radius: this.radius,
26840             locationName: this.locationName,
26841             addressComponents: {
26842                 formatted_address: null,
26843                 addressLine1: null,
26844                 addressLine2: null,
26845                 streetName: null,
26846                 streetNumber: null,
26847                 city: null,
26848                 district: null,
26849                 state: null,
26850                 stateOrProvince: null
26851             },
26852             settings: this,
26853             domContainer: this.el.dom,
26854             geodecoder: new google.maps.Geocoder()
26855         };
26856     },
26857     
26858     drawCircle: function(center, radius, options) 
26859     {
26860         if (this.gMapContext.circle != null) {
26861             this.gMapContext.circle.setMap(null);
26862         }
26863         if (radius > 0) {
26864             radius *= 1;
26865             options = Roo.apply({}, options, {
26866                 strokeColor: "#0000FF",
26867                 strokeOpacity: .35,
26868                 strokeWeight: 2,
26869                 fillColor: "#0000FF",
26870                 fillOpacity: .2
26871             });
26872             
26873             options.map = this.gMapContext.map;
26874             options.radius = radius;
26875             options.center = center;
26876             this.gMapContext.circle = new google.maps.Circle(options);
26877             return this.gMapContext.circle;
26878         }
26879         
26880         return null;
26881     },
26882     
26883     setPosition: function(location) 
26884     {
26885         this.gMapContext.location = location;
26886         this.gMapContext.marker.setPosition(location);
26887         this.gMapContext.map.panTo(location);
26888         this.drawCircle(location, this.gMapContext.radius, {});
26889         
26890         var _this = this;
26891         
26892         if (this.gMapContext.settings.enableReverseGeocode) {
26893             this.gMapContext.geodecoder.geocode({
26894                 latLng: this.gMapContext.location
26895             }, function(results, status) {
26896                 
26897                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26898                     _this.gMapContext.locationName = results[0].formatted_address;
26899                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26900                     
26901                     _this.fireEvent('positionchanged', this, location);
26902                 }
26903             });
26904             
26905             return;
26906         }
26907         
26908         this.fireEvent('positionchanged', this, location);
26909     },
26910     
26911     resize: function()
26912     {
26913         google.maps.event.trigger(this.gMapContext.map, "resize");
26914         
26915         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26916         
26917         this.fireEvent('resize', this);
26918     },
26919     
26920     setPositionByLatLng: function(latitude, longitude)
26921     {
26922         this.setPosition(new google.maps.LatLng(latitude, longitude));
26923     },
26924     
26925     getCurrentPosition: function() 
26926     {
26927         return {
26928             latitude: this.gMapContext.location.lat(),
26929             longitude: this.gMapContext.location.lng()
26930         };
26931     },
26932     
26933     getAddressName: function() 
26934     {
26935         return this.gMapContext.locationName;
26936     },
26937     
26938     getAddressComponents: function() 
26939     {
26940         return this.gMapContext.addressComponents;
26941     },
26942     
26943     address_component_from_google_geocode: function(address_components) 
26944     {
26945         var result = {};
26946         
26947         for (var i = 0; i < address_components.length; i++) {
26948             var component = address_components[i];
26949             if (component.types.indexOf("postal_code") >= 0) {
26950                 result.postalCode = component.short_name;
26951             } else if (component.types.indexOf("street_number") >= 0) {
26952                 result.streetNumber = component.short_name;
26953             } else if (component.types.indexOf("route") >= 0) {
26954                 result.streetName = component.short_name;
26955             } else if (component.types.indexOf("neighborhood") >= 0) {
26956                 result.city = component.short_name;
26957             } else if (component.types.indexOf("locality") >= 0) {
26958                 result.city = component.short_name;
26959             } else if (component.types.indexOf("sublocality") >= 0) {
26960                 result.district = component.short_name;
26961             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26962                 result.stateOrProvince = component.short_name;
26963             } else if (component.types.indexOf("country") >= 0) {
26964                 result.country = component.short_name;
26965             }
26966         }
26967         
26968         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26969         result.addressLine2 = "";
26970         return result;
26971     },
26972     
26973     setZoomLevel: function(zoom)
26974     {
26975         this.gMapContext.map.setZoom(zoom);
26976     },
26977     
26978     show: function()
26979     {
26980         if(!this.el){
26981             return;
26982         }
26983         
26984         this.el.show();
26985         
26986         this.resize();
26987         
26988         this.fireEvent('show', this);
26989     },
26990     
26991     hide: function()
26992     {
26993         if(!this.el){
26994             return;
26995         }
26996         
26997         this.el.hide();
26998         
26999         this.fireEvent('hide', this);
27000     }
27001     
27002 });
27003
27004 Roo.apply(Roo.bootstrap.LocationPicker, {
27005     
27006     OverlayView : function(map, options)
27007     {
27008         options = options || {};
27009         
27010         this.setMap(map);
27011     }
27012     
27013     
27014 });/*
27015  * - LGPL
27016  *
27017  * Alert
27018  * 
27019  */
27020
27021 /**
27022  * @class Roo.bootstrap.Alert
27023  * @extends Roo.bootstrap.Component
27024  * Bootstrap Alert class
27025  * @cfg {String} title The title of alert
27026  * @cfg {String} html The content of alert
27027  * @cfg {String} weight (  success | info | warning | danger )
27028  * @cfg {String} faicon font-awesomeicon
27029  * 
27030  * @constructor
27031  * Create a new alert
27032  * @param {Object} config The config object
27033  */
27034
27035
27036 Roo.bootstrap.Alert = function(config){
27037     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27038     
27039 };
27040
27041 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27042     
27043     title: '',
27044     html: '',
27045     weight: false,
27046     faicon: false,
27047     
27048     getAutoCreate : function()
27049     {
27050         
27051         var cfg = {
27052             tag : 'div',
27053             cls : 'alert',
27054             cn : [
27055                 {
27056                     tag : 'i',
27057                     cls : 'roo-alert-icon'
27058                     
27059                 },
27060                 {
27061                     tag : 'b',
27062                     cls : 'roo-alert-title',
27063                     html : this.title
27064                 },
27065                 {
27066                     tag : 'span',
27067                     cls : 'roo-alert-text',
27068                     html : this.html
27069                 }
27070             ]
27071         };
27072         
27073         if(this.faicon){
27074             cfg.cn[0].cls += ' fa ' + this.faicon;
27075         }
27076         
27077         if(this.weight){
27078             cfg.cls += ' alert-' + this.weight;
27079         }
27080         
27081         return cfg;
27082     },
27083     
27084     initEvents: function() 
27085     {
27086         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27087     },
27088     
27089     setTitle : function(str)
27090     {
27091         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27092     },
27093     
27094     setText : function(str)
27095     {
27096         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27097     },
27098     
27099     setWeight : function(weight)
27100     {
27101         if(this.weight){
27102             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27103         }
27104         
27105         this.weight = weight;
27106         
27107         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27108     },
27109     
27110     setIcon : function(icon)
27111     {
27112         if(this.faicon){
27113             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27114         }
27115         
27116         this.faicon = icon;
27117         
27118         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27119     },
27120     
27121     hide: function() 
27122     {
27123         this.el.hide();   
27124     },
27125     
27126     show: function() 
27127     {  
27128         this.el.show();   
27129     }
27130     
27131 });
27132
27133  
27134 /*
27135 * Licence: LGPL
27136 */
27137
27138 /**
27139  * @class Roo.bootstrap.UploadCropbox
27140  * @extends Roo.bootstrap.Component
27141  * Bootstrap UploadCropbox class
27142  * @cfg {String} emptyText show when image has been loaded
27143  * @cfg {String} rotateNotify show when image too small to rotate
27144  * @cfg {Number} errorTimeout default 3000
27145  * @cfg {Number} minWidth default 300
27146  * @cfg {Number} minHeight default 300
27147  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27148  * @cfg {Boolean} isDocument (true|false) default false
27149  * @cfg {String} url action url
27150  * @cfg {String} paramName default 'imageUpload'
27151  * @cfg {String} method default POST
27152  * @cfg {Boolean} loadMask (true|false) default true
27153  * @cfg {Boolean} loadingText default 'Loading...'
27154  * 
27155  * @constructor
27156  * Create a new UploadCropbox
27157  * @param {Object} config The config object
27158  */
27159
27160 Roo.bootstrap.UploadCropbox = function(config){
27161     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27162     
27163     this.addEvents({
27164         /**
27165          * @event beforeselectfile
27166          * Fire before select file
27167          * @param {Roo.bootstrap.UploadCropbox} this
27168          */
27169         "beforeselectfile" : true,
27170         /**
27171          * @event initial
27172          * Fire after initEvent
27173          * @param {Roo.bootstrap.UploadCropbox} this
27174          */
27175         "initial" : true,
27176         /**
27177          * @event crop
27178          * Fire after initEvent
27179          * @param {Roo.bootstrap.UploadCropbox} this
27180          * @param {String} data
27181          */
27182         "crop" : true,
27183         /**
27184          * @event prepare
27185          * Fire when preparing the file data
27186          * @param {Roo.bootstrap.UploadCropbox} this
27187          * @param {Object} file
27188          */
27189         "prepare" : true,
27190         /**
27191          * @event exception
27192          * Fire when get exception
27193          * @param {Roo.bootstrap.UploadCropbox} this
27194          * @param {XMLHttpRequest} xhr
27195          */
27196         "exception" : true,
27197         /**
27198          * @event beforeloadcanvas
27199          * Fire before load the canvas
27200          * @param {Roo.bootstrap.UploadCropbox} this
27201          * @param {String} src
27202          */
27203         "beforeloadcanvas" : true,
27204         /**
27205          * @event trash
27206          * Fire when trash image
27207          * @param {Roo.bootstrap.UploadCropbox} this
27208          */
27209         "trash" : true,
27210         /**
27211          * @event download
27212          * Fire when download the image
27213          * @param {Roo.bootstrap.UploadCropbox} this
27214          */
27215         "download" : true,
27216         /**
27217          * @event footerbuttonclick
27218          * Fire when footerbuttonclick
27219          * @param {Roo.bootstrap.UploadCropbox} this
27220          * @param {String} type
27221          */
27222         "footerbuttonclick" : true,
27223         /**
27224          * @event resize
27225          * Fire when resize
27226          * @param {Roo.bootstrap.UploadCropbox} this
27227          */
27228         "resize" : true,
27229         /**
27230          * @event rotate
27231          * Fire when rotate the image
27232          * @param {Roo.bootstrap.UploadCropbox} this
27233          * @param {String} pos
27234          */
27235         "rotate" : true,
27236         /**
27237          * @event inspect
27238          * Fire when inspect the file
27239          * @param {Roo.bootstrap.UploadCropbox} this
27240          * @param {Object} file
27241          */
27242         "inspect" : true,
27243         /**
27244          * @event upload
27245          * Fire when xhr upload the file
27246          * @param {Roo.bootstrap.UploadCropbox} this
27247          * @param {Object} data
27248          */
27249         "upload" : true,
27250         /**
27251          * @event arrange
27252          * Fire when arrange the file data
27253          * @param {Roo.bootstrap.UploadCropbox} this
27254          * @param {Object} formData
27255          */
27256         "arrange" : true
27257     });
27258     
27259     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27260 };
27261
27262 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27263     
27264     emptyText : 'Click to upload image',
27265     rotateNotify : 'Image is too small to rotate',
27266     errorTimeout : 3000,
27267     scale : 0,
27268     baseScale : 1,
27269     rotate : 0,
27270     dragable : false,
27271     pinching : false,
27272     mouseX : 0,
27273     mouseY : 0,
27274     cropData : false,
27275     minWidth : 300,
27276     minHeight : 300,
27277     file : false,
27278     exif : {},
27279     baseRotate : 1,
27280     cropType : 'image/jpeg',
27281     buttons : false,
27282     canvasLoaded : false,
27283     isDocument : false,
27284     method : 'POST',
27285     paramName : 'imageUpload',
27286     loadMask : true,
27287     loadingText : 'Loading...',
27288     maskEl : false,
27289     
27290     getAutoCreate : function()
27291     {
27292         var cfg = {
27293             tag : 'div',
27294             cls : 'roo-upload-cropbox',
27295             cn : [
27296                 {
27297                     tag : 'input',
27298                     cls : 'roo-upload-cropbox-selector',
27299                     type : 'file'
27300                 },
27301                 {
27302                     tag : 'div',
27303                     cls : 'roo-upload-cropbox-body',
27304                     style : 'cursor:pointer',
27305                     cn : [
27306                         {
27307                             tag : 'div',
27308                             cls : 'roo-upload-cropbox-preview'
27309                         },
27310                         {
27311                             tag : 'div',
27312                             cls : 'roo-upload-cropbox-thumb'
27313                         },
27314                         {
27315                             tag : 'div',
27316                             cls : 'roo-upload-cropbox-empty-notify',
27317                             html : this.emptyText
27318                         },
27319                         {
27320                             tag : 'div',
27321                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27322                             html : this.rotateNotify
27323                         }
27324                     ]
27325                 },
27326                 {
27327                     tag : 'div',
27328                     cls : 'roo-upload-cropbox-footer',
27329                     cn : {
27330                         tag : 'div',
27331                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27332                         cn : []
27333                     }
27334                 }
27335             ]
27336         };
27337         
27338         return cfg;
27339     },
27340     
27341     onRender : function(ct, position)
27342     {
27343         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27344         
27345         if (this.buttons.length) {
27346             
27347             Roo.each(this.buttons, function(bb) {
27348                 
27349                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27350                 
27351                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27352                 
27353             }, this);
27354         }
27355         
27356         if(this.loadMask){
27357             this.maskEl = this.el;
27358         }
27359     },
27360     
27361     initEvents : function()
27362     {
27363         this.urlAPI = (window.createObjectURL && window) || 
27364                                 (window.URL && URL.revokeObjectURL && URL) || 
27365                                 (window.webkitURL && webkitURL);
27366                         
27367         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27368         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27369         
27370         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27371         this.selectorEl.hide();
27372         
27373         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27374         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27375         
27376         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27377         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27378         this.thumbEl.hide();
27379         
27380         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27381         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27382         
27383         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27384         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27385         this.errorEl.hide();
27386         
27387         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27388         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27389         this.footerEl.hide();
27390         
27391         this.setThumbBoxSize();
27392         
27393         this.bind();
27394         
27395         this.resize();
27396         
27397         this.fireEvent('initial', this);
27398     },
27399
27400     bind : function()
27401     {
27402         var _this = this;
27403         
27404         window.addEventListener("resize", function() { _this.resize(); } );
27405         
27406         this.bodyEl.on('click', this.beforeSelectFile, this);
27407         
27408         if(Roo.isTouch){
27409             this.bodyEl.on('touchstart', this.onTouchStart, this);
27410             this.bodyEl.on('touchmove', this.onTouchMove, this);
27411             this.bodyEl.on('touchend', this.onTouchEnd, this);
27412         }
27413         
27414         if(!Roo.isTouch){
27415             this.bodyEl.on('mousedown', this.onMouseDown, this);
27416             this.bodyEl.on('mousemove', this.onMouseMove, this);
27417             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27418             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27419             Roo.get(document).on('mouseup', this.onMouseUp, this);
27420         }
27421         
27422         this.selectorEl.on('change', this.onFileSelected, this);
27423     },
27424     
27425     reset : function()
27426     {    
27427         this.scale = 0;
27428         this.baseScale = 1;
27429         this.rotate = 0;
27430         this.baseRotate = 1;
27431         this.dragable = false;
27432         this.pinching = false;
27433         this.mouseX = 0;
27434         this.mouseY = 0;
27435         this.cropData = false;
27436         this.notifyEl.dom.innerHTML = this.emptyText;
27437         
27438         this.selectorEl.dom.value = '';
27439         
27440     },
27441     
27442     resize : function()
27443     {
27444         if(this.fireEvent('resize', this) != false){
27445             this.setThumbBoxPosition();
27446             this.setCanvasPosition();
27447         }
27448     },
27449     
27450     onFooterButtonClick : function(e, el, o, type)
27451     {
27452         switch (type) {
27453             case 'rotate-left' :
27454                 this.onRotateLeft(e);
27455                 break;
27456             case 'rotate-right' :
27457                 this.onRotateRight(e);
27458                 break;
27459             case 'picture' :
27460                 this.beforeSelectFile(e);
27461                 break;
27462             case 'trash' :
27463                 this.trash(e);
27464                 break;
27465             case 'crop' :
27466                 this.crop(e);
27467                 break;
27468             case 'download' :
27469                 this.download(e);
27470                 break;
27471             default :
27472                 break;
27473         }
27474         
27475         this.fireEvent('footerbuttonclick', this, type);
27476     },
27477     
27478     beforeSelectFile : function(e)
27479     {
27480         e.preventDefault();
27481         
27482         if(this.fireEvent('beforeselectfile', this) != false){
27483             this.selectorEl.dom.click();
27484         }
27485     },
27486     
27487     onFileSelected : function(e)
27488     {
27489         e.preventDefault();
27490         
27491         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27492             return;
27493         }
27494         
27495         var file = this.selectorEl.dom.files[0];
27496         
27497         if(this.fireEvent('inspect', this, file) != false){
27498             this.prepare(file);
27499         }
27500         
27501     },
27502     
27503     trash : function(e)
27504     {
27505         this.fireEvent('trash', this);
27506     },
27507     
27508     download : function(e)
27509     {
27510         this.fireEvent('download', this);
27511     },
27512     
27513     loadCanvas : function(src)
27514     {   
27515         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27516             
27517             this.reset();
27518             
27519             this.imageEl = document.createElement('img');
27520             
27521             var _this = this;
27522             
27523             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27524             
27525             this.imageEl.src = src;
27526         }
27527     },
27528     
27529     onLoadCanvas : function()
27530     {   
27531         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27532         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27533         
27534         this.bodyEl.un('click', this.beforeSelectFile, this);
27535         
27536         this.notifyEl.hide();
27537         this.thumbEl.show();
27538         this.footerEl.show();
27539         
27540         this.baseRotateLevel();
27541         
27542         if(this.isDocument){
27543             this.setThumbBoxSize();
27544         }
27545         
27546         this.setThumbBoxPosition();
27547         
27548         this.baseScaleLevel();
27549         
27550         this.draw();
27551         
27552         this.resize();
27553         
27554         this.canvasLoaded = true;
27555         
27556         if(this.loadMask){
27557             this.maskEl.unmask();
27558         }
27559         
27560     },
27561     
27562     setCanvasPosition : function()
27563     {   
27564         if(!this.canvasEl){
27565             return;
27566         }
27567         
27568         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27569         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27570         
27571         this.previewEl.setLeft(pw);
27572         this.previewEl.setTop(ph);
27573         
27574     },
27575     
27576     onMouseDown : function(e)
27577     {   
27578         e.stopEvent();
27579         
27580         this.dragable = true;
27581         this.pinching = false;
27582         
27583         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27584             this.dragable = false;
27585             return;
27586         }
27587         
27588         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27589         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27590         
27591     },
27592     
27593     onMouseMove : function(e)
27594     {   
27595         e.stopEvent();
27596         
27597         if(!this.canvasLoaded){
27598             return;
27599         }
27600         
27601         if (!this.dragable){
27602             return;
27603         }
27604         
27605         var minX = Math.ceil(this.thumbEl.getLeft(true));
27606         var minY = Math.ceil(this.thumbEl.getTop(true));
27607         
27608         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27609         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27610         
27611         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27612         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27613         
27614         x = x - this.mouseX;
27615         y = y - this.mouseY;
27616         
27617         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27618         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27619         
27620         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27621         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27622         
27623         this.previewEl.setLeft(bgX);
27624         this.previewEl.setTop(bgY);
27625         
27626         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27627         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27628     },
27629     
27630     onMouseUp : function(e)
27631     {   
27632         e.stopEvent();
27633         
27634         this.dragable = false;
27635     },
27636     
27637     onMouseWheel : function(e)
27638     {   
27639         e.stopEvent();
27640         
27641         this.startScale = this.scale;
27642         
27643         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27644         
27645         if(!this.zoomable()){
27646             this.scale = this.startScale;
27647             return;
27648         }
27649         
27650         this.draw();
27651         
27652         return;
27653     },
27654     
27655     zoomable : function()
27656     {
27657         var minScale = this.thumbEl.getWidth() / this.minWidth;
27658         
27659         if(this.minWidth < this.minHeight){
27660             minScale = this.thumbEl.getHeight() / this.minHeight;
27661         }
27662         
27663         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27664         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27665         
27666         if(
27667                 this.isDocument &&
27668                 (this.rotate == 0 || this.rotate == 180) && 
27669                 (
27670                     width > this.imageEl.OriginWidth || 
27671                     height > this.imageEl.OriginHeight ||
27672                     (width < this.minWidth && height < this.minHeight)
27673                 )
27674         ){
27675             return false;
27676         }
27677         
27678         if(
27679                 this.isDocument &&
27680                 (this.rotate == 90 || this.rotate == 270) && 
27681                 (
27682                     width > this.imageEl.OriginWidth || 
27683                     height > this.imageEl.OriginHeight ||
27684                     (width < this.minHeight && height < this.minWidth)
27685                 )
27686         ){
27687             return false;
27688         }
27689         
27690         if(
27691                 !this.isDocument &&
27692                 (this.rotate == 0 || this.rotate == 180) && 
27693                 (
27694                     width < this.minWidth || 
27695                     width > this.imageEl.OriginWidth || 
27696                     height < this.minHeight || 
27697                     height > this.imageEl.OriginHeight
27698                 )
27699         ){
27700             return false;
27701         }
27702         
27703         if(
27704                 !this.isDocument &&
27705                 (this.rotate == 90 || this.rotate == 270) && 
27706                 (
27707                     width < this.minHeight || 
27708                     width > this.imageEl.OriginWidth || 
27709                     height < this.minWidth || 
27710                     height > this.imageEl.OriginHeight
27711                 )
27712         ){
27713             return false;
27714         }
27715         
27716         return true;
27717         
27718     },
27719     
27720     onRotateLeft : function(e)
27721     {   
27722         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27723             
27724             var minScale = this.thumbEl.getWidth() / this.minWidth;
27725             
27726             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27727             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27728             
27729             this.startScale = this.scale;
27730             
27731             while (this.getScaleLevel() < minScale){
27732             
27733                 this.scale = this.scale + 1;
27734                 
27735                 if(!this.zoomable()){
27736                     break;
27737                 }
27738                 
27739                 if(
27740                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27741                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27742                 ){
27743                     continue;
27744                 }
27745                 
27746                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27747
27748                 this.draw();
27749                 
27750                 return;
27751             }
27752             
27753             this.scale = this.startScale;
27754             
27755             this.onRotateFail();
27756             
27757             return false;
27758         }
27759         
27760         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27761
27762         if(this.isDocument){
27763             this.setThumbBoxSize();
27764             this.setThumbBoxPosition();
27765             this.setCanvasPosition();
27766         }
27767         
27768         this.draw();
27769         
27770         this.fireEvent('rotate', this, 'left');
27771         
27772     },
27773     
27774     onRotateRight : function(e)
27775     {
27776         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27777             
27778             var minScale = this.thumbEl.getWidth() / this.minWidth;
27779         
27780             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27781             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27782             
27783             this.startScale = this.scale;
27784             
27785             while (this.getScaleLevel() < minScale){
27786             
27787                 this.scale = this.scale + 1;
27788                 
27789                 if(!this.zoomable()){
27790                     break;
27791                 }
27792                 
27793                 if(
27794                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27795                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27796                 ){
27797                     continue;
27798                 }
27799                 
27800                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27801
27802                 this.draw();
27803                 
27804                 return;
27805             }
27806             
27807             this.scale = this.startScale;
27808             
27809             this.onRotateFail();
27810             
27811             return false;
27812         }
27813         
27814         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27815
27816         if(this.isDocument){
27817             this.setThumbBoxSize();
27818             this.setThumbBoxPosition();
27819             this.setCanvasPosition();
27820         }
27821         
27822         this.draw();
27823         
27824         this.fireEvent('rotate', this, 'right');
27825     },
27826     
27827     onRotateFail : function()
27828     {
27829         this.errorEl.show(true);
27830         
27831         var _this = this;
27832         
27833         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27834     },
27835     
27836     draw : function()
27837     {
27838         this.previewEl.dom.innerHTML = '';
27839         
27840         var canvasEl = document.createElement("canvas");
27841         
27842         var contextEl = canvasEl.getContext("2d");
27843         
27844         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27845         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27846         var center = this.imageEl.OriginWidth / 2;
27847         
27848         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27849             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27850             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27851             center = this.imageEl.OriginHeight / 2;
27852         }
27853         
27854         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27855         
27856         contextEl.translate(center, center);
27857         contextEl.rotate(this.rotate * Math.PI / 180);
27858
27859         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27860         
27861         this.canvasEl = document.createElement("canvas");
27862         
27863         this.contextEl = this.canvasEl.getContext("2d");
27864         
27865         switch (this.rotate) {
27866             case 0 :
27867                 
27868                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27869                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27870                 
27871                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27872                 
27873                 break;
27874             case 90 : 
27875                 
27876                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27877                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27878                 
27879                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27880                     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);
27881                     break;
27882                 }
27883                 
27884                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27885                 
27886                 break;
27887             case 180 :
27888                 
27889                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27890                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27891                 
27892                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27893                     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);
27894                     break;
27895                 }
27896                 
27897                 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);
27898                 
27899                 break;
27900             case 270 :
27901                 
27902                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27903                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27904         
27905                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27906                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27907                     break;
27908                 }
27909                 
27910                 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);
27911                 
27912                 break;
27913             default : 
27914                 break;
27915         }
27916         
27917         this.previewEl.appendChild(this.canvasEl);
27918         
27919         this.setCanvasPosition();
27920     },
27921     
27922     crop : function()
27923     {
27924         if(!this.canvasLoaded){
27925             return;
27926         }
27927         
27928         var imageCanvas = document.createElement("canvas");
27929         
27930         var imageContext = imageCanvas.getContext("2d");
27931         
27932         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27933         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27934         
27935         var center = imageCanvas.width / 2;
27936         
27937         imageContext.translate(center, center);
27938         
27939         imageContext.rotate(this.rotate * Math.PI / 180);
27940         
27941         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27942         
27943         var canvas = document.createElement("canvas");
27944         
27945         var context = canvas.getContext("2d");
27946                 
27947         canvas.width = this.minWidth;
27948         canvas.height = this.minHeight;
27949
27950         switch (this.rotate) {
27951             case 0 :
27952                 
27953                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27954                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27955                 
27956                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27957                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27958                 
27959                 var targetWidth = this.minWidth - 2 * x;
27960                 var targetHeight = this.minHeight - 2 * y;
27961                 
27962                 var scale = 1;
27963                 
27964                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27965                     scale = targetWidth / width;
27966                 }
27967                 
27968                 if(x > 0 && y == 0){
27969                     scale = targetHeight / height;
27970                 }
27971                 
27972                 if(x > 0 && y > 0){
27973                     scale = targetWidth / width;
27974                     
27975                     if(width < height){
27976                         scale = targetHeight / height;
27977                     }
27978                 }
27979                 
27980                 context.scale(scale, scale);
27981                 
27982                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27983                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27984
27985                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27986                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27987
27988                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27989                 
27990                 break;
27991             case 90 : 
27992                 
27993                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27994                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27995                 
27996                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27997                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27998                 
27999                 var targetWidth = this.minWidth - 2 * x;
28000                 var targetHeight = this.minHeight - 2 * y;
28001                 
28002                 var scale = 1;
28003                 
28004                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28005                     scale = targetWidth / width;
28006                 }
28007                 
28008                 if(x > 0 && y == 0){
28009                     scale = targetHeight / height;
28010                 }
28011                 
28012                 if(x > 0 && y > 0){
28013                     scale = targetWidth / width;
28014                     
28015                     if(width < height){
28016                         scale = targetHeight / height;
28017                     }
28018                 }
28019                 
28020                 context.scale(scale, scale);
28021                 
28022                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28023                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28024
28025                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28026                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28027                 
28028                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28029                 
28030                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28031                 
28032                 break;
28033             case 180 :
28034                 
28035                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28036                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28037                 
28038                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28039                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28040                 
28041                 var targetWidth = this.minWidth - 2 * x;
28042                 var targetHeight = this.minHeight - 2 * y;
28043                 
28044                 var scale = 1;
28045                 
28046                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28047                     scale = targetWidth / width;
28048                 }
28049                 
28050                 if(x > 0 && y == 0){
28051                     scale = targetHeight / height;
28052                 }
28053                 
28054                 if(x > 0 && y > 0){
28055                     scale = targetWidth / width;
28056                     
28057                     if(width < height){
28058                         scale = targetHeight / height;
28059                     }
28060                 }
28061                 
28062                 context.scale(scale, scale);
28063                 
28064                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28065                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28066
28067                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28068                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28069
28070                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28071                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28072                 
28073                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28074                 
28075                 break;
28076             case 270 :
28077                 
28078                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28079                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28080                 
28081                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28082                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28083                 
28084                 var targetWidth = this.minWidth - 2 * x;
28085                 var targetHeight = this.minHeight - 2 * y;
28086                 
28087                 var scale = 1;
28088                 
28089                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28090                     scale = targetWidth / width;
28091                 }
28092                 
28093                 if(x > 0 && y == 0){
28094                     scale = targetHeight / height;
28095                 }
28096                 
28097                 if(x > 0 && y > 0){
28098                     scale = targetWidth / width;
28099                     
28100                     if(width < height){
28101                         scale = targetHeight / height;
28102                     }
28103                 }
28104                 
28105                 context.scale(scale, scale);
28106                 
28107                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28108                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28109
28110                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28111                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28112                 
28113                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28114                 
28115                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28116                 
28117                 break;
28118             default : 
28119                 break;
28120         }
28121         
28122         this.cropData = canvas.toDataURL(this.cropType);
28123         
28124         if(this.fireEvent('crop', this, this.cropData) !== false){
28125             this.process(this.file, this.cropData);
28126         }
28127         
28128         return;
28129         
28130     },
28131     
28132     setThumbBoxSize : function()
28133     {
28134         var width, height;
28135         
28136         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28137             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28138             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28139             
28140             this.minWidth = width;
28141             this.minHeight = height;
28142             
28143             if(this.rotate == 90 || this.rotate == 270){
28144                 this.minWidth = height;
28145                 this.minHeight = width;
28146             }
28147         }
28148         
28149         height = 300;
28150         width = Math.ceil(this.minWidth * height / this.minHeight);
28151         
28152         if(this.minWidth > this.minHeight){
28153             width = 300;
28154             height = Math.ceil(this.minHeight * width / this.minWidth);
28155         }
28156         
28157         this.thumbEl.setStyle({
28158             width : width + 'px',
28159             height : height + 'px'
28160         });
28161
28162         return;
28163             
28164     },
28165     
28166     setThumbBoxPosition : function()
28167     {
28168         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28169         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28170         
28171         this.thumbEl.setLeft(x);
28172         this.thumbEl.setTop(y);
28173         
28174     },
28175     
28176     baseRotateLevel : function()
28177     {
28178         this.baseRotate = 1;
28179         
28180         if(
28181                 typeof(this.exif) != 'undefined' &&
28182                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28183                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28184         ){
28185             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28186         }
28187         
28188         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28189         
28190     },
28191     
28192     baseScaleLevel : function()
28193     {
28194         var width, height;
28195         
28196         if(this.isDocument){
28197             
28198             if(this.baseRotate == 6 || this.baseRotate == 8){
28199             
28200                 height = this.thumbEl.getHeight();
28201                 this.baseScale = height / this.imageEl.OriginWidth;
28202
28203                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28204                     width = this.thumbEl.getWidth();
28205                     this.baseScale = width / this.imageEl.OriginHeight;
28206                 }
28207
28208                 return;
28209             }
28210
28211             height = this.thumbEl.getHeight();
28212             this.baseScale = height / this.imageEl.OriginHeight;
28213
28214             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28215                 width = this.thumbEl.getWidth();
28216                 this.baseScale = width / this.imageEl.OriginWidth;
28217             }
28218
28219             return;
28220         }
28221         
28222         if(this.baseRotate == 6 || this.baseRotate == 8){
28223             
28224             width = this.thumbEl.getHeight();
28225             this.baseScale = width / this.imageEl.OriginHeight;
28226             
28227             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28228                 height = this.thumbEl.getWidth();
28229                 this.baseScale = height / this.imageEl.OriginHeight;
28230             }
28231             
28232             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28233                 height = this.thumbEl.getWidth();
28234                 this.baseScale = height / this.imageEl.OriginHeight;
28235                 
28236                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28237                     width = this.thumbEl.getHeight();
28238                     this.baseScale = width / this.imageEl.OriginWidth;
28239                 }
28240             }
28241             
28242             return;
28243         }
28244         
28245         width = this.thumbEl.getWidth();
28246         this.baseScale = width / this.imageEl.OriginWidth;
28247         
28248         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28249             height = this.thumbEl.getHeight();
28250             this.baseScale = height / this.imageEl.OriginHeight;
28251         }
28252         
28253         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28254             
28255             height = this.thumbEl.getHeight();
28256             this.baseScale = height / this.imageEl.OriginHeight;
28257             
28258             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28259                 width = this.thumbEl.getWidth();
28260                 this.baseScale = width / this.imageEl.OriginWidth;
28261             }
28262             
28263         }
28264         
28265         return;
28266     },
28267     
28268     getScaleLevel : function()
28269     {
28270         return this.baseScale * Math.pow(1.1, this.scale);
28271     },
28272     
28273     onTouchStart : function(e)
28274     {
28275         if(!this.canvasLoaded){
28276             this.beforeSelectFile(e);
28277             return;
28278         }
28279         
28280         var touches = e.browserEvent.touches;
28281         
28282         if(!touches){
28283             return;
28284         }
28285         
28286         if(touches.length == 1){
28287             this.onMouseDown(e);
28288             return;
28289         }
28290         
28291         if(touches.length != 2){
28292             return;
28293         }
28294         
28295         var coords = [];
28296         
28297         for(var i = 0, finger; finger = touches[i]; i++){
28298             coords.push(finger.pageX, finger.pageY);
28299         }
28300         
28301         var x = Math.pow(coords[0] - coords[2], 2);
28302         var y = Math.pow(coords[1] - coords[3], 2);
28303         
28304         this.startDistance = Math.sqrt(x + y);
28305         
28306         this.startScale = this.scale;
28307         
28308         this.pinching = true;
28309         this.dragable = false;
28310         
28311     },
28312     
28313     onTouchMove : function(e)
28314     {
28315         if(!this.pinching && !this.dragable){
28316             return;
28317         }
28318         
28319         var touches = e.browserEvent.touches;
28320         
28321         if(!touches){
28322             return;
28323         }
28324         
28325         if(this.dragable){
28326             this.onMouseMove(e);
28327             return;
28328         }
28329         
28330         var coords = [];
28331         
28332         for(var i = 0, finger; finger = touches[i]; i++){
28333             coords.push(finger.pageX, finger.pageY);
28334         }
28335         
28336         var x = Math.pow(coords[0] - coords[2], 2);
28337         var y = Math.pow(coords[1] - coords[3], 2);
28338         
28339         this.endDistance = Math.sqrt(x + y);
28340         
28341         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28342         
28343         if(!this.zoomable()){
28344             this.scale = this.startScale;
28345             return;
28346         }
28347         
28348         this.draw();
28349         
28350     },
28351     
28352     onTouchEnd : function(e)
28353     {
28354         this.pinching = false;
28355         this.dragable = false;
28356         
28357     },
28358     
28359     process : function(file, crop)
28360     {
28361         if(this.loadMask){
28362             this.maskEl.mask(this.loadingText);
28363         }
28364         
28365         this.xhr = new XMLHttpRequest();
28366         
28367         file.xhr = this.xhr;
28368
28369         this.xhr.open(this.method, this.url, true);
28370         
28371         var headers = {
28372             "Accept": "application/json",
28373             "Cache-Control": "no-cache",
28374             "X-Requested-With": "XMLHttpRequest"
28375         };
28376         
28377         for (var headerName in headers) {
28378             var headerValue = headers[headerName];
28379             if (headerValue) {
28380                 this.xhr.setRequestHeader(headerName, headerValue);
28381             }
28382         }
28383         
28384         var _this = this;
28385         
28386         this.xhr.onload = function()
28387         {
28388             _this.xhrOnLoad(_this.xhr);
28389         }
28390         
28391         this.xhr.onerror = function()
28392         {
28393             _this.xhrOnError(_this.xhr);
28394         }
28395         
28396         var formData = new FormData();
28397
28398         formData.append('returnHTML', 'NO');
28399         
28400         if(crop){
28401             formData.append('crop', crop);
28402         }
28403         
28404         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28405             formData.append(this.paramName, file, file.name);
28406         }
28407         
28408         if(typeof(file.filename) != 'undefined'){
28409             formData.append('filename', file.filename);
28410         }
28411         
28412         if(typeof(file.mimetype) != 'undefined'){
28413             formData.append('mimetype', file.mimetype);
28414         }
28415         
28416         if(this.fireEvent('arrange', this, formData) != false){
28417             this.xhr.send(formData);
28418         };
28419     },
28420     
28421     xhrOnLoad : function(xhr)
28422     {
28423         if(this.loadMask){
28424             this.maskEl.unmask();
28425         }
28426         
28427         if (xhr.readyState !== 4) {
28428             this.fireEvent('exception', this, xhr);
28429             return;
28430         }
28431
28432         var response = Roo.decode(xhr.responseText);
28433         
28434         if(!response.success){
28435             this.fireEvent('exception', this, xhr);
28436             return;
28437         }
28438         
28439         var response = Roo.decode(xhr.responseText);
28440         
28441         this.fireEvent('upload', this, response);
28442         
28443     },
28444     
28445     xhrOnError : function()
28446     {
28447         if(this.loadMask){
28448             this.maskEl.unmask();
28449         }
28450         
28451         Roo.log('xhr on error');
28452         
28453         var response = Roo.decode(xhr.responseText);
28454           
28455         Roo.log(response);
28456         
28457     },
28458     
28459     prepare : function(file)
28460     {   
28461         if(this.loadMask){
28462             this.maskEl.mask(this.loadingText);
28463         }
28464         
28465         this.file = false;
28466         this.exif = {};
28467         
28468         if(typeof(file) === 'string'){
28469             this.loadCanvas(file);
28470             return;
28471         }
28472         
28473         if(!file || !this.urlAPI){
28474             return;
28475         }
28476         
28477         this.file = file;
28478         this.cropType = file.type;
28479         
28480         var _this = this;
28481         
28482         if(this.fireEvent('prepare', this, this.file) != false){
28483             
28484             var reader = new FileReader();
28485             
28486             reader.onload = function (e) {
28487                 if (e.target.error) {
28488                     Roo.log(e.target.error);
28489                     return;
28490                 }
28491                 
28492                 var buffer = e.target.result,
28493                     dataView = new DataView(buffer),
28494                     offset = 2,
28495                     maxOffset = dataView.byteLength - 4,
28496                     markerBytes,
28497                     markerLength;
28498                 
28499                 if (dataView.getUint16(0) === 0xffd8) {
28500                     while (offset < maxOffset) {
28501                         markerBytes = dataView.getUint16(offset);
28502                         
28503                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28504                             markerLength = dataView.getUint16(offset + 2) + 2;
28505                             if (offset + markerLength > dataView.byteLength) {
28506                                 Roo.log('Invalid meta data: Invalid segment size.');
28507                                 break;
28508                             }
28509                             
28510                             if(markerBytes == 0xffe1){
28511                                 _this.parseExifData(
28512                                     dataView,
28513                                     offset,
28514                                     markerLength
28515                                 );
28516                             }
28517                             
28518                             offset += markerLength;
28519                             
28520                             continue;
28521                         }
28522                         
28523                         break;
28524                     }
28525                     
28526                 }
28527                 
28528                 var url = _this.urlAPI.createObjectURL(_this.file);
28529                 
28530                 _this.loadCanvas(url);
28531                 
28532                 return;
28533             }
28534             
28535             reader.readAsArrayBuffer(this.file);
28536             
28537         }
28538         
28539     },
28540     
28541     parseExifData : function(dataView, offset, length)
28542     {
28543         var tiffOffset = offset + 10,
28544             littleEndian,
28545             dirOffset;
28546     
28547         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28548             // No Exif data, might be XMP data instead
28549             return;
28550         }
28551         
28552         // Check for the ASCII code for "Exif" (0x45786966):
28553         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28554             // No Exif data, might be XMP data instead
28555             return;
28556         }
28557         if (tiffOffset + 8 > dataView.byteLength) {
28558             Roo.log('Invalid Exif data: Invalid segment size.');
28559             return;
28560         }
28561         // Check for the two null bytes:
28562         if (dataView.getUint16(offset + 8) !== 0x0000) {
28563             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28564             return;
28565         }
28566         // Check the byte alignment:
28567         switch (dataView.getUint16(tiffOffset)) {
28568         case 0x4949:
28569             littleEndian = true;
28570             break;
28571         case 0x4D4D:
28572             littleEndian = false;
28573             break;
28574         default:
28575             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28576             return;
28577         }
28578         // Check for the TIFF tag marker (0x002A):
28579         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28580             Roo.log('Invalid Exif data: Missing TIFF marker.');
28581             return;
28582         }
28583         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28584         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28585         
28586         this.parseExifTags(
28587             dataView,
28588             tiffOffset,
28589             tiffOffset + dirOffset,
28590             littleEndian
28591         );
28592     },
28593     
28594     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28595     {
28596         var tagsNumber,
28597             dirEndOffset,
28598             i;
28599         if (dirOffset + 6 > dataView.byteLength) {
28600             Roo.log('Invalid Exif data: Invalid directory offset.');
28601             return;
28602         }
28603         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28604         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28605         if (dirEndOffset + 4 > dataView.byteLength) {
28606             Roo.log('Invalid Exif data: Invalid directory size.');
28607             return;
28608         }
28609         for (i = 0; i < tagsNumber; i += 1) {
28610             this.parseExifTag(
28611                 dataView,
28612                 tiffOffset,
28613                 dirOffset + 2 + 12 * i, // tag offset
28614                 littleEndian
28615             );
28616         }
28617         // Return the offset to the next directory:
28618         return dataView.getUint32(dirEndOffset, littleEndian);
28619     },
28620     
28621     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28622     {
28623         var tag = dataView.getUint16(offset, littleEndian);
28624         
28625         this.exif[tag] = this.getExifValue(
28626             dataView,
28627             tiffOffset,
28628             offset,
28629             dataView.getUint16(offset + 2, littleEndian), // tag type
28630             dataView.getUint32(offset + 4, littleEndian), // tag length
28631             littleEndian
28632         );
28633     },
28634     
28635     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28636     {
28637         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28638             tagSize,
28639             dataOffset,
28640             values,
28641             i,
28642             str,
28643             c;
28644     
28645         if (!tagType) {
28646             Roo.log('Invalid Exif data: Invalid tag type.');
28647             return;
28648         }
28649         
28650         tagSize = tagType.size * length;
28651         // Determine if the value is contained in the dataOffset bytes,
28652         // or if the value at the dataOffset is a pointer to the actual data:
28653         dataOffset = tagSize > 4 ?
28654                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28655         if (dataOffset + tagSize > dataView.byteLength) {
28656             Roo.log('Invalid Exif data: Invalid data offset.');
28657             return;
28658         }
28659         if (length === 1) {
28660             return tagType.getValue(dataView, dataOffset, littleEndian);
28661         }
28662         values = [];
28663         for (i = 0; i < length; i += 1) {
28664             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28665         }
28666         
28667         if (tagType.ascii) {
28668             str = '';
28669             // Concatenate the chars:
28670             for (i = 0; i < values.length; i += 1) {
28671                 c = values[i];
28672                 // Ignore the terminating NULL byte(s):
28673                 if (c === '\u0000') {
28674                     break;
28675                 }
28676                 str += c;
28677             }
28678             return str;
28679         }
28680         return values;
28681     }
28682     
28683 });
28684
28685 Roo.apply(Roo.bootstrap.UploadCropbox, {
28686     tags : {
28687         'Orientation': 0x0112
28688     },
28689     
28690     Orientation: {
28691             1: 0, //'top-left',
28692 //            2: 'top-right',
28693             3: 180, //'bottom-right',
28694 //            4: 'bottom-left',
28695 //            5: 'left-top',
28696             6: 90, //'right-top',
28697 //            7: 'right-bottom',
28698             8: 270 //'left-bottom'
28699     },
28700     
28701     exifTagTypes : {
28702         // byte, 8-bit unsigned int:
28703         1: {
28704             getValue: function (dataView, dataOffset) {
28705                 return dataView.getUint8(dataOffset);
28706             },
28707             size: 1
28708         },
28709         // ascii, 8-bit byte:
28710         2: {
28711             getValue: function (dataView, dataOffset) {
28712                 return String.fromCharCode(dataView.getUint8(dataOffset));
28713             },
28714             size: 1,
28715             ascii: true
28716         },
28717         // short, 16 bit int:
28718         3: {
28719             getValue: function (dataView, dataOffset, littleEndian) {
28720                 return dataView.getUint16(dataOffset, littleEndian);
28721             },
28722             size: 2
28723         },
28724         // long, 32 bit int:
28725         4: {
28726             getValue: function (dataView, dataOffset, littleEndian) {
28727                 return dataView.getUint32(dataOffset, littleEndian);
28728             },
28729             size: 4
28730         },
28731         // rational = two long values, first is numerator, second is denominator:
28732         5: {
28733             getValue: function (dataView, dataOffset, littleEndian) {
28734                 return dataView.getUint32(dataOffset, littleEndian) /
28735                     dataView.getUint32(dataOffset + 4, littleEndian);
28736             },
28737             size: 8
28738         },
28739         // slong, 32 bit signed int:
28740         9: {
28741             getValue: function (dataView, dataOffset, littleEndian) {
28742                 return dataView.getInt32(dataOffset, littleEndian);
28743             },
28744             size: 4
28745         },
28746         // srational, two slongs, first is numerator, second is denominator:
28747         10: {
28748             getValue: function (dataView, dataOffset, littleEndian) {
28749                 return dataView.getInt32(dataOffset, littleEndian) /
28750                     dataView.getInt32(dataOffset + 4, littleEndian);
28751             },
28752             size: 8
28753         }
28754     },
28755     
28756     footer : {
28757         STANDARD : [
28758             {
28759                 tag : 'div',
28760                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28761                 action : 'rotate-left',
28762                 cn : [
28763                     {
28764                         tag : 'button',
28765                         cls : 'btn btn-default',
28766                         html : '<i class="fa fa-undo"></i>'
28767                     }
28768                 ]
28769             },
28770             {
28771                 tag : 'div',
28772                 cls : 'btn-group roo-upload-cropbox-picture',
28773                 action : 'picture',
28774                 cn : [
28775                     {
28776                         tag : 'button',
28777                         cls : 'btn btn-default',
28778                         html : '<i class="fa fa-picture-o"></i>'
28779                     }
28780                 ]
28781             },
28782             {
28783                 tag : 'div',
28784                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28785                 action : 'rotate-right',
28786                 cn : [
28787                     {
28788                         tag : 'button',
28789                         cls : 'btn btn-default',
28790                         html : '<i class="fa fa-repeat"></i>'
28791                     }
28792                 ]
28793             }
28794         ],
28795         DOCUMENT : [
28796             {
28797                 tag : 'div',
28798                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28799                 action : 'rotate-left',
28800                 cn : [
28801                     {
28802                         tag : 'button',
28803                         cls : 'btn btn-default',
28804                         html : '<i class="fa fa-undo"></i>'
28805                     }
28806                 ]
28807             },
28808             {
28809                 tag : 'div',
28810                 cls : 'btn-group roo-upload-cropbox-download',
28811                 action : 'download',
28812                 cn : [
28813                     {
28814                         tag : 'button',
28815                         cls : 'btn btn-default',
28816                         html : '<i class="fa fa-download"></i>'
28817                     }
28818                 ]
28819             },
28820             {
28821                 tag : 'div',
28822                 cls : 'btn-group roo-upload-cropbox-crop',
28823                 action : 'crop',
28824                 cn : [
28825                     {
28826                         tag : 'button',
28827                         cls : 'btn btn-default',
28828                         html : '<i class="fa fa-crop"></i>'
28829                     }
28830                 ]
28831             },
28832             {
28833                 tag : 'div',
28834                 cls : 'btn-group roo-upload-cropbox-trash',
28835                 action : 'trash',
28836                 cn : [
28837                     {
28838                         tag : 'button',
28839                         cls : 'btn btn-default',
28840                         html : '<i class="fa fa-trash"></i>'
28841                     }
28842                 ]
28843             },
28844             {
28845                 tag : 'div',
28846                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28847                 action : 'rotate-right',
28848                 cn : [
28849                     {
28850                         tag : 'button',
28851                         cls : 'btn btn-default',
28852                         html : '<i class="fa fa-repeat"></i>'
28853                     }
28854                 ]
28855             }
28856         ],
28857         ROTATOR : [
28858             {
28859                 tag : 'div',
28860                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28861                 action : 'rotate-left',
28862                 cn : [
28863                     {
28864                         tag : 'button',
28865                         cls : 'btn btn-default',
28866                         html : '<i class="fa fa-undo"></i>'
28867                     }
28868                 ]
28869             },
28870             {
28871                 tag : 'div',
28872                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28873                 action : 'rotate-right',
28874                 cn : [
28875                     {
28876                         tag : 'button',
28877                         cls : 'btn btn-default',
28878                         html : '<i class="fa fa-repeat"></i>'
28879                     }
28880                 ]
28881             }
28882         ]
28883     }
28884 });
28885
28886 /*
28887 * Licence: LGPL
28888 */
28889
28890 /**
28891  * @class Roo.bootstrap.DocumentManager
28892  * @extends Roo.bootstrap.Component
28893  * Bootstrap DocumentManager class
28894  * @cfg {String} paramName default 'imageUpload'
28895  * @cfg {String} toolTipName default 'filename'
28896  * @cfg {String} method default POST
28897  * @cfg {String} url action url
28898  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28899  * @cfg {Boolean} multiple multiple upload default true
28900  * @cfg {Number} thumbSize default 300
28901  * @cfg {String} fieldLabel
28902  * @cfg {Number} labelWidth default 4
28903  * @cfg {String} labelAlign (left|top) default left
28904  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28905 * @cfg {Number} labellg set the width of label (1-12)
28906  * @cfg {Number} labelmd set the width of label (1-12)
28907  * @cfg {Number} labelsm set the width of label (1-12)
28908  * @cfg {Number} labelxs set the width of label (1-12)
28909  * 
28910  * @constructor
28911  * Create a new DocumentManager
28912  * @param {Object} config The config object
28913  */
28914
28915 Roo.bootstrap.DocumentManager = function(config){
28916     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28917     
28918     this.files = [];
28919     this.delegates = [];
28920     
28921     this.addEvents({
28922         /**
28923          * @event initial
28924          * Fire when initial the DocumentManager
28925          * @param {Roo.bootstrap.DocumentManager} this
28926          */
28927         "initial" : true,
28928         /**
28929          * @event inspect
28930          * inspect selected file
28931          * @param {Roo.bootstrap.DocumentManager} this
28932          * @param {File} file
28933          */
28934         "inspect" : true,
28935         /**
28936          * @event exception
28937          * Fire when xhr load exception
28938          * @param {Roo.bootstrap.DocumentManager} this
28939          * @param {XMLHttpRequest} xhr
28940          */
28941         "exception" : true,
28942         /**
28943          * @event afterupload
28944          * Fire when xhr load exception
28945          * @param {Roo.bootstrap.DocumentManager} this
28946          * @param {XMLHttpRequest} xhr
28947          */
28948         "afterupload" : true,
28949         /**
28950          * @event prepare
28951          * prepare the form data
28952          * @param {Roo.bootstrap.DocumentManager} this
28953          * @param {Object} formData
28954          */
28955         "prepare" : true,
28956         /**
28957          * @event remove
28958          * Fire when remove the file
28959          * @param {Roo.bootstrap.DocumentManager} this
28960          * @param {Object} file
28961          */
28962         "remove" : true,
28963         /**
28964          * @event refresh
28965          * Fire after refresh the file
28966          * @param {Roo.bootstrap.DocumentManager} this
28967          */
28968         "refresh" : true,
28969         /**
28970          * @event click
28971          * Fire after click the image
28972          * @param {Roo.bootstrap.DocumentManager} this
28973          * @param {Object} file
28974          */
28975         "click" : true,
28976         /**
28977          * @event edit
28978          * Fire when upload a image and editable set to true
28979          * @param {Roo.bootstrap.DocumentManager} this
28980          * @param {Object} file
28981          */
28982         "edit" : true,
28983         /**
28984          * @event beforeselectfile
28985          * Fire before select file
28986          * @param {Roo.bootstrap.DocumentManager} this
28987          */
28988         "beforeselectfile" : true,
28989         /**
28990          * @event process
28991          * Fire before process file
28992          * @param {Roo.bootstrap.DocumentManager} this
28993          * @param {Object} file
28994          */
28995         "process" : true,
28996         /**
28997          * @event previewrendered
28998          * Fire when preview rendered
28999          * @param {Roo.bootstrap.DocumentManager} this
29000          * @param {Object} file
29001          */
29002         "previewrendered" : true,
29003         /**
29004          */
29005         "previewResize" : true
29006         
29007     });
29008 };
29009
29010 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29011     
29012     boxes : 0,
29013     inputName : '',
29014     thumbSize : 300,
29015     multiple : true,
29016     files : false,
29017     method : 'POST',
29018     url : '',
29019     paramName : 'imageUpload',
29020     toolTipName : 'filename',
29021     fieldLabel : '',
29022     labelWidth : 4,
29023     labelAlign : 'left',
29024     editable : true,
29025     delegates : false,
29026     xhr : false, 
29027     
29028     labellg : 0,
29029     labelmd : 0,
29030     labelsm : 0,
29031     labelxs : 0,
29032     
29033     getAutoCreate : function()
29034     {   
29035         var managerWidget = {
29036             tag : 'div',
29037             cls : 'roo-document-manager',
29038             cn : [
29039                 {
29040                     tag : 'input',
29041                     cls : 'roo-document-manager-selector',
29042                     type : 'file'
29043                 },
29044                 {
29045                     tag : 'div',
29046                     cls : 'roo-document-manager-uploader',
29047                     cn : [
29048                         {
29049                             tag : 'div',
29050                             cls : 'roo-document-manager-upload-btn',
29051                             html : '<i class="fa fa-plus"></i>'
29052                         }
29053                     ]
29054                     
29055                 }
29056             ]
29057         };
29058         
29059         var content = [
29060             {
29061                 tag : 'div',
29062                 cls : 'column col-md-12',
29063                 cn : managerWidget
29064             }
29065         ];
29066         
29067         if(this.fieldLabel.length){
29068             
29069             content = [
29070                 {
29071                     tag : 'div',
29072                     cls : 'column col-md-12',
29073                     html : this.fieldLabel
29074                 },
29075                 {
29076                     tag : 'div',
29077                     cls : 'column col-md-12',
29078                     cn : managerWidget
29079                 }
29080             ];
29081
29082             if(this.labelAlign == 'left'){
29083                 content = [
29084                     {
29085                         tag : 'div',
29086                         cls : 'column',
29087                         html : this.fieldLabel
29088                     },
29089                     {
29090                         tag : 'div',
29091                         cls : 'column',
29092                         cn : managerWidget
29093                     }
29094                 ];
29095                 
29096                 if(this.labelWidth > 12){
29097                     content[0].style = "width: " + this.labelWidth + 'px';
29098                 }
29099
29100                 if(this.labelWidth < 13 && this.labelmd == 0){
29101                     this.labelmd = this.labelWidth;
29102                 }
29103
29104                 if(this.labellg > 0){
29105                     content[0].cls += ' col-lg-' + this.labellg;
29106                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29107                 }
29108
29109                 if(this.labelmd > 0){
29110                     content[0].cls += ' col-md-' + this.labelmd;
29111                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29112                 }
29113
29114                 if(this.labelsm > 0){
29115                     content[0].cls += ' col-sm-' + this.labelsm;
29116                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29117                 }
29118
29119                 if(this.labelxs > 0){
29120                     content[0].cls += ' col-xs-' + this.labelxs;
29121                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29122                 }
29123                 
29124             }
29125         }
29126         
29127         var cfg = {
29128             tag : 'div',
29129             cls : 'row clearfix',
29130             cn : content
29131         };
29132         
29133         return cfg;
29134         
29135     },
29136     
29137     initEvents : function()
29138     {
29139         this.managerEl = this.el.select('.roo-document-manager', true).first();
29140         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29141         
29142         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29143         this.selectorEl.hide();
29144         
29145         if(this.multiple){
29146             this.selectorEl.attr('multiple', 'multiple');
29147         }
29148         
29149         this.selectorEl.on('change', this.onFileSelected, this);
29150         
29151         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29152         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29153         
29154         this.uploader.on('click', this.onUploaderClick, this);
29155         
29156         this.renderProgressDialog();
29157         
29158         var _this = this;
29159         
29160         window.addEventListener("resize", function() { _this.refresh(); } );
29161         
29162         this.fireEvent('initial', this);
29163     },
29164     
29165     renderProgressDialog : function()
29166     {
29167         var _this = this;
29168         
29169         this.progressDialog = new Roo.bootstrap.Modal({
29170             cls : 'roo-document-manager-progress-dialog',
29171             allow_close : false,
29172             title : '',
29173             buttons : [
29174                 {
29175                     name  :'cancel',
29176                     weight : 'danger',
29177                     html : 'Cancel'
29178                 }
29179             ], 
29180             listeners : { 
29181                 btnclick : function() {
29182                     _this.uploadCancel();
29183                     this.hide();
29184                 }
29185             }
29186         });
29187          
29188         this.progressDialog.render(Roo.get(document.body));
29189          
29190         this.progress = new Roo.bootstrap.Progress({
29191             cls : 'roo-document-manager-progress',
29192             active : true,
29193             striped : true
29194         });
29195         
29196         this.progress.render(this.progressDialog.getChildContainer());
29197         
29198         this.progressBar = new Roo.bootstrap.ProgressBar({
29199             cls : 'roo-document-manager-progress-bar',
29200             aria_valuenow : 0,
29201             aria_valuemin : 0,
29202             aria_valuemax : 12,
29203             panel : 'success'
29204         });
29205         
29206         this.progressBar.render(this.progress.getChildContainer());
29207     },
29208     
29209     onUploaderClick : function(e)
29210     {
29211         e.preventDefault();
29212      
29213         if(this.fireEvent('beforeselectfile', this) != false){
29214             this.selectorEl.dom.click();
29215         }
29216         
29217     },
29218     
29219     onFileSelected : function(e)
29220     {
29221         e.preventDefault();
29222         
29223         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29224             return;
29225         }
29226         
29227         Roo.each(this.selectorEl.dom.files, function(file){
29228             if(this.fireEvent('inspect', this, file) != false){
29229                 this.files.push(file);
29230             }
29231         }, this);
29232         
29233         this.queue();
29234         
29235     },
29236     
29237     queue : function()
29238     {
29239         this.selectorEl.dom.value = '';
29240         
29241         if(!this.files || !this.files.length){
29242             return;
29243         }
29244         
29245         if(this.boxes > 0 && this.files.length > this.boxes){
29246             this.files = this.files.slice(0, this.boxes);
29247         }
29248         
29249         this.uploader.show();
29250         
29251         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29252             this.uploader.hide();
29253         }
29254         
29255         var _this = this;
29256         
29257         var files = [];
29258         
29259         var docs = [];
29260         
29261         Roo.each(this.files, function(file){
29262             
29263             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29264                 var f = this.renderPreview(file);
29265                 files.push(f);
29266                 return;
29267             }
29268             
29269             if(file.type.indexOf('image') != -1){
29270                 this.delegates.push(
29271                     (function(){
29272                         _this.process(file);
29273                     }).createDelegate(this)
29274                 );
29275         
29276                 return;
29277             }
29278             
29279             docs.push(
29280                 (function(){
29281                     _this.process(file);
29282                 }).createDelegate(this)
29283             );
29284             
29285         }, this);
29286         
29287         this.files = files;
29288         
29289         this.delegates = this.delegates.concat(docs);
29290         
29291         if(!this.delegates.length){
29292             this.refresh();
29293             return;
29294         }
29295         
29296         this.progressBar.aria_valuemax = this.delegates.length;
29297         
29298         this.arrange();
29299         
29300         return;
29301     },
29302     
29303     arrange : function()
29304     {
29305         if(!this.delegates.length){
29306             this.progressDialog.hide();
29307             this.refresh();
29308             return;
29309         }
29310         
29311         var delegate = this.delegates.shift();
29312         
29313         this.progressDialog.show();
29314         
29315         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29316         
29317         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29318         
29319         delegate();
29320     },
29321     
29322     refresh : function()
29323     {
29324         this.uploader.show();
29325         
29326         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29327             this.uploader.hide();
29328         }
29329         
29330         Roo.isTouch ? this.closable(false) : this.closable(true);
29331         
29332         this.fireEvent('refresh', this);
29333     },
29334     
29335     onRemove : function(e, el, o)
29336     {
29337         e.preventDefault();
29338         
29339         this.fireEvent('remove', this, o);
29340         
29341     },
29342     
29343     remove : function(o)
29344     {
29345         var files = [];
29346         
29347         Roo.each(this.files, function(file){
29348             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29349                 files.push(file);
29350                 return;
29351             }
29352
29353             o.target.remove();
29354
29355         }, this);
29356         
29357         this.files = files;
29358         
29359         this.refresh();
29360     },
29361     
29362     clear : function()
29363     {
29364         Roo.each(this.files, function(file){
29365             if(!file.target){
29366                 return;
29367             }
29368             
29369             file.target.remove();
29370
29371         }, this);
29372         
29373         this.files = [];
29374         
29375         this.refresh();
29376     },
29377     
29378     onClick : function(e, el, o)
29379     {
29380         e.preventDefault();
29381         
29382         this.fireEvent('click', this, o);
29383         
29384     },
29385     
29386     closable : function(closable)
29387     {
29388         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29389             
29390             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29391             
29392             if(closable){
29393                 el.show();
29394                 return;
29395             }
29396             
29397             el.hide();
29398             
29399         }, this);
29400     },
29401     
29402     xhrOnLoad : function(xhr)
29403     {
29404         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29405             el.remove();
29406         }, this);
29407         
29408         if (xhr.readyState !== 4) {
29409             this.arrange();
29410             this.fireEvent('exception', this, xhr);
29411             return;
29412         }
29413
29414         var response = Roo.decode(xhr.responseText);
29415         
29416         if(!response.success){
29417             this.arrange();
29418             this.fireEvent('exception', this, xhr);
29419             return;
29420         }
29421         
29422         var file = this.renderPreview(response.data);
29423         
29424         this.files.push(file);
29425         
29426         this.arrange();
29427         
29428         this.fireEvent('afterupload', this, xhr);
29429         
29430     },
29431     
29432     xhrOnError : function(xhr)
29433     {
29434         Roo.log('xhr on error');
29435         
29436         var response = Roo.decode(xhr.responseText);
29437           
29438         Roo.log(response);
29439         
29440         this.arrange();
29441     },
29442     
29443     process : function(file)
29444     {
29445         if(this.fireEvent('process', this, file) !== false){
29446             if(this.editable && file.type.indexOf('image') != -1){
29447                 this.fireEvent('edit', this, file);
29448                 return;
29449             }
29450
29451             this.uploadStart(file, false);
29452
29453             return;
29454         }
29455         
29456     },
29457     
29458     uploadStart : function(file, crop)
29459     {
29460         this.xhr = new XMLHttpRequest();
29461         
29462         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29463             this.arrange();
29464             return;
29465         }
29466         
29467         file.xhr = this.xhr;
29468             
29469         this.managerEl.createChild({
29470             tag : 'div',
29471             cls : 'roo-document-manager-loading',
29472             cn : [
29473                 {
29474                     tag : 'div',
29475                     tooltip : file.name,
29476                     cls : 'roo-document-manager-thumb',
29477                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29478                 }
29479             ]
29480
29481         });
29482
29483         this.xhr.open(this.method, this.url, true);
29484         
29485         var headers = {
29486             "Accept": "application/json",
29487             "Cache-Control": "no-cache",
29488             "X-Requested-With": "XMLHttpRequest"
29489         };
29490         
29491         for (var headerName in headers) {
29492             var headerValue = headers[headerName];
29493             if (headerValue) {
29494                 this.xhr.setRequestHeader(headerName, headerValue);
29495             }
29496         }
29497         
29498         var _this = this;
29499         
29500         this.xhr.onload = function()
29501         {
29502             _this.xhrOnLoad(_this.xhr);
29503         }
29504         
29505         this.xhr.onerror = function()
29506         {
29507             _this.xhrOnError(_this.xhr);
29508         }
29509         
29510         var formData = new FormData();
29511
29512         formData.append('returnHTML', 'NO');
29513         
29514         if(crop){
29515             formData.append('crop', crop);
29516         }
29517         
29518         formData.append(this.paramName, file, file.name);
29519         
29520         var options = {
29521             file : file, 
29522             manually : false
29523         };
29524         
29525         if(this.fireEvent('prepare', this, formData, options) != false){
29526             
29527             if(options.manually){
29528                 return;
29529             }
29530             
29531             this.xhr.send(formData);
29532             return;
29533         };
29534         
29535         this.uploadCancel();
29536     },
29537     
29538     uploadCancel : function()
29539     {
29540         if (this.xhr) {
29541             this.xhr.abort();
29542         }
29543         
29544         this.delegates = [];
29545         
29546         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29547             el.remove();
29548         }, this);
29549         
29550         this.arrange();
29551     },
29552     
29553     renderPreview : function(file)
29554     {
29555         if(typeof(file.target) != 'undefined' && file.target){
29556             return file;
29557         }
29558         
29559         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29560         
29561         var previewEl = this.managerEl.createChild({
29562             tag : 'div',
29563             cls : 'roo-document-manager-preview',
29564             cn : [
29565                 {
29566                     tag : 'div',
29567                     tooltip : file[this.toolTipName],
29568                     cls : 'roo-document-manager-thumb',
29569                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29570                 },
29571                 {
29572                     tag : 'button',
29573                     cls : 'close',
29574                     html : '<i class="fa fa-times-circle"></i>'
29575                 }
29576             ]
29577         });
29578
29579         var close = previewEl.select('button.close', true).first();
29580
29581         close.on('click', this.onRemove, this, file);
29582
29583         file.target = previewEl;
29584
29585         var image = previewEl.select('img', true).first();
29586         
29587         var _this = this;
29588         
29589         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29590         
29591         image.on('click', this.onClick, this, file);
29592         
29593         this.fireEvent('previewrendered', this, file);
29594         
29595         return file;
29596         
29597     },
29598     
29599     onPreviewLoad : function(file, image)
29600     {
29601         if(typeof(file.target) == 'undefined' || !file.target){
29602             return;
29603         }
29604         
29605         var width = image.dom.naturalWidth || image.dom.width;
29606         var height = image.dom.naturalHeight || image.dom.height;
29607         
29608         if(!this.previewResize) {
29609             return;
29610         }
29611         
29612         if(width > height){
29613             file.target.addClass('wide');
29614             return;
29615         }
29616         
29617         file.target.addClass('tall');
29618         return;
29619         
29620     },
29621     
29622     uploadFromSource : function(file, crop)
29623     {
29624         this.xhr = new XMLHttpRequest();
29625         
29626         this.managerEl.createChild({
29627             tag : 'div',
29628             cls : 'roo-document-manager-loading',
29629             cn : [
29630                 {
29631                     tag : 'div',
29632                     tooltip : file.name,
29633                     cls : 'roo-document-manager-thumb',
29634                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29635                 }
29636             ]
29637
29638         });
29639
29640         this.xhr.open(this.method, this.url, true);
29641         
29642         var headers = {
29643             "Accept": "application/json",
29644             "Cache-Control": "no-cache",
29645             "X-Requested-With": "XMLHttpRequest"
29646         };
29647         
29648         for (var headerName in headers) {
29649             var headerValue = headers[headerName];
29650             if (headerValue) {
29651                 this.xhr.setRequestHeader(headerName, headerValue);
29652             }
29653         }
29654         
29655         var _this = this;
29656         
29657         this.xhr.onload = function()
29658         {
29659             _this.xhrOnLoad(_this.xhr);
29660         }
29661         
29662         this.xhr.onerror = function()
29663         {
29664             _this.xhrOnError(_this.xhr);
29665         }
29666         
29667         var formData = new FormData();
29668
29669         formData.append('returnHTML', 'NO');
29670         
29671         formData.append('crop', crop);
29672         
29673         if(typeof(file.filename) != 'undefined'){
29674             formData.append('filename', file.filename);
29675         }
29676         
29677         if(typeof(file.mimetype) != 'undefined'){
29678             formData.append('mimetype', file.mimetype);
29679         }
29680         
29681         Roo.log(formData);
29682         
29683         if(this.fireEvent('prepare', this, formData) != false){
29684             this.xhr.send(formData);
29685         };
29686     }
29687 });
29688
29689 /*
29690 * Licence: LGPL
29691 */
29692
29693 /**
29694  * @class Roo.bootstrap.DocumentViewer
29695  * @extends Roo.bootstrap.Component
29696  * Bootstrap DocumentViewer class
29697  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29698  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29699  * 
29700  * @constructor
29701  * Create a new DocumentViewer
29702  * @param {Object} config The config object
29703  */
29704
29705 Roo.bootstrap.DocumentViewer = function(config){
29706     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29707     
29708     this.addEvents({
29709         /**
29710          * @event initial
29711          * Fire after initEvent
29712          * @param {Roo.bootstrap.DocumentViewer} this
29713          */
29714         "initial" : true,
29715         /**
29716          * @event click
29717          * Fire after click
29718          * @param {Roo.bootstrap.DocumentViewer} this
29719          */
29720         "click" : true,
29721         /**
29722          * @event download
29723          * Fire after download button
29724          * @param {Roo.bootstrap.DocumentViewer} this
29725          */
29726         "download" : true,
29727         /**
29728          * @event trash
29729          * Fire after trash button
29730          * @param {Roo.bootstrap.DocumentViewer} this
29731          */
29732         "trash" : true
29733         
29734     });
29735 };
29736
29737 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29738     
29739     showDownload : true,
29740     
29741     showTrash : true,
29742     
29743     getAutoCreate : function()
29744     {
29745         var cfg = {
29746             tag : 'div',
29747             cls : 'roo-document-viewer',
29748             cn : [
29749                 {
29750                     tag : 'div',
29751                     cls : 'roo-document-viewer-body',
29752                     cn : [
29753                         {
29754                             tag : 'div',
29755                             cls : 'roo-document-viewer-thumb',
29756                             cn : [
29757                                 {
29758                                     tag : 'img',
29759                                     cls : 'roo-document-viewer-image'
29760                                 }
29761                             ]
29762                         }
29763                     ]
29764                 },
29765                 {
29766                     tag : 'div',
29767                     cls : 'roo-document-viewer-footer',
29768                     cn : {
29769                         tag : 'div',
29770                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29771                         cn : [
29772                             {
29773                                 tag : 'div',
29774                                 cls : 'btn-group roo-document-viewer-download',
29775                                 cn : [
29776                                     {
29777                                         tag : 'button',
29778                                         cls : 'btn btn-default',
29779                                         html : '<i class="fa fa-download"></i>'
29780                                     }
29781                                 ]
29782                             },
29783                             {
29784                                 tag : 'div',
29785                                 cls : 'btn-group roo-document-viewer-trash',
29786                                 cn : [
29787                                     {
29788                                         tag : 'button',
29789                                         cls : 'btn btn-default',
29790                                         html : '<i class="fa fa-trash"></i>'
29791                                     }
29792                                 ]
29793                             }
29794                         ]
29795                     }
29796                 }
29797             ]
29798         };
29799         
29800         return cfg;
29801     },
29802     
29803     initEvents : function()
29804     {
29805         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29806         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29807         
29808         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29809         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29810         
29811         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29812         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29813         
29814         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29815         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29816         
29817         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29818         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29819         
29820         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29821         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29822         
29823         this.bodyEl.on('click', this.onClick, this);
29824         this.downloadBtn.on('click', this.onDownload, this);
29825         this.trashBtn.on('click', this.onTrash, this);
29826         
29827         this.downloadBtn.hide();
29828         this.trashBtn.hide();
29829         
29830         if(this.showDownload){
29831             this.downloadBtn.show();
29832         }
29833         
29834         if(this.showTrash){
29835             this.trashBtn.show();
29836         }
29837         
29838         if(!this.showDownload && !this.showTrash) {
29839             this.footerEl.hide();
29840         }
29841         
29842     },
29843     
29844     initial : function()
29845     {
29846         this.fireEvent('initial', this);
29847         
29848     },
29849     
29850     onClick : function(e)
29851     {
29852         e.preventDefault();
29853         
29854         this.fireEvent('click', this);
29855     },
29856     
29857     onDownload : function(e)
29858     {
29859         e.preventDefault();
29860         
29861         this.fireEvent('download', this);
29862     },
29863     
29864     onTrash : function(e)
29865     {
29866         e.preventDefault();
29867         
29868         this.fireEvent('trash', this);
29869     }
29870     
29871 });
29872 /*
29873  * - LGPL
29874  *
29875  * nav progress bar
29876  * 
29877  */
29878
29879 /**
29880  * @class Roo.bootstrap.NavProgressBar
29881  * @extends Roo.bootstrap.Component
29882  * Bootstrap NavProgressBar class
29883  * 
29884  * @constructor
29885  * Create a new nav progress bar
29886  * @param {Object} config The config object
29887  */
29888
29889 Roo.bootstrap.NavProgressBar = function(config){
29890     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29891
29892     this.bullets = this.bullets || [];
29893    
29894 //    Roo.bootstrap.NavProgressBar.register(this);
29895      this.addEvents({
29896         /**
29897              * @event changed
29898              * Fires when the active item changes
29899              * @param {Roo.bootstrap.NavProgressBar} this
29900              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29901              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29902          */
29903         'changed': true
29904      });
29905     
29906 };
29907
29908 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29909     
29910     bullets : [],
29911     barItems : [],
29912     
29913     getAutoCreate : function()
29914     {
29915         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29916         
29917         cfg = {
29918             tag : 'div',
29919             cls : 'roo-navigation-bar-group',
29920             cn : [
29921                 {
29922                     tag : 'div',
29923                     cls : 'roo-navigation-top-bar'
29924                 },
29925                 {
29926                     tag : 'div',
29927                     cls : 'roo-navigation-bullets-bar',
29928                     cn : [
29929                         {
29930                             tag : 'ul',
29931                             cls : 'roo-navigation-bar'
29932                         }
29933                     ]
29934                 },
29935                 
29936                 {
29937                     tag : 'div',
29938                     cls : 'roo-navigation-bottom-bar'
29939                 }
29940             ]
29941             
29942         };
29943         
29944         return cfg;
29945         
29946     },
29947     
29948     initEvents: function() 
29949     {
29950         
29951     },
29952     
29953     onRender : function(ct, position) 
29954     {
29955         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29956         
29957         if(this.bullets.length){
29958             Roo.each(this.bullets, function(b){
29959                this.addItem(b);
29960             }, this);
29961         }
29962         
29963         this.format();
29964         
29965     },
29966     
29967     addItem : function(cfg)
29968     {
29969         var item = new Roo.bootstrap.NavProgressItem(cfg);
29970         
29971         item.parentId = this.id;
29972         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29973         
29974         if(cfg.html){
29975             var top = new Roo.bootstrap.Element({
29976                 tag : 'div',
29977                 cls : 'roo-navigation-bar-text'
29978             });
29979             
29980             var bottom = new Roo.bootstrap.Element({
29981                 tag : 'div',
29982                 cls : 'roo-navigation-bar-text'
29983             });
29984             
29985             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29986             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29987             
29988             var topText = new Roo.bootstrap.Element({
29989                 tag : 'span',
29990                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29991             });
29992             
29993             var bottomText = new Roo.bootstrap.Element({
29994                 tag : 'span',
29995                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29996             });
29997             
29998             topText.onRender(top.el, null);
29999             bottomText.onRender(bottom.el, null);
30000             
30001             item.topEl = top;
30002             item.bottomEl = bottom;
30003         }
30004         
30005         this.barItems.push(item);
30006         
30007         return item;
30008     },
30009     
30010     getActive : function()
30011     {
30012         var active = false;
30013         
30014         Roo.each(this.barItems, function(v){
30015             
30016             if (!v.isActive()) {
30017                 return;
30018             }
30019             
30020             active = v;
30021             return false;
30022             
30023         });
30024         
30025         return active;
30026     },
30027     
30028     setActiveItem : function(item)
30029     {
30030         var prev = false;
30031         
30032         Roo.each(this.barItems, function(v){
30033             if (v.rid == item.rid) {
30034                 return ;
30035             }
30036             
30037             if (v.isActive()) {
30038                 v.setActive(false);
30039                 prev = v;
30040             }
30041         });
30042
30043         item.setActive(true);
30044         
30045         this.fireEvent('changed', this, item, prev);
30046     },
30047     
30048     getBarItem: function(rid)
30049     {
30050         var ret = false;
30051         
30052         Roo.each(this.barItems, function(e) {
30053             if (e.rid != rid) {
30054                 return;
30055             }
30056             
30057             ret =  e;
30058             return false;
30059         });
30060         
30061         return ret;
30062     },
30063     
30064     indexOfItem : function(item)
30065     {
30066         var index = false;
30067         
30068         Roo.each(this.barItems, function(v, i){
30069             
30070             if (v.rid != item.rid) {
30071                 return;
30072             }
30073             
30074             index = i;
30075             return false
30076         });
30077         
30078         return index;
30079     },
30080     
30081     setActiveNext : function()
30082     {
30083         var i = this.indexOfItem(this.getActive());
30084         
30085         if (i > this.barItems.length) {
30086             return;
30087         }
30088         
30089         this.setActiveItem(this.barItems[i+1]);
30090     },
30091     
30092     setActivePrev : function()
30093     {
30094         var i = this.indexOfItem(this.getActive());
30095         
30096         if (i  < 1) {
30097             return;
30098         }
30099         
30100         this.setActiveItem(this.barItems[i-1]);
30101     },
30102     
30103     format : function()
30104     {
30105         if(!this.barItems.length){
30106             return;
30107         }
30108      
30109         var width = 100 / this.barItems.length;
30110         
30111         Roo.each(this.barItems, function(i){
30112             i.el.setStyle('width', width + '%');
30113             i.topEl.el.setStyle('width', width + '%');
30114             i.bottomEl.el.setStyle('width', width + '%');
30115         }, this);
30116         
30117     }
30118     
30119 });
30120 /*
30121  * - LGPL
30122  *
30123  * Nav Progress Item
30124  * 
30125  */
30126
30127 /**
30128  * @class Roo.bootstrap.NavProgressItem
30129  * @extends Roo.bootstrap.Component
30130  * Bootstrap NavProgressItem class
30131  * @cfg {String} rid the reference id
30132  * @cfg {Boolean} active (true|false) Is item active default false
30133  * @cfg {Boolean} disabled (true|false) Is item active default false
30134  * @cfg {String} html
30135  * @cfg {String} position (top|bottom) text position default bottom
30136  * @cfg {String} icon show icon instead of number
30137  * 
30138  * @constructor
30139  * Create a new NavProgressItem
30140  * @param {Object} config The config object
30141  */
30142 Roo.bootstrap.NavProgressItem = function(config){
30143     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30144     this.addEvents({
30145         // raw events
30146         /**
30147          * @event click
30148          * The raw click event for the entire grid.
30149          * @param {Roo.bootstrap.NavProgressItem} this
30150          * @param {Roo.EventObject} e
30151          */
30152         "click" : true
30153     });
30154    
30155 };
30156
30157 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30158     
30159     rid : '',
30160     active : false,
30161     disabled : false,
30162     html : '',
30163     position : 'bottom',
30164     icon : false,
30165     
30166     getAutoCreate : function()
30167     {
30168         var iconCls = 'roo-navigation-bar-item-icon';
30169         
30170         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30171         
30172         var cfg = {
30173             tag: 'li',
30174             cls: 'roo-navigation-bar-item',
30175             cn : [
30176                 {
30177                     tag : 'i',
30178                     cls : iconCls
30179                 }
30180             ]
30181         };
30182         
30183         if(this.active){
30184             cfg.cls += ' active';
30185         }
30186         if(this.disabled){
30187             cfg.cls += ' disabled';
30188         }
30189         
30190         return cfg;
30191     },
30192     
30193     disable : function()
30194     {
30195         this.setDisabled(true);
30196     },
30197     
30198     enable : function()
30199     {
30200         this.setDisabled(false);
30201     },
30202     
30203     initEvents: function() 
30204     {
30205         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30206         
30207         this.iconEl.on('click', this.onClick, this);
30208     },
30209     
30210     onClick : function(e)
30211     {
30212         e.preventDefault();
30213         
30214         if(this.disabled){
30215             return;
30216         }
30217         
30218         if(this.fireEvent('click', this, e) === false){
30219             return;
30220         };
30221         
30222         this.parent().setActiveItem(this);
30223     },
30224     
30225     isActive: function () 
30226     {
30227         return this.active;
30228     },
30229     
30230     setActive : function(state)
30231     {
30232         if(this.active == state){
30233             return;
30234         }
30235         
30236         this.active = state;
30237         
30238         if (state) {
30239             this.el.addClass('active');
30240             return;
30241         }
30242         
30243         this.el.removeClass('active');
30244         
30245         return;
30246     },
30247     
30248     setDisabled : function(state)
30249     {
30250         if(this.disabled == state){
30251             return;
30252         }
30253         
30254         this.disabled = state;
30255         
30256         if (state) {
30257             this.el.addClass('disabled');
30258             return;
30259         }
30260         
30261         this.el.removeClass('disabled');
30262     },
30263     
30264     tooltipEl : function()
30265     {
30266         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30267     }
30268 });
30269  
30270
30271  /*
30272  * - LGPL
30273  *
30274  * FieldLabel
30275  * 
30276  */
30277
30278 /**
30279  * @class Roo.bootstrap.FieldLabel
30280  * @extends Roo.bootstrap.Component
30281  * Bootstrap FieldLabel class
30282  * @cfg {String} html contents of the element
30283  * @cfg {String} tag tag of the element default label
30284  * @cfg {String} cls class of the element
30285  * @cfg {String} target label target 
30286  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30287  * @cfg {String} invalidClass default "text-warning"
30288  * @cfg {String} validClass default "text-success"
30289  * @cfg {String} iconTooltip default "This field is required"
30290  * @cfg {String} indicatorpos (left|right) default left
30291  * 
30292  * @constructor
30293  * Create a new FieldLabel
30294  * @param {Object} config The config object
30295  */
30296
30297 Roo.bootstrap.FieldLabel = function(config){
30298     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30299     
30300     this.addEvents({
30301             /**
30302              * @event invalid
30303              * Fires after the field has been marked as invalid.
30304              * @param {Roo.form.FieldLabel} this
30305              * @param {String} msg The validation message
30306              */
30307             invalid : true,
30308             /**
30309              * @event valid
30310              * Fires after the field has been validated with no errors.
30311              * @param {Roo.form.FieldLabel} this
30312              */
30313             valid : true
30314         });
30315 };
30316
30317 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30318     
30319     tag: 'label',
30320     cls: '',
30321     html: '',
30322     target: '',
30323     allowBlank : true,
30324     invalidClass : 'has-warning',
30325     validClass : 'has-success',
30326     iconTooltip : 'This field is required',
30327     indicatorpos : 'left',
30328     
30329     getAutoCreate : function(){
30330         
30331         var cls = "";
30332         if (!this.allowBlank) {
30333             cls  = "visible";
30334         }
30335         
30336         var cfg = {
30337             tag : this.tag,
30338             cls : 'roo-bootstrap-field-label ' + this.cls,
30339             for : this.target,
30340             cn : [
30341                 {
30342                     tag : 'i',
30343                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30344                     tooltip : this.iconTooltip
30345                 },
30346                 {
30347                     tag : 'span',
30348                     html : this.html
30349                 }
30350             ] 
30351         };
30352         
30353         if(this.indicatorpos == 'right'){
30354             var cfg = {
30355                 tag : this.tag,
30356                 cls : 'roo-bootstrap-field-label ' + this.cls,
30357                 for : this.target,
30358                 cn : [
30359                     {
30360                         tag : 'span',
30361                         html : this.html
30362                     },
30363                     {
30364                         tag : 'i',
30365                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30366                         tooltip : this.iconTooltip
30367                     }
30368                 ] 
30369             };
30370         }
30371         
30372         return cfg;
30373     },
30374     
30375     initEvents: function() 
30376     {
30377         Roo.bootstrap.Element.superclass.initEvents.call(this);
30378         
30379         this.indicator = this.indicatorEl();
30380         
30381         if(this.indicator){
30382             this.indicator.removeClass('visible');
30383             this.indicator.addClass('invisible');
30384         }
30385         
30386         Roo.bootstrap.FieldLabel.register(this);
30387     },
30388     
30389     indicatorEl : function()
30390     {
30391         var indicator = this.el.select('i.roo-required-indicator',true).first();
30392         
30393         if(!indicator){
30394             return false;
30395         }
30396         
30397         return indicator;
30398         
30399     },
30400     
30401     /**
30402      * Mark this field as valid
30403      */
30404     markValid : function()
30405     {
30406         if(this.indicator){
30407             this.indicator.removeClass('visible');
30408             this.indicator.addClass('invisible');
30409         }
30410         
30411         this.el.removeClass(this.invalidClass);
30412         
30413         this.el.addClass(this.validClass);
30414         
30415         this.fireEvent('valid', this);
30416     },
30417     
30418     /**
30419      * Mark this field as invalid
30420      * @param {String} msg The validation message
30421      */
30422     markInvalid : function(msg)
30423     {
30424         if(this.indicator){
30425             this.indicator.removeClass('invisible');
30426             this.indicator.addClass('visible');
30427         }
30428         
30429         this.el.removeClass(this.validClass);
30430         
30431         this.el.addClass(this.invalidClass);
30432         
30433         this.fireEvent('invalid', this, msg);
30434     }
30435     
30436    
30437 });
30438
30439 Roo.apply(Roo.bootstrap.FieldLabel, {
30440     
30441     groups: {},
30442     
30443      /**
30444     * register a FieldLabel Group
30445     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30446     */
30447     register : function(label)
30448     {
30449         if(this.groups.hasOwnProperty(label.target)){
30450             return;
30451         }
30452      
30453         this.groups[label.target] = label;
30454         
30455     },
30456     /**
30457     * fetch a FieldLabel Group based on the target
30458     * @param {string} target
30459     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30460     */
30461     get: function(target) {
30462         if (typeof(this.groups[target]) == 'undefined') {
30463             return false;
30464         }
30465         
30466         return this.groups[target] ;
30467     }
30468 });
30469
30470  
30471
30472  /*
30473  * - LGPL
30474  *
30475  * page DateSplitField.
30476  * 
30477  */
30478
30479
30480 /**
30481  * @class Roo.bootstrap.DateSplitField
30482  * @extends Roo.bootstrap.Component
30483  * Bootstrap DateSplitField class
30484  * @cfg {string} fieldLabel - the label associated
30485  * @cfg {Number} labelWidth set the width of label (0-12)
30486  * @cfg {String} labelAlign (top|left)
30487  * @cfg {Boolean} dayAllowBlank (true|false) default false
30488  * @cfg {Boolean} monthAllowBlank (true|false) default false
30489  * @cfg {Boolean} yearAllowBlank (true|false) default false
30490  * @cfg {string} dayPlaceholder 
30491  * @cfg {string} monthPlaceholder
30492  * @cfg {string} yearPlaceholder
30493  * @cfg {string} dayFormat default 'd'
30494  * @cfg {string} monthFormat default 'm'
30495  * @cfg {string} yearFormat default 'Y'
30496  * @cfg {Number} labellg set the width of label (1-12)
30497  * @cfg {Number} labelmd set the width of label (1-12)
30498  * @cfg {Number} labelsm set the width of label (1-12)
30499  * @cfg {Number} labelxs set the width of label (1-12)
30500
30501  *     
30502  * @constructor
30503  * Create a new DateSplitField
30504  * @param {Object} config The config object
30505  */
30506
30507 Roo.bootstrap.DateSplitField = function(config){
30508     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30509     
30510     this.addEvents({
30511         // raw events
30512          /**
30513          * @event years
30514          * getting the data of years
30515          * @param {Roo.bootstrap.DateSplitField} this
30516          * @param {Object} years
30517          */
30518         "years" : true,
30519         /**
30520          * @event days
30521          * getting the data of days
30522          * @param {Roo.bootstrap.DateSplitField} this
30523          * @param {Object} days
30524          */
30525         "days" : true,
30526         /**
30527          * @event invalid
30528          * Fires after the field has been marked as invalid.
30529          * @param {Roo.form.Field} this
30530          * @param {String} msg The validation message
30531          */
30532         invalid : true,
30533        /**
30534          * @event valid
30535          * Fires after the field has been validated with no errors.
30536          * @param {Roo.form.Field} this
30537          */
30538         valid : true
30539     });
30540 };
30541
30542 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30543     
30544     fieldLabel : '',
30545     labelAlign : 'top',
30546     labelWidth : 3,
30547     dayAllowBlank : false,
30548     monthAllowBlank : false,
30549     yearAllowBlank : false,
30550     dayPlaceholder : '',
30551     monthPlaceholder : '',
30552     yearPlaceholder : '',
30553     dayFormat : 'd',
30554     monthFormat : 'm',
30555     yearFormat : 'Y',
30556     isFormField : true,
30557     labellg : 0,
30558     labelmd : 0,
30559     labelsm : 0,
30560     labelxs : 0,
30561     
30562     getAutoCreate : function()
30563     {
30564         var cfg = {
30565             tag : 'div',
30566             cls : 'row roo-date-split-field-group',
30567             cn : [
30568                 {
30569                     tag : 'input',
30570                     type : 'hidden',
30571                     cls : 'form-hidden-field roo-date-split-field-group-value',
30572                     name : this.name
30573                 }
30574             ]
30575         };
30576         
30577         var labelCls = 'col-md-12';
30578         var contentCls = 'col-md-4';
30579         
30580         if(this.fieldLabel){
30581             
30582             var label = {
30583                 tag : 'div',
30584                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30585                 cn : [
30586                     {
30587                         tag : 'label',
30588                         html : this.fieldLabel
30589                     }
30590                 ]
30591             };
30592             
30593             if(this.labelAlign == 'left'){
30594             
30595                 if(this.labelWidth > 12){
30596                     label.style = "width: " + this.labelWidth + 'px';
30597                 }
30598
30599                 if(this.labelWidth < 13 && this.labelmd == 0){
30600                     this.labelmd = this.labelWidth;
30601                 }
30602
30603                 if(this.labellg > 0){
30604                     labelCls = ' col-lg-' + this.labellg;
30605                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30606                 }
30607
30608                 if(this.labelmd > 0){
30609                     labelCls = ' col-md-' + this.labelmd;
30610                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30611                 }
30612
30613                 if(this.labelsm > 0){
30614                     labelCls = ' col-sm-' + this.labelsm;
30615                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30616                 }
30617
30618                 if(this.labelxs > 0){
30619                     labelCls = ' col-xs-' + this.labelxs;
30620                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30621                 }
30622             }
30623             
30624             label.cls += ' ' + labelCls;
30625             
30626             cfg.cn.push(label);
30627         }
30628         
30629         Roo.each(['day', 'month', 'year'], function(t){
30630             cfg.cn.push({
30631                 tag : 'div',
30632                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30633             });
30634         }, this);
30635         
30636         return cfg;
30637     },
30638     
30639     inputEl: function ()
30640     {
30641         return this.el.select('.roo-date-split-field-group-value', true).first();
30642     },
30643     
30644     onRender : function(ct, position) 
30645     {
30646         var _this = this;
30647         
30648         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30649         
30650         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30651         
30652         this.dayField = new Roo.bootstrap.ComboBox({
30653             allowBlank : this.dayAllowBlank,
30654             alwaysQuery : true,
30655             displayField : 'value',
30656             editable : false,
30657             fieldLabel : '',
30658             forceSelection : true,
30659             mode : 'local',
30660             placeholder : this.dayPlaceholder,
30661             selectOnFocus : true,
30662             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30663             triggerAction : 'all',
30664             typeAhead : true,
30665             valueField : 'value',
30666             store : new Roo.data.SimpleStore({
30667                 data : (function() {    
30668                     var days = [];
30669                     _this.fireEvent('days', _this, days);
30670                     return days;
30671                 })(),
30672                 fields : [ 'value' ]
30673             }),
30674             listeners : {
30675                 select : function (_self, record, index)
30676                 {
30677                     _this.setValue(_this.getValue());
30678                 }
30679             }
30680         });
30681
30682         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30683         
30684         this.monthField = new Roo.bootstrap.MonthField({
30685             after : '<i class=\"fa fa-calendar\"></i>',
30686             allowBlank : this.monthAllowBlank,
30687             placeholder : this.monthPlaceholder,
30688             readOnly : true,
30689             listeners : {
30690                 render : function (_self)
30691                 {
30692                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30693                         e.preventDefault();
30694                         _self.focus();
30695                     });
30696                 },
30697                 select : function (_self, oldvalue, newvalue)
30698                 {
30699                     _this.setValue(_this.getValue());
30700                 }
30701             }
30702         });
30703         
30704         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30705         
30706         this.yearField = new Roo.bootstrap.ComboBox({
30707             allowBlank : this.yearAllowBlank,
30708             alwaysQuery : true,
30709             displayField : 'value',
30710             editable : false,
30711             fieldLabel : '',
30712             forceSelection : true,
30713             mode : 'local',
30714             placeholder : this.yearPlaceholder,
30715             selectOnFocus : true,
30716             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30717             triggerAction : 'all',
30718             typeAhead : true,
30719             valueField : 'value',
30720             store : new Roo.data.SimpleStore({
30721                 data : (function() {
30722                     var years = [];
30723                     _this.fireEvent('years', _this, years);
30724                     return years;
30725                 })(),
30726                 fields : [ 'value' ]
30727             }),
30728             listeners : {
30729                 select : function (_self, record, index)
30730                 {
30731                     _this.setValue(_this.getValue());
30732                 }
30733             }
30734         });
30735
30736         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30737     },
30738     
30739     setValue : function(v, format)
30740     {
30741         this.inputEl.dom.value = v;
30742         
30743         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30744         
30745         var d = Date.parseDate(v, f);
30746         
30747         if(!d){
30748             this.validate();
30749             return;
30750         }
30751         
30752         this.setDay(d.format(this.dayFormat));
30753         this.setMonth(d.format(this.monthFormat));
30754         this.setYear(d.format(this.yearFormat));
30755         
30756         this.validate();
30757         
30758         return;
30759     },
30760     
30761     setDay : function(v)
30762     {
30763         this.dayField.setValue(v);
30764         this.inputEl.dom.value = this.getValue();
30765         this.validate();
30766         return;
30767     },
30768     
30769     setMonth : function(v)
30770     {
30771         this.monthField.setValue(v, true);
30772         this.inputEl.dom.value = this.getValue();
30773         this.validate();
30774         return;
30775     },
30776     
30777     setYear : function(v)
30778     {
30779         this.yearField.setValue(v);
30780         this.inputEl.dom.value = this.getValue();
30781         this.validate();
30782         return;
30783     },
30784     
30785     getDay : function()
30786     {
30787         return this.dayField.getValue();
30788     },
30789     
30790     getMonth : function()
30791     {
30792         return this.monthField.getValue();
30793     },
30794     
30795     getYear : function()
30796     {
30797         return this.yearField.getValue();
30798     },
30799     
30800     getValue : function()
30801     {
30802         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30803         
30804         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30805         
30806         return date;
30807     },
30808     
30809     reset : function()
30810     {
30811         this.setDay('');
30812         this.setMonth('');
30813         this.setYear('');
30814         this.inputEl.dom.value = '';
30815         this.validate();
30816         return;
30817     },
30818     
30819     validate : function()
30820     {
30821         var d = this.dayField.validate();
30822         var m = this.monthField.validate();
30823         var y = this.yearField.validate();
30824         
30825         var valid = true;
30826         
30827         if(
30828                 (!this.dayAllowBlank && !d) ||
30829                 (!this.monthAllowBlank && !m) ||
30830                 (!this.yearAllowBlank && !y)
30831         ){
30832             valid = false;
30833         }
30834         
30835         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30836             return valid;
30837         }
30838         
30839         if(valid){
30840             this.markValid();
30841             return valid;
30842         }
30843         
30844         this.markInvalid();
30845         
30846         return valid;
30847     },
30848     
30849     markValid : function()
30850     {
30851         
30852         var label = this.el.select('label', true).first();
30853         var icon = this.el.select('i.fa-star', true).first();
30854
30855         if(label && icon){
30856             icon.remove();
30857         }
30858         
30859         this.fireEvent('valid', this);
30860     },
30861     
30862      /**
30863      * Mark this field as invalid
30864      * @param {String} msg The validation message
30865      */
30866     markInvalid : function(msg)
30867     {
30868         
30869         var label = this.el.select('label', true).first();
30870         var icon = this.el.select('i.fa-star', true).first();
30871
30872         if(label && !icon){
30873             this.el.select('.roo-date-split-field-label', true).createChild({
30874                 tag : 'i',
30875                 cls : 'text-danger fa fa-lg fa-star',
30876                 tooltip : 'This field is required',
30877                 style : 'margin-right:5px;'
30878             }, label, true);
30879         }
30880         
30881         this.fireEvent('invalid', this, msg);
30882     },
30883     
30884     clearInvalid : function()
30885     {
30886         var label = this.el.select('label', true).first();
30887         var icon = this.el.select('i.fa-star', true).first();
30888
30889         if(label && icon){
30890             icon.remove();
30891         }
30892         
30893         this.fireEvent('valid', this);
30894     },
30895     
30896     getName: function()
30897     {
30898         return this.name;
30899     }
30900     
30901 });
30902
30903  /**
30904  *
30905  * This is based on 
30906  * http://masonry.desandro.com
30907  *
30908  * The idea is to render all the bricks based on vertical width...
30909  *
30910  * The original code extends 'outlayer' - we might need to use that....
30911  * 
30912  */
30913
30914
30915 /**
30916  * @class Roo.bootstrap.LayoutMasonry
30917  * @extends Roo.bootstrap.Component
30918  * Bootstrap Layout Masonry class
30919  * 
30920  * @constructor
30921  * Create a new Element
30922  * @param {Object} config The config object
30923  */
30924
30925 Roo.bootstrap.LayoutMasonry = function(config){
30926     
30927     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30928     
30929     this.bricks = [];
30930     
30931     Roo.bootstrap.LayoutMasonry.register(this);
30932     
30933     this.addEvents({
30934         // raw events
30935         /**
30936          * @event layout
30937          * Fire after layout the items
30938          * @param {Roo.bootstrap.LayoutMasonry} this
30939          * @param {Roo.EventObject} e
30940          */
30941         "layout" : true
30942     });
30943     
30944 };
30945
30946 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30947     
30948     /**
30949      * @cfg {Boolean} isLayoutInstant = no animation?
30950      */   
30951     isLayoutInstant : false, // needed?
30952    
30953     /**
30954      * @cfg {Number} boxWidth  width of the columns
30955      */   
30956     boxWidth : 450,
30957     
30958       /**
30959      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30960      */   
30961     boxHeight : 0,
30962     
30963     /**
30964      * @cfg {Number} padWidth padding below box..
30965      */   
30966     padWidth : 10, 
30967     
30968     /**
30969      * @cfg {Number} gutter gutter width..
30970      */   
30971     gutter : 10,
30972     
30973      /**
30974      * @cfg {Number} maxCols maximum number of columns
30975      */   
30976     
30977     maxCols: 0,
30978     
30979     /**
30980      * @cfg {Boolean} isAutoInitial defalut true
30981      */   
30982     isAutoInitial : true, 
30983     
30984     containerWidth: 0,
30985     
30986     /**
30987      * @cfg {Boolean} isHorizontal defalut false
30988      */   
30989     isHorizontal : false, 
30990
30991     currentSize : null,
30992     
30993     tag: 'div',
30994     
30995     cls: '',
30996     
30997     bricks: null, //CompositeElement
30998     
30999     cols : 1,
31000     
31001     _isLayoutInited : false,
31002     
31003 //    isAlternative : false, // only use for vertical layout...
31004     
31005     /**
31006      * @cfg {Number} alternativePadWidth padding below box..
31007      */   
31008     alternativePadWidth : 50,
31009     
31010     selectedBrick : [],
31011     
31012     getAutoCreate : function(){
31013         
31014         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31015         
31016         var cfg = {
31017             tag: this.tag,
31018             cls: 'blog-masonary-wrapper ' + this.cls,
31019             cn : {
31020                 cls : 'mas-boxes masonary'
31021             }
31022         };
31023         
31024         return cfg;
31025     },
31026     
31027     getChildContainer: function( )
31028     {
31029         if (this.boxesEl) {
31030             return this.boxesEl;
31031         }
31032         
31033         this.boxesEl = this.el.select('.mas-boxes').first();
31034         
31035         return this.boxesEl;
31036     },
31037     
31038     
31039     initEvents : function()
31040     {
31041         var _this = this;
31042         
31043         if(this.isAutoInitial){
31044             Roo.log('hook children rendered');
31045             this.on('childrenrendered', function() {
31046                 Roo.log('children rendered');
31047                 _this.initial();
31048             } ,this);
31049         }
31050     },
31051     
31052     initial : function()
31053     {
31054         this.selectedBrick = [];
31055         
31056         this.currentSize = this.el.getBox(true);
31057         
31058         Roo.EventManager.onWindowResize(this.resize, this); 
31059
31060         if(!this.isAutoInitial){
31061             this.layout();
31062             return;
31063         }
31064         
31065         this.layout();
31066         
31067         return;
31068         //this.layout.defer(500,this);
31069         
31070     },
31071     
31072     resize : function()
31073     {
31074         var cs = this.el.getBox(true);
31075         
31076         if (
31077                 this.currentSize.width == cs.width && 
31078                 this.currentSize.x == cs.x && 
31079                 this.currentSize.height == cs.height && 
31080                 this.currentSize.y == cs.y 
31081         ) {
31082             Roo.log("no change in with or X or Y");
31083             return;
31084         }
31085         
31086         this.currentSize = cs;
31087         
31088         this.layout();
31089         
31090     },
31091     
31092     layout : function()
31093     {   
31094         this._resetLayout();
31095         
31096         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31097         
31098         this.layoutItems( isInstant );
31099       
31100         this._isLayoutInited = true;
31101         
31102         this.fireEvent('layout', this);
31103         
31104     },
31105     
31106     _resetLayout : function()
31107     {
31108         if(this.isHorizontal){
31109             this.horizontalMeasureColumns();
31110             return;
31111         }
31112         
31113         this.verticalMeasureColumns();
31114         
31115     },
31116     
31117     verticalMeasureColumns : function()
31118     {
31119         this.getContainerWidth();
31120         
31121 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31122 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31123 //            return;
31124 //        }
31125         
31126         var boxWidth = this.boxWidth + this.padWidth;
31127         
31128         if(this.containerWidth < this.boxWidth){
31129             boxWidth = this.containerWidth
31130         }
31131         
31132         var containerWidth = this.containerWidth;
31133         
31134         var cols = Math.floor(containerWidth / boxWidth);
31135         
31136         this.cols = Math.max( cols, 1 );
31137         
31138         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31139         
31140         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31141         
31142         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31143         
31144         this.colWidth = boxWidth + avail - this.padWidth;
31145         
31146         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31147         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31148     },
31149     
31150     horizontalMeasureColumns : function()
31151     {
31152         this.getContainerWidth();
31153         
31154         var boxWidth = this.boxWidth;
31155         
31156         if(this.containerWidth < boxWidth){
31157             boxWidth = this.containerWidth;
31158         }
31159         
31160         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31161         
31162         this.el.setHeight(boxWidth);
31163         
31164     },
31165     
31166     getContainerWidth : function()
31167     {
31168         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31169     },
31170     
31171     layoutItems : function( isInstant )
31172     {
31173         Roo.log(this.bricks);
31174         
31175         var items = Roo.apply([], this.bricks);
31176         
31177         if(this.isHorizontal){
31178             this._horizontalLayoutItems( items , isInstant );
31179             return;
31180         }
31181         
31182 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31183 //            this._verticalAlternativeLayoutItems( items , isInstant );
31184 //            return;
31185 //        }
31186         
31187         this._verticalLayoutItems( items , isInstant );
31188         
31189     },
31190     
31191     _verticalLayoutItems : function ( items , isInstant)
31192     {
31193         if ( !items || !items.length ) {
31194             return;
31195         }
31196         
31197         var standard = [
31198             ['xs', 'xs', 'xs', 'tall'],
31199             ['xs', 'xs', 'tall'],
31200             ['xs', 'xs', 'sm'],
31201             ['xs', 'xs', 'xs'],
31202             ['xs', 'tall'],
31203             ['xs', 'sm'],
31204             ['xs', 'xs'],
31205             ['xs'],
31206             
31207             ['sm', 'xs', 'xs'],
31208             ['sm', 'xs'],
31209             ['sm'],
31210             
31211             ['tall', 'xs', 'xs', 'xs'],
31212             ['tall', 'xs', 'xs'],
31213             ['tall', 'xs'],
31214             ['tall']
31215             
31216         ];
31217         
31218         var queue = [];
31219         
31220         var boxes = [];
31221         
31222         var box = [];
31223         
31224         Roo.each(items, function(item, k){
31225             
31226             switch (item.size) {
31227                 // these layouts take up a full box,
31228                 case 'md' :
31229                 case 'md-left' :
31230                 case 'md-right' :
31231                 case 'wide' :
31232                     
31233                     if(box.length){
31234                         boxes.push(box);
31235                         box = [];
31236                     }
31237                     
31238                     boxes.push([item]);
31239                     
31240                     break;
31241                     
31242                 case 'xs' :
31243                 case 'sm' :
31244                 case 'tall' :
31245                     
31246                     box.push(item);
31247                     
31248                     break;
31249                 default :
31250                     break;
31251                     
31252             }
31253             
31254         }, this);
31255         
31256         if(box.length){
31257             boxes.push(box);
31258             box = [];
31259         }
31260         
31261         var filterPattern = function(box, length)
31262         {
31263             if(!box.length){
31264                 return;
31265             }
31266             
31267             var match = false;
31268             
31269             var pattern = box.slice(0, length);
31270             
31271             var format = [];
31272             
31273             Roo.each(pattern, function(i){
31274                 format.push(i.size);
31275             }, this);
31276             
31277             Roo.each(standard, function(s){
31278                 
31279                 if(String(s) != String(format)){
31280                     return;
31281                 }
31282                 
31283                 match = true;
31284                 return false;
31285                 
31286             }, this);
31287             
31288             if(!match && length == 1){
31289                 return;
31290             }
31291             
31292             if(!match){
31293                 filterPattern(box, length - 1);
31294                 return;
31295             }
31296                 
31297             queue.push(pattern);
31298
31299             box = box.slice(length, box.length);
31300
31301             filterPattern(box, 4);
31302
31303             return;
31304             
31305         }
31306         
31307         Roo.each(boxes, function(box, k){
31308             
31309             if(!box.length){
31310                 return;
31311             }
31312             
31313             if(box.length == 1){
31314                 queue.push(box);
31315                 return;
31316             }
31317             
31318             filterPattern(box, 4);
31319             
31320         }, this);
31321         
31322         this._processVerticalLayoutQueue( queue, isInstant );
31323         
31324     },
31325     
31326 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31327 //    {
31328 //        if ( !items || !items.length ) {
31329 //            return;
31330 //        }
31331 //
31332 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31333 //        
31334 //    },
31335     
31336     _horizontalLayoutItems : function ( items , isInstant)
31337     {
31338         if ( !items || !items.length || items.length < 3) {
31339             return;
31340         }
31341         
31342         items.reverse();
31343         
31344         var eItems = items.slice(0, 3);
31345         
31346         items = items.slice(3, items.length);
31347         
31348         var standard = [
31349             ['xs', 'xs', 'xs', 'wide'],
31350             ['xs', 'xs', 'wide'],
31351             ['xs', 'xs', 'sm'],
31352             ['xs', 'xs', 'xs'],
31353             ['xs', 'wide'],
31354             ['xs', 'sm'],
31355             ['xs', 'xs'],
31356             ['xs'],
31357             
31358             ['sm', 'xs', 'xs'],
31359             ['sm', 'xs'],
31360             ['sm'],
31361             
31362             ['wide', 'xs', 'xs', 'xs'],
31363             ['wide', 'xs', 'xs'],
31364             ['wide', 'xs'],
31365             ['wide'],
31366             
31367             ['wide-thin']
31368         ];
31369         
31370         var queue = [];
31371         
31372         var boxes = [];
31373         
31374         var box = [];
31375         
31376         Roo.each(items, function(item, k){
31377             
31378             switch (item.size) {
31379                 case 'md' :
31380                 case 'md-left' :
31381                 case 'md-right' :
31382                 case 'tall' :
31383                     
31384                     if(box.length){
31385                         boxes.push(box);
31386                         box = [];
31387                     }
31388                     
31389                     boxes.push([item]);
31390                     
31391                     break;
31392                     
31393                 case 'xs' :
31394                 case 'sm' :
31395                 case 'wide' :
31396                 case 'wide-thin' :
31397                     
31398                     box.push(item);
31399                     
31400                     break;
31401                 default :
31402                     break;
31403                     
31404             }
31405             
31406         }, this);
31407         
31408         if(box.length){
31409             boxes.push(box);
31410             box = [];
31411         }
31412         
31413         var filterPattern = function(box, length)
31414         {
31415             if(!box.length){
31416                 return;
31417             }
31418             
31419             var match = false;
31420             
31421             var pattern = box.slice(0, length);
31422             
31423             var format = [];
31424             
31425             Roo.each(pattern, function(i){
31426                 format.push(i.size);
31427             }, this);
31428             
31429             Roo.each(standard, function(s){
31430                 
31431                 if(String(s) != String(format)){
31432                     return;
31433                 }
31434                 
31435                 match = true;
31436                 return false;
31437                 
31438             }, this);
31439             
31440             if(!match && length == 1){
31441                 return;
31442             }
31443             
31444             if(!match){
31445                 filterPattern(box, length - 1);
31446                 return;
31447             }
31448                 
31449             queue.push(pattern);
31450
31451             box = box.slice(length, box.length);
31452
31453             filterPattern(box, 4);
31454
31455             return;
31456             
31457         }
31458         
31459         Roo.each(boxes, function(box, k){
31460             
31461             if(!box.length){
31462                 return;
31463             }
31464             
31465             if(box.length == 1){
31466                 queue.push(box);
31467                 return;
31468             }
31469             
31470             filterPattern(box, 4);
31471             
31472         }, this);
31473         
31474         
31475         var prune = [];
31476         
31477         var pos = this.el.getBox(true);
31478         
31479         var minX = pos.x;
31480         
31481         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31482         
31483         var hit_end = false;
31484         
31485         Roo.each(queue, function(box){
31486             
31487             if(hit_end){
31488                 
31489                 Roo.each(box, function(b){
31490                 
31491                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31492                     b.el.hide();
31493
31494                 }, this);
31495
31496                 return;
31497             }
31498             
31499             var mx = 0;
31500             
31501             Roo.each(box, function(b){
31502                 
31503                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31504                 b.el.show();
31505
31506                 mx = Math.max(mx, b.x);
31507                 
31508             }, this);
31509             
31510             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31511             
31512             if(maxX < minX){
31513                 
31514                 Roo.each(box, function(b){
31515                 
31516                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31517                     b.el.hide();
31518                     
31519                 }, this);
31520                 
31521                 hit_end = true;
31522                 
31523                 return;
31524             }
31525             
31526             prune.push(box);
31527             
31528         }, this);
31529         
31530         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31531     },
31532     
31533     /** Sets position of item in DOM
31534     * @param {Element} item
31535     * @param {Number} x - horizontal position
31536     * @param {Number} y - vertical position
31537     * @param {Boolean} isInstant - disables transitions
31538     */
31539     _processVerticalLayoutQueue : function( queue, isInstant )
31540     {
31541         var pos = this.el.getBox(true);
31542         var x = pos.x;
31543         var y = pos.y;
31544         var maxY = [];
31545         
31546         for (var i = 0; i < this.cols; i++){
31547             maxY[i] = pos.y;
31548         }
31549         
31550         Roo.each(queue, function(box, k){
31551             
31552             var col = k % this.cols;
31553             
31554             Roo.each(box, function(b,kk){
31555                 
31556                 b.el.position('absolute');
31557                 
31558                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31559                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31560                 
31561                 if(b.size == 'md-left' || b.size == 'md-right'){
31562                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31563                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31564                 }
31565                 
31566                 b.el.setWidth(width);
31567                 b.el.setHeight(height);
31568                 // iframe?
31569                 b.el.select('iframe',true).setSize(width,height);
31570                 
31571             }, this);
31572             
31573             for (var i = 0; i < this.cols; i++){
31574                 
31575                 if(maxY[i] < maxY[col]){
31576                     col = i;
31577                     continue;
31578                 }
31579                 
31580                 col = Math.min(col, i);
31581                 
31582             }
31583             
31584             x = pos.x + col * (this.colWidth + this.padWidth);
31585             
31586             y = maxY[col];
31587             
31588             var positions = [];
31589             
31590             switch (box.length){
31591                 case 1 :
31592                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31593                     break;
31594                 case 2 :
31595                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31596                     break;
31597                 case 3 :
31598                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31599                     break;
31600                 case 4 :
31601                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31602                     break;
31603                 default :
31604                     break;
31605             }
31606             
31607             Roo.each(box, function(b,kk){
31608                 
31609                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31610                 
31611                 var sz = b.el.getSize();
31612                 
31613                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31614                 
31615             }, this);
31616             
31617         }, this);
31618         
31619         var mY = 0;
31620         
31621         for (var i = 0; i < this.cols; i++){
31622             mY = Math.max(mY, maxY[i]);
31623         }
31624         
31625         this.el.setHeight(mY - pos.y);
31626         
31627     },
31628     
31629 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31630 //    {
31631 //        var pos = this.el.getBox(true);
31632 //        var x = pos.x;
31633 //        var y = pos.y;
31634 //        var maxX = pos.right;
31635 //        
31636 //        var maxHeight = 0;
31637 //        
31638 //        Roo.each(items, function(item, k){
31639 //            
31640 //            var c = k % 2;
31641 //            
31642 //            item.el.position('absolute');
31643 //                
31644 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31645 //
31646 //            item.el.setWidth(width);
31647 //
31648 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31649 //
31650 //            item.el.setHeight(height);
31651 //            
31652 //            if(c == 0){
31653 //                item.el.setXY([x, y], isInstant ? false : true);
31654 //            } else {
31655 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31656 //            }
31657 //            
31658 //            y = y + height + this.alternativePadWidth;
31659 //            
31660 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31661 //            
31662 //        }, this);
31663 //        
31664 //        this.el.setHeight(maxHeight);
31665 //        
31666 //    },
31667     
31668     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31669     {
31670         var pos = this.el.getBox(true);
31671         
31672         var minX = pos.x;
31673         var minY = pos.y;
31674         
31675         var maxX = pos.right;
31676         
31677         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31678         
31679         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31680         
31681         Roo.each(queue, function(box, k){
31682             
31683             Roo.each(box, function(b, kk){
31684                 
31685                 b.el.position('absolute');
31686                 
31687                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31688                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31689                 
31690                 if(b.size == 'md-left' || b.size == 'md-right'){
31691                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31692                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31693                 }
31694                 
31695                 b.el.setWidth(width);
31696                 b.el.setHeight(height);
31697                 
31698             }, this);
31699             
31700             if(!box.length){
31701                 return;
31702             }
31703             
31704             var positions = [];
31705             
31706             switch (box.length){
31707                 case 1 :
31708                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31709                     break;
31710                 case 2 :
31711                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31712                     break;
31713                 case 3 :
31714                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31715                     break;
31716                 case 4 :
31717                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31718                     break;
31719                 default :
31720                     break;
31721             }
31722             
31723             Roo.each(box, function(b,kk){
31724                 
31725                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31726                 
31727                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31728                 
31729             }, this);
31730             
31731         }, this);
31732         
31733     },
31734     
31735     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31736     {
31737         Roo.each(eItems, function(b,k){
31738             
31739             b.size = (k == 0) ? 'sm' : 'xs';
31740             b.x = (k == 0) ? 2 : 1;
31741             b.y = (k == 0) ? 2 : 1;
31742             
31743             b.el.position('absolute');
31744             
31745             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31746                 
31747             b.el.setWidth(width);
31748             
31749             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31750             
31751             b.el.setHeight(height);
31752             
31753         }, this);
31754
31755         var positions = [];
31756         
31757         positions.push({
31758             x : maxX - this.unitWidth * 2 - this.gutter,
31759             y : minY
31760         });
31761         
31762         positions.push({
31763             x : maxX - this.unitWidth,
31764             y : minY + (this.unitWidth + this.gutter) * 2
31765         });
31766         
31767         positions.push({
31768             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31769             y : minY
31770         });
31771         
31772         Roo.each(eItems, function(b,k){
31773             
31774             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31775
31776         }, this);
31777         
31778     },
31779     
31780     getVerticalOneBoxColPositions : function(x, y, box)
31781     {
31782         var pos = [];
31783         
31784         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31785         
31786         if(box[0].size == 'md-left'){
31787             rand = 0;
31788         }
31789         
31790         if(box[0].size == 'md-right'){
31791             rand = 1;
31792         }
31793         
31794         pos.push({
31795             x : x + (this.unitWidth + this.gutter) * rand,
31796             y : y
31797         });
31798         
31799         return pos;
31800     },
31801     
31802     getVerticalTwoBoxColPositions : function(x, y, box)
31803     {
31804         var pos = [];
31805         
31806         if(box[0].size == 'xs'){
31807             
31808             pos.push({
31809                 x : x,
31810                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31811             });
31812
31813             pos.push({
31814                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31815                 y : y
31816             });
31817             
31818             return pos;
31819             
31820         }
31821         
31822         pos.push({
31823             x : x,
31824             y : y
31825         });
31826
31827         pos.push({
31828             x : x + (this.unitWidth + this.gutter) * 2,
31829             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31830         });
31831         
31832         return pos;
31833         
31834     },
31835     
31836     getVerticalThreeBoxColPositions : function(x, y, box)
31837     {
31838         var pos = [];
31839         
31840         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31841             
31842             pos.push({
31843                 x : x,
31844                 y : y
31845             });
31846
31847             pos.push({
31848                 x : x + (this.unitWidth + this.gutter) * 1,
31849                 y : y
31850             });
31851             
31852             pos.push({
31853                 x : x + (this.unitWidth + this.gutter) * 2,
31854                 y : y
31855             });
31856             
31857             return pos;
31858             
31859         }
31860         
31861         if(box[0].size == 'xs' && box[1].size == 'xs'){
31862             
31863             pos.push({
31864                 x : x,
31865                 y : y
31866             });
31867
31868             pos.push({
31869                 x : x,
31870                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31871             });
31872             
31873             pos.push({
31874                 x : x + (this.unitWidth + this.gutter) * 1,
31875                 y : y
31876             });
31877             
31878             return pos;
31879             
31880         }
31881         
31882         pos.push({
31883             x : x,
31884             y : y
31885         });
31886
31887         pos.push({
31888             x : x + (this.unitWidth + this.gutter) * 2,
31889             y : y
31890         });
31891
31892         pos.push({
31893             x : x + (this.unitWidth + this.gutter) * 2,
31894             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31895         });
31896             
31897         return pos;
31898         
31899     },
31900     
31901     getVerticalFourBoxColPositions : function(x, y, box)
31902     {
31903         var pos = [];
31904         
31905         if(box[0].size == 'xs'){
31906             
31907             pos.push({
31908                 x : x,
31909                 y : y
31910             });
31911
31912             pos.push({
31913                 x : x,
31914                 y : y + (this.unitHeight + this.gutter) * 1
31915             });
31916             
31917             pos.push({
31918                 x : x,
31919                 y : y + (this.unitHeight + this.gutter) * 2
31920             });
31921             
31922             pos.push({
31923                 x : x + (this.unitWidth + this.gutter) * 1,
31924                 y : y
31925             });
31926             
31927             return pos;
31928             
31929         }
31930         
31931         pos.push({
31932             x : x,
31933             y : y
31934         });
31935
31936         pos.push({
31937             x : x + (this.unitWidth + this.gutter) * 2,
31938             y : y
31939         });
31940
31941         pos.push({
31942             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31943             y : y + (this.unitHeight + this.gutter) * 1
31944         });
31945
31946         pos.push({
31947             x : x + (this.unitWidth + this.gutter) * 2,
31948             y : y + (this.unitWidth + this.gutter) * 2
31949         });
31950
31951         return pos;
31952         
31953     },
31954     
31955     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31956     {
31957         var pos = [];
31958         
31959         if(box[0].size == 'md-left'){
31960             pos.push({
31961                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31962                 y : minY
31963             });
31964             
31965             return pos;
31966         }
31967         
31968         if(box[0].size == 'md-right'){
31969             pos.push({
31970                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31971                 y : minY + (this.unitWidth + this.gutter) * 1
31972             });
31973             
31974             return pos;
31975         }
31976         
31977         var rand = Math.floor(Math.random() * (4 - box[0].y));
31978         
31979         pos.push({
31980             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31981             y : minY + (this.unitWidth + this.gutter) * rand
31982         });
31983         
31984         return pos;
31985         
31986     },
31987     
31988     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31989     {
31990         var pos = [];
31991         
31992         if(box[0].size == 'xs'){
31993             
31994             pos.push({
31995                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31996                 y : minY
31997             });
31998
31999             pos.push({
32000                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32001                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32002             });
32003             
32004             return pos;
32005             
32006         }
32007         
32008         pos.push({
32009             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32010             y : minY
32011         });
32012
32013         pos.push({
32014             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32015             y : minY + (this.unitWidth + this.gutter) * 2
32016         });
32017         
32018         return pos;
32019         
32020     },
32021     
32022     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32023     {
32024         var pos = [];
32025         
32026         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32027             
32028             pos.push({
32029                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32030                 y : minY
32031             });
32032
32033             pos.push({
32034                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32035                 y : minY + (this.unitWidth + this.gutter) * 1
32036             });
32037             
32038             pos.push({
32039                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32040                 y : minY + (this.unitWidth + this.gutter) * 2
32041             });
32042             
32043             return pos;
32044             
32045         }
32046         
32047         if(box[0].size == 'xs' && box[1].size == 'xs'){
32048             
32049             pos.push({
32050                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32051                 y : minY
32052             });
32053
32054             pos.push({
32055                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32056                 y : minY
32057             });
32058             
32059             pos.push({
32060                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32061                 y : minY + (this.unitWidth + this.gutter) * 1
32062             });
32063             
32064             return pos;
32065             
32066         }
32067         
32068         pos.push({
32069             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32070             y : minY
32071         });
32072
32073         pos.push({
32074             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32075             y : minY + (this.unitWidth + this.gutter) * 2
32076         });
32077
32078         pos.push({
32079             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32080             y : minY + (this.unitWidth + this.gutter) * 2
32081         });
32082             
32083         return pos;
32084         
32085     },
32086     
32087     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32088     {
32089         var pos = [];
32090         
32091         if(box[0].size == 'xs'){
32092             
32093             pos.push({
32094                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32095                 y : minY
32096             });
32097
32098             pos.push({
32099                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32100                 y : minY
32101             });
32102             
32103             pos.push({
32104                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32105                 y : minY
32106             });
32107             
32108             pos.push({
32109                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32110                 y : minY + (this.unitWidth + this.gutter) * 1
32111             });
32112             
32113             return pos;
32114             
32115         }
32116         
32117         pos.push({
32118             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32119             y : minY
32120         });
32121         
32122         pos.push({
32123             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32124             y : minY + (this.unitWidth + this.gutter) * 2
32125         });
32126         
32127         pos.push({
32128             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32129             y : minY + (this.unitWidth + this.gutter) * 2
32130         });
32131         
32132         pos.push({
32133             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32134             y : minY + (this.unitWidth + this.gutter) * 2
32135         });
32136
32137         return pos;
32138         
32139     },
32140     
32141     /**
32142     * remove a Masonry Brick
32143     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32144     */
32145     removeBrick : function(brick_id)
32146     {
32147         if (!brick_id) {
32148             return;
32149         }
32150         
32151         for (var i = 0; i<this.bricks.length; i++) {
32152             if (this.bricks[i].id == brick_id) {
32153                 this.bricks.splice(i,1);
32154                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32155                 this.initial();
32156             }
32157         }
32158     },
32159     
32160     /**
32161     * adds a Masonry Brick
32162     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32163     */
32164     addBrick : function(cfg)
32165     {
32166         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32167         //this.register(cn);
32168         cn.parentId = this.id;
32169         cn.render(this.el);
32170         return cn;
32171     },
32172     
32173     /**
32174     * register a Masonry Brick
32175     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32176     */
32177     
32178     register : function(brick)
32179     {
32180         this.bricks.push(brick);
32181         brick.masonryId = this.id;
32182     },
32183     
32184     /**
32185     * clear all the Masonry Brick
32186     */
32187     clearAll : function()
32188     {
32189         this.bricks = [];
32190         //this.getChildContainer().dom.innerHTML = "";
32191         this.el.dom.innerHTML = '';
32192     },
32193     
32194     getSelected : function()
32195     {
32196         if (!this.selectedBrick) {
32197             return false;
32198         }
32199         
32200         return this.selectedBrick;
32201     }
32202 });
32203
32204 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32205     
32206     groups: {},
32207      /**
32208     * register a Masonry Layout
32209     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32210     */
32211     
32212     register : function(layout)
32213     {
32214         this.groups[layout.id] = layout;
32215     },
32216     /**
32217     * fetch a  Masonry Layout based on the masonry layout ID
32218     * @param {string} the masonry layout to add
32219     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32220     */
32221     
32222     get: function(layout_id) {
32223         if (typeof(this.groups[layout_id]) == 'undefined') {
32224             return false;
32225         }
32226         return this.groups[layout_id] ;
32227     }
32228     
32229     
32230     
32231 });
32232
32233  
32234
32235  /**
32236  *
32237  * This is based on 
32238  * http://masonry.desandro.com
32239  *
32240  * The idea is to render all the bricks based on vertical width...
32241  *
32242  * The original code extends 'outlayer' - we might need to use that....
32243  * 
32244  */
32245
32246
32247 /**
32248  * @class Roo.bootstrap.LayoutMasonryAuto
32249  * @extends Roo.bootstrap.Component
32250  * Bootstrap Layout Masonry class
32251  * 
32252  * @constructor
32253  * Create a new Element
32254  * @param {Object} config The config object
32255  */
32256
32257 Roo.bootstrap.LayoutMasonryAuto = function(config){
32258     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32259 };
32260
32261 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32262     
32263       /**
32264      * @cfg {Boolean} isFitWidth  - resize the width..
32265      */   
32266     isFitWidth : false,  // options..
32267     /**
32268      * @cfg {Boolean} isOriginLeft = left align?
32269      */   
32270     isOriginLeft : true,
32271     /**
32272      * @cfg {Boolean} isOriginTop = top align?
32273      */   
32274     isOriginTop : false,
32275     /**
32276      * @cfg {Boolean} isLayoutInstant = no animation?
32277      */   
32278     isLayoutInstant : false, // needed?
32279     /**
32280      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32281      */   
32282     isResizingContainer : true,
32283     /**
32284      * @cfg {Number} columnWidth  width of the columns 
32285      */   
32286     
32287     columnWidth : 0,
32288     
32289     /**
32290      * @cfg {Number} maxCols maximum number of columns
32291      */   
32292     
32293     maxCols: 0,
32294     /**
32295      * @cfg {Number} padHeight padding below box..
32296      */   
32297     
32298     padHeight : 10, 
32299     
32300     /**
32301      * @cfg {Boolean} isAutoInitial defalut true
32302      */   
32303     
32304     isAutoInitial : true, 
32305     
32306     // private?
32307     gutter : 0,
32308     
32309     containerWidth: 0,
32310     initialColumnWidth : 0,
32311     currentSize : null,
32312     
32313     colYs : null, // array.
32314     maxY : 0,
32315     padWidth: 10,
32316     
32317     
32318     tag: 'div',
32319     cls: '',
32320     bricks: null, //CompositeElement
32321     cols : 0, // array?
32322     // element : null, // wrapped now this.el
32323     _isLayoutInited : null, 
32324     
32325     
32326     getAutoCreate : function(){
32327         
32328         var cfg = {
32329             tag: this.tag,
32330             cls: 'blog-masonary-wrapper ' + this.cls,
32331             cn : {
32332                 cls : 'mas-boxes masonary'
32333             }
32334         };
32335         
32336         return cfg;
32337     },
32338     
32339     getChildContainer: function( )
32340     {
32341         if (this.boxesEl) {
32342             return this.boxesEl;
32343         }
32344         
32345         this.boxesEl = this.el.select('.mas-boxes').first();
32346         
32347         return this.boxesEl;
32348     },
32349     
32350     
32351     initEvents : function()
32352     {
32353         var _this = this;
32354         
32355         if(this.isAutoInitial){
32356             Roo.log('hook children rendered');
32357             this.on('childrenrendered', function() {
32358                 Roo.log('children rendered');
32359                 _this.initial();
32360             } ,this);
32361         }
32362         
32363     },
32364     
32365     initial : function()
32366     {
32367         this.reloadItems();
32368
32369         this.currentSize = this.el.getBox(true);
32370
32371         /// was window resize... - let's see if this works..
32372         Roo.EventManager.onWindowResize(this.resize, this); 
32373
32374         if(!this.isAutoInitial){
32375             this.layout();
32376             return;
32377         }
32378         
32379         this.layout.defer(500,this);
32380     },
32381     
32382     reloadItems: function()
32383     {
32384         this.bricks = this.el.select('.masonry-brick', true);
32385         
32386         this.bricks.each(function(b) {
32387             //Roo.log(b.getSize());
32388             if (!b.attr('originalwidth')) {
32389                 b.attr('originalwidth',  b.getSize().width);
32390             }
32391             
32392         });
32393         
32394         Roo.log(this.bricks.elements.length);
32395     },
32396     
32397     resize : function()
32398     {
32399         Roo.log('resize');
32400         var cs = this.el.getBox(true);
32401         
32402         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32403             Roo.log("no change in with or X");
32404             return;
32405         }
32406         this.currentSize = cs;
32407         this.layout();
32408     },
32409     
32410     layout : function()
32411     {
32412          Roo.log('layout');
32413         this._resetLayout();
32414         //this._manageStamps();
32415       
32416         // don't animate first layout
32417         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32418         this.layoutItems( isInstant );
32419       
32420         // flag for initalized
32421         this._isLayoutInited = true;
32422     },
32423     
32424     layoutItems : function( isInstant )
32425     {
32426         //var items = this._getItemsForLayout( this.items );
32427         // original code supports filtering layout items.. we just ignore it..
32428         
32429         this._layoutItems( this.bricks , isInstant );
32430       
32431         this._postLayout();
32432     },
32433     _layoutItems : function ( items , isInstant)
32434     {
32435        //this.fireEvent( 'layout', this, items );
32436     
32437
32438         if ( !items || !items.elements.length ) {
32439           // no items, emit event with empty array
32440             return;
32441         }
32442
32443         var queue = [];
32444         items.each(function(item) {
32445             Roo.log("layout item");
32446             Roo.log(item);
32447             // get x/y object from method
32448             var position = this._getItemLayoutPosition( item );
32449             // enqueue
32450             position.item = item;
32451             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32452             queue.push( position );
32453         }, this);
32454       
32455         this._processLayoutQueue( queue );
32456     },
32457     /** Sets position of item in DOM
32458     * @param {Element} item
32459     * @param {Number} x - horizontal position
32460     * @param {Number} y - vertical position
32461     * @param {Boolean} isInstant - disables transitions
32462     */
32463     _processLayoutQueue : function( queue )
32464     {
32465         for ( var i=0, len = queue.length; i < len; i++ ) {
32466             var obj = queue[i];
32467             obj.item.position('absolute');
32468             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32469         }
32470     },
32471       
32472     
32473     /**
32474     * Any logic you want to do after each layout,
32475     * i.e. size the container
32476     */
32477     _postLayout : function()
32478     {
32479         this.resizeContainer();
32480     },
32481     
32482     resizeContainer : function()
32483     {
32484         if ( !this.isResizingContainer ) {
32485             return;
32486         }
32487         var size = this._getContainerSize();
32488         if ( size ) {
32489             this.el.setSize(size.width,size.height);
32490             this.boxesEl.setSize(size.width,size.height);
32491         }
32492     },
32493     
32494     
32495     
32496     _resetLayout : function()
32497     {
32498         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32499         this.colWidth = this.el.getWidth();
32500         //this.gutter = this.el.getWidth(); 
32501         
32502         this.measureColumns();
32503
32504         // reset column Y
32505         var i = this.cols;
32506         this.colYs = [];
32507         while (i--) {
32508             this.colYs.push( 0 );
32509         }
32510     
32511         this.maxY = 0;
32512     },
32513
32514     measureColumns : function()
32515     {
32516         this.getContainerWidth();
32517       // if columnWidth is 0, default to outerWidth of first item
32518         if ( !this.columnWidth ) {
32519             var firstItem = this.bricks.first();
32520             Roo.log(firstItem);
32521             this.columnWidth  = this.containerWidth;
32522             if (firstItem && firstItem.attr('originalwidth') ) {
32523                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32524             }
32525             // columnWidth fall back to item of first element
32526             Roo.log("set column width?");
32527                         this.initialColumnWidth = this.columnWidth  ;
32528
32529             // if first elem has no width, default to size of container
32530             
32531         }
32532         
32533         
32534         if (this.initialColumnWidth) {
32535             this.columnWidth = this.initialColumnWidth;
32536         }
32537         
32538         
32539             
32540         // column width is fixed at the top - however if container width get's smaller we should
32541         // reduce it...
32542         
32543         // this bit calcs how man columns..
32544             
32545         var columnWidth = this.columnWidth += this.gutter;
32546       
32547         // calculate columns
32548         var containerWidth = this.containerWidth + this.gutter;
32549         
32550         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32551         // fix rounding errors, typically with gutters
32552         var excess = columnWidth - containerWidth % columnWidth;
32553         
32554         
32555         // if overshoot is less than a pixel, round up, otherwise floor it
32556         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32557         cols = Math[ mathMethod ]( cols );
32558         this.cols = Math.max( cols, 1 );
32559         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32560         
32561          // padding positioning..
32562         var totalColWidth = this.cols * this.columnWidth;
32563         var padavail = this.containerWidth - totalColWidth;
32564         // so for 2 columns - we need 3 'pads'
32565         
32566         var padNeeded = (1+this.cols) * this.padWidth;
32567         
32568         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32569         
32570         this.columnWidth += padExtra
32571         //this.padWidth = Math.floor(padavail /  ( this.cols));
32572         
32573         // adjust colum width so that padding is fixed??
32574         
32575         // we have 3 columns ... total = width * 3
32576         // we have X left over... that should be used by 
32577         
32578         //if (this.expandC) {
32579             
32580         //}
32581         
32582         
32583         
32584     },
32585     
32586     getContainerWidth : function()
32587     {
32588        /* // container is parent if fit width
32589         var container = this.isFitWidth ? this.element.parentNode : this.element;
32590         // check that this.size and size are there
32591         // IE8 triggers resize on body size change, so they might not be
32592         
32593         var size = getSize( container );  //FIXME
32594         this.containerWidth = size && size.innerWidth; //FIXME
32595         */
32596          
32597         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32598         
32599     },
32600     
32601     _getItemLayoutPosition : function( item )  // what is item?
32602     {
32603         // we resize the item to our columnWidth..
32604       
32605         item.setWidth(this.columnWidth);
32606         item.autoBoxAdjust  = false;
32607         
32608         var sz = item.getSize();
32609  
32610         // how many columns does this brick span
32611         var remainder = this.containerWidth % this.columnWidth;
32612         
32613         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32614         // round if off by 1 pixel, otherwise use ceil
32615         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32616         colSpan = Math.min( colSpan, this.cols );
32617         
32618         // normally this should be '1' as we dont' currently allow multi width columns..
32619         
32620         var colGroup = this._getColGroup( colSpan );
32621         // get the minimum Y value from the columns
32622         var minimumY = Math.min.apply( Math, colGroup );
32623         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32624         
32625         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32626          
32627         // position the brick
32628         var position = {
32629             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32630             y: this.currentSize.y + minimumY + this.padHeight
32631         };
32632         
32633         Roo.log(position);
32634         // apply setHeight to necessary columns
32635         var setHeight = minimumY + sz.height + this.padHeight;
32636         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32637         
32638         var setSpan = this.cols + 1 - colGroup.length;
32639         for ( var i = 0; i < setSpan; i++ ) {
32640           this.colYs[ shortColIndex + i ] = setHeight ;
32641         }
32642       
32643         return position;
32644     },
32645     
32646     /**
32647      * @param {Number} colSpan - number of columns the element spans
32648      * @returns {Array} colGroup
32649      */
32650     _getColGroup : function( colSpan )
32651     {
32652         if ( colSpan < 2 ) {
32653           // if brick spans only one column, use all the column Ys
32654           return this.colYs;
32655         }
32656       
32657         var colGroup = [];
32658         // how many different places could this brick fit horizontally
32659         var groupCount = this.cols + 1 - colSpan;
32660         // for each group potential horizontal position
32661         for ( var i = 0; i < groupCount; i++ ) {
32662           // make an array of colY values for that one group
32663           var groupColYs = this.colYs.slice( i, i + colSpan );
32664           // and get the max value of the array
32665           colGroup[i] = Math.max.apply( Math, groupColYs );
32666         }
32667         return colGroup;
32668     },
32669     /*
32670     _manageStamp : function( stamp )
32671     {
32672         var stampSize =  stamp.getSize();
32673         var offset = stamp.getBox();
32674         // get the columns that this stamp affects
32675         var firstX = this.isOriginLeft ? offset.x : offset.right;
32676         var lastX = firstX + stampSize.width;
32677         var firstCol = Math.floor( firstX / this.columnWidth );
32678         firstCol = Math.max( 0, firstCol );
32679         
32680         var lastCol = Math.floor( lastX / this.columnWidth );
32681         // lastCol should not go over if multiple of columnWidth #425
32682         lastCol -= lastX % this.columnWidth ? 0 : 1;
32683         lastCol = Math.min( this.cols - 1, lastCol );
32684         
32685         // set colYs to bottom of the stamp
32686         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32687             stampSize.height;
32688             
32689         for ( var i = firstCol; i <= lastCol; i++ ) {
32690           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32691         }
32692     },
32693     */
32694     
32695     _getContainerSize : function()
32696     {
32697         this.maxY = Math.max.apply( Math, this.colYs );
32698         var size = {
32699             height: this.maxY
32700         };
32701       
32702         if ( this.isFitWidth ) {
32703             size.width = this._getContainerFitWidth();
32704         }
32705       
32706         return size;
32707     },
32708     
32709     _getContainerFitWidth : function()
32710     {
32711         var unusedCols = 0;
32712         // count unused columns
32713         var i = this.cols;
32714         while ( --i ) {
32715           if ( this.colYs[i] !== 0 ) {
32716             break;
32717           }
32718           unusedCols++;
32719         }
32720         // fit container to columns that have been used
32721         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32722     },
32723     
32724     needsResizeLayout : function()
32725     {
32726         var previousWidth = this.containerWidth;
32727         this.getContainerWidth();
32728         return previousWidth !== this.containerWidth;
32729     }
32730  
32731 });
32732
32733  
32734
32735  /*
32736  * - LGPL
32737  *
32738  * element
32739  * 
32740  */
32741
32742 /**
32743  * @class Roo.bootstrap.MasonryBrick
32744  * @extends Roo.bootstrap.Component
32745  * Bootstrap MasonryBrick class
32746  * 
32747  * @constructor
32748  * Create a new MasonryBrick
32749  * @param {Object} config The config object
32750  */
32751
32752 Roo.bootstrap.MasonryBrick = function(config){
32753     
32754     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32755     
32756     Roo.bootstrap.MasonryBrick.register(this);
32757     
32758     this.addEvents({
32759         // raw events
32760         /**
32761          * @event click
32762          * When a MasonryBrick is clcik
32763          * @param {Roo.bootstrap.MasonryBrick} this
32764          * @param {Roo.EventObject} e
32765          */
32766         "click" : true
32767     });
32768 };
32769
32770 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32771     
32772     /**
32773      * @cfg {String} title
32774      */   
32775     title : '',
32776     /**
32777      * @cfg {String} html
32778      */   
32779     html : '',
32780     /**
32781      * @cfg {String} bgimage
32782      */   
32783     bgimage : '',
32784     /**
32785      * @cfg {String} videourl
32786      */   
32787     videourl : '',
32788     /**
32789      * @cfg {String} cls
32790      */   
32791     cls : '',
32792     /**
32793      * @cfg {String} href
32794      */   
32795     href : '',
32796     /**
32797      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32798      */   
32799     size : 'xs',
32800     
32801     /**
32802      * @cfg {String} placetitle (center|bottom)
32803      */   
32804     placetitle : '',
32805     
32806     /**
32807      * @cfg {Boolean} isFitContainer defalut true
32808      */   
32809     isFitContainer : true, 
32810     
32811     /**
32812      * @cfg {Boolean} preventDefault defalut false
32813      */   
32814     preventDefault : false, 
32815     
32816     /**
32817      * @cfg {Boolean} inverse defalut false
32818      */   
32819     maskInverse : false, 
32820     
32821     getAutoCreate : function()
32822     {
32823         if(!this.isFitContainer){
32824             return this.getSplitAutoCreate();
32825         }
32826         
32827         var cls = 'masonry-brick masonry-brick-full';
32828         
32829         if(this.href.length){
32830             cls += ' masonry-brick-link';
32831         }
32832         
32833         if(this.bgimage.length){
32834             cls += ' masonry-brick-image';
32835         }
32836         
32837         if(this.maskInverse){
32838             cls += ' mask-inverse';
32839         }
32840         
32841         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32842             cls += ' enable-mask';
32843         }
32844         
32845         if(this.size){
32846             cls += ' masonry-' + this.size + '-brick';
32847         }
32848         
32849         if(this.placetitle.length){
32850             
32851             switch (this.placetitle) {
32852                 case 'center' :
32853                     cls += ' masonry-center-title';
32854                     break;
32855                 case 'bottom' :
32856                     cls += ' masonry-bottom-title';
32857                     break;
32858                 default:
32859                     break;
32860             }
32861             
32862         } else {
32863             if(!this.html.length && !this.bgimage.length){
32864                 cls += ' masonry-center-title';
32865             }
32866
32867             if(!this.html.length && this.bgimage.length){
32868                 cls += ' masonry-bottom-title';
32869             }
32870         }
32871         
32872         if(this.cls){
32873             cls += ' ' + this.cls;
32874         }
32875         
32876         var cfg = {
32877             tag: (this.href.length) ? 'a' : 'div',
32878             cls: cls,
32879             cn: [
32880                 {
32881                     tag: 'div',
32882                     cls: 'masonry-brick-mask'
32883                 },
32884                 {
32885                     tag: 'div',
32886                     cls: 'masonry-brick-paragraph',
32887                     cn: []
32888                 }
32889             ]
32890         };
32891         
32892         if(this.href.length){
32893             cfg.href = this.href;
32894         }
32895         
32896         var cn = cfg.cn[1].cn;
32897         
32898         if(this.title.length){
32899             cn.push({
32900                 tag: 'h4',
32901                 cls: 'masonry-brick-title',
32902                 html: this.title
32903             });
32904         }
32905         
32906         if(this.html.length){
32907             cn.push({
32908                 tag: 'p',
32909                 cls: 'masonry-brick-text',
32910                 html: this.html
32911             });
32912         }
32913         
32914         if (!this.title.length && !this.html.length) {
32915             cfg.cn[1].cls += ' hide';
32916         }
32917         
32918         if(this.bgimage.length){
32919             cfg.cn.push({
32920                 tag: 'img',
32921                 cls: 'masonry-brick-image-view',
32922                 src: this.bgimage
32923             });
32924         }
32925         
32926         if(this.videourl.length){
32927             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32928             // youtube support only?
32929             cfg.cn.push({
32930                 tag: 'iframe',
32931                 cls: 'masonry-brick-image-view',
32932                 src: vurl,
32933                 frameborder : 0,
32934                 allowfullscreen : true
32935             });
32936         }
32937         
32938         return cfg;
32939         
32940     },
32941     
32942     getSplitAutoCreate : function()
32943     {
32944         var cls = 'masonry-brick masonry-brick-split';
32945         
32946         if(this.href.length){
32947             cls += ' masonry-brick-link';
32948         }
32949         
32950         if(this.bgimage.length){
32951             cls += ' masonry-brick-image';
32952         }
32953         
32954         if(this.size){
32955             cls += ' masonry-' + this.size + '-brick';
32956         }
32957         
32958         switch (this.placetitle) {
32959             case 'center' :
32960                 cls += ' masonry-center-title';
32961                 break;
32962             case 'bottom' :
32963                 cls += ' masonry-bottom-title';
32964                 break;
32965             default:
32966                 if(!this.bgimage.length){
32967                     cls += ' masonry-center-title';
32968                 }
32969
32970                 if(this.bgimage.length){
32971                     cls += ' masonry-bottom-title';
32972                 }
32973                 break;
32974         }
32975         
32976         if(this.cls){
32977             cls += ' ' + this.cls;
32978         }
32979         
32980         var cfg = {
32981             tag: (this.href.length) ? 'a' : 'div',
32982             cls: cls,
32983             cn: [
32984                 {
32985                     tag: 'div',
32986                     cls: 'masonry-brick-split-head',
32987                     cn: [
32988                         {
32989                             tag: 'div',
32990                             cls: 'masonry-brick-paragraph',
32991                             cn: []
32992                         }
32993                     ]
32994                 },
32995                 {
32996                     tag: 'div',
32997                     cls: 'masonry-brick-split-body',
32998                     cn: []
32999                 }
33000             ]
33001         };
33002         
33003         if(this.href.length){
33004             cfg.href = this.href;
33005         }
33006         
33007         if(this.title.length){
33008             cfg.cn[0].cn[0].cn.push({
33009                 tag: 'h4',
33010                 cls: 'masonry-brick-title',
33011                 html: this.title
33012             });
33013         }
33014         
33015         if(this.html.length){
33016             cfg.cn[1].cn.push({
33017                 tag: 'p',
33018                 cls: 'masonry-brick-text',
33019                 html: this.html
33020             });
33021         }
33022
33023         if(this.bgimage.length){
33024             cfg.cn[0].cn.push({
33025                 tag: 'img',
33026                 cls: 'masonry-brick-image-view',
33027                 src: this.bgimage
33028             });
33029         }
33030         
33031         if(this.videourl.length){
33032             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33033             // youtube support only?
33034             cfg.cn[0].cn.cn.push({
33035                 tag: 'iframe',
33036                 cls: 'masonry-brick-image-view',
33037                 src: vurl,
33038                 frameborder : 0,
33039                 allowfullscreen : true
33040             });
33041         }
33042         
33043         return cfg;
33044     },
33045     
33046     initEvents: function() 
33047     {
33048         switch (this.size) {
33049             case 'xs' :
33050                 this.x = 1;
33051                 this.y = 1;
33052                 break;
33053             case 'sm' :
33054                 this.x = 2;
33055                 this.y = 2;
33056                 break;
33057             case 'md' :
33058             case 'md-left' :
33059             case 'md-right' :
33060                 this.x = 3;
33061                 this.y = 3;
33062                 break;
33063             case 'tall' :
33064                 this.x = 2;
33065                 this.y = 3;
33066                 break;
33067             case 'wide' :
33068                 this.x = 3;
33069                 this.y = 2;
33070                 break;
33071             case 'wide-thin' :
33072                 this.x = 3;
33073                 this.y = 1;
33074                 break;
33075                         
33076             default :
33077                 break;
33078         }
33079         
33080         if(Roo.isTouch){
33081             this.el.on('touchstart', this.onTouchStart, this);
33082             this.el.on('touchmove', this.onTouchMove, this);
33083             this.el.on('touchend', this.onTouchEnd, this);
33084             this.el.on('contextmenu', this.onContextMenu, this);
33085         } else {
33086             this.el.on('mouseenter'  ,this.enter, this);
33087             this.el.on('mouseleave', this.leave, this);
33088             this.el.on('click', this.onClick, this);
33089         }
33090         
33091         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33092             this.parent().bricks.push(this);   
33093         }
33094         
33095     },
33096     
33097     onClick: function(e, el)
33098     {
33099         var time = this.endTimer - this.startTimer;
33100         // Roo.log(e.preventDefault());
33101         if(Roo.isTouch){
33102             if(time > 1000){
33103                 e.preventDefault();
33104                 return;
33105             }
33106         }
33107         
33108         if(!this.preventDefault){
33109             return;
33110         }
33111         
33112         e.preventDefault();
33113         
33114         if (this.activeClass != '') {
33115             this.selectBrick();
33116         }
33117         
33118         this.fireEvent('click', this, e);
33119     },
33120     
33121     enter: function(e, el)
33122     {
33123         e.preventDefault();
33124         
33125         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33126             return;
33127         }
33128         
33129         if(this.bgimage.length && this.html.length){
33130             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33131         }
33132     },
33133     
33134     leave: function(e, el)
33135     {
33136         e.preventDefault();
33137         
33138         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33139             return;
33140         }
33141         
33142         if(this.bgimage.length && this.html.length){
33143             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33144         }
33145     },
33146     
33147     onTouchStart: function(e, el)
33148     {
33149 //        e.preventDefault();
33150         
33151         this.touchmoved = false;
33152         
33153         if(!this.isFitContainer){
33154             return;
33155         }
33156         
33157         if(!this.bgimage.length || !this.html.length){
33158             return;
33159         }
33160         
33161         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33162         
33163         this.timer = new Date().getTime();
33164         
33165     },
33166     
33167     onTouchMove: function(e, el)
33168     {
33169         this.touchmoved = true;
33170     },
33171     
33172     onContextMenu : function(e,el)
33173     {
33174         e.preventDefault();
33175         e.stopPropagation();
33176         return false;
33177     },
33178     
33179     onTouchEnd: function(e, el)
33180     {
33181 //        e.preventDefault();
33182         
33183         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33184         
33185             this.leave(e,el);
33186             
33187             return;
33188         }
33189         
33190         if(!this.bgimage.length || !this.html.length){
33191             
33192             if(this.href.length){
33193                 window.location.href = this.href;
33194             }
33195             
33196             return;
33197         }
33198         
33199         if(!this.isFitContainer){
33200             return;
33201         }
33202         
33203         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33204         
33205         window.location.href = this.href;
33206     },
33207     
33208     //selection on single brick only
33209     selectBrick : function() {
33210         
33211         if (!this.parentId) {
33212             return;
33213         }
33214         
33215         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33216         var index = m.selectedBrick.indexOf(this.id);
33217         
33218         if ( index > -1) {
33219             m.selectedBrick.splice(index,1);
33220             this.el.removeClass(this.activeClass);
33221             return;
33222         }
33223         
33224         for(var i = 0; i < m.selectedBrick.length; i++) {
33225             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33226             b.el.removeClass(b.activeClass);
33227         }
33228         
33229         m.selectedBrick = [];
33230         
33231         m.selectedBrick.push(this.id);
33232         this.el.addClass(this.activeClass);
33233         return;
33234     },
33235     
33236     isSelected : function(){
33237         return this.el.hasClass(this.activeClass);
33238         
33239     }
33240 });
33241
33242 Roo.apply(Roo.bootstrap.MasonryBrick, {
33243     
33244     //groups: {},
33245     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33246      /**
33247     * register a Masonry Brick
33248     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33249     */
33250     
33251     register : function(brick)
33252     {
33253         //this.groups[brick.id] = brick;
33254         this.groups.add(brick.id, brick);
33255     },
33256     /**
33257     * fetch a  masonry brick based on the masonry brick ID
33258     * @param {string} the masonry brick to add
33259     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33260     */
33261     
33262     get: function(brick_id) 
33263     {
33264         // if (typeof(this.groups[brick_id]) == 'undefined') {
33265         //     return false;
33266         // }
33267         // return this.groups[brick_id] ;
33268         
33269         if(this.groups.key(brick_id)) {
33270             return this.groups.key(brick_id);
33271         }
33272         
33273         return false;
33274     }
33275     
33276     
33277     
33278 });
33279
33280  /*
33281  * - LGPL
33282  *
33283  * element
33284  * 
33285  */
33286
33287 /**
33288  * @class Roo.bootstrap.Brick
33289  * @extends Roo.bootstrap.Component
33290  * Bootstrap Brick class
33291  * 
33292  * @constructor
33293  * Create a new Brick
33294  * @param {Object} config The config object
33295  */
33296
33297 Roo.bootstrap.Brick = function(config){
33298     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33299     
33300     this.addEvents({
33301         // raw events
33302         /**
33303          * @event click
33304          * When a Brick is click
33305          * @param {Roo.bootstrap.Brick} this
33306          * @param {Roo.EventObject} e
33307          */
33308         "click" : true
33309     });
33310 };
33311
33312 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33313     
33314     /**
33315      * @cfg {String} title
33316      */   
33317     title : '',
33318     /**
33319      * @cfg {String} html
33320      */   
33321     html : '',
33322     /**
33323      * @cfg {String} bgimage
33324      */   
33325     bgimage : '',
33326     /**
33327      * @cfg {String} cls
33328      */   
33329     cls : '',
33330     /**
33331      * @cfg {String} href
33332      */   
33333     href : '',
33334     /**
33335      * @cfg {String} video
33336      */   
33337     video : '',
33338     /**
33339      * @cfg {Boolean} square
33340      */   
33341     square : true,
33342     
33343     getAutoCreate : function()
33344     {
33345         var cls = 'roo-brick';
33346         
33347         if(this.href.length){
33348             cls += ' roo-brick-link';
33349         }
33350         
33351         if(this.bgimage.length){
33352             cls += ' roo-brick-image';
33353         }
33354         
33355         if(!this.html.length && !this.bgimage.length){
33356             cls += ' roo-brick-center-title';
33357         }
33358         
33359         if(!this.html.length && this.bgimage.length){
33360             cls += ' roo-brick-bottom-title';
33361         }
33362         
33363         if(this.cls){
33364             cls += ' ' + this.cls;
33365         }
33366         
33367         var cfg = {
33368             tag: (this.href.length) ? 'a' : 'div',
33369             cls: cls,
33370             cn: [
33371                 {
33372                     tag: 'div',
33373                     cls: 'roo-brick-paragraph',
33374                     cn: []
33375                 }
33376             ]
33377         };
33378         
33379         if(this.href.length){
33380             cfg.href = this.href;
33381         }
33382         
33383         var cn = cfg.cn[0].cn;
33384         
33385         if(this.title.length){
33386             cn.push({
33387                 tag: 'h4',
33388                 cls: 'roo-brick-title',
33389                 html: this.title
33390             });
33391         }
33392         
33393         if(this.html.length){
33394             cn.push({
33395                 tag: 'p',
33396                 cls: 'roo-brick-text',
33397                 html: this.html
33398             });
33399         } else {
33400             cn.cls += ' hide';
33401         }
33402         
33403         if(this.bgimage.length){
33404             cfg.cn.push({
33405                 tag: 'img',
33406                 cls: 'roo-brick-image-view',
33407                 src: this.bgimage
33408             });
33409         }
33410         
33411         return cfg;
33412     },
33413     
33414     initEvents: function() 
33415     {
33416         if(this.title.length || this.html.length){
33417             this.el.on('mouseenter'  ,this.enter, this);
33418             this.el.on('mouseleave', this.leave, this);
33419         }
33420         
33421         Roo.EventManager.onWindowResize(this.resize, this); 
33422         
33423         if(this.bgimage.length){
33424             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33425             this.imageEl.on('load', this.onImageLoad, this);
33426             return;
33427         }
33428         
33429         this.resize();
33430     },
33431     
33432     onImageLoad : function()
33433     {
33434         this.resize();
33435     },
33436     
33437     resize : function()
33438     {
33439         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33440         
33441         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33442         
33443         if(this.bgimage.length){
33444             var image = this.el.select('.roo-brick-image-view', true).first();
33445             
33446             image.setWidth(paragraph.getWidth());
33447             
33448             if(this.square){
33449                 image.setHeight(paragraph.getWidth());
33450             }
33451             
33452             this.el.setHeight(image.getHeight());
33453             paragraph.setHeight(image.getHeight());
33454             
33455         }
33456         
33457     },
33458     
33459     enter: function(e, el)
33460     {
33461         e.preventDefault();
33462         
33463         if(this.bgimage.length){
33464             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33465             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33466         }
33467     },
33468     
33469     leave: function(e, el)
33470     {
33471         e.preventDefault();
33472         
33473         if(this.bgimage.length){
33474             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33475             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33476         }
33477     }
33478     
33479 });
33480
33481  
33482
33483  /*
33484  * - LGPL
33485  *
33486  * Number field 
33487  */
33488
33489 /**
33490  * @class Roo.bootstrap.NumberField
33491  * @extends Roo.bootstrap.Input
33492  * Bootstrap NumberField class
33493  * 
33494  * 
33495  * 
33496  * 
33497  * @constructor
33498  * Create a new NumberField
33499  * @param {Object} config The config object
33500  */
33501
33502 Roo.bootstrap.NumberField = function(config){
33503     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33504 };
33505
33506 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33507     
33508     /**
33509      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33510      */
33511     allowDecimals : true,
33512     /**
33513      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33514      */
33515     decimalSeparator : ".",
33516     /**
33517      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33518      */
33519     decimalPrecision : 2,
33520     /**
33521      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33522      */
33523     allowNegative : true,
33524     
33525     /**
33526      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33527      */
33528     allowZero: true,
33529     /**
33530      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33531      */
33532     minValue : Number.NEGATIVE_INFINITY,
33533     /**
33534      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33535      */
33536     maxValue : Number.MAX_VALUE,
33537     /**
33538      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33539      */
33540     minText : "The minimum value for this field is {0}",
33541     /**
33542      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33543      */
33544     maxText : "The maximum value for this field is {0}",
33545     /**
33546      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33547      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33548      */
33549     nanText : "{0} is not a valid number",
33550     /**
33551      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33552      */
33553     thousandsDelimiter : false,
33554     /**
33555      * @cfg {String} valueAlign alignment of value
33556      */
33557     valueAlign : "left",
33558
33559     getAutoCreate : function()
33560     {
33561         var hiddenInput = {
33562             tag: 'input',
33563             type: 'hidden',
33564             id: Roo.id(),
33565             cls: 'hidden-number-input'
33566         };
33567         
33568         if (this.name) {
33569             hiddenInput.name = this.name;
33570         }
33571         
33572         this.name = '';
33573         
33574         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33575         
33576         this.name = hiddenInput.name;
33577         
33578         if(cfg.cn.length > 0) {
33579             cfg.cn.push(hiddenInput);
33580         }
33581         
33582         return cfg;
33583     },
33584
33585     // private
33586     initEvents : function()
33587     {   
33588         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33589         
33590         var allowed = "0123456789";
33591         
33592         if(this.allowDecimals){
33593             allowed += this.decimalSeparator;
33594         }
33595         
33596         if(this.allowNegative){
33597             allowed += "-";
33598         }
33599         
33600         if(this.thousandsDelimiter) {
33601             allowed += ",";
33602         }
33603         
33604         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33605         
33606         var keyPress = function(e){
33607             
33608             var k = e.getKey();
33609             
33610             var c = e.getCharCode();
33611             
33612             if(
33613                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33614                     allowed.indexOf(String.fromCharCode(c)) === -1
33615             ){
33616                 e.stopEvent();
33617                 return;
33618             }
33619             
33620             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33621                 return;
33622             }
33623             
33624             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33625                 e.stopEvent();
33626             }
33627         };
33628         
33629         this.el.on("keypress", keyPress, this);
33630     },
33631     
33632     validateValue : function(value)
33633     {
33634         
33635         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33636             return false;
33637         }
33638         
33639         var num = this.parseValue(value);
33640         
33641         if(isNaN(num)){
33642             this.markInvalid(String.format(this.nanText, value));
33643             return false;
33644         }
33645         
33646         if(num < this.minValue){
33647             this.markInvalid(String.format(this.minText, this.minValue));
33648             return false;
33649         }
33650         
33651         if(num > this.maxValue){
33652             this.markInvalid(String.format(this.maxText, this.maxValue));
33653             return false;
33654         }
33655         
33656         return true;
33657     },
33658
33659     getValue : function()
33660     {
33661         var v = this.hiddenEl().getValue();
33662         
33663         return this.fixPrecision(this.parseValue(v));
33664     },
33665
33666     parseValue : function(value)
33667     {
33668         if(this.thousandsDelimiter) {
33669             value += "";
33670             r = new RegExp(",", "g");
33671             value = value.replace(r, "");
33672         }
33673         
33674         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33675         return isNaN(value) ? '' : value;
33676     },
33677
33678     fixPrecision : function(value)
33679     {
33680         if(this.thousandsDelimiter) {
33681             value += "";
33682             r = new RegExp(",", "g");
33683             value = value.replace(r, "");
33684         }
33685         
33686         var nan = isNaN(value);
33687         
33688         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33689             return nan ? '' : value;
33690         }
33691         return parseFloat(value).toFixed(this.decimalPrecision);
33692     },
33693
33694     setValue : function(v)
33695     {
33696         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33697         
33698         this.value = v;
33699         
33700         if(this.rendered){
33701             
33702             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33703             
33704             this.inputEl().dom.value = (v == '') ? '' :
33705                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33706             
33707             if(!this.allowZero && v === '0') {
33708                 this.hiddenEl().dom.value = '';
33709                 this.inputEl().dom.value = '';
33710             }
33711             
33712             this.validate();
33713         }
33714     },
33715
33716     decimalPrecisionFcn : function(v)
33717     {
33718         return Math.floor(v);
33719     },
33720
33721     beforeBlur : function()
33722     {
33723         var v = this.parseValue(this.getRawValue());
33724         
33725         if(v || v === 0 || v === ''){
33726             this.setValue(v);
33727         }
33728     },
33729     
33730     hiddenEl : function()
33731     {
33732         return this.el.select('input.hidden-number-input',true).first();
33733     }
33734     
33735 });
33736
33737  
33738
33739 /*
33740 * Licence: LGPL
33741 */
33742
33743 /**
33744  * @class Roo.bootstrap.DocumentSlider
33745  * @extends Roo.bootstrap.Component
33746  * Bootstrap DocumentSlider class
33747  * 
33748  * @constructor
33749  * Create a new DocumentViewer
33750  * @param {Object} config The config object
33751  */
33752
33753 Roo.bootstrap.DocumentSlider = function(config){
33754     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33755     
33756     this.files = [];
33757     
33758     this.addEvents({
33759         /**
33760          * @event initial
33761          * Fire after initEvent
33762          * @param {Roo.bootstrap.DocumentSlider} this
33763          */
33764         "initial" : true,
33765         /**
33766          * @event update
33767          * Fire after update
33768          * @param {Roo.bootstrap.DocumentSlider} this
33769          */
33770         "update" : true,
33771         /**
33772          * @event click
33773          * Fire after click
33774          * @param {Roo.bootstrap.DocumentSlider} this
33775          */
33776         "click" : true
33777     });
33778 };
33779
33780 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33781     
33782     files : false,
33783     
33784     indicator : 0,
33785     
33786     getAutoCreate : function()
33787     {
33788         var cfg = {
33789             tag : 'div',
33790             cls : 'roo-document-slider',
33791             cn : [
33792                 {
33793                     tag : 'div',
33794                     cls : 'roo-document-slider-header',
33795                     cn : [
33796                         {
33797                             tag : 'div',
33798                             cls : 'roo-document-slider-header-title'
33799                         }
33800                     ]
33801                 },
33802                 {
33803                     tag : 'div',
33804                     cls : 'roo-document-slider-body',
33805                     cn : [
33806                         {
33807                             tag : 'div',
33808                             cls : 'roo-document-slider-prev',
33809                             cn : [
33810                                 {
33811                                     tag : 'i',
33812                                     cls : 'fa fa-chevron-left'
33813                                 }
33814                             ]
33815                         },
33816                         {
33817                             tag : 'div',
33818                             cls : 'roo-document-slider-thumb',
33819                             cn : [
33820                                 {
33821                                     tag : 'img',
33822                                     cls : 'roo-document-slider-image'
33823                                 }
33824                             ]
33825                         },
33826                         {
33827                             tag : 'div',
33828                             cls : 'roo-document-slider-next',
33829                             cn : [
33830                                 {
33831                                     tag : 'i',
33832                                     cls : 'fa fa-chevron-right'
33833                                 }
33834                             ]
33835                         }
33836                     ]
33837                 }
33838             ]
33839         };
33840         
33841         return cfg;
33842     },
33843     
33844     initEvents : function()
33845     {
33846         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33847         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33848         
33849         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33850         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33851         
33852         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33853         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33854         
33855         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33856         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33857         
33858         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33859         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33860         
33861         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33862         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33863         
33864         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33865         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33866         
33867         this.thumbEl.on('click', this.onClick, this);
33868         
33869         this.prevIndicator.on('click', this.prev, this);
33870         
33871         this.nextIndicator.on('click', this.next, this);
33872         
33873     },
33874     
33875     initial : function()
33876     {
33877         if(this.files.length){
33878             this.indicator = 1;
33879             this.update()
33880         }
33881         
33882         this.fireEvent('initial', this);
33883     },
33884     
33885     update : function()
33886     {
33887         this.imageEl.attr('src', this.files[this.indicator - 1]);
33888         
33889         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33890         
33891         this.prevIndicator.show();
33892         
33893         if(this.indicator == 1){
33894             this.prevIndicator.hide();
33895         }
33896         
33897         this.nextIndicator.show();
33898         
33899         if(this.indicator == this.files.length){
33900             this.nextIndicator.hide();
33901         }
33902         
33903         this.thumbEl.scrollTo('top');
33904         
33905         this.fireEvent('update', this);
33906     },
33907     
33908     onClick : function(e)
33909     {
33910         e.preventDefault();
33911         
33912         this.fireEvent('click', this);
33913     },
33914     
33915     prev : function(e)
33916     {
33917         e.preventDefault();
33918         
33919         this.indicator = Math.max(1, this.indicator - 1);
33920         
33921         this.update();
33922     },
33923     
33924     next : function(e)
33925     {
33926         e.preventDefault();
33927         
33928         this.indicator = Math.min(this.files.length, this.indicator + 1);
33929         
33930         this.update();
33931     }
33932 });
33933 /*
33934  * - LGPL
33935  *
33936  * RadioSet
33937  *
33938  *
33939  */
33940
33941 /**
33942  * @class Roo.bootstrap.RadioSet
33943  * @extends Roo.bootstrap.Input
33944  * Bootstrap RadioSet class
33945  * @cfg {String} indicatorpos (left|right) default left
33946  * @cfg {Boolean} inline (true|false) inline the element (default true)
33947  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33948  * @constructor
33949  * Create a new RadioSet
33950  * @param {Object} config The config object
33951  */
33952
33953 Roo.bootstrap.RadioSet = function(config){
33954     
33955     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33956     
33957     this.radioes = [];
33958     
33959     Roo.bootstrap.RadioSet.register(this);
33960     
33961     this.addEvents({
33962         /**
33963         * @event check
33964         * Fires when the element is checked or unchecked.
33965         * @param {Roo.bootstrap.RadioSet} this This radio
33966         * @param {Roo.bootstrap.Radio} item The checked item
33967         */
33968        check : true,
33969        /**
33970         * @event click
33971         * Fires when the element is click.
33972         * @param {Roo.bootstrap.RadioSet} this This radio set
33973         * @param {Roo.bootstrap.Radio} item The checked item
33974         * @param {Roo.EventObject} e The event object
33975         */
33976        click : true
33977     });
33978     
33979 };
33980
33981 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33982
33983     radioes : false,
33984     
33985     inline : true,
33986     
33987     weight : '',
33988     
33989     indicatorpos : 'left',
33990     
33991     getAutoCreate : function()
33992     {
33993         var label = {
33994             tag : 'label',
33995             cls : 'roo-radio-set-label',
33996             cn : [
33997                 {
33998                     tag : 'span',
33999                     html : this.fieldLabel
34000                 }
34001             ]
34002         };
34003         
34004         if(this.indicatorpos == 'left'){
34005             label.cn.unshift({
34006                 tag : 'i',
34007                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34008                 tooltip : 'This field is required'
34009             });
34010         } else {
34011             label.cn.push({
34012                 tag : 'i',
34013                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34014                 tooltip : 'This field is required'
34015             });
34016         }
34017         
34018         var items = {
34019             tag : 'div',
34020             cls : 'roo-radio-set-items'
34021         };
34022         
34023         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34024         
34025         if (align === 'left' && this.fieldLabel.length) {
34026             
34027             items = {
34028                 cls : "roo-radio-set-right", 
34029                 cn: [
34030                     items
34031                 ]
34032             };
34033             
34034             if(this.labelWidth > 12){
34035                 label.style = "width: " + this.labelWidth + 'px';
34036             }
34037             
34038             if(this.labelWidth < 13 && this.labelmd == 0){
34039                 this.labelmd = this.labelWidth;
34040             }
34041             
34042             if(this.labellg > 0){
34043                 label.cls += ' col-lg-' + this.labellg;
34044                 items.cls += ' col-lg-' + (12 - this.labellg);
34045             }
34046             
34047             if(this.labelmd > 0){
34048                 label.cls += ' col-md-' + this.labelmd;
34049                 items.cls += ' col-md-' + (12 - this.labelmd);
34050             }
34051             
34052             if(this.labelsm > 0){
34053                 label.cls += ' col-sm-' + this.labelsm;
34054                 items.cls += ' col-sm-' + (12 - this.labelsm);
34055             }
34056             
34057             if(this.labelxs > 0){
34058                 label.cls += ' col-xs-' + this.labelxs;
34059                 items.cls += ' col-xs-' + (12 - this.labelxs);
34060             }
34061         }
34062         
34063         var cfg = {
34064             tag : 'div',
34065             cls : 'roo-radio-set',
34066             cn : [
34067                 {
34068                     tag : 'input',
34069                     cls : 'roo-radio-set-input',
34070                     type : 'hidden',
34071                     name : this.name,
34072                     value : this.value ? this.value :  ''
34073                 },
34074                 label,
34075                 items
34076             ]
34077         };
34078         
34079         if(this.weight.length){
34080             cfg.cls += ' roo-radio-' + this.weight;
34081         }
34082         
34083         if(this.inline) {
34084             cfg.cls += ' roo-radio-set-inline';
34085         }
34086         
34087         var settings=this;
34088         ['xs','sm','md','lg'].map(function(size){
34089             if (settings[size]) {
34090                 cfg.cls += ' col-' + size + '-' + settings[size];
34091             }
34092         });
34093         
34094         return cfg;
34095         
34096     },
34097
34098     initEvents : function()
34099     {
34100         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34101         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34102         
34103         if(!this.fieldLabel.length){
34104             this.labelEl.hide();
34105         }
34106         
34107         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34108         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34109         
34110         this.indicator = this.indicatorEl();
34111         
34112         if(this.indicator){
34113             this.indicator.addClass('invisible');
34114         }
34115         
34116         this.originalValue = this.getValue();
34117         
34118     },
34119     
34120     inputEl: function ()
34121     {
34122         return this.el.select('.roo-radio-set-input', true).first();
34123     },
34124     
34125     getChildContainer : function()
34126     {
34127         return this.itemsEl;
34128     },
34129     
34130     register : function(item)
34131     {
34132         this.radioes.push(item);
34133         
34134     },
34135     
34136     validate : function()
34137     {   
34138         if(this.getVisibilityEl().hasClass('hidden')){
34139             return true;
34140         }
34141         
34142         var valid = false;
34143         
34144         Roo.each(this.radioes, function(i){
34145             if(!i.checked){
34146                 return;
34147             }
34148             
34149             valid = true;
34150             return false;
34151         });
34152         
34153         if(this.allowBlank) {
34154             return true;
34155         }
34156         
34157         if(this.disabled || valid){
34158             this.markValid();
34159             return true;
34160         }
34161         
34162         this.markInvalid();
34163         return false;
34164         
34165     },
34166     
34167     markValid : function()
34168     {
34169         if(this.labelEl.isVisible(true)){
34170             this.indicatorEl().removeClass('visible');
34171             this.indicatorEl().addClass('invisible');
34172         }
34173         
34174         this.el.removeClass([this.invalidClass, this.validClass]);
34175         this.el.addClass(this.validClass);
34176         
34177         this.fireEvent('valid', this);
34178     },
34179     
34180     markInvalid : function(msg)
34181     {
34182         if(this.allowBlank || this.disabled){
34183             return;
34184         }
34185         
34186         if(this.labelEl.isVisible(true)){
34187             this.indicatorEl().removeClass('invisible');
34188             this.indicatorEl().addClass('visible');
34189         }
34190         
34191         this.el.removeClass([this.invalidClass, this.validClass]);
34192         this.el.addClass(this.invalidClass);
34193         
34194         this.fireEvent('invalid', this, msg);
34195         
34196     },
34197     
34198     setValue : function(v, suppressEvent)
34199     {   
34200         if(this.value === v){
34201             return;
34202         }
34203         
34204         this.value = v;
34205         
34206         if(this.rendered){
34207             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34208         }
34209         
34210         Roo.each(this.radioes, function(i){
34211             i.checked = false;
34212             i.el.removeClass('checked');
34213         });
34214         
34215         Roo.each(this.radioes, function(i){
34216             
34217             if(i.value === v || i.value.toString() === v.toString()){
34218                 i.checked = true;
34219                 i.el.addClass('checked');
34220                 
34221                 if(suppressEvent !== true){
34222                     this.fireEvent('check', this, i);
34223                 }
34224                 
34225                 return false;
34226             }
34227             
34228         }, this);
34229         
34230         this.validate();
34231     },
34232     
34233     clearInvalid : function(){
34234         
34235         if(!this.el || this.preventMark){
34236             return;
34237         }
34238         
34239         this.el.removeClass([this.invalidClass]);
34240         
34241         this.fireEvent('valid', this);
34242     }
34243     
34244 });
34245
34246 Roo.apply(Roo.bootstrap.RadioSet, {
34247     
34248     groups: {},
34249     
34250     register : function(set)
34251     {
34252         this.groups[set.name] = set;
34253     },
34254     
34255     get: function(name) 
34256     {
34257         if (typeof(this.groups[name]) == 'undefined') {
34258             return false;
34259         }
34260         
34261         return this.groups[name] ;
34262     }
34263     
34264 });
34265 /*
34266  * Based on:
34267  * Ext JS Library 1.1.1
34268  * Copyright(c) 2006-2007, Ext JS, LLC.
34269  *
34270  * Originally Released Under LGPL - original licence link has changed is not relivant.
34271  *
34272  * Fork - LGPL
34273  * <script type="text/javascript">
34274  */
34275
34276
34277 /**
34278  * @class Roo.bootstrap.SplitBar
34279  * @extends Roo.util.Observable
34280  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34281  * <br><br>
34282  * Usage:
34283  * <pre><code>
34284 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34285                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34286 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34287 split.minSize = 100;
34288 split.maxSize = 600;
34289 split.animate = true;
34290 split.on('moved', splitterMoved);
34291 </code></pre>
34292  * @constructor
34293  * Create a new SplitBar
34294  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34295  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34296  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34297  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34298                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34299                         position of the SplitBar).
34300  */
34301 Roo.bootstrap.SplitBar = function(cfg){
34302     
34303     /** @private */
34304     
34305     //{
34306     //  dragElement : elm
34307     //  resizingElement: el,
34308         // optional..
34309     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34310     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34311         // existingProxy ???
34312     //}
34313     
34314     this.el = Roo.get(cfg.dragElement, true);
34315     this.el.dom.unselectable = "on";
34316     /** @private */
34317     this.resizingEl = Roo.get(cfg.resizingElement, true);
34318
34319     /**
34320      * @private
34321      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34322      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34323      * @type Number
34324      */
34325     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34326     
34327     /**
34328      * The minimum size of the resizing element. (Defaults to 0)
34329      * @type Number
34330      */
34331     this.minSize = 0;
34332     
34333     /**
34334      * The maximum size of the resizing element. (Defaults to 2000)
34335      * @type Number
34336      */
34337     this.maxSize = 2000;
34338     
34339     /**
34340      * Whether to animate the transition to the new size
34341      * @type Boolean
34342      */
34343     this.animate = false;
34344     
34345     /**
34346      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34347      * @type Boolean
34348      */
34349     this.useShim = false;
34350     
34351     /** @private */
34352     this.shim = null;
34353     
34354     if(!cfg.existingProxy){
34355         /** @private */
34356         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34357     }else{
34358         this.proxy = Roo.get(cfg.existingProxy).dom;
34359     }
34360     /** @private */
34361     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34362     
34363     /** @private */
34364     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34365     
34366     /** @private */
34367     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34368     
34369     /** @private */
34370     this.dragSpecs = {};
34371     
34372     /**
34373      * @private The adapter to use to positon and resize elements
34374      */
34375     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34376     this.adapter.init(this);
34377     
34378     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34379         /** @private */
34380         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34381         this.el.addClass("roo-splitbar-h");
34382     }else{
34383         /** @private */
34384         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34385         this.el.addClass("roo-splitbar-v");
34386     }
34387     
34388     this.addEvents({
34389         /**
34390          * @event resize
34391          * Fires when the splitter is moved (alias for {@link #event-moved})
34392          * @param {Roo.bootstrap.SplitBar} this
34393          * @param {Number} newSize the new width or height
34394          */
34395         "resize" : true,
34396         /**
34397          * @event moved
34398          * Fires when the splitter is moved
34399          * @param {Roo.bootstrap.SplitBar} this
34400          * @param {Number} newSize the new width or height
34401          */
34402         "moved" : true,
34403         /**
34404          * @event beforeresize
34405          * Fires before the splitter is dragged
34406          * @param {Roo.bootstrap.SplitBar} this
34407          */
34408         "beforeresize" : true,
34409
34410         "beforeapply" : true
34411     });
34412
34413     Roo.util.Observable.call(this);
34414 };
34415
34416 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34417     onStartProxyDrag : function(x, y){
34418         this.fireEvent("beforeresize", this);
34419         if(!this.overlay){
34420             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34421             o.unselectable();
34422             o.enableDisplayMode("block");
34423             // all splitbars share the same overlay
34424             Roo.bootstrap.SplitBar.prototype.overlay = o;
34425         }
34426         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34427         this.overlay.show();
34428         Roo.get(this.proxy).setDisplayed("block");
34429         var size = this.adapter.getElementSize(this);
34430         this.activeMinSize = this.getMinimumSize();;
34431         this.activeMaxSize = this.getMaximumSize();;
34432         var c1 = size - this.activeMinSize;
34433         var c2 = Math.max(this.activeMaxSize - size, 0);
34434         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34435             this.dd.resetConstraints();
34436             this.dd.setXConstraint(
34437                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34438                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34439             );
34440             this.dd.setYConstraint(0, 0);
34441         }else{
34442             this.dd.resetConstraints();
34443             this.dd.setXConstraint(0, 0);
34444             this.dd.setYConstraint(
34445                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34446                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34447             );
34448          }
34449         this.dragSpecs.startSize = size;
34450         this.dragSpecs.startPoint = [x, y];
34451         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34452     },
34453     
34454     /** 
34455      * @private Called after the drag operation by the DDProxy
34456      */
34457     onEndProxyDrag : function(e){
34458         Roo.get(this.proxy).setDisplayed(false);
34459         var endPoint = Roo.lib.Event.getXY(e);
34460         if(this.overlay){
34461             this.overlay.hide();
34462         }
34463         var newSize;
34464         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34465             newSize = this.dragSpecs.startSize + 
34466                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34467                     endPoint[0] - this.dragSpecs.startPoint[0] :
34468                     this.dragSpecs.startPoint[0] - endPoint[0]
34469                 );
34470         }else{
34471             newSize = this.dragSpecs.startSize + 
34472                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34473                     endPoint[1] - this.dragSpecs.startPoint[1] :
34474                     this.dragSpecs.startPoint[1] - endPoint[1]
34475                 );
34476         }
34477         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34478         if(newSize != this.dragSpecs.startSize){
34479             if(this.fireEvent('beforeapply', this, newSize) !== false){
34480                 this.adapter.setElementSize(this, newSize);
34481                 this.fireEvent("moved", this, newSize);
34482                 this.fireEvent("resize", this, newSize);
34483             }
34484         }
34485     },
34486     
34487     /**
34488      * Get the adapter this SplitBar uses
34489      * @return The adapter object
34490      */
34491     getAdapter : function(){
34492         return this.adapter;
34493     },
34494     
34495     /**
34496      * Set the adapter this SplitBar uses
34497      * @param {Object} adapter A SplitBar adapter object
34498      */
34499     setAdapter : function(adapter){
34500         this.adapter = adapter;
34501         this.adapter.init(this);
34502     },
34503     
34504     /**
34505      * Gets the minimum size for the resizing element
34506      * @return {Number} The minimum size
34507      */
34508     getMinimumSize : function(){
34509         return this.minSize;
34510     },
34511     
34512     /**
34513      * Sets the minimum size for the resizing element
34514      * @param {Number} minSize The minimum size
34515      */
34516     setMinimumSize : function(minSize){
34517         this.minSize = minSize;
34518     },
34519     
34520     /**
34521      * Gets the maximum size for the resizing element
34522      * @return {Number} The maximum size
34523      */
34524     getMaximumSize : function(){
34525         return this.maxSize;
34526     },
34527     
34528     /**
34529      * Sets the maximum size for the resizing element
34530      * @param {Number} maxSize The maximum size
34531      */
34532     setMaximumSize : function(maxSize){
34533         this.maxSize = maxSize;
34534     },
34535     
34536     /**
34537      * Sets the initialize size for the resizing element
34538      * @param {Number} size The initial size
34539      */
34540     setCurrentSize : function(size){
34541         var oldAnimate = this.animate;
34542         this.animate = false;
34543         this.adapter.setElementSize(this, size);
34544         this.animate = oldAnimate;
34545     },
34546     
34547     /**
34548      * Destroy this splitbar. 
34549      * @param {Boolean} removeEl True to remove the element
34550      */
34551     destroy : function(removeEl){
34552         if(this.shim){
34553             this.shim.remove();
34554         }
34555         this.dd.unreg();
34556         this.proxy.parentNode.removeChild(this.proxy);
34557         if(removeEl){
34558             this.el.remove();
34559         }
34560     }
34561 });
34562
34563 /**
34564  * @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.
34565  */
34566 Roo.bootstrap.SplitBar.createProxy = function(dir){
34567     var proxy = new Roo.Element(document.createElement("div"));
34568     proxy.unselectable();
34569     var cls = 'roo-splitbar-proxy';
34570     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34571     document.body.appendChild(proxy.dom);
34572     return proxy.dom;
34573 };
34574
34575 /** 
34576  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34577  * Default Adapter. It assumes the splitter and resizing element are not positioned
34578  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34579  */
34580 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34581 };
34582
34583 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34584     // do nothing for now
34585     init : function(s){
34586     
34587     },
34588     /**
34589      * Called before drag operations to get the current size of the resizing element. 
34590      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34591      */
34592      getElementSize : function(s){
34593         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34594             return s.resizingEl.getWidth();
34595         }else{
34596             return s.resizingEl.getHeight();
34597         }
34598     },
34599     
34600     /**
34601      * Called after drag operations to set the size of the resizing element.
34602      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34603      * @param {Number} newSize The new size to set
34604      * @param {Function} onComplete A function to be invoked when resizing is complete
34605      */
34606     setElementSize : function(s, newSize, onComplete){
34607         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34608             if(!s.animate){
34609                 s.resizingEl.setWidth(newSize);
34610                 if(onComplete){
34611                     onComplete(s, newSize);
34612                 }
34613             }else{
34614                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34615             }
34616         }else{
34617             
34618             if(!s.animate){
34619                 s.resizingEl.setHeight(newSize);
34620                 if(onComplete){
34621                     onComplete(s, newSize);
34622                 }
34623             }else{
34624                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34625             }
34626         }
34627     }
34628 };
34629
34630 /** 
34631  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34632  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34633  * Adapter that  moves the splitter element to align with the resized sizing element. 
34634  * Used with an absolute positioned SplitBar.
34635  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34636  * document.body, make sure you assign an id to the body element.
34637  */
34638 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34639     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34640     this.container = Roo.get(container);
34641 };
34642
34643 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34644     init : function(s){
34645         this.basic.init(s);
34646     },
34647     
34648     getElementSize : function(s){
34649         return this.basic.getElementSize(s);
34650     },
34651     
34652     setElementSize : function(s, newSize, onComplete){
34653         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34654     },
34655     
34656     moveSplitter : function(s){
34657         var yes = Roo.bootstrap.SplitBar;
34658         switch(s.placement){
34659             case yes.LEFT:
34660                 s.el.setX(s.resizingEl.getRight());
34661                 break;
34662             case yes.RIGHT:
34663                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34664                 break;
34665             case yes.TOP:
34666                 s.el.setY(s.resizingEl.getBottom());
34667                 break;
34668             case yes.BOTTOM:
34669                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34670                 break;
34671         }
34672     }
34673 };
34674
34675 /**
34676  * Orientation constant - Create a vertical SplitBar
34677  * @static
34678  * @type Number
34679  */
34680 Roo.bootstrap.SplitBar.VERTICAL = 1;
34681
34682 /**
34683  * Orientation constant - Create a horizontal SplitBar
34684  * @static
34685  * @type Number
34686  */
34687 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34688
34689 /**
34690  * Placement constant - The resizing element is to the left of the splitter element
34691  * @static
34692  * @type Number
34693  */
34694 Roo.bootstrap.SplitBar.LEFT = 1;
34695
34696 /**
34697  * Placement constant - The resizing element is to the right of the splitter element
34698  * @static
34699  * @type Number
34700  */
34701 Roo.bootstrap.SplitBar.RIGHT = 2;
34702
34703 /**
34704  * Placement constant - The resizing element is positioned above the splitter element
34705  * @static
34706  * @type Number
34707  */
34708 Roo.bootstrap.SplitBar.TOP = 3;
34709
34710 /**
34711  * Placement constant - The resizing element is positioned under splitter element
34712  * @static
34713  * @type Number
34714  */
34715 Roo.bootstrap.SplitBar.BOTTOM = 4;
34716 Roo.namespace("Roo.bootstrap.layout");/*
34717  * Based on:
34718  * Ext JS Library 1.1.1
34719  * Copyright(c) 2006-2007, Ext JS, LLC.
34720  *
34721  * Originally Released Under LGPL - original licence link has changed is not relivant.
34722  *
34723  * Fork - LGPL
34724  * <script type="text/javascript">
34725  */
34726
34727 /**
34728  * @class Roo.bootstrap.layout.Manager
34729  * @extends Roo.bootstrap.Component
34730  * Base class for layout managers.
34731  */
34732 Roo.bootstrap.layout.Manager = function(config)
34733 {
34734     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34735
34736
34737
34738
34739
34740     /** false to disable window resize monitoring @type Boolean */
34741     this.monitorWindowResize = true;
34742     this.regions = {};
34743     this.addEvents({
34744         /**
34745          * @event layout
34746          * Fires when a layout is performed.
34747          * @param {Roo.LayoutManager} this
34748          */
34749         "layout" : true,
34750         /**
34751          * @event regionresized
34752          * Fires when the user resizes a region.
34753          * @param {Roo.LayoutRegion} region The resized region
34754          * @param {Number} newSize The new size (width for east/west, height for north/south)
34755          */
34756         "regionresized" : true,
34757         /**
34758          * @event regioncollapsed
34759          * Fires when a region is collapsed.
34760          * @param {Roo.LayoutRegion} region The collapsed region
34761          */
34762         "regioncollapsed" : true,
34763         /**
34764          * @event regionexpanded
34765          * Fires when a region is expanded.
34766          * @param {Roo.LayoutRegion} region The expanded region
34767          */
34768         "regionexpanded" : true
34769     });
34770     this.updating = false;
34771
34772     if (config.el) {
34773         this.el = Roo.get(config.el);
34774         this.initEvents();
34775     }
34776
34777 };
34778
34779 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34780
34781
34782     regions : null,
34783
34784     monitorWindowResize : true,
34785
34786
34787     updating : false,
34788
34789
34790     onRender : function(ct, position)
34791     {
34792         if(!this.el){
34793             this.el = Roo.get(ct);
34794             this.initEvents();
34795         }
34796         //this.fireEvent('render',this);
34797     },
34798
34799
34800     initEvents: function()
34801     {
34802
34803
34804         // ie scrollbar fix
34805         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34806             document.body.scroll = "no";
34807         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34808             this.el.position('relative');
34809         }
34810         this.id = this.el.id;
34811         this.el.addClass("roo-layout-container");
34812         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34813         if(this.el.dom != document.body ) {
34814             this.el.on('resize', this.layout,this);
34815             this.el.on('show', this.layout,this);
34816         }
34817
34818     },
34819
34820     /**
34821      * Returns true if this layout is currently being updated
34822      * @return {Boolean}
34823      */
34824     isUpdating : function(){
34825         return this.updating;
34826     },
34827
34828     /**
34829      * Suspend the LayoutManager from doing auto-layouts while
34830      * making multiple add or remove calls
34831      */
34832     beginUpdate : function(){
34833         this.updating = true;
34834     },
34835
34836     /**
34837      * Restore auto-layouts and optionally disable the manager from performing a layout
34838      * @param {Boolean} noLayout true to disable a layout update
34839      */
34840     endUpdate : function(noLayout){
34841         this.updating = false;
34842         if(!noLayout){
34843             this.layout();
34844         }
34845     },
34846
34847     layout: function(){
34848         // abstract...
34849     },
34850
34851     onRegionResized : function(region, newSize){
34852         this.fireEvent("regionresized", region, newSize);
34853         this.layout();
34854     },
34855
34856     onRegionCollapsed : function(region){
34857         this.fireEvent("regioncollapsed", region);
34858     },
34859
34860     onRegionExpanded : function(region){
34861         this.fireEvent("regionexpanded", region);
34862     },
34863
34864     /**
34865      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34866      * performs box-model adjustments.
34867      * @return {Object} The size as an object {width: (the width), height: (the height)}
34868      */
34869     getViewSize : function()
34870     {
34871         var size;
34872         if(this.el.dom != document.body){
34873             size = this.el.getSize();
34874         }else{
34875             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34876         }
34877         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34878         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34879         return size;
34880     },
34881
34882     /**
34883      * Returns the Element this layout is bound to.
34884      * @return {Roo.Element}
34885      */
34886     getEl : function(){
34887         return this.el;
34888     },
34889
34890     /**
34891      * Returns the specified region.
34892      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34893      * @return {Roo.LayoutRegion}
34894      */
34895     getRegion : function(target){
34896         return this.regions[target.toLowerCase()];
34897     },
34898
34899     onWindowResize : function(){
34900         if(this.monitorWindowResize){
34901             this.layout();
34902         }
34903     }
34904 });
34905 /*
34906  * Based on:
34907  * Ext JS Library 1.1.1
34908  * Copyright(c) 2006-2007, Ext JS, LLC.
34909  *
34910  * Originally Released Under LGPL - original licence link has changed is not relivant.
34911  *
34912  * Fork - LGPL
34913  * <script type="text/javascript">
34914  */
34915 /**
34916  * @class Roo.bootstrap.layout.Border
34917  * @extends Roo.bootstrap.layout.Manager
34918  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34919  * please see: examples/bootstrap/nested.html<br><br>
34920  
34921 <b>The container the layout is rendered into can be either the body element or any other element.
34922 If it is not the body element, the container needs to either be an absolute positioned element,
34923 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34924 the container size if it is not the body element.</b>
34925
34926 * @constructor
34927 * Create a new Border
34928 * @param {Object} config Configuration options
34929  */
34930 Roo.bootstrap.layout.Border = function(config){
34931     config = config || {};
34932     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34933     
34934     
34935     
34936     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34937         if(config[region]){
34938             config[region].region = region;
34939             this.addRegion(config[region]);
34940         }
34941     },this);
34942     
34943 };
34944
34945 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34946
34947 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34948     /**
34949      * Creates and adds a new region if it doesn't already exist.
34950      * @param {String} target The target region key (north, south, east, west or center).
34951      * @param {Object} config The regions config object
34952      * @return {BorderLayoutRegion} The new region
34953      */
34954     addRegion : function(config)
34955     {
34956         if(!this.regions[config.region]){
34957             var r = this.factory(config);
34958             this.bindRegion(r);
34959         }
34960         return this.regions[config.region];
34961     },
34962
34963     // private (kinda)
34964     bindRegion : function(r){
34965         this.regions[r.config.region] = r;
34966         
34967         r.on("visibilitychange",    this.layout, this);
34968         r.on("paneladded",          this.layout, this);
34969         r.on("panelremoved",        this.layout, this);
34970         r.on("invalidated",         this.layout, this);
34971         r.on("resized",             this.onRegionResized, this);
34972         r.on("collapsed",           this.onRegionCollapsed, this);
34973         r.on("expanded",            this.onRegionExpanded, this);
34974     },
34975
34976     /**
34977      * Performs a layout update.
34978      */
34979     layout : function()
34980     {
34981         if(this.updating) {
34982             return;
34983         }
34984         
34985         // render all the rebions if they have not been done alreayd?
34986         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34987             if(this.regions[region] && !this.regions[region].bodyEl){
34988                 this.regions[region].onRender(this.el)
34989             }
34990         },this);
34991         
34992         var size = this.getViewSize();
34993         var w = size.width;
34994         var h = size.height;
34995         var centerW = w;
34996         var centerH = h;
34997         var centerY = 0;
34998         var centerX = 0;
34999         //var x = 0, y = 0;
35000
35001         var rs = this.regions;
35002         var north = rs["north"];
35003         var south = rs["south"]; 
35004         var west = rs["west"];
35005         var east = rs["east"];
35006         var center = rs["center"];
35007         //if(this.hideOnLayout){ // not supported anymore
35008             //c.el.setStyle("display", "none");
35009         //}
35010         if(north && north.isVisible()){
35011             var b = north.getBox();
35012             var m = north.getMargins();
35013             b.width = w - (m.left+m.right);
35014             b.x = m.left;
35015             b.y = m.top;
35016             centerY = b.height + b.y + m.bottom;
35017             centerH -= centerY;
35018             north.updateBox(this.safeBox(b));
35019         }
35020         if(south && south.isVisible()){
35021             var b = south.getBox();
35022             var m = south.getMargins();
35023             b.width = w - (m.left+m.right);
35024             b.x = m.left;
35025             var totalHeight = (b.height + m.top + m.bottom);
35026             b.y = h - totalHeight + m.top;
35027             centerH -= totalHeight;
35028             south.updateBox(this.safeBox(b));
35029         }
35030         if(west && west.isVisible()){
35031             var b = west.getBox();
35032             var m = west.getMargins();
35033             b.height = centerH - (m.top+m.bottom);
35034             b.x = m.left;
35035             b.y = centerY + m.top;
35036             var totalWidth = (b.width + m.left + m.right);
35037             centerX += totalWidth;
35038             centerW -= totalWidth;
35039             west.updateBox(this.safeBox(b));
35040         }
35041         if(east && east.isVisible()){
35042             var b = east.getBox();
35043             var m = east.getMargins();
35044             b.height = centerH - (m.top+m.bottom);
35045             var totalWidth = (b.width + m.left + m.right);
35046             b.x = w - totalWidth + m.left;
35047             b.y = centerY + m.top;
35048             centerW -= totalWidth;
35049             east.updateBox(this.safeBox(b));
35050         }
35051         if(center){
35052             var m = center.getMargins();
35053             var centerBox = {
35054                 x: centerX + m.left,
35055                 y: centerY + m.top,
35056                 width: centerW - (m.left+m.right),
35057                 height: centerH - (m.top+m.bottom)
35058             };
35059             //if(this.hideOnLayout){
35060                 //center.el.setStyle("display", "block");
35061             //}
35062             center.updateBox(this.safeBox(centerBox));
35063         }
35064         this.el.repaint();
35065         this.fireEvent("layout", this);
35066     },
35067
35068     // private
35069     safeBox : function(box){
35070         box.width = Math.max(0, box.width);
35071         box.height = Math.max(0, box.height);
35072         return box;
35073     },
35074
35075     /**
35076      * Adds a ContentPanel (or subclass) to this layout.
35077      * @param {String} target The target region key (north, south, east, west or center).
35078      * @param {Roo.ContentPanel} panel The panel to add
35079      * @return {Roo.ContentPanel} The added panel
35080      */
35081     add : function(target, panel){
35082          
35083         target = target.toLowerCase();
35084         return this.regions[target].add(panel);
35085     },
35086
35087     /**
35088      * Remove a ContentPanel (or subclass) to this layout.
35089      * @param {String} target The target region key (north, south, east, west or center).
35090      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35091      * @return {Roo.ContentPanel} The removed panel
35092      */
35093     remove : function(target, panel){
35094         target = target.toLowerCase();
35095         return this.regions[target].remove(panel);
35096     },
35097
35098     /**
35099      * Searches all regions for a panel with the specified id
35100      * @param {String} panelId
35101      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35102      */
35103     findPanel : function(panelId){
35104         var rs = this.regions;
35105         for(var target in rs){
35106             if(typeof rs[target] != "function"){
35107                 var p = rs[target].getPanel(panelId);
35108                 if(p){
35109                     return p;
35110                 }
35111             }
35112         }
35113         return null;
35114     },
35115
35116     /**
35117      * Searches all regions for a panel with the specified id and activates (shows) it.
35118      * @param {String/ContentPanel} panelId The panels id or the panel itself
35119      * @return {Roo.ContentPanel} The shown panel or null
35120      */
35121     showPanel : function(panelId) {
35122       var rs = this.regions;
35123       for(var target in rs){
35124          var r = rs[target];
35125          if(typeof r != "function"){
35126             if(r.hasPanel(panelId)){
35127                return r.showPanel(panelId);
35128             }
35129          }
35130       }
35131       return null;
35132    },
35133
35134    /**
35135      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35136      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35137      */
35138    /*
35139     restoreState : function(provider){
35140         if(!provider){
35141             provider = Roo.state.Manager;
35142         }
35143         var sm = new Roo.LayoutStateManager();
35144         sm.init(this, provider);
35145     },
35146 */
35147  
35148  
35149     /**
35150      * Adds a xtype elements to the layout.
35151      * <pre><code>
35152
35153 layout.addxtype({
35154        xtype : 'ContentPanel',
35155        region: 'west',
35156        items: [ .... ]
35157    }
35158 );
35159
35160 layout.addxtype({
35161         xtype : 'NestedLayoutPanel',
35162         region: 'west',
35163         layout: {
35164            center: { },
35165            west: { }   
35166         },
35167         items : [ ... list of content panels or nested layout panels.. ]
35168    }
35169 );
35170 </code></pre>
35171      * @param {Object} cfg Xtype definition of item to add.
35172      */
35173     addxtype : function(cfg)
35174     {
35175         // basically accepts a pannel...
35176         // can accept a layout region..!?!?
35177         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35178         
35179         
35180         // theory?  children can only be panels??
35181         
35182         //if (!cfg.xtype.match(/Panel$/)) {
35183         //    return false;
35184         //}
35185         var ret = false;
35186         
35187         if (typeof(cfg.region) == 'undefined') {
35188             Roo.log("Failed to add Panel, region was not set");
35189             Roo.log(cfg);
35190             return false;
35191         }
35192         var region = cfg.region;
35193         delete cfg.region;
35194         
35195           
35196         var xitems = [];
35197         if (cfg.items) {
35198             xitems = cfg.items;
35199             delete cfg.items;
35200         }
35201         var nb = false;
35202         
35203         switch(cfg.xtype) 
35204         {
35205             case 'Content':  // ContentPanel (el, cfg)
35206             case 'Scroll':  // ContentPanel (el, cfg)
35207             case 'View': 
35208                 cfg.autoCreate = true;
35209                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35210                 //} else {
35211                 //    var el = this.el.createChild();
35212                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35213                 //}
35214                 
35215                 this.add(region, ret);
35216                 break;
35217             
35218             /*
35219             case 'TreePanel': // our new panel!
35220                 cfg.el = this.el.createChild();
35221                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35222                 this.add(region, ret);
35223                 break;
35224             */
35225             
35226             case 'Nest': 
35227                 // create a new Layout (which is  a Border Layout...
35228                 
35229                 var clayout = cfg.layout;
35230                 clayout.el  = this.el.createChild();
35231                 clayout.items   = clayout.items  || [];
35232                 
35233                 delete cfg.layout;
35234                 
35235                 // replace this exitems with the clayout ones..
35236                 xitems = clayout.items;
35237                  
35238                 // force background off if it's in center...
35239                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35240                     cfg.background = false;
35241                 }
35242                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35243                 
35244                 
35245                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35246                 //console.log('adding nested layout panel '  + cfg.toSource());
35247                 this.add(region, ret);
35248                 nb = {}; /// find first...
35249                 break;
35250             
35251             case 'Grid':
35252                 
35253                 // needs grid and region
35254                 
35255                 //var el = this.getRegion(region).el.createChild();
35256                 /*
35257                  *var el = this.el.createChild();
35258                 // create the grid first...
35259                 cfg.grid.container = el;
35260                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35261                 */
35262                 
35263                 if (region == 'center' && this.active ) {
35264                     cfg.background = false;
35265                 }
35266                 
35267                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35268                 
35269                 this.add(region, ret);
35270                 /*
35271                 if (cfg.background) {
35272                     // render grid on panel activation (if panel background)
35273                     ret.on('activate', function(gp) {
35274                         if (!gp.grid.rendered) {
35275                     //        gp.grid.render(el);
35276                         }
35277                     });
35278                 } else {
35279                   //  cfg.grid.render(el);
35280                 }
35281                 */
35282                 break;
35283            
35284            
35285             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35286                 // it was the old xcomponent building that caused this before.
35287                 // espeically if border is the top element in the tree.
35288                 ret = this;
35289                 break; 
35290                 
35291                     
35292                 
35293                 
35294                 
35295             default:
35296                 /*
35297                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35298                     
35299                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35300                     this.add(region, ret);
35301                 } else {
35302                 */
35303                     Roo.log(cfg);
35304                     throw "Can not add '" + cfg.xtype + "' to Border";
35305                     return null;
35306              
35307                                 
35308              
35309         }
35310         this.beginUpdate();
35311         // add children..
35312         var region = '';
35313         var abn = {};
35314         Roo.each(xitems, function(i)  {
35315             region = nb && i.region ? i.region : false;
35316             
35317             var add = ret.addxtype(i);
35318            
35319             if (region) {
35320                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35321                 if (!i.background) {
35322                     abn[region] = nb[region] ;
35323                 }
35324             }
35325             
35326         });
35327         this.endUpdate();
35328
35329         // make the last non-background panel active..
35330         //if (nb) { Roo.log(abn); }
35331         if (nb) {
35332             
35333             for(var r in abn) {
35334                 region = this.getRegion(r);
35335                 if (region) {
35336                     // tried using nb[r], but it does not work..
35337                      
35338                     region.showPanel(abn[r]);
35339                    
35340                 }
35341             }
35342         }
35343         return ret;
35344         
35345     },
35346     
35347     
35348 // private
35349     factory : function(cfg)
35350     {
35351         
35352         var validRegions = Roo.bootstrap.layout.Border.regions;
35353
35354         var target = cfg.region;
35355         cfg.mgr = this;
35356         
35357         var r = Roo.bootstrap.layout;
35358         Roo.log(target);
35359         switch(target){
35360             case "north":
35361                 return new r.North(cfg);
35362             case "south":
35363                 return new r.South(cfg);
35364             case "east":
35365                 return new r.East(cfg);
35366             case "west":
35367                 return new r.West(cfg);
35368             case "center":
35369                 return new r.Center(cfg);
35370         }
35371         throw 'Layout region "'+target+'" not supported.';
35372     }
35373     
35374     
35375 });
35376  /*
35377  * Based on:
35378  * Ext JS Library 1.1.1
35379  * Copyright(c) 2006-2007, Ext JS, LLC.
35380  *
35381  * Originally Released Under LGPL - original licence link has changed is not relivant.
35382  *
35383  * Fork - LGPL
35384  * <script type="text/javascript">
35385  */
35386  
35387 /**
35388  * @class Roo.bootstrap.layout.Basic
35389  * @extends Roo.util.Observable
35390  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35391  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35392  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35393  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35394  * @cfg {string}   region  the region that it inhabits..
35395  * @cfg {bool}   skipConfig skip config?
35396  * 
35397
35398  */
35399 Roo.bootstrap.layout.Basic = function(config){
35400     
35401     this.mgr = config.mgr;
35402     
35403     this.position = config.region;
35404     
35405     var skipConfig = config.skipConfig;
35406     
35407     this.events = {
35408         /**
35409          * @scope Roo.BasicLayoutRegion
35410          */
35411         
35412         /**
35413          * @event beforeremove
35414          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35415          * @param {Roo.LayoutRegion} this
35416          * @param {Roo.ContentPanel} panel The panel
35417          * @param {Object} e The cancel event object
35418          */
35419         "beforeremove" : true,
35420         /**
35421          * @event invalidated
35422          * Fires when the layout for this region is changed.
35423          * @param {Roo.LayoutRegion} this
35424          */
35425         "invalidated" : true,
35426         /**
35427          * @event visibilitychange
35428          * Fires when this region is shown or hidden 
35429          * @param {Roo.LayoutRegion} this
35430          * @param {Boolean} visibility true or false
35431          */
35432         "visibilitychange" : true,
35433         /**
35434          * @event paneladded
35435          * Fires when a panel is added. 
35436          * @param {Roo.LayoutRegion} this
35437          * @param {Roo.ContentPanel} panel The panel
35438          */
35439         "paneladded" : true,
35440         /**
35441          * @event panelremoved
35442          * Fires when a panel is removed. 
35443          * @param {Roo.LayoutRegion} this
35444          * @param {Roo.ContentPanel} panel The panel
35445          */
35446         "panelremoved" : true,
35447         /**
35448          * @event beforecollapse
35449          * Fires when this region before collapse.
35450          * @param {Roo.LayoutRegion} this
35451          */
35452         "beforecollapse" : true,
35453         /**
35454          * @event collapsed
35455          * Fires when this region is collapsed.
35456          * @param {Roo.LayoutRegion} this
35457          */
35458         "collapsed" : true,
35459         /**
35460          * @event expanded
35461          * Fires when this region is expanded.
35462          * @param {Roo.LayoutRegion} this
35463          */
35464         "expanded" : true,
35465         /**
35466          * @event slideshow
35467          * Fires when this region is slid into view.
35468          * @param {Roo.LayoutRegion} this
35469          */
35470         "slideshow" : true,
35471         /**
35472          * @event slidehide
35473          * Fires when this region slides out of view. 
35474          * @param {Roo.LayoutRegion} this
35475          */
35476         "slidehide" : true,
35477         /**
35478          * @event panelactivated
35479          * Fires when a panel is activated. 
35480          * @param {Roo.LayoutRegion} this
35481          * @param {Roo.ContentPanel} panel The activated panel
35482          */
35483         "panelactivated" : true,
35484         /**
35485          * @event resized
35486          * Fires when the user resizes this region. 
35487          * @param {Roo.LayoutRegion} this
35488          * @param {Number} newSize The new size (width for east/west, height for north/south)
35489          */
35490         "resized" : true
35491     };
35492     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35493     this.panels = new Roo.util.MixedCollection();
35494     this.panels.getKey = this.getPanelId.createDelegate(this);
35495     this.box = null;
35496     this.activePanel = null;
35497     // ensure listeners are added...
35498     
35499     if (config.listeners || config.events) {
35500         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35501             listeners : config.listeners || {},
35502             events : config.events || {}
35503         });
35504     }
35505     
35506     if(skipConfig !== true){
35507         this.applyConfig(config);
35508     }
35509 };
35510
35511 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35512 {
35513     getPanelId : function(p){
35514         return p.getId();
35515     },
35516     
35517     applyConfig : function(config){
35518         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35519         this.config = config;
35520         
35521     },
35522     
35523     /**
35524      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35525      * the width, for horizontal (north, south) the height.
35526      * @param {Number} newSize The new width or height
35527      */
35528     resizeTo : function(newSize){
35529         var el = this.el ? this.el :
35530                  (this.activePanel ? this.activePanel.getEl() : null);
35531         if(el){
35532             switch(this.position){
35533                 case "east":
35534                 case "west":
35535                     el.setWidth(newSize);
35536                     this.fireEvent("resized", this, newSize);
35537                 break;
35538                 case "north":
35539                 case "south":
35540                     el.setHeight(newSize);
35541                     this.fireEvent("resized", this, newSize);
35542                 break;                
35543             }
35544         }
35545     },
35546     
35547     getBox : function(){
35548         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35549     },
35550     
35551     getMargins : function(){
35552         return this.margins;
35553     },
35554     
35555     updateBox : function(box){
35556         this.box = box;
35557         var el = this.activePanel.getEl();
35558         el.dom.style.left = box.x + "px";
35559         el.dom.style.top = box.y + "px";
35560         this.activePanel.setSize(box.width, box.height);
35561     },
35562     
35563     /**
35564      * Returns the container element for this region.
35565      * @return {Roo.Element}
35566      */
35567     getEl : function(){
35568         return this.activePanel;
35569     },
35570     
35571     /**
35572      * Returns true if this region is currently visible.
35573      * @return {Boolean}
35574      */
35575     isVisible : function(){
35576         return this.activePanel ? true : false;
35577     },
35578     
35579     setActivePanel : function(panel){
35580         panel = this.getPanel(panel);
35581         if(this.activePanel && this.activePanel != panel){
35582             this.activePanel.setActiveState(false);
35583             this.activePanel.getEl().setLeftTop(-10000,-10000);
35584         }
35585         this.activePanel = panel;
35586         panel.setActiveState(true);
35587         if(this.box){
35588             panel.setSize(this.box.width, this.box.height);
35589         }
35590         this.fireEvent("panelactivated", this, panel);
35591         this.fireEvent("invalidated");
35592     },
35593     
35594     /**
35595      * Show the specified panel.
35596      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35597      * @return {Roo.ContentPanel} The shown panel or null
35598      */
35599     showPanel : function(panel){
35600         panel = this.getPanel(panel);
35601         if(panel){
35602             this.setActivePanel(panel);
35603         }
35604         return panel;
35605     },
35606     
35607     /**
35608      * Get the active panel for this region.
35609      * @return {Roo.ContentPanel} The active panel or null
35610      */
35611     getActivePanel : function(){
35612         return this.activePanel;
35613     },
35614     
35615     /**
35616      * Add the passed ContentPanel(s)
35617      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35618      * @return {Roo.ContentPanel} The panel added (if only one was added)
35619      */
35620     add : function(panel){
35621         if(arguments.length > 1){
35622             for(var i = 0, len = arguments.length; i < len; i++) {
35623                 this.add(arguments[i]);
35624             }
35625             return null;
35626         }
35627         if(this.hasPanel(panel)){
35628             this.showPanel(panel);
35629             return panel;
35630         }
35631         var el = panel.getEl();
35632         if(el.dom.parentNode != this.mgr.el.dom){
35633             this.mgr.el.dom.appendChild(el.dom);
35634         }
35635         if(panel.setRegion){
35636             panel.setRegion(this);
35637         }
35638         this.panels.add(panel);
35639         el.setStyle("position", "absolute");
35640         if(!panel.background){
35641             this.setActivePanel(panel);
35642             if(this.config.initialSize && this.panels.getCount()==1){
35643                 this.resizeTo(this.config.initialSize);
35644             }
35645         }
35646         this.fireEvent("paneladded", this, panel);
35647         return panel;
35648     },
35649     
35650     /**
35651      * Returns true if the panel is in this region.
35652      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35653      * @return {Boolean}
35654      */
35655     hasPanel : function(panel){
35656         if(typeof panel == "object"){ // must be panel obj
35657             panel = panel.getId();
35658         }
35659         return this.getPanel(panel) ? true : false;
35660     },
35661     
35662     /**
35663      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35664      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35665      * @param {Boolean} preservePanel Overrides the config preservePanel option
35666      * @return {Roo.ContentPanel} The panel that was removed
35667      */
35668     remove : function(panel, preservePanel){
35669         panel = this.getPanel(panel);
35670         if(!panel){
35671             return null;
35672         }
35673         var e = {};
35674         this.fireEvent("beforeremove", this, panel, e);
35675         if(e.cancel === true){
35676             return null;
35677         }
35678         var panelId = panel.getId();
35679         this.panels.removeKey(panelId);
35680         return panel;
35681     },
35682     
35683     /**
35684      * Returns the panel specified or null if it's not in this region.
35685      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35686      * @return {Roo.ContentPanel}
35687      */
35688     getPanel : function(id){
35689         if(typeof id == "object"){ // must be panel obj
35690             return id;
35691         }
35692         return this.panels.get(id);
35693     },
35694     
35695     /**
35696      * Returns this regions position (north/south/east/west/center).
35697      * @return {String} 
35698      */
35699     getPosition: function(){
35700         return this.position;    
35701     }
35702 });/*
35703  * Based on:
35704  * Ext JS Library 1.1.1
35705  * Copyright(c) 2006-2007, Ext JS, LLC.
35706  *
35707  * Originally Released Under LGPL - original licence link has changed is not relivant.
35708  *
35709  * Fork - LGPL
35710  * <script type="text/javascript">
35711  */
35712  
35713 /**
35714  * @class Roo.bootstrap.layout.Region
35715  * @extends Roo.bootstrap.layout.Basic
35716  * This class represents a region in a layout manager.
35717  
35718  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35719  * @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})
35720  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35721  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35722  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35723  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35724  * @cfg {String}    title           The title for the region (overrides panel titles)
35725  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35726  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35727  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35728  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35729  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35730  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35731  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35732  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35733  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35734  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35735
35736  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35737  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35738  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35739  * @cfg {Number}    width           For East/West panels
35740  * @cfg {Number}    height          For North/South panels
35741  * @cfg {Boolean}   split           To show the splitter
35742  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35743  * 
35744  * @cfg {string}   cls             Extra CSS classes to add to region
35745  * 
35746  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35747  * @cfg {string}   region  the region that it inhabits..
35748  *
35749
35750  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35751  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35752
35753  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35754  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35755  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35756  */
35757 Roo.bootstrap.layout.Region = function(config)
35758 {
35759     this.applyConfig(config);
35760
35761     var mgr = config.mgr;
35762     var pos = config.region;
35763     config.skipConfig = true;
35764     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35765     
35766     if (mgr.el) {
35767         this.onRender(mgr.el);   
35768     }
35769      
35770     this.visible = true;
35771     this.collapsed = false;
35772     this.unrendered_panels = [];
35773 };
35774
35775 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35776
35777     position: '', // set by wrapper (eg. north/south etc..)
35778     unrendered_panels : null,  // unrendered panels.
35779     createBody : function(){
35780         /** This region's body element 
35781         * @type Roo.Element */
35782         this.bodyEl = this.el.createChild({
35783                 tag: "div",
35784                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35785         });
35786     },
35787
35788     onRender: function(ctr, pos)
35789     {
35790         var dh = Roo.DomHelper;
35791         /** This region's container element 
35792         * @type Roo.Element */
35793         this.el = dh.append(ctr.dom, {
35794                 tag: "div",
35795                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35796             }, true);
35797         /** This region's title element 
35798         * @type Roo.Element */
35799     
35800         this.titleEl = dh.append(this.el.dom,
35801             {
35802                     tag: "div",
35803                     unselectable: "on",
35804                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35805                     children:[
35806                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35807                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35808                     ]}, true);
35809         
35810         this.titleEl.enableDisplayMode();
35811         /** This region's title text element 
35812         * @type HTMLElement */
35813         this.titleTextEl = this.titleEl.dom.firstChild;
35814         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35815         /*
35816         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35817         this.closeBtn.enableDisplayMode();
35818         this.closeBtn.on("click", this.closeClicked, this);
35819         this.closeBtn.hide();
35820     */
35821         this.createBody(this.config);
35822         if(this.config.hideWhenEmpty){
35823             this.hide();
35824             this.on("paneladded", this.validateVisibility, this);
35825             this.on("panelremoved", this.validateVisibility, this);
35826         }
35827         if(this.autoScroll){
35828             this.bodyEl.setStyle("overflow", "auto");
35829         }else{
35830             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35831         }
35832         //if(c.titlebar !== false){
35833             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35834                 this.titleEl.hide();
35835             }else{
35836                 this.titleEl.show();
35837                 if(this.config.title){
35838                     this.titleTextEl.innerHTML = this.config.title;
35839                 }
35840             }
35841         //}
35842         if(this.config.collapsed){
35843             this.collapse(true);
35844         }
35845         if(this.config.hidden){
35846             this.hide();
35847         }
35848         
35849         if (this.unrendered_panels && this.unrendered_panels.length) {
35850             for (var i =0;i< this.unrendered_panels.length; i++) {
35851                 this.add(this.unrendered_panels[i]);
35852             }
35853             this.unrendered_panels = null;
35854             
35855         }
35856         
35857     },
35858     
35859     applyConfig : function(c)
35860     {
35861         /*
35862          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35863             var dh = Roo.DomHelper;
35864             if(c.titlebar !== false){
35865                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35866                 this.collapseBtn.on("click", this.collapse, this);
35867                 this.collapseBtn.enableDisplayMode();
35868                 /*
35869                 if(c.showPin === true || this.showPin){
35870                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35871                     this.stickBtn.enableDisplayMode();
35872                     this.stickBtn.on("click", this.expand, this);
35873                     this.stickBtn.hide();
35874                 }
35875                 
35876             }
35877             */
35878             /** This region's collapsed element
35879             * @type Roo.Element */
35880             /*
35881              *
35882             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35883                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35884             ]}, true);
35885             
35886             if(c.floatable !== false){
35887                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35888                this.collapsedEl.on("click", this.collapseClick, this);
35889             }
35890
35891             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35892                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35893                    id: "message", unselectable: "on", style:{"float":"left"}});
35894                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35895              }
35896             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35897             this.expandBtn.on("click", this.expand, this);
35898             
35899         }
35900         
35901         if(this.collapseBtn){
35902             this.collapseBtn.setVisible(c.collapsible == true);
35903         }
35904         
35905         this.cmargins = c.cmargins || this.cmargins ||
35906                          (this.position == "west" || this.position == "east" ?
35907                              {top: 0, left: 2, right:2, bottom: 0} :
35908                              {top: 2, left: 0, right:0, bottom: 2});
35909         */
35910         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35911         
35912         
35913         this.bottomTabs = c.tabPosition != "top";
35914         
35915         this.autoScroll = c.autoScroll || false;
35916         
35917         
35918        
35919         
35920         this.duration = c.duration || .30;
35921         this.slideDuration = c.slideDuration || .45;
35922         this.config = c;
35923        
35924     },
35925     /**
35926      * Returns true if this region is currently visible.
35927      * @return {Boolean}
35928      */
35929     isVisible : function(){
35930         return this.visible;
35931     },
35932
35933     /**
35934      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35935      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35936      */
35937     //setCollapsedTitle : function(title){
35938     //    title = title || "&#160;";
35939      //   if(this.collapsedTitleTextEl){
35940       //      this.collapsedTitleTextEl.innerHTML = title;
35941        // }
35942     //},
35943
35944     getBox : function(){
35945         var b;
35946       //  if(!this.collapsed){
35947             b = this.el.getBox(false, true);
35948        // }else{
35949           //  b = this.collapsedEl.getBox(false, true);
35950         //}
35951         return b;
35952     },
35953
35954     getMargins : function(){
35955         return this.margins;
35956         //return this.collapsed ? this.cmargins : this.margins;
35957     },
35958 /*
35959     highlight : function(){
35960         this.el.addClass("x-layout-panel-dragover");
35961     },
35962
35963     unhighlight : function(){
35964         this.el.removeClass("x-layout-panel-dragover");
35965     },
35966 */
35967     updateBox : function(box)
35968     {
35969         if (!this.bodyEl) {
35970             return; // not rendered yet..
35971         }
35972         
35973         this.box = box;
35974         if(!this.collapsed){
35975             this.el.dom.style.left = box.x + "px";
35976             this.el.dom.style.top = box.y + "px";
35977             this.updateBody(box.width, box.height);
35978         }else{
35979             this.collapsedEl.dom.style.left = box.x + "px";
35980             this.collapsedEl.dom.style.top = box.y + "px";
35981             this.collapsedEl.setSize(box.width, box.height);
35982         }
35983         if(this.tabs){
35984             this.tabs.autoSizeTabs();
35985         }
35986     },
35987
35988     updateBody : function(w, h)
35989     {
35990         if(w !== null){
35991             this.el.setWidth(w);
35992             w -= this.el.getBorderWidth("rl");
35993             if(this.config.adjustments){
35994                 w += this.config.adjustments[0];
35995             }
35996         }
35997         if(h !== null && h > 0){
35998             this.el.setHeight(h);
35999             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36000             h -= this.el.getBorderWidth("tb");
36001             if(this.config.adjustments){
36002                 h += this.config.adjustments[1];
36003             }
36004             this.bodyEl.setHeight(h);
36005             if(this.tabs){
36006                 h = this.tabs.syncHeight(h);
36007             }
36008         }
36009         if(this.panelSize){
36010             w = w !== null ? w : this.panelSize.width;
36011             h = h !== null ? h : this.panelSize.height;
36012         }
36013         if(this.activePanel){
36014             var el = this.activePanel.getEl();
36015             w = w !== null ? w : el.getWidth();
36016             h = h !== null ? h : el.getHeight();
36017             this.panelSize = {width: w, height: h};
36018             this.activePanel.setSize(w, h);
36019         }
36020         if(Roo.isIE && this.tabs){
36021             this.tabs.el.repaint();
36022         }
36023     },
36024
36025     /**
36026      * Returns the container element for this region.
36027      * @return {Roo.Element}
36028      */
36029     getEl : function(){
36030         return this.el;
36031     },
36032
36033     /**
36034      * Hides this region.
36035      */
36036     hide : function(){
36037         //if(!this.collapsed){
36038             this.el.dom.style.left = "-2000px";
36039             this.el.hide();
36040         //}else{
36041          //   this.collapsedEl.dom.style.left = "-2000px";
36042          //   this.collapsedEl.hide();
36043        // }
36044         this.visible = false;
36045         this.fireEvent("visibilitychange", this, false);
36046     },
36047
36048     /**
36049      * Shows this region if it was previously hidden.
36050      */
36051     show : function(){
36052         //if(!this.collapsed){
36053             this.el.show();
36054         //}else{
36055         //    this.collapsedEl.show();
36056        // }
36057         this.visible = true;
36058         this.fireEvent("visibilitychange", this, true);
36059     },
36060 /*
36061     closeClicked : function(){
36062         if(this.activePanel){
36063             this.remove(this.activePanel);
36064         }
36065     },
36066
36067     collapseClick : function(e){
36068         if(this.isSlid){
36069            e.stopPropagation();
36070            this.slideIn();
36071         }else{
36072            e.stopPropagation();
36073            this.slideOut();
36074         }
36075     },
36076 */
36077     /**
36078      * Collapses this region.
36079      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36080      */
36081     /*
36082     collapse : function(skipAnim, skipCheck = false){
36083         if(this.collapsed) {
36084             return;
36085         }
36086         
36087         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36088             
36089             this.collapsed = true;
36090             if(this.split){
36091                 this.split.el.hide();
36092             }
36093             if(this.config.animate && skipAnim !== true){
36094                 this.fireEvent("invalidated", this);
36095                 this.animateCollapse();
36096             }else{
36097                 this.el.setLocation(-20000,-20000);
36098                 this.el.hide();
36099                 this.collapsedEl.show();
36100                 this.fireEvent("collapsed", this);
36101                 this.fireEvent("invalidated", this);
36102             }
36103         }
36104         
36105     },
36106 */
36107     animateCollapse : function(){
36108         // overridden
36109     },
36110
36111     /**
36112      * Expands this region if it was previously collapsed.
36113      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36114      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36115      */
36116     /*
36117     expand : function(e, skipAnim){
36118         if(e) {
36119             e.stopPropagation();
36120         }
36121         if(!this.collapsed || this.el.hasActiveFx()) {
36122             return;
36123         }
36124         if(this.isSlid){
36125             this.afterSlideIn();
36126             skipAnim = true;
36127         }
36128         this.collapsed = false;
36129         if(this.config.animate && skipAnim !== true){
36130             this.animateExpand();
36131         }else{
36132             this.el.show();
36133             if(this.split){
36134                 this.split.el.show();
36135             }
36136             this.collapsedEl.setLocation(-2000,-2000);
36137             this.collapsedEl.hide();
36138             this.fireEvent("invalidated", this);
36139             this.fireEvent("expanded", this);
36140         }
36141     },
36142 */
36143     animateExpand : function(){
36144         // overridden
36145     },
36146
36147     initTabs : function()
36148     {
36149         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36150         
36151         var ts = new Roo.bootstrap.panel.Tabs({
36152                 el: this.bodyEl.dom,
36153                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36154                 disableTooltips: this.config.disableTabTips,
36155                 toolbar : this.config.toolbar
36156             });
36157         
36158         if(this.config.hideTabs){
36159             ts.stripWrap.setDisplayed(false);
36160         }
36161         this.tabs = ts;
36162         ts.resizeTabs = this.config.resizeTabs === true;
36163         ts.minTabWidth = this.config.minTabWidth || 40;
36164         ts.maxTabWidth = this.config.maxTabWidth || 250;
36165         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36166         ts.monitorResize = false;
36167         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36168         ts.bodyEl.addClass('roo-layout-tabs-body');
36169         this.panels.each(this.initPanelAsTab, this);
36170     },
36171
36172     initPanelAsTab : function(panel){
36173         var ti = this.tabs.addTab(
36174             panel.getEl().id,
36175             panel.getTitle(),
36176             null,
36177             this.config.closeOnTab && panel.isClosable(),
36178             panel.tpl
36179         );
36180         if(panel.tabTip !== undefined){
36181             ti.setTooltip(panel.tabTip);
36182         }
36183         ti.on("activate", function(){
36184               this.setActivePanel(panel);
36185         }, this);
36186         
36187         if(this.config.closeOnTab){
36188             ti.on("beforeclose", function(t, e){
36189                 e.cancel = true;
36190                 this.remove(panel);
36191             }, this);
36192         }
36193         
36194         panel.tabItem = ti;
36195         
36196         return ti;
36197     },
36198
36199     updatePanelTitle : function(panel, title)
36200     {
36201         if(this.activePanel == panel){
36202             this.updateTitle(title);
36203         }
36204         if(this.tabs){
36205             var ti = this.tabs.getTab(panel.getEl().id);
36206             ti.setText(title);
36207             if(panel.tabTip !== undefined){
36208                 ti.setTooltip(panel.tabTip);
36209             }
36210         }
36211     },
36212
36213     updateTitle : function(title){
36214         if(this.titleTextEl && !this.config.title){
36215             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36216         }
36217     },
36218
36219     setActivePanel : function(panel)
36220     {
36221         panel = this.getPanel(panel);
36222         if(this.activePanel && this.activePanel != panel){
36223             if(this.activePanel.setActiveState(false) === false){
36224                 return;
36225             }
36226         }
36227         this.activePanel = panel;
36228         panel.setActiveState(true);
36229         if(this.panelSize){
36230             panel.setSize(this.panelSize.width, this.panelSize.height);
36231         }
36232         if(this.closeBtn){
36233             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36234         }
36235         this.updateTitle(panel.getTitle());
36236         if(this.tabs){
36237             this.fireEvent("invalidated", this);
36238         }
36239         this.fireEvent("panelactivated", this, panel);
36240     },
36241
36242     /**
36243      * Shows the specified panel.
36244      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36245      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36246      */
36247     showPanel : function(panel)
36248     {
36249         panel = this.getPanel(panel);
36250         if(panel){
36251             if(this.tabs){
36252                 var tab = this.tabs.getTab(panel.getEl().id);
36253                 if(tab.isHidden()){
36254                     this.tabs.unhideTab(tab.id);
36255                 }
36256                 tab.activate();
36257             }else{
36258                 this.setActivePanel(panel);
36259             }
36260         }
36261         return panel;
36262     },
36263
36264     /**
36265      * Get the active panel for this region.
36266      * @return {Roo.ContentPanel} The active panel or null
36267      */
36268     getActivePanel : function(){
36269         return this.activePanel;
36270     },
36271
36272     validateVisibility : function(){
36273         if(this.panels.getCount() < 1){
36274             this.updateTitle("&#160;");
36275             this.closeBtn.hide();
36276             this.hide();
36277         }else{
36278             if(!this.isVisible()){
36279                 this.show();
36280             }
36281         }
36282     },
36283
36284     /**
36285      * Adds the passed ContentPanel(s) to this region.
36286      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36287      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36288      */
36289     add : function(panel)
36290     {
36291         if(arguments.length > 1){
36292             for(var i = 0, len = arguments.length; i < len; i++) {
36293                 this.add(arguments[i]);
36294             }
36295             return null;
36296         }
36297         
36298         // if we have not been rendered yet, then we can not really do much of this..
36299         if (!this.bodyEl) {
36300             this.unrendered_panels.push(panel);
36301             return panel;
36302         }
36303         
36304         
36305         
36306         
36307         if(this.hasPanel(panel)){
36308             this.showPanel(panel);
36309             return panel;
36310         }
36311         panel.setRegion(this);
36312         this.panels.add(panel);
36313        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36314             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36315             // and hide them... ???
36316             this.bodyEl.dom.appendChild(panel.getEl().dom);
36317             if(panel.background !== true){
36318                 this.setActivePanel(panel);
36319             }
36320             this.fireEvent("paneladded", this, panel);
36321             return panel;
36322         }
36323         */
36324         if(!this.tabs){
36325             this.initTabs();
36326         }else{
36327             this.initPanelAsTab(panel);
36328         }
36329         
36330         
36331         if(panel.background !== true){
36332             this.tabs.activate(panel.getEl().id);
36333         }
36334         this.fireEvent("paneladded", this, panel);
36335         return panel;
36336     },
36337
36338     /**
36339      * Hides the tab for the specified panel.
36340      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36341      */
36342     hidePanel : function(panel){
36343         if(this.tabs && (panel = this.getPanel(panel))){
36344             this.tabs.hideTab(panel.getEl().id);
36345         }
36346     },
36347
36348     /**
36349      * Unhides the tab for a previously hidden panel.
36350      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36351      */
36352     unhidePanel : function(panel){
36353         if(this.tabs && (panel = this.getPanel(panel))){
36354             this.tabs.unhideTab(panel.getEl().id);
36355         }
36356     },
36357
36358     clearPanels : function(){
36359         while(this.panels.getCount() > 0){
36360              this.remove(this.panels.first());
36361         }
36362     },
36363
36364     /**
36365      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36366      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36367      * @param {Boolean} preservePanel Overrides the config preservePanel option
36368      * @return {Roo.ContentPanel} The panel that was removed
36369      */
36370     remove : function(panel, preservePanel)
36371     {
36372         panel = this.getPanel(panel);
36373         if(!panel){
36374             return null;
36375         }
36376         var e = {};
36377         this.fireEvent("beforeremove", this, panel, e);
36378         if(e.cancel === true){
36379             return null;
36380         }
36381         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36382         var panelId = panel.getId();
36383         this.panels.removeKey(panelId);
36384         if(preservePanel){
36385             document.body.appendChild(panel.getEl().dom);
36386         }
36387         if(this.tabs){
36388             this.tabs.removeTab(panel.getEl().id);
36389         }else if (!preservePanel){
36390             this.bodyEl.dom.removeChild(panel.getEl().dom);
36391         }
36392         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36393             var p = this.panels.first();
36394             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36395             tempEl.appendChild(p.getEl().dom);
36396             this.bodyEl.update("");
36397             this.bodyEl.dom.appendChild(p.getEl().dom);
36398             tempEl = null;
36399             this.updateTitle(p.getTitle());
36400             this.tabs = null;
36401             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36402             this.setActivePanel(p);
36403         }
36404         panel.setRegion(null);
36405         if(this.activePanel == panel){
36406             this.activePanel = null;
36407         }
36408         if(this.config.autoDestroy !== false && preservePanel !== true){
36409             try{panel.destroy();}catch(e){}
36410         }
36411         this.fireEvent("panelremoved", this, panel);
36412         return panel;
36413     },
36414
36415     /**
36416      * Returns the TabPanel component used by this region
36417      * @return {Roo.TabPanel}
36418      */
36419     getTabs : function(){
36420         return this.tabs;
36421     },
36422
36423     createTool : function(parentEl, className){
36424         var btn = Roo.DomHelper.append(parentEl, {
36425             tag: "div",
36426             cls: "x-layout-tools-button",
36427             children: [ {
36428                 tag: "div",
36429                 cls: "roo-layout-tools-button-inner " + className,
36430                 html: "&#160;"
36431             }]
36432         }, true);
36433         btn.addClassOnOver("roo-layout-tools-button-over");
36434         return btn;
36435     }
36436 });/*
36437  * Based on:
36438  * Ext JS Library 1.1.1
36439  * Copyright(c) 2006-2007, Ext JS, LLC.
36440  *
36441  * Originally Released Under LGPL - original licence link has changed is not relivant.
36442  *
36443  * Fork - LGPL
36444  * <script type="text/javascript">
36445  */
36446  
36447
36448
36449 /**
36450  * @class Roo.SplitLayoutRegion
36451  * @extends Roo.LayoutRegion
36452  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36453  */
36454 Roo.bootstrap.layout.Split = function(config){
36455     this.cursor = config.cursor;
36456     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36457 };
36458
36459 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36460 {
36461     splitTip : "Drag to resize.",
36462     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36463     useSplitTips : false,
36464
36465     applyConfig : function(config){
36466         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36467     },
36468     
36469     onRender : function(ctr,pos) {
36470         
36471         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36472         if(!this.config.split){
36473             return;
36474         }
36475         if(!this.split){
36476             
36477             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36478                             tag: "div",
36479                             id: this.el.id + "-split",
36480                             cls: "roo-layout-split roo-layout-split-"+this.position,
36481                             html: "&#160;"
36482             });
36483             /** The SplitBar for this region 
36484             * @type Roo.SplitBar */
36485             // does not exist yet...
36486             Roo.log([this.position, this.orientation]);
36487             
36488             this.split = new Roo.bootstrap.SplitBar({
36489                 dragElement : splitEl,
36490                 resizingElement: this.el,
36491                 orientation : this.orientation
36492             });
36493             
36494             this.split.on("moved", this.onSplitMove, this);
36495             this.split.useShim = this.config.useShim === true;
36496             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36497             if(this.useSplitTips){
36498                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36499             }
36500             //if(config.collapsible){
36501             //    this.split.el.on("dblclick", this.collapse,  this);
36502             //}
36503         }
36504         if(typeof this.config.minSize != "undefined"){
36505             this.split.minSize = this.config.minSize;
36506         }
36507         if(typeof this.config.maxSize != "undefined"){
36508             this.split.maxSize = this.config.maxSize;
36509         }
36510         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36511             this.hideSplitter();
36512         }
36513         
36514     },
36515
36516     getHMaxSize : function(){
36517          var cmax = this.config.maxSize || 10000;
36518          var center = this.mgr.getRegion("center");
36519          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36520     },
36521
36522     getVMaxSize : function(){
36523          var cmax = this.config.maxSize || 10000;
36524          var center = this.mgr.getRegion("center");
36525          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36526     },
36527
36528     onSplitMove : function(split, newSize){
36529         this.fireEvent("resized", this, newSize);
36530     },
36531     
36532     /** 
36533      * Returns the {@link Roo.SplitBar} for this region.
36534      * @return {Roo.SplitBar}
36535      */
36536     getSplitBar : function(){
36537         return this.split;
36538     },
36539     
36540     hide : function(){
36541         this.hideSplitter();
36542         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36543     },
36544
36545     hideSplitter : function(){
36546         if(this.split){
36547             this.split.el.setLocation(-2000,-2000);
36548             this.split.el.hide();
36549         }
36550     },
36551
36552     show : function(){
36553         if(this.split){
36554             this.split.el.show();
36555         }
36556         Roo.bootstrap.layout.Split.superclass.show.call(this);
36557     },
36558     
36559     beforeSlide: function(){
36560         if(Roo.isGecko){// firefox overflow auto bug workaround
36561             this.bodyEl.clip();
36562             if(this.tabs) {
36563                 this.tabs.bodyEl.clip();
36564             }
36565             if(this.activePanel){
36566                 this.activePanel.getEl().clip();
36567                 
36568                 if(this.activePanel.beforeSlide){
36569                     this.activePanel.beforeSlide();
36570                 }
36571             }
36572         }
36573     },
36574     
36575     afterSlide : function(){
36576         if(Roo.isGecko){// firefox overflow auto bug workaround
36577             this.bodyEl.unclip();
36578             if(this.tabs) {
36579                 this.tabs.bodyEl.unclip();
36580             }
36581             if(this.activePanel){
36582                 this.activePanel.getEl().unclip();
36583                 if(this.activePanel.afterSlide){
36584                     this.activePanel.afterSlide();
36585                 }
36586             }
36587         }
36588     },
36589
36590     initAutoHide : function(){
36591         if(this.autoHide !== false){
36592             if(!this.autoHideHd){
36593                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36594                 this.autoHideHd = {
36595                     "mouseout": function(e){
36596                         if(!e.within(this.el, true)){
36597                             st.delay(500);
36598                         }
36599                     },
36600                     "mouseover" : function(e){
36601                         st.cancel();
36602                     },
36603                     scope : this
36604                 };
36605             }
36606             this.el.on(this.autoHideHd);
36607         }
36608     },
36609
36610     clearAutoHide : function(){
36611         if(this.autoHide !== false){
36612             this.el.un("mouseout", this.autoHideHd.mouseout);
36613             this.el.un("mouseover", this.autoHideHd.mouseover);
36614         }
36615     },
36616
36617     clearMonitor : function(){
36618         Roo.get(document).un("click", this.slideInIf, this);
36619     },
36620
36621     // these names are backwards but not changed for compat
36622     slideOut : function(){
36623         if(this.isSlid || this.el.hasActiveFx()){
36624             return;
36625         }
36626         this.isSlid = true;
36627         if(this.collapseBtn){
36628             this.collapseBtn.hide();
36629         }
36630         this.closeBtnState = this.closeBtn.getStyle('display');
36631         this.closeBtn.hide();
36632         if(this.stickBtn){
36633             this.stickBtn.show();
36634         }
36635         this.el.show();
36636         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36637         this.beforeSlide();
36638         this.el.setStyle("z-index", 10001);
36639         this.el.slideIn(this.getSlideAnchor(), {
36640             callback: function(){
36641                 this.afterSlide();
36642                 this.initAutoHide();
36643                 Roo.get(document).on("click", this.slideInIf, this);
36644                 this.fireEvent("slideshow", this);
36645             },
36646             scope: this,
36647             block: true
36648         });
36649     },
36650
36651     afterSlideIn : function(){
36652         this.clearAutoHide();
36653         this.isSlid = false;
36654         this.clearMonitor();
36655         this.el.setStyle("z-index", "");
36656         if(this.collapseBtn){
36657             this.collapseBtn.show();
36658         }
36659         this.closeBtn.setStyle('display', this.closeBtnState);
36660         if(this.stickBtn){
36661             this.stickBtn.hide();
36662         }
36663         this.fireEvent("slidehide", this);
36664     },
36665
36666     slideIn : function(cb){
36667         if(!this.isSlid || this.el.hasActiveFx()){
36668             Roo.callback(cb);
36669             return;
36670         }
36671         this.isSlid = false;
36672         this.beforeSlide();
36673         this.el.slideOut(this.getSlideAnchor(), {
36674             callback: function(){
36675                 this.el.setLeftTop(-10000, -10000);
36676                 this.afterSlide();
36677                 this.afterSlideIn();
36678                 Roo.callback(cb);
36679             },
36680             scope: this,
36681             block: true
36682         });
36683     },
36684     
36685     slideInIf : function(e){
36686         if(!e.within(this.el)){
36687             this.slideIn();
36688         }
36689     },
36690
36691     animateCollapse : function(){
36692         this.beforeSlide();
36693         this.el.setStyle("z-index", 20000);
36694         var anchor = this.getSlideAnchor();
36695         this.el.slideOut(anchor, {
36696             callback : function(){
36697                 this.el.setStyle("z-index", "");
36698                 this.collapsedEl.slideIn(anchor, {duration:.3});
36699                 this.afterSlide();
36700                 this.el.setLocation(-10000,-10000);
36701                 this.el.hide();
36702                 this.fireEvent("collapsed", this);
36703             },
36704             scope: this,
36705             block: true
36706         });
36707     },
36708
36709     animateExpand : function(){
36710         this.beforeSlide();
36711         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36712         this.el.setStyle("z-index", 20000);
36713         this.collapsedEl.hide({
36714             duration:.1
36715         });
36716         this.el.slideIn(this.getSlideAnchor(), {
36717             callback : function(){
36718                 this.el.setStyle("z-index", "");
36719                 this.afterSlide();
36720                 if(this.split){
36721                     this.split.el.show();
36722                 }
36723                 this.fireEvent("invalidated", this);
36724                 this.fireEvent("expanded", this);
36725             },
36726             scope: this,
36727             block: true
36728         });
36729     },
36730
36731     anchors : {
36732         "west" : "left",
36733         "east" : "right",
36734         "north" : "top",
36735         "south" : "bottom"
36736     },
36737
36738     sanchors : {
36739         "west" : "l",
36740         "east" : "r",
36741         "north" : "t",
36742         "south" : "b"
36743     },
36744
36745     canchors : {
36746         "west" : "tl-tr",
36747         "east" : "tr-tl",
36748         "north" : "tl-bl",
36749         "south" : "bl-tl"
36750     },
36751
36752     getAnchor : function(){
36753         return this.anchors[this.position];
36754     },
36755
36756     getCollapseAnchor : function(){
36757         return this.canchors[this.position];
36758     },
36759
36760     getSlideAnchor : function(){
36761         return this.sanchors[this.position];
36762     },
36763
36764     getAlignAdj : function(){
36765         var cm = this.cmargins;
36766         switch(this.position){
36767             case "west":
36768                 return [0, 0];
36769             break;
36770             case "east":
36771                 return [0, 0];
36772             break;
36773             case "north":
36774                 return [0, 0];
36775             break;
36776             case "south":
36777                 return [0, 0];
36778             break;
36779         }
36780     },
36781
36782     getExpandAdj : function(){
36783         var c = this.collapsedEl, cm = this.cmargins;
36784         switch(this.position){
36785             case "west":
36786                 return [-(cm.right+c.getWidth()+cm.left), 0];
36787             break;
36788             case "east":
36789                 return [cm.right+c.getWidth()+cm.left, 0];
36790             break;
36791             case "north":
36792                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36793             break;
36794             case "south":
36795                 return [0, cm.top+cm.bottom+c.getHeight()];
36796             break;
36797         }
36798     }
36799 });/*
36800  * Based on:
36801  * Ext JS Library 1.1.1
36802  * Copyright(c) 2006-2007, Ext JS, LLC.
36803  *
36804  * Originally Released Under LGPL - original licence link has changed is not relivant.
36805  *
36806  * Fork - LGPL
36807  * <script type="text/javascript">
36808  */
36809 /*
36810  * These classes are private internal classes
36811  */
36812 Roo.bootstrap.layout.Center = function(config){
36813     config.region = "center";
36814     Roo.bootstrap.layout.Region.call(this, config);
36815     this.visible = true;
36816     this.minWidth = config.minWidth || 20;
36817     this.minHeight = config.minHeight || 20;
36818 };
36819
36820 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36821     hide : function(){
36822         // center panel can't be hidden
36823     },
36824     
36825     show : function(){
36826         // center panel can't be hidden
36827     },
36828     
36829     getMinWidth: function(){
36830         return this.minWidth;
36831     },
36832     
36833     getMinHeight: function(){
36834         return this.minHeight;
36835     }
36836 });
36837
36838
36839
36840
36841  
36842
36843
36844
36845
36846
36847 Roo.bootstrap.layout.North = function(config)
36848 {
36849     config.region = 'north';
36850     config.cursor = 'n-resize';
36851     
36852     Roo.bootstrap.layout.Split.call(this, config);
36853     
36854     
36855     if(this.split){
36856         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36857         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36858         this.split.el.addClass("roo-layout-split-v");
36859     }
36860     var size = config.initialSize || config.height;
36861     if(typeof size != "undefined"){
36862         this.el.setHeight(size);
36863     }
36864 };
36865 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36866 {
36867     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36868     
36869     
36870     
36871     getBox : function(){
36872         if(this.collapsed){
36873             return this.collapsedEl.getBox();
36874         }
36875         var box = this.el.getBox();
36876         if(this.split){
36877             box.height += this.split.el.getHeight();
36878         }
36879         return box;
36880     },
36881     
36882     updateBox : function(box){
36883         if(this.split && !this.collapsed){
36884             box.height -= this.split.el.getHeight();
36885             this.split.el.setLeft(box.x);
36886             this.split.el.setTop(box.y+box.height);
36887             this.split.el.setWidth(box.width);
36888         }
36889         if(this.collapsed){
36890             this.updateBody(box.width, null);
36891         }
36892         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36893     }
36894 });
36895
36896
36897
36898
36899
36900 Roo.bootstrap.layout.South = function(config){
36901     config.region = 'south';
36902     config.cursor = 's-resize';
36903     Roo.bootstrap.layout.Split.call(this, config);
36904     if(this.split){
36905         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36906         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36907         this.split.el.addClass("roo-layout-split-v");
36908     }
36909     var size = config.initialSize || config.height;
36910     if(typeof size != "undefined"){
36911         this.el.setHeight(size);
36912     }
36913 };
36914
36915 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36916     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36917     getBox : function(){
36918         if(this.collapsed){
36919             return this.collapsedEl.getBox();
36920         }
36921         var box = this.el.getBox();
36922         if(this.split){
36923             var sh = this.split.el.getHeight();
36924             box.height += sh;
36925             box.y -= sh;
36926         }
36927         return box;
36928     },
36929     
36930     updateBox : function(box){
36931         if(this.split && !this.collapsed){
36932             var sh = this.split.el.getHeight();
36933             box.height -= sh;
36934             box.y += sh;
36935             this.split.el.setLeft(box.x);
36936             this.split.el.setTop(box.y-sh);
36937             this.split.el.setWidth(box.width);
36938         }
36939         if(this.collapsed){
36940             this.updateBody(box.width, null);
36941         }
36942         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36943     }
36944 });
36945
36946 Roo.bootstrap.layout.East = function(config){
36947     config.region = "east";
36948     config.cursor = "e-resize";
36949     Roo.bootstrap.layout.Split.call(this, config);
36950     if(this.split){
36951         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36952         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36953         this.split.el.addClass("roo-layout-split-h");
36954     }
36955     var size = config.initialSize || config.width;
36956     if(typeof size != "undefined"){
36957         this.el.setWidth(size);
36958     }
36959 };
36960 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36961     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36962     getBox : function(){
36963         if(this.collapsed){
36964             return this.collapsedEl.getBox();
36965         }
36966         var box = this.el.getBox();
36967         if(this.split){
36968             var sw = this.split.el.getWidth();
36969             box.width += sw;
36970             box.x -= sw;
36971         }
36972         return box;
36973     },
36974
36975     updateBox : function(box){
36976         if(this.split && !this.collapsed){
36977             var sw = this.split.el.getWidth();
36978             box.width -= sw;
36979             this.split.el.setLeft(box.x);
36980             this.split.el.setTop(box.y);
36981             this.split.el.setHeight(box.height);
36982             box.x += sw;
36983         }
36984         if(this.collapsed){
36985             this.updateBody(null, box.height);
36986         }
36987         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36988     }
36989 });
36990
36991 Roo.bootstrap.layout.West = function(config){
36992     config.region = "west";
36993     config.cursor = "w-resize";
36994     
36995     Roo.bootstrap.layout.Split.call(this, config);
36996     if(this.split){
36997         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36998         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36999         this.split.el.addClass("roo-layout-split-h");
37000     }
37001     
37002 };
37003 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37004     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37005     
37006     onRender: function(ctr, pos)
37007     {
37008         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37009         var size = this.config.initialSize || this.config.width;
37010         if(typeof size != "undefined"){
37011             this.el.setWidth(size);
37012         }
37013     },
37014     
37015     getBox : function(){
37016         if(this.collapsed){
37017             return this.collapsedEl.getBox();
37018         }
37019         var box = this.el.getBox();
37020         if(this.split){
37021             box.width += this.split.el.getWidth();
37022         }
37023         return box;
37024     },
37025     
37026     updateBox : function(box){
37027         if(this.split && !this.collapsed){
37028             var sw = this.split.el.getWidth();
37029             box.width -= sw;
37030             this.split.el.setLeft(box.x+box.width);
37031             this.split.el.setTop(box.y);
37032             this.split.el.setHeight(box.height);
37033         }
37034         if(this.collapsed){
37035             this.updateBody(null, box.height);
37036         }
37037         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37038     }
37039 });
37040 Roo.namespace("Roo.bootstrap.panel");/*
37041  * Based on:
37042  * Ext JS Library 1.1.1
37043  * Copyright(c) 2006-2007, Ext JS, LLC.
37044  *
37045  * Originally Released Under LGPL - original licence link has changed is not relivant.
37046  *
37047  * Fork - LGPL
37048  * <script type="text/javascript">
37049  */
37050 /**
37051  * @class Roo.ContentPanel
37052  * @extends Roo.util.Observable
37053  * A basic ContentPanel element.
37054  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37055  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37056  * @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
37057  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37058  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37059  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37060  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37061  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37062  * @cfg {String} title          The title for this panel
37063  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37064  * @cfg {String} url            Calls {@link #setUrl} with this value
37065  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37066  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37067  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37068  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37069  * @cfg {Boolean} badges render the badges
37070
37071  * @constructor
37072  * Create a new ContentPanel.
37073  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37074  * @param {String/Object} config A string to set only the title or a config object
37075  * @param {String} content (optional) Set the HTML content for this panel
37076  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37077  */
37078 Roo.bootstrap.panel.Content = function( config){
37079     
37080     this.tpl = config.tpl || false;
37081     
37082     var el = config.el;
37083     var content = config.content;
37084
37085     if(config.autoCreate){ // xtype is available if this is called from factory
37086         el = Roo.id();
37087     }
37088     this.el = Roo.get(el);
37089     if(!this.el && config && config.autoCreate){
37090         if(typeof config.autoCreate == "object"){
37091             if(!config.autoCreate.id){
37092                 config.autoCreate.id = config.id||el;
37093             }
37094             this.el = Roo.DomHelper.append(document.body,
37095                         config.autoCreate, true);
37096         }else{
37097             var elcfg =  {   tag: "div",
37098                             cls: "roo-layout-inactive-content",
37099                             id: config.id||el
37100                             };
37101             if (config.html) {
37102                 elcfg.html = config.html;
37103                 
37104             }
37105                         
37106             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37107         }
37108     } 
37109     this.closable = false;
37110     this.loaded = false;
37111     this.active = false;
37112    
37113       
37114     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37115         
37116         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37117         
37118         this.wrapEl = this.el; //this.el.wrap();
37119         var ti = [];
37120         if (config.toolbar.items) {
37121             ti = config.toolbar.items ;
37122             delete config.toolbar.items ;
37123         }
37124         
37125         var nitems = [];
37126         this.toolbar.render(this.wrapEl, 'before');
37127         for(var i =0;i < ti.length;i++) {
37128           //  Roo.log(['add child', items[i]]);
37129             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37130         }
37131         this.toolbar.items = nitems;
37132         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37133         delete config.toolbar;
37134         
37135     }
37136     /*
37137     // xtype created footer. - not sure if will work as we normally have to render first..
37138     if (this.footer && !this.footer.el && this.footer.xtype) {
37139         if (!this.wrapEl) {
37140             this.wrapEl = this.el.wrap();
37141         }
37142     
37143         this.footer.container = this.wrapEl.createChild();
37144          
37145         this.footer = Roo.factory(this.footer, Roo);
37146         
37147     }
37148     */
37149     
37150      if(typeof config == "string"){
37151         this.title = config;
37152     }else{
37153         Roo.apply(this, config);
37154     }
37155     
37156     if(this.resizeEl){
37157         this.resizeEl = Roo.get(this.resizeEl, true);
37158     }else{
37159         this.resizeEl = this.el;
37160     }
37161     // handle view.xtype
37162     
37163  
37164     
37165     
37166     this.addEvents({
37167         /**
37168          * @event activate
37169          * Fires when this panel is activated. 
37170          * @param {Roo.ContentPanel} this
37171          */
37172         "activate" : true,
37173         /**
37174          * @event deactivate
37175          * Fires when this panel is activated. 
37176          * @param {Roo.ContentPanel} this
37177          */
37178         "deactivate" : true,
37179
37180         /**
37181          * @event resize
37182          * Fires when this panel is resized if fitToFrame is true.
37183          * @param {Roo.ContentPanel} this
37184          * @param {Number} width The width after any component adjustments
37185          * @param {Number} height The height after any component adjustments
37186          */
37187         "resize" : true,
37188         
37189          /**
37190          * @event render
37191          * Fires when this tab is created
37192          * @param {Roo.ContentPanel} this
37193          */
37194         "render" : true
37195         
37196         
37197         
37198     });
37199     
37200
37201     
37202     
37203     if(this.autoScroll){
37204         this.resizeEl.setStyle("overflow", "auto");
37205     } else {
37206         // fix randome scrolling
37207         //this.el.on('scroll', function() {
37208         //    Roo.log('fix random scolling');
37209         //    this.scrollTo('top',0); 
37210         //});
37211     }
37212     content = content || this.content;
37213     if(content){
37214         this.setContent(content);
37215     }
37216     if(config && config.url){
37217         this.setUrl(this.url, this.params, this.loadOnce);
37218     }
37219     
37220     
37221     
37222     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37223     
37224     if (this.view && typeof(this.view.xtype) != 'undefined') {
37225         this.view.el = this.el.appendChild(document.createElement("div"));
37226         this.view = Roo.factory(this.view); 
37227         this.view.render  &&  this.view.render(false, '');  
37228     }
37229     
37230     
37231     this.fireEvent('render', this);
37232 };
37233
37234 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37235     
37236     tabTip : '',
37237     
37238     setRegion : function(region){
37239         this.region = region;
37240         this.setActiveClass(region && !this.background);
37241     },
37242     
37243     
37244     setActiveClass: function(state)
37245     {
37246         if(state){
37247            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37248            this.el.setStyle('position','relative');
37249         }else{
37250            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37251            this.el.setStyle('position', 'absolute');
37252         } 
37253     },
37254     
37255     /**
37256      * Returns the toolbar for this Panel if one was configured. 
37257      * @return {Roo.Toolbar} 
37258      */
37259     getToolbar : function(){
37260         return this.toolbar;
37261     },
37262     
37263     setActiveState : function(active)
37264     {
37265         this.active = active;
37266         this.setActiveClass(active);
37267         if(!active){
37268             if(this.fireEvent("deactivate", this) === false){
37269                 return false;
37270             }
37271             return true;
37272         }
37273         this.fireEvent("activate", this);
37274         return true;
37275     },
37276     /**
37277      * Updates this panel's element
37278      * @param {String} content The new content
37279      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37280     */
37281     setContent : function(content, loadScripts){
37282         this.el.update(content, loadScripts);
37283     },
37284
37285     ignoreResize : function(w, h){
37286         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37287             return true;
37288         }else{
37289             this.lastSize = {width: w, height: h};
37290             return false;
37291         }
37292     },
37293     /**
37294      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37295      * @return {Roo.UpdateManager} The UpdateManager
37296      */
37297     getUpdateManager : function(){
37298         return this.el.getUpdateManager();
37299     },
37300      /**
37301      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37302      * @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:
37303 <pre><code>
37304 panel.load({
37305     url: "your-url.php",
37306     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37307     callback: yourFunction,
37308     scope: yourObject, //(optional scope)
37309     discardUrl: false,
37310     nocache: false,
37311     text: "Loading...",
37312     timeout: 30,
37313     scripts: false
37314 });
37315 </code></pre>
37316      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37317      * 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.
37318      * @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}
37319      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37320      * @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.
37321      * @return {Roo.ContentPanel} this
37322      */
37323     load : function(){
37324         var um = this.el.getUpdateManager();
37325         um.update.apply(um, arguments);
37326         return this;
37327     },
37328
37329
37330     /**
37331      * 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.
37332      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37333      * @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)
37334      * @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)
37335      * @return {Roo.UpdateManager} The UpdateManager
37336      */
37337     setUrl : function(url, params, loadOnce){
37338         if(this.refreshDelegate){
37339             this.removeListener("activate", this.refreshDelegate);
37340         }
37341         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37342         this.on("activate", this.refreshDelegate);
37343         return this.el.getUpdateManager();
37344     },
37345     
37346     _handleRefresh : function(url, params, loadOnce){
37347         if(!loadOnce || !this.loaded){
37348             var updater = this.el.getUpdateManager();
37349             updater.update(url, params, this._setLoaded.createDelegate(this));
37350         }
37351     },
37352     
37353     _setLoaded : function(){
37354         this.loaded = true;
37355     }, 
37356     
37357     /**
37358      * Returns this panel's id
37359      * @return {String} 
37360      */
37361     getId : function(){
37362         return this.el.id;
37363     },
37364     
37365     /** 
37366      * Returns this panel's element - used by regiosn to add.
37367      * @return {Roo.Element} 
37368      */
37369     getEl : function(){
37370         return this.wrapEl || this.el;
37371     },
37372     
37373    
37374     
37375     adjustForComponents : function(width, height)
37376     {
37377         //Roo.log('adjustForComponents ');
37378         if(this.resizeEl != this.el){
37379             width -= this.el.getFrameWidth('lr');
37380             height -= this.el.getFrameWidth('tb');
37381         }
37382         if(this.toolbar){
37383             var te = this.toolbar.getEl();
37384             te.setWidth(width);
37385             height -= te.getHeight();
37386         }
37387         if(this.footer){
37388             var te = this.footer.getEl();
37389             te.setWidth(width);
37390             height -= te.getHeight();
37391         }
37392         
37393         
37394         if(this.adjustments){
37395             width += this.adjustments[0];
37396             height += this.adjustments[1];
37397         }
37398         return {"width": width, "height": height};
37399     },
37400     
37401     setSize : function(width, height){
37402         if(this.fitToFrame && !this.ignoreResize(width, height)){
37403             if(this.fitContainer && this.resizeEl != this.el){
37404                 this.el.setSize(width, height);
37405             }
37406             var size = this.adjustForComponents(width, height);
37407             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37408             this.fireEvent('resize', this, size.width, size.height);
37409         }
37410     },
37411     
37412     /**
37413      * Returns this panel's title
37414      * @return {String} 
37415      */
37416     getTitle : function(){
37417         
37418         if (typeof(this.title) != 'object') {
37419             return this.title;
37420         }
37421         
37422         var t = '';
37423         for (var k in this.title) {
37424             if (!this.title.hasOwnProperty(k)) {
37425                 continue;
37426             }
37427             
37428             if (k.indexOf('-') >= 0) {
37429                 var s = k.split('-');
37430                 for (var i = 0; i<s.length; i++) {
37431                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37432                 }
37433             } else {
37434                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37435             }
37436         }
37437         return t;
37438     },
37439     
37440     /**
37441      * Set this panel's title
37442      * @param {String} title
37443      */
37444     setTitle : function(title){
37445         this.title = title;
37446         if(this.region){
37447             this.region.updatePanelTitle(this, title);
37448         }
37449     },
37450     
37451     /**
37452      * Returns true is this panel was configured to be closable
37453      * @return {Boolean} 
37454      */
37455     isClosable : function(){
37456         return this.closable;
37457     },
37458     
37459     beforeSlide : function(){
37460         this.el.clip();
37461         this.resizeEl.clip();
37462     },
37463     
37464     afterSlide : function(){
37465         this.el.unclip();
37466         this.resizeEl.unclip();
37467     },
37468     
37469     /**
37470      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37471      *   Will fail silently if the {@link #setUrl} method has not been called.
37472      *   This does not activate the panel, just updates its content.
37473      */
37474     refresh : function(){
37475         if(this.refreshDelegate){
37476            this.loaded = false;
37477            this.refreshDelegate();
37478         }
37479     },
37480     
37481     /**
37482      * Destroys this panel
37483      */
37484     destroy : function(){
37485         this.el.removeAllListeners();
37486         var tempEl = document.createElement("span");
37487         tempEl.appendChild(this.el.dom);
37488         tempEl.innerHTML = "";
37489         this.el.remove();
37490         this.el = null;
37491     },
37492     
37493     /**
37494      * form - if the content panel contains a form - this is a reference to it.
37495      * @type {Roo.form.Form}
37496      */
37497     form : false,
37498     /**
37499      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37500      *    This contains a reference to it.
37501      * @type {Roo.View}
37502      */
37503     view : false,
37504     
37505       /**
37506      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37507      * <pre><code>
37508
37509 layout.addxtype({
37510        xtype : 'Form',
37511        items: [ .... ]
37512    }
37513 );
37514
37515 </code></pre>
37516      * @param {Object} cfg Xtype definition of item to add.
37517      */
37518     
37519     
37520     getChildContainer: function () {
37521         return this.getEl();
37522     }
37523     
37524     
37525     /*
37526         var  ret = new Roo.factory(cfg);
37527         return ret;
37528         
37529         
37530         // add form..
37531         if (cfg.xtype.match(/^Form$/)) {
37532             
37533             var el;
37534             //if (this.footer) {
37535             //    el = this.footer.container.insertSibling(false, 'before');
37536             //} else {
37537                 el = this.el.createChild();
37538             //}
37539
37540             this.form = new  Roo.form.Form(cfg);
37541             
37542             
37543             if ( this.form.allItems.length) {
37544                 this.form.render(el.dom);
37545             }
37546             return this.form;
37547         }
37548         // should only have one of theses..
37549         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37550             // views.. should not be just added - used named prop 'view''
37551             
37552             cfg.el = this.el.appendChild(document.createElement("div"));
37553             // factory?
37554             
37555             var ret = new Roo.factory(cfg);
37556              
37557              ret.render && ret.render(false, ''); // render blank..
37558             this.view = ret;
37559             return ret;
37560         }
37561         return false;
37562     }
37563     \*/
37564 });
37565  
37566 /**
37567  * @class Roo.bootstrap.panel.Grid
37568  * @extends Roo.bootstrap.panel.Content
37569  * @constructor
37570  * Create a new GridPanel.
37571  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37572  * @param {Object} config A the config object
37573   
37574  */
37575
37576
37577
37578 Roo.bootstrap.panel.Grid = function(config)
37579 {
37580     
37581       
37582     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37583         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37584
37585     config.el = this.wrapper;
37586     //this.el = this.wrapper;
37587     
37588       if (config.container) {
37589         // ctor'ed from a Border/panel.grid
37590         
37591         
37592         this.wrapper.setStyle("overflow", "hidden");
37593         this.wrapper.addClass('roo-grid-container');
37594
37595     }
37596     
37597     
37598     if(config.toolbar){
37599         var tool_el = this.wrapper.createChild();    
37600         this.toolbar = Roo.factory(config.toolbar);
37601         var ti = [];
37602         if (config.toolbar.items) {
37603             ti = config.toolbar.items ;
37604             delete config.toolbar.items ;
37605         }
37606         
37607         var nitems = [];
37608         this.toolbar.render(tool_el);
37609         for(var i =0;i < ti.length;i++) {
37610           //  Roo.log(['add child', items[i]]);
37611             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37612         }
37613         this.toolbar.items = nitems;
37614         
37615         delete config.toolbar;
37616     }
37617     
37618     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37619     config.grid.scrollBody = true;;
37620     config.grid.monitorWindowResize = false; // turn off autosizing
37621     config.grid.autoHeight = false;
37622     config.grid.autoWidth = false;
37623     
37624     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37625     
37626     if (config.background) {
37627         // render grid on panel activation (if panel background)
37628         this.on('activate', function(gp) {
37629             if (!gp.grid.rendered) {
37630                 gp.grid.render(this.wrapper);
37631                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37632             }
37633         });
37634             
37635     } else {
37636         this.grid.render(this.wrapper);
37637         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37638
37639     }
37640     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37641     // ??? needed ??? config.el = this.wrapper;
37642     
37643     
37644     
37645   
37646     // xtype created footer. - not sure if will work as we normally have to render first..
37647     if (this.footer && !this.footer.el && this.footer.xtype) {
37648         
37649         var ctr = this.grid.getView().getFooterPanel(true);
37650         this.footer.dataSource = this.grid.dataSource;
37651         this.footer = Roo.factory(this.footer, Roo);
37652         this.footer.render(ctr);
37653         
37654     }
37655     
37656     
37657     
37658     
37659      
37660 };
37661
37662 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37663     getId : function(){
37664         return this.grid.id;
37665     },
37666     
37667     /**
37668      * Returns the grid for this panel
37669      * @return {Roo.bootstrap.Table} 
37670      */
37671     getGrid : function(){
37672         return this.grid;    
37673     },
37674     
37675     setSize : function(width, height){
37676         if(!this.ignoreResize(width, height)){
37677             var grid = this.grid;
37678             var size = this.adjustForComponents(width, height);
37679             var gridel = grid.getGridEl();
37680             gridel.setSize(size.width, size.height);
37681             /*
37682             var thd = grid.getGridEl().select('thead',true).first();
37683             var tbd = grid.getGridEl().select('tbody', true).first();
37684             if (tbd) {
37685                 tbd.setSize(width, height - thd.getHeight());
37686             }
37687             */
37688             grid.autoSize();
37689         }
37690     },
37691      
37692     
37693     
37694     beforeSlide : function(){
37695         this.grid.getView().scroller.clip();
37696     },
37697     
37698     afterSlide : function(){
37699         this.grid.getView().scroller.unclip();
37700     },
37701     
37702     destroy : function(){
37703         this.grid.destroy();
37704         delete this.grid;
37705         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37706     }
37707 });
37708
37709 /**
37710  * @class Roo.bootstrap.panel.Nest
37711  * @extends Roo.bootstrap.panel.Content
37712  * @constructor
37713  * Create a new Panel, that can contain a layout.Border.
37714  * 
37715  * 
37716  * @param {Roo.BorderLayout} layout The layout for this panel
37717  * @param {String/Object} config A string to set only the title or a config object
37718  */
37719 Roo.bootstrap.panel.Nest = function(config)
37720 {
37721     // construct with only one argument..
37722     /* FIXME - implement nicer consturctors
37723     if (layout.layout) {
37724         config = layout;
37725         layout = config.layout;
37726         delete config.layout;
37727     }
37728     if (layout.xtype && !layout.getEl) {
37729         // then layout needs constructing..
37730         layout = Roo.factory(layout, Roo);
37731     }
37732     */
37733     
37734     config.el =  config.layout.getEl();
37735     
37736     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37737     
37738     config.layout.monitorWindowResize = false; // turn off autosizing
37739     this.layout = config.layout;
37740     this.layout.getEl().addClass("roo-layout-nested-layout");
37741     
37742     
37743     
37744     
37745 };
37746
37747 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37748
37749     setSize : function(width, height){
37750         if(!this.ignoreResize(width, height)){
37751             var size = this.adjustForComponents(width, height);
37752             var el = this.layout.getEl();
37753             if (size.height < 1) {
37754                 el.setWidth(size.width);   
37755             } else {
37756                 el.setSize(size.width, size.height);
37757             }
37758             var touch = el.dom.offsetWidth;
37759             this.layout.layout();
37760             // ie requires a double layout on the first pass
37761             if(Roo.isIE && !this.initialized){
37762                 this.initialized = true;
37763                 this.layout.layout();
37764             }
37765         }
37766     },
37767     
37768     // activate all subpanels if not currently active..
37769     
37770     setActiveState : function(active){
37771         this.active = active;
37772         this.setActiveClass(active);
37773         
37774         if(!active){
37775             this.fireEvent("deactivate", this);
37776             return;
37777         }
37778         
37779         this.fireEvent("activate", this);
37780         // not sure if this should happen before or after..
37781         if (!this.layout) {
37782             return; // should not happen..
37783         }
37784         var reg = false;
37785         for (var r in this.layout.regions) {
37786             reg = this.layout.getRegion(r);
37787             if (reg.getActivePanel()) {
37788                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37789                 reg.setActivePanel(reg.getActivePanel());
37790                 continue;
37791             }
37792             if (!reg.panels.length) {
37793                 continue;
37794             }
37795             reg.showPanel(reg.getPanel(0));
37796         }
37797         
37798         
37799         
37800         
37801     },
37802     
37803     /**
37804      * Returns the nested BorderLayout for this panel
37805      * @return {Roo.BorderLayout} 
37806      */
37807     getLayout : function(){
37808         return this.layout;
37809     },
37810     
37811      /**
37812      * Adds a xtype elements to the layout of the nested panel
37813      * <pre><code>
37814
37815 panel.addxtype({
37816        xtype : 'ContentPanel',
37817        region: 'west',
37818        items: [ .... ]
37819    }
37820 );
37821
37822 panel.addxtype({
37823         xtype : 'NestedLayoutPanel',
37824         region: 'west',
37825         layout: {
37826            center: { },
37827            west: { }   
37828         },
37829         items : [ ... list of content panels or nested layout panels.. ]
37830    }
37831 );
37832 </code></pre>
37833      * @param {Object} cfg Xtype definition of item to add.
37834      */
37835     addxtype : function(cfg) {
37836         return this.layout.addxtype(cfg);
37837     
37838     }
37839 });        /*
37840  * Based on:
37841  * Ext JS Library 1.1.1
37842  * Copyright(c) 2006-2007, Ext JS, LLC.
37843  *
37844  * Originally Released Under LGPL - original licence link has changed is not relivant.
37845  *
37846  * Fork - LGPL
37847  * <script type="text/javascript">
37848  */
37849 /**
37850  * @class Roo.TabPanel
37851  * @extends Roo.util.Observable
37852  * A lightweight tab container.
37853  * <br><br>
37854  * Usage:
37855  * <pre><code>
37856 // basic tabs 1, built from existing content
37857 var tabs = new Roo.TabPanel("tabs1");
37858 tabs.addTab("script", "View Script");
37859 tabs.addTab("markup", "View Markup");
37860 tabs.activate("script");
37861
37862 // more advanced tabs, built from javascript
37863 var jtabs = new Roo.TabPanel("jtabs");
37864 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37865
37866 // set up the UpdateManager
37867 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37868 var updater = tab2.getUpdateManager();
37869 updater.setDefaultUrl("ajax1.htm");
37870 tab2.on('activate', updater.refresh, updater, true);
37871
37872 // Use setUrl for Ajax loading
37873 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37874 tab3.setUrl("ajax2.htm", null, true);
37875
37876 // Disabled tab
37877 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37878 tab4.disable();
37879
37880 jtabs.activate("jtabs-1");
37881  * </code></pre>
37882  * @constructor
37883  * Create a new TabPanel.
37884  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37885  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37886  */
37887 Roo.bootstrap.panel.Tabs = function(config){
37888     /**
37889     * The container element for this TabPanel.
37890     * @type Roo.Element
37891     */
37892     this.el = Roo.get(config.el);
37893     delete config.el;
37894     if(config){
37895         if(typeof config == "boolean"){
37896             this.tabPosition = config ? "bottom" : "top";
37897         }else{
37898             Roo.apply(this, config);
37899         }
37900     }
37901     
37902     if(this.tabPosition == "bottom"){
37903         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37904         this.el.addClass("roo-tabs-bottom");
37905     }
37906     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37907     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37908     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37909     if(Roo.isIE){
37910         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37911     }
37912     if(this.tabPosition != "bottom"){
37913         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37914          * @type Roo.Element
37915          */
37916         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37917         this.el.addClass("roo-tabs-top");
37918     }
37919     this.items = [];
37920
37921     this.bodyEl.setStyle("position", "relative");
37922
37923     this.active = null;
37924     this.activateDelegate = this.activate.createDelegate(this);
37925
37926     this.addEvents({
37927         /**
37928          * @event tabchange
37929          * Fires when the active tab changes
37930          * @param {Roo.TabPanel} this
37931          * @param {Roo.TabPanelItem} activePanel The new active tab
37932          */
37933         "tabchange": true,
37934         /**
37935          * @event beforetabchange
37936          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37937          * @param {Roo.TabPanel} this
37938          * @param {Object} e Set cancel to true on this object to cancel the tab change
37939          * @param {Roo.TabPanelItem} tab The tab being changed to
37940          */
37941         "beforetabchange" : true
37942     });
37943
37944     Roo.EventManager.onWindowResize(this.onResize, this);
37945     this.cpad = this.el.getPadding("lr");
37946     this.hiddenCount = 0;
37947
37948
37949     // toolbar on the tabbar support...
37950     if (this.toolbar) {
37951         alert("no toolbar support yet");
37952         this.toolbar  = false;
37953         /*
37954         var tcfg = this.toolbar;
37955         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37956         this.toolbar = new Roo.Toolbar(tcfg);
37957         if (Roo.isSafari) {
37958             var tbl = tcfg.container.child('table', true);
37959             tbl.setAttribute('width', '100%');
37960         }
37961         */
37962         
37963     }
37964    
37965
37966
37967     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37968 };
37969
37970 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37971     /*
37972      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37973      */
37974     tabPosition : "top",
37975     /*
37976      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37977      */
37978     currentTabWidth : 0,
37979     /*
37980      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37981      */
37982     minTabWidth : 40,
37983     /*
37984      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37985      */
37986     maxTabWidth : 250,
37987     /*
37988      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37989      */
37990     preferredTabWidth : 175,
37991     /*
37992      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37993      */
37994     resizeTabs : false,
37995     /*
37996      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37997      */
37998     monitorResize : true,
37999     /*
38000      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38001      */
38002     toolbar : false,
38003
38004     /**
38005      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38006      * @param {String} id The id of the div to use <b>or create</b>
38007      * @param {String} text The text for the tab
38008      * @param {String} content (optional) Content to put in the TabPanelItem body
38009      * @param {Boolean} closable (optional) True to create a close icon on the tab
38010      * @return {Roo.TabPanelItem} The created TabPanelItem
38011      */
38012     addTab : function(id, text, content, closable, tpl)
38013     {
38014         var item = new Roo.bootstrap.panel.TabItem({
38015             panel: this,
38016             id : id,
38017             text : text,
38018             closable : closable,
38019             tpl : tpl
38020         });
38021         this.addTabItem(item);
38022         if(content){
38023             item.setContent(content);
38024         }
38025         return item;
38026     },
38027
38028     /**
38029      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38030      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38031      * @return {Roo.TabPanelItem}
38032      */
38033     getTab : function(id){
38034         return this.items[id];
38035     },
38036
38037     /**
38038      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38039      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38040      */
38041     hideTab : function(id){
38042         var t = this.items[id];
38043         if(!t.isHidden()){
38044            t.setHidden(true);
38045            this.hiddenCount++;
38046            this.autoSizeTabs();
38047         }
38048     },
38049
38050     /**
38051      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38052      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38053      */
38054     unhideTab : function(id){
38055         var t = this.items[id];
38056         if(t.isHidden()){
38057            t.setHidden(false);
38058            this.hiddenCount--;
38059            this.autoSizeTabs();
38060         }
38061     },
38062
38063     /**
38064      * Adds an existing {@link Roo.TabPanelItem}.
38065      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38066      */
38067     addTabItem : function(item){
38068         this.items[item.id] = item;
38069         this.items.push(item);
38070       //  if(this.resizeTabs){
38071     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38072   //         this.autoSizeTabs();
38073 //        }else{
38074 //            item.autoSize();
38075        // }
38076     },
38077
38078     /**
38079      * Removes a {@link Roo.TabPanelItem}.
38080      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38081      */
38082     removeTab : function(id){
38083         var items = this.items;
38084         var tab = items[id];
38085         if(!tab) { return; }
38086         var index = items.indexOf(tab);
38087         if(this.active == tab && items.length > 1){
38088             var newTab = this.getNextAvailable(index);
38089             if(newTab) {
38090                 newTab.activate();
38091             }
38092         }
38093         this.stripEl.dom.removeChild(tab.pnode.dom);
38094         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38095             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38096         }
38097         items.splice(index, 1);
38098         delete this.items[tab.id];
38099         tab.fireEvent("close", tab);
38100         tab.purgeListeners();
38101         this.autoSizeTabs();
38102     },
38103
38104     getNextAvailable : function(start){
38105         var items = this.items;
38106         var index = start;
38107         // look for a next tab that will slide over to
38108         // replace the one being removed
38109         while(index < items.length){
38110             var item = items[++index];
38111             if(item && !item.isHidden()){
38112                 return item;
38113             }
38114         }
38115         // if one isn't found select the previous tab (on the left)
38116         index = start;
38117         while(index >= 0){
38118             var item = items[--index];
38119             if(item && !item.isHidden()){
38120                 return item;
38121             }
38122         }
38123         return null;
38124     },
38125
38126     /**
38127      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38128      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38129      */
38130     disableTab : function(id){
38131         var tab = this.items[id];
38132         if(tab && this.active != tab){
38133             tab.disable();
38134         }
38135     },
38136
38137     /**
38138      * Enables a {@link Roo.TabPanelItem} that is disabled.
38139      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38140      */
38141     enableTab : function(id){
38142         var tab = this.items[id];
38143         tab.enable();
38144     },
38145
38146     /**
38147      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38148      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38149      * @return {Roo.TabPanelItem} The TabPanelItem.
38150      */
38151     activate : function(id){
38152         var tab = this.items[id];
38153         if(!tab){
38154             return null;
38155         }
38156         if(tab == this.active || tab.disabled){
38157             return tab;
38158         }
38159         var e = {};
38160         this.fireEvent("beforetabchange", this, e, tab);
38161         if(e.cancel !== true && !tab.disabled){
38162             if(this.active){
38163                 this.active.hide();
38164             }
38165             this.active = this.items[id];
38166             this.active.show();
38167             this.fireEvent("tabchange", this, this.active);
38168         }
38169         return tab;
38170     },
38171
38172     /**
38173      * Gets the active {@link Roo.TabPanelItem}.
38174      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38175      */
38176     getActiveTab : function(){
38177         return this.active;
38178     },
38179
38180     /**
38181      * Updates the tab body element to fit the height of the container element
38182      * for overflow scrolling
38183      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38184      */
38185     syncHeight : function(targetHeight){
38186         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38187         var bm = this.bodyEl.getMargins();
38188         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38189         this.bodyEl.setHeight(newHeight);
38190         return newHeight;
38191     },
38192
38193     onResize : function(){
38194         if(this.monitorResize){
38195             this.autoSizeTabs();
38196         }
38197     },
38198
38199     /**
38200      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38201      */
38202     beginUpdate : function(){
38203         this.updating = true;
38204     },
38205
38206     /**
38207      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38208      */
38209     endUpdate : function(){
38210         this.updating = false;
38211         this.autoSizeTabs();
38212     },
38213
38214     /**
38215      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38216      */
38217     autoSizeTabs : function(){
38218         var count = this.items.length;
38219         var vcount = count - this.hiddenCount;
38220         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38221             return;
38222         }
38223         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38224         var availWidth = Math.floor(w / vcount);
38225         var b = this.stripBody;
38226         if(b.getWidth() > w){
38227             var tabs = this.items;
38228             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38229             if(availWidth < this.minTabWidth){
38230                 /*if(!this.sleft){    // incomplete scrolling code
38231                     this.createScrollButtons();
38232                 }
38233                 this.showScroll();
38234                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38235             }
38236         }else{
38237             if(this.currentTabWidth < this.preferredTabWidth){
38238                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38239             }
38240         }
38241     },
38242
38243     /**
38244      * Returns the number of tabs in this TabPanel.
38245      * @return {Number}
38246      */
38247      getCount : function(){
38248          return this.items.length;
38249      },
38250
38251     /**
38252      * Resizes all the tabs to the passed width
38253      * @param {Number} The new width
38254      */
38255     setTabWidth : function(width){
38256         this.currentTabWidth = width;
38257         for(var i = 0, len = this.items.length; i < len; i++) {
38258                 if(!this.items[i].isHidden()) {
38259                 this.items[i].setWidth(width);
38260             }
38261         }
38262     },
38263
38264     /**
38265      * Destroys this TabPanel
38266      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38267      */
38268     destroy : function(removeEl){
38269         Roo.EventManager.removeResizeListener(this.onResize, this);
38270         for(var i = 0, len = this.items.length; i < len; i++){
38271             this.items[i].purgeListeners();
38272         }
38273         if(removeEl === true){
38274             this.el.update("");
38275             this.el.remove();
38276         }
38277     },
38278     
38279     createStrip : function(container)
38280     {
38281         var strip = document.createElement("nav");
38282         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38283         container.appendChild(strip);
38284         return strip;
38285     },
38286     
38287     createStripList : function(strip)
38288     {
38289         // div wrapper for retard IE
38290         // returns the "tr" element.
38291         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38292         //'<div class="x-tabs-strip-wrap">'+
38293           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38294           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38295         return strip.firstChild; //.firstChild.firstChild.firstChild;
38296     },
38297     createBody : function(container)
38298     {
38299         var body = document.createElement("div");
38300         Roo.id(body, "tab-body");
38301         //Roo.fly(body).addClass("x-tabs-body");
38302         Roo.fly(body).addClass("tab-content");
38303         container.appendChild(body);
38304         return body;
38305     },
38306     createItemBody :function(bodyEl, id){
38307         var body = Roo.getDom(id);
38308         if(!body){
38309             body = document.createElement("div");
38310             body.id = id;
38311         }
38312         //Roo.fly(body).addClass("x-tabs-item-body");
38313         Roo.fly(body).addClass("tab-pane");
38314          bodyEl.insertBefore(body, bodyEl.firstChild);
38315         return body;
38316     },
38317     /** @private */
38318     createStripElements :  function(stripEl, text, closable, tpl)
38319     {
38320         var td = document.createElement("li"); // was td..
38321         
38322         
38323         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38324         
38325         
38326         stripEl.appendChild(td);
38327         /*if(closable){
38328             td.className = "x-tabs-closable";
38329             if(!this.closeTpl){
38330                 this.closeTpl = new Roo.Template(
38331                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38332                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38333                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38334                 );
38335             }
38336             var el = this.closeTpl.overwrite(td, {"text": text});
38337             var close = el.getElementsByTagName("div")[0];
38338             var inner = el.getElementsByTagName("em")[0];
38339             return {"el": el, "close": close, "inner": inner};
38340         } else {
38341         */
38342         // not sure what this is..
38343 //            if(!this.tabTpl){
38344                 //this.tabTpl = new Roo.Template(
38345                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38346                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38347                 //);
38348 //                this.tabTpl = new Roo.Template(
38349 //                   '<a href="#">' +
38350 //                   '<span unselectable="on"' +
38351 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38352 //                            ' >{text}</span></a>'
38353 //                );
38354 //                
38355 //            }
38356
38357
38358             var template = tpl || this.tabTpl || false;
38359             
38360             if(!template){
38361                 
38362                 template = new Roo.Template(
38363                    '<a href="#">' +
38364                    '<span unselectable="on"' +
38365                             (this.disableTooltips ? '' : ' title="{text}"') +
38366                             ' >{text}</span></a>'
38367                 );
38368             }
38369             
38370             switch (typeof(template)) {
38371                 case 'object' :
38372                     break;
38373                 case 'string' :
38374                     template = new Roo.Template(template);
38375                     break;
38376                 default :
38377                     break;
38378             }
38379             
38380             var el = template.overwrite(td, {"text": text});
38381             
38382             var inner = el.getElementsByTagName("span")[0];
38383             
38384             return {"el": el, "inner": inner};
38385             
38386     }
38387         
38388     
38389 });
38390
38391 /**
38392  * @class Roo.TabPanelItem
38393  * @extends Roo.util.Observable
38394  * Represents an individual item (tab plus body) in a TabPanel.
38395  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38396  * @param {String} id The id of this TabPanelItem
38397  * @param {String} text The text for the tab of this TabPanelItem
38398  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38399  */
38400 Roo.bootstrap.panel.TabItem = function(config){
38401     /**
38402      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38403      * @type Roo.TabPanel
38404      */
38405     this.tabPanel = config.panel;
38406     /**
38407      * The id for this TabPanelItem
38408      * @type String
38409      */
38410     this.id = config.id;
38411     /** @private */
38412     this.disabled = false;
38413     /** @private */
38414     this.text = config.text;
38415     /** @private */
38416     this.loaded = false;
38417     this.closable = config.closable;
38418
38419     /**
38420      * The body element for this TabPanelItem.
38421      * @type Roo.Element
38422      */
38423     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38424     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38425     this.bodyEl.setStyle("display", "block");
38426     this.bodyEl.setStyle("zoom", "1");
38427     //this.hideAction();
38428
38429     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38430     /** @private */
38431     this.el = Roo.get(els.el);
38432     this.inner = Roo.get(els.inner, true);
38433     this.textEl = Roo.get(this.el.dom.firstChild, true);
38434     this.pnode = Roo.get(els.el.parentNode, true);
38435 //    this.el.on("mousedown", this.onTabMouseDown, this);
38436     this.el.on("click", this.onTabClick, this);
38437     /** @private */
38438     if(config.closable){
38439         var c = Roo.get(els.close, true);
38440         c.dom.title = this.closeText;
38441         c.addClassOnOver("close-over");
38442         c.on("click", this.closeClick, this);
38443      }
38444
38445     this.addEvents({
38446          /**
38447          * @event activate
38448          * Fires when this tab becomes the active tab.
38449          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38450          * @param {Roo.TabPanelItem} this
38451          */
38452         "activate": true,
38453         /**
38454          * @event beforeclose
38455          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38456          * @param {Roo.TabPanelItem} this
38457          * @param {Object} e Set cancel to true on this object to cancel the close.
38458          */
38459         "beforeclose": true,
38460         /**
38461          * @event close
38462          * Fires when this tab is closed.
38463          * @param {Roo.TabPanelItem} this
38464          */
38465          "close": true,
38466         /**
38467          * @event deactivate
38468          * Fires when this tab is no longer the active tab.
38469          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38470          * @param {Roo.TabPanelItem} this
38471          */
38472          "deactivate" : true
38473     });
38474     this.hidden = false;
38475
38476     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38477 };
38478
38479 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38480            {
38481     purgeListeners : function(){
38482        Roo.util.Observable.prototype.purgeListeners.call(this);
38483        this.el.removeAllListeners();
38484     },
38485     /**
38486      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38487      */
38488     show : function(){
38489         this.pnode.addClass("active");
38490         this.showAction();
38491         if(Roo.isOpera){
38492             this.tabPanel.stripWrap.repaint();
38493         }
38494         this.fireEvent("activate", this.tabPanel, this);
38495     },
38496
38497     /**
38498      * Returns true if this tab is the active tab.
38499      * @return {Boolean}
38500      */
38501     isActive : function(){
38502         return this.tabPanel.getActiveTab() == this;
38503     },
38504
38505     /**
38506      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38507      */
38508     hide : function(){
38509         this.pnode.removeClass("active");
38510         this.hideAction();
38511         this.fireEvent("deactivate", this.tabPanel, this);
38512     },
38513
38514     hideAction : function(){
38515         this.bodyEl.hide();
38516         this.bodyEl.setStyle("position", "absolute");
38517         this.bodyEl.setLeft("-20000px");
38518         this.bodyEl.setTop("-20000px");
38519     },
38520
38521     showAction : function(){
38522         this.bodyEl.setStyle("position", "relative");
38523         this.bodyEl.setTop("");
38524         this.bodyEl.setLeft("");
38525         this.bodyEl.show();
38526     },
38527
38528     /**
38529      * Set the tooltip for the tab.
38530      * @param {String} tooltip The tab's tooltip
38531      */
38532     setTooltip : function(text){
38533         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38534             this.textEl.dom.qtip = text;
38535             this.textEl.dom.removeAttribute('title');
38536         }else{
38537             this.textEl.dom.title = text;
38538         }
38539     },
38540
38541     onTabClick : function(e){
38542         e.preventDefault();
38543         this.tabPanel.activate(this.id);
38544     },
38545
38546     onTabMouseDown : function(e){
38547         e.preventDefault();
38548         this.tabPanel.activate(this.id);
38549     },
38550 /*
38551     getWidth : function(){
38552         return this.inner.getWidth();
38553     },
38554
38555     setWidth : function(width){
38556         var iwidth = width - this.pnode.getPadding("lr");
38557         this.inner.setWidth(iwidth);
38558         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38559         this.pnode.setWidth(width);
38560     },
38561 */
38562     /**
38563      * Show or hide the tab
38564      * @param {Boolean} hidden True to hide or false to show.
38565      */
38566     setHidden : function(hidden){
38567         this.hidden = hidden;
38568         this.pnode.setStyle("display", hidden ? "none" : "");
38569     },
38570
38571     /**
38572      * Returns true if this tab is "hidden"
38573      * @return {Boolean}
38574      */
38575     isHidden : function(){
38576         return this.hidden;
38577     },
38578
38579     /**
38580      * Returns the text for this tab
38581      * @return {String}
38582      */
38583     getText : function(){
38584         return this.text;
38585     },
38586     /*
38587     autoSize : function(){
38588         //this.el.beginMeasure();
38589         this.textEl.setWidth(1);
38590         /*
38591          *  #2804 [new] Tabs in Roojs
38592          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38593          */
38594         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38595         //this.el.endMeasure();
38596     //},
38597
38598     /**
38599      * Sets the text for the tab (Note: this also sets the tooltip text)
38600      * @param {String} text The tab's text and tooltip
38601      */
38602     setText : function(text){
38603         this.text = text;
38604         this.textEl.update(text);
38605         this.setTooltip(text);
38606         //if(!this.tabPanel.resizeTabs){
38607         //    this.autoSize();
38608         //}
38609     },
38610     /**
38611      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38612      */
38613     activate : function(){
38614         this.tabPanel.activate(this.id);
38615     },
38616
38617     /**
38618      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38619      */
38620     disable : function(){
38621         if(this.tabPanel.active != this){
38622             this.disabled = true;
38623             this.pnode.addClass("disabled");
38624         }
38625     },
38626
38627     /**
38628      * Enables this TabPanelItem if it was previously disabled.
38629      */
38630     enable : function(){
38631         this.disabled = false;
38632         this.pnode.removeClass("disabled");
38633     },
38634
38635     /**
38636      * Sets the content for this TabPanelItem.
38637      * @param {String} content The content
38638      * @param {Boolean} loadScripts true to look for and load scripts
38639      */
38640     setContent : function(content, loadScripts){
38641         this.bodyEl.update(content, loadScripts);
38642     },
38643
38644     /**
38645      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38646      * @return {Roo.UpdateManager} The UpdateManager
38647      */
38648     getUpdateManager : function(){
38649         return this.bodyEl.getUpdateManager();
38650     },
38651
38652     /**
38653      * Set a URL to be used to load the content for this TabPanelItem.
38654      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38655      * @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)
38656      * @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)
38657      * @return {Roo.UpdateManager} The UpdateManager
38658      */
38659     setUrl : function(url, params, loadOnce){
38660         if(this.refreshDelegate){
38661             this.un('activate', this.refreshDelegate);
38662         }
38663         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38664         this.on("activate", this.refreshDelegate);
38665         return this.bodyEl.getUpdateManager();
38666     },
38667
38668     /** @private */
38669     _handleRefresh : function(url, params, loadOnce){
38670         if(!loadOnce || !this.loaded){
38671             var updater = this.bodyEl.getUpdateManager();
38672             updater.update(url, params, this._setLoaded.createDelegate(this));
38673         }
38674     },
38675
38676     /**
38677      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38678      *   Will fail silently if the setUrl method has not been called.
38679      *   This does not activate the panel, just updates its content.
38680      */
38681     refresh : function(){
38682         if(this.refreshDelegate){
38683            this.loaded = false;
38684            this.refreshDelegate();
38685         }
38686     },
38687
38688     /** @private */
38689     _setLoaded : function(){
38690         this.loaded = true;
38691     },
38692
38693     /** @private */
38694     closeClick : function(e){
38695         var o = {};
38696         e.stopEvent();
38697         this.fireEvent("beforeclose", this, o);
38698         if(o.cancel !== true){
38699             this.tabPanel.removeTab(this.id);
38700         }
38701     },
38702     /**
38703      * The text displayed in the tooltip for the close icon.
38704      * @type String
38705      */
38706     closeText : "Close this tab"
38707 });
38708 /**
38709 *    This script refer to:
38710 *    Title: International Telephone Input
38711 *    Author: Jack O'Connor
38712 *    Code version:  v12.1.12
38713 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38714 **/
38715
38716 Roo.bootstrap.PhoneInputData = function() {
38717     var d = [
38718       [
38719         "Afghanistan (‫افغانستان‬‎)",
38720         "af",
38721         "93"
38722       ],
38723       [
38724         "Albania (Shqipëri)",
38725         "al",
38726         "355"
38727       ],
38728       [
38729         "Algeria (‫الجزائر‬‎)",
38730         "dz",
38731         "213"
38732       ],
38733       [
38734         "American Samoa",
38735         "as",
38736         "1684"
38737       ],
38738       [
38739         "Andorra",
38740         "ad",
38741         "376"
38742       ],
38743       [
38744         "Angola",
38745         "ao",
38746         "244"
38747       ],
38748       [
38749         "Anguilla",
38750         "ai",
38751         "1264"
38752       ],
38753       [
38754         "Antigua and Barbuda",
38755         "ag",
38756         "1268"
38757       ],
38758       [
38759         "Argentina",
38760         "ar",
38761         "54"
38762       ],
38763       [
38764         "Armenia (Հայաստան)",
38765         "am",
38766         "374"
38767       ],
38768       [
38769         "Aruba",
38770         "aw",
38771         "297"
38772       ],
38773       [
38774         "Australia",
38775         "au",
38776         "61",
38777         0
38778       ],
38779       [
38780         "Austria (Österreich)",
38781         "at",
38782         "43"
38783       ],
38784       [
38785         "Azerbaijan (Azərbaycan)",
38786         "az",
38787         "994"
38788       ],
38789       [
38790         "Bahamas",
38791         "bs",
38792         "1242"
38793       ],
38794       [
38795         "Bahrain (‫البحرين‬‎)",
38796         "bh",
38797         "973"
38798       ],
38799       [
38800         "Bangladesh (বাংলাদেশ)",
38801         "bd",
38802         "880"
38803       ],
38804       [
38805         "Barbados",
38806         "bb",
38807         "1246"
38808       ],
38809       [
38810         "Belarus (Беларусь)",
38811         "by",
38812         "375"
38813       ],
38814       [
38815         "Belgium (België)",
38816         "be",
38817         "32"
38818       ],
38819       [
38820         "Belize",
38821         "bz",
38822         "501"
38823       ],
38824       [
38825         "Benin (Bénin)",
38826         "bj",
38827         "229"
38828       ],
38829       [
38830         "Bermuda",
38831         "bm",
38832         "1441"
38833       ],
38834       [
38835         "Bhutan (འབྲུག)",
38836         "bt",
38837         "975"
38838       ],
38839       [
38840         "Bolivia",
38841         "bo",
38842         "591"
38843       ],
38844       [
38845         "Bosnia and Herzegovina (Босна и Херцеговина)",
38846         "ba",
38847         "387"
38848       ],
38849       [
38850         "Botswana",
38851         "bw",
38852         "267"
38853       ],
38854       [
38855         "Brazil (Brasil)",
38856         "br",
38857         "55"
38858       ],
38859       [
38860         "British Indian Ocean Territory",
38861         "io",
38862         "246"
38863       ],
38864       [
38865         "British Virgin Islands",
38866         "vg",
38867         "1284"
38868       ],
38869       [
38870         "Brunei",
38871         "bn",
38872         "673"
38873       ],
38874       [
38875         "Bulgaria (България)",
38876         "bg",
38877         "359"
38878       ],
38879       [
38880         "Burkina Faso",
38881         "bf",
38882         "226"
38883       ],
38884       [
38885         "Burundi (Uburundi)",
38886         "bi",
38887         "257"
38888       ],
38889       [
38890         "Cambodia (កម្ពុជា)",
38891         "kh",
38892         "855"
38893       ],
38894       [
38895         "Cameroon (Cameroun)",
38896         "cm",
38897         "237"
38898       ],
38899       [
38900         "Canada",
38901         "ca",
38902         "1",
38903         1,
38904         ["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"]
38905       ],
38906       [
38907         "Cape Verde (Kabu Verdi)",
38908         "cv",
38909         "238"
38910       ],
38911       [
38912         "Caribbean Netherlands",
38913         "bq",
38914         "599",
38915         1
38916       ],
38917       [
38918         "Cayman Islands",
38919         "ky",
38920         "1345"
38921       ],
38922       [
38923         "Central African Republic (République centrafricaine)",
38924         "cf",
38925         "236"
38926       ],
38927       [
38928         "Chad (Tchad)",
38929         "td",
38930         "235"
38931       ],
38932       [
38933         "Chile",
38934         "cl",
38935         "56"
38936       ],
38937       [
38938         "China (中国)",
38939         "cn",
38940         "86"
38941       ],
38942       [
38943         "Christmas Island",
38944         "cx",
38945         "61",
38946         2
38947       ],
38948       [
38949         "Cocos (Keeling) Islands",
38950         "cc",
38951         "61",
38952         1
38953       ],
38954       [
38955         "Colombia",
38956         "co",
38957         "57"
38958       ],
38959       [
38960         "Comoros (‫جزر القمر‬‎)",
38961         "km",
38962         "269"
38963       ],
38964       [
38965         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38966         "cd",
38967         "243"
38968       ],
38969       [
38970         "Congo (Republic) (Congo-Brazzaville)",
38971         "cg",
38972         "242"
38973       ],
38974       [
38975         "Cook Islands",
38976         "ck",
38977         "682"
38978       ],
38979       [
38980         "Costa Rica",
38981         "cr",
38982         "506"
38983       ],
38984       [
38985         "Côte d’Ivoire",
38986         "ci",
38987         "225"
38988       ],
38989       [
38990         "Croatia (Hrvatska)",
38991         "hr",
38992         "385"
38993       ],
38994       [
38995         "Cuba",
38996         "cu",
38997         "53"
38998       ],
38999       [
39000         "Curaçao",
39001         "cw",
39002         "599",
39003         0
39004       ],
39005       [
39006         "Cyprus (Κύπρος)",
39007         "cy",
39008         "357"
39009       ],
39010       [
39011         "Czech Republic (Česká republika)",
39012         "cz",
39013         "420"
39014       ],
39015       [
39016         "Denmark (Danmark)",
39017         "dk",
39018         "45"
39019       ],
39020       [
39021         "Djibouti",
39022         "dj",
39023         "253"
39024       ],
39025       [
39026         "Dominica",
39027         "dm",
39028         "1767"
39029       ],
39030       [
39031         "Dominican Republic (República Dominicana)",
39032         "do",
39033         "1",
39034         2,
39035         ["809", "829", "849"]
39036       ],
39037       [
39038         "Ecuador",
39039         "ec",
39040         "593"
39041       ],
39042       [
39043         "Egypt (‫مصر‬‎)",
39044         "eg",
39045         "20"
39046       ],
39047       [
39048         "El Salvador",
39049         "sv",
39050         "503"
39051       ],
39052       [
39053         "Equatorial Guinea (Guinea Ecuatorial)",
39054         "gq",
39055         "240"
39056       ],
39057       [
39058         "Eritrea",
39059         "er",
39060         "291"
39061       ],
39062       [
39063         "Estonia (Eesti)",
39064         "ee",
39065         "372"
39066       ],
39067       [
39068         "Ethiopia",
39069         "et",
39070         "251"
39071       ],
39072       [
39073         "Falkland Islands (Islas Malvinas)",
39074         "fk",
39075         "500"
39076       ],
39077       [
39078         "Faroe Islands (Føroyar)",
39079         "fo",
39080         "298"
39081       ],
39082       [
39083         "Fiji",
39084         "fj",
39085         "679"
39086       ],
39087       [
39088         "Finland (Suomi)",
39089         "fi",
39090         "358",
39091         0
39092       ],
39093       [
39094         "France",
39095         "fr",
39096         "33"
39097       ],
39098       [
39099         "French Guiana (Guyane française)",
39100         "gf",
39101         "594"
39102       ],
39103       [
39104         "French Polynesia (Polynésie française)",
39105         "pf",
39106         "689"
39107       ],
39108       [
39109         "Gabon",
39110         "ga",
39111         "241"
39112       ],
39113       [
39114         "Gambia",
39115         "gm",
39116         "220"
39117       ],
39118       [
39119         "Georgia (საქართველო)",
39120         "ge",
39121         "995"
39122       ],
39123       [
39124         "Germany (Deutschland)",
39125         "de",
39126         "49"
39127       ],
39128       [
39129         "Ghana (Gaana)",
39130         "gh",
39131         "233"
39132       ],
39133       [
39134         "Gibraltar",
39135         "gi",
39136         "350"
39137       ],
39138       [
39139         "Greece (Ελλάδα)",
39140         "gr",
39141         "30"
39142       ],
39143       [
39144         "Greenland (Kalaallit Nunaat)",
39145         "gl",
39146         "299"
39147       ],
39148       [
39149         "Grenada",
39150         "gd",
39151         "1473"
39152       ],
39153       [
39154         "Guadeloupe",
39155         "gp",
39156         "590",
39157         0
39158       ],
39159       [
39160         "Guam",
39161         "gu",
39162         "1671"
39163       ],
39164       [
39165         "Guatemala",
39166         "gt",
39167         "502"
39168       ],
39169       [
39170         "Guernsey",
39171         "gg",
39172         "44",
39173         1
39174       ],
39175       [
39176         "Guinea (Guinée)",
39177         "gn",
39178         "224"
39179       ],
39180       [
39181         "Guinea-Bissau (Guiné Bissau)",
39182         "gw",
39183         "245"
39184       ],
39185       [
39186         "Guyana",
39187         "gy",
39188         "592"
39189       ],
39190       [
39191         "Haiti",
39192         "ht",
39193         "509"
39194       ],
39195       [
39196         "Honduras",
39197         "hn",
39198         "504"
39199       ],
39200       [
39201         "Hong Kong (香港)",
39202         "hk",
39203         "852"
39204       ],
39205       [
39206         "Hungary (Magyarország)",
39207         "hu",
39208         "36"
39209       ],
39210       [
39211         "Iceland (Ísland)",
39212         "is",
39213         "354"
39214       ],
39215       [
39216         "India (भारत)",
39217         "in",
39218         "91"
39219       ],
39220       [
39221         "Indonesia",
39222         "id",
39223         "62"
39224       ],
39225       [
39226         "Iran (‫ایران‬‎)",
39227         "ir",
39228         "98"
39229       ],
39230       [
39231         "Iraq (‫العراق‬‎)",
39232         "iq",
39233         "964"
39234       ],
39235       [
39236         "Ireland",
39237         "ie",
39238         "353"
39239       ],
39240       [
39241         "Isle of Man",
39242         "im",
39243         "44",
39244         2
39245       ],
39246       [
39247         "Israel (‫ישראל‬‎)",
39248         "il",
39249         "972"
39250       ],
39251       [
39252         "Italy (Italia)",
39253         "it",
39254         "39",
39255         0
39256       ],
39257       [
39258         "Jamaica",
39259         "jm",
39260         "1876"
39261       ],
39262       [
39263         "Japan (日本)",
39264         "jp",
39265         "81"
39266       ],
39267       [
39268         "Jersey",
39269         "je",
39270         "44",
39271         3
39272       ],
39273       [
39274         "Jordan (‫الأردن‬‎)",
39275         "jo",
39276         "962"
39277       ],
39278       [
39279         "Kazakhstan (Казахстан)",
39280         "kz",
39281         "7",
39282         1
39283       ],
39284       [
39285         "Kenya",
39286         "ke",
39287         "254"
39288       ],
39289       [
39290         "Kiribati",
39291         "ki",
39292         "686"
39293       ],
39294       [
39295         "Kosovo",
39296         "xk",
39297         "383"
39298       ],
39299       [
39300         "Kuwait (‫الكويت‬‎)",
39301         "kw",
39302         "965"
39303       ],
39304       [
39305         "Kyrgyzstan (Кыргызстан)",
39306         "kg",
39307         "996"
39308       ],
39309       [
39310         "Laos (ລາວ)",
39311         "la",
39312         "856"
39313       ],
39314       [
39315         "Latvia (Latvija)",
39316         "lv",
39317         "371"
39318       ],
39319       [
39320         "Lebanon (‫لبنان‬‎)",
39321         "lb",
39322         "961"
39323       ],
39324       [
39325         "Lesotho",
39326         "ls",
39327         "266"
39328       ],
39329       [
39330         "Liberia",
39331         "lr",
39332         "231"
39333       ],
39334       [
39335         "Libya (‫ليبيا‬‎)",
39336         "ly",
39337         "218"
39338       ],
39339       [
39340         "Liechtenstein",
39341         "li",
39342         "423"
39343       ],
39344       [
39345         "Lithuania (Lietuva)",
39346         "lt",
39347         "370"
39348       ],
39349       [
39350         "Luxembourg",
39351         "lu",
39352         "352"
39353       ],
39354       [
39355         "Macau (澳門)",
39356         "mo",
39357         "853"
39358       ],
39359       [
39360         "Macedonia (FYROM) (Македонија)",
39361         "mk",
39362         "389"
39363       ],
39364       [
39365         "Madagascar (Madagasikara)",
39366         "mg",
39367         "261"
39368       ],
39369       [
39370         "Malawi",
39371         "mw",
39372         "265"
39373       ],
39374       [
39375         "Malaysia",
39376         "my",
39377         "60"
39378       ],
39379       [
39380         "Maldives",
39381         "mv",
39382         "960"
39383       ],
39384       [
39385         "Mali",
39386         "ml",
39387         "223"
39388       ],
39389       [
39390         "Malta",
39391         "mt",
39392         "356"
39393       ],
39394       [
39395         "Marshall Islands",
39396         "mh",
39397         "692"
39398       ],
39399       [
39400         "Martinique",
39401         "mq",
39402         "596"
39403       ],
39404       [
39405         "Mauritania (‫موريتانيا‬‎)",
39406         "mr",
39407         "222"
39408       ],
39409       [
39410         "Mauritius (Moris)",
39411         "mu",
39412         "230"
39413       ],
39414       [
39415         "Mayotte",
39416         "yt",
39417         "262",
39418         1
39419       ],
39420       [
39421         "Mexico (México)",
39422         "mx",
39423         "52"
39424       ],
39425       [
39426         "Micronesia",
39427         "fm",
39428         "691"
39429       ],
39430       [
39431         "Moldova (Republica Moldova)",
39432         "md",
39433         "373"
39434       ],
39435       [
39436         "Monaco",
39437         "mc",
39438         "377"
39439       ],
39440       [
39441         "Mongolia (Монгол)",
39442         "mn",
39443         "976"
39444       ],
39445       [
39446         "Montenegro (Crna Gora)",
39447         "me",
39448         "382"
39449       ],
39450       [
39451         "Montserrat",
39452         "ms",
39453         "1664"
39454       ],
39455       [
39456         "Morocco (‫المغرب‬‎)",
39457         "ma",
39458         "212",
39459         0
39460       ],
39461       [
39462         "Mozambique (Moçambique)",
39463         "mz",
39464         "258"
39465       ],
39466       [
39467         "Myanmar (Burma) (မြန်မာ)",
39468         "mm",
39469         "95"
39470       ],
39471       [
39472         "Namibia (Namibië)",
39473         "na",
39474         "264"
39475       ],
39476       [
39477         "Nauru",
39478         "nr",
39479         "674"
39480       ],
39481       [
39482         "Nepal (नेपाल)",
39483         "np",
39484         "977"
39485       ],
39486       [
39487         "Netherlands (Nederland)",
39488         "nl",
39489         "31"
39490       ],
39491       [
39492         "New Caledonia (Nouvelle-Calédonie)",
39493         "nc",
39494         "687"
39495       ],
39496       [
39497         "New Zealand",
39498         "nz",
39499         "64"
39500       ],
39501       [
39502         "Nicaragua",
39503         "ni",
39504         "505"
39505       ],
39506       [
39507         "Niger (Nijar)",
39508         "ne",
39509         "227"
39510       ],
39511       [
39512         "Nigeria",
39513         "ng",
39514         "234"
39515       ],
39516       [
39517         "Niue",
39518         "nu",
39519         "683"
39520       ],
39521       [
39522         "Norfolk Island",
39523         "nf",
39524         "672"
39525       ],
39526       [
39527         "North Korea (조선 민주주의 인민 공화국)",
39528         "kp",
39529         "850"
39530       ],
39531       [
39532         "Northern Mariana Islands",
39533         "mp",
39534         "1670"
39535       ],
39536       [
39537         "Norway (Norge)",
39538         "no",
39539         "47",
39540         0
39541       ],
39542       [
39543         "Oman (‫عُمان‬‎)",
39544         "om",
39545         "968"
39546       ],
39547       [
39548         "Pakistan (‫پاکستان‬‎)",
39549         "pk",
39550         "92"
39551       ],
39552       [
39553         "Palau",
39554         "pw",
39555         "680"
39556       ],
39557       [
39558         "Palestine (‫فلسطين‬‎)",
39559         "ps",
39560         "970"
39561       ],
39562       [
39563         "Panama (Panamá)",
39564         "pa",
39565         "507"
39566       ],
39567       [
39568         "Papua New Guinea",
39569         "pg",
39570         "675"
39571       ],
39572       [
39573         "Paraguay",
39574         "py",
39575         "595"
39576       ],
39577       [
39578         "Peru (Perú)",
39579         "pe",
39580         "51"
39581       ],
39582       [
39583         "Philippines",
39584         "ph",
39585         "63"
39586       ],
39587       [
39588         "Poland (Polska)",
39589         "pl",
39590         "48"
39591       ],
39592       [
39593         "Portugal",
39594         "pt",
39595         "351"
39596       ],
39597       [
39598         "Puerto Rico",
39599         "pr",
39600         "1",
39601         3,
39602         ["787", "939"]
39603       ],
39604       [
39605         "Qatar (‫قطر‬‎)",
39606         "qa",
39607         "974"
39608       ],
39609       [
39610         "Réunion (La Réunion)",
39611         "re",
39612         "262",
39613         0
39614       ],
39615       [
39616         "Romania (România)",
39617         "ro",
39618         "40"
39619       ],
39620       [
39621         "Russia (Россия)",
39622         "ru",
39623         "7",
39624         0
39625       ],
39626       [
39627         "Rwanda",
39628         "rw",
39629         "250"
39630       ],
39631       [
39632         "Saint Barthélemy",
39633         "bl",
39634         "590",
39635         1
39636       ],
39637       [
39638         "Saint Helena",
39639         "sh",
39640         "290"
39641       ],
39642       [
39643         "Saint Kitts and Nevis",
39644         "kn",
39645         "1869"
39646       ],
39647       [
39648         "Saint Lucia",
39649         "lc",
39650         "1758"
39651       ],
39652       [
39653         "Saint Martin (Saint-Martin (partie française))",
39654         "mf",
39655         "590",
39656         2
39657       ],
39658       [
39659         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39660         "pm",
39661         "508"
39662       ],
39663       [
39664         "Saint Vincent and the Grenadines",
39665         "vc",
39666         "1784"
39667       ],
39668       [
39669         "Samoa",
39670         "ws",
39671         "685"
39672       ],
39673       [
39674         "San Marino",
39675         "sm",
39676         "378"
39677       ],
39678       [
39679         "São Tomé and Príncipe (São Tomé e Príncipe)",
39680         "st",
39681         "239"
39682       ],
39683       [
39684         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39685         "sa",
39686         "966"
39687       ],
39688       [
39689         "Senegal (Sénégal)",
39690         "sn",
39691         "221"
39692       ],
39693       [
39694         "Serbia (Србија)",
39695         "rs",
39696         "381"
39697       ],
39698       [
39699         "Seychelles",
39700         "sc",
39701         "248"
39702       ],
39703       [
39704         "Sierra Leone",
39705         "sl",
39706         "232"
39707       ],
39708       [
39709         "Singapore",
39710         "sg",
39711         "65"
39712       ],
39713       [
39714         "Sint Maarten",
39715         "sx",
39716         "1721"
39717       ],
39718       [
39719         "Slovakia (Slovensko)",
39720         "sk",
39721         "421"
39722       ],
39723       [
39724         "Slovenia (Slovenija)",
39725         "si",
39726         "386"
39727       ],
39728       [
39729         "Solomon Islands",
39730         "sb",
39731         "677"
39732       ],
39733       [
39734         "Somalia (Soomaaliya)",
39735         "so",
39736         "252"
39737       ],
39738       [
39739         "South Africa",
39740         "za",
39741         "27"
39742       ],
39743       [
39744         "South Korea (대한민국)",
39745         "kr",
39746         "82"
39747       ],
39748       [
39749         "South Sudan (‫جنوب السودان‬‎)",
39750         "ss",
39751         "211"
39752       ],
39753       [
39754         "Spain (España)",
39755         "es",
39756         "34"
39757       ],
39758       [
39759         "Sri Lanka (ශ්‍රී ලංකාව)",
39760         "lk",
39761         "94"
39762       ],
39763       [
39764         "Sudan (‫السودان‬‎)",
39765         "sd",
39766         "249"
39767       ],
39768       [
39769         "Suriname",
39770         "sr",
39771         "597"
39772       ],
39773       [
39774         "Svalbard and Jan Mayen",
39775         "sj",
39776         "47",
39777         1
39778       ],
39779       [
39780         "Swaziland",
39781         "sz",
39782         "268"
39783       ],
39784       [
39785         "Sweden (Sverige)",
39786         "se",
39787         "46"
39788       ],
39789       [
39790         "Switzerland (Schweiz)",
39791         "ch",
39792         "41"
39793       ],
39794       [
39795         "Syria (‫سوريا‬‎)",
39796         "sy",
39797         "963"
39798       ],
39799       [
39800         "Taiwan (台灣)",
39801         "tw",
39802         "886"
39803       ],
39804       [
39805         "Tajikistan",
39806         "tj",
39807         "992"
39808       ],
39809       [
39810         "Tanzania",
39811         "tz",
39812         "255"
39813       ],
39814       [
39815         "Thailand (ไทย)",
39816         "th",
39817         "66"
39818       ],
39819       [
39820         "Timor-Leste",
39821         "tl",
39822         "670"
39823       ],
39824       [
39825         "Togo",
39826         "tg",
39827         "228"
39828       ],
39829       [
39830         "Tokelau",
39831         "tk",
39832         "690"
39833       ],
39834       [
39835         "Tonga",
39836         "to",
39837         "676"
39838       ],
39839       [
39840         "Trinidad and Tobago",
39841         "tt",
39842         "1868"
39843       ],
39844       [
39845         "Tunisia (‫تونس‬‎)",
39846         "tn",
39847         "216"
39848       ],
39849       [
39850         "Turkey (Türkiye)",
39851         "tr",
39852         "90"
39853       ],
39854       [
39855         "Turkmenistan",
39856         "tm",
39857         "993"
39858       ],
39859       [
39860         "Turks and Caicos Islands",
39861         "tc",
39862         "1649"
39863       ],
39864       [
39865         "Tuvalu",
39866         "tv",
39867         "688"
39868       ],
39869       [
39870         "U.S. Virgin Islands",
39871         "vi",
39872         "1340"
39873       ],
39874       [
39875         "Uganda",
39876         "ug",
39877         "256"
39878       ],
39879       [
39880         "Ukraine (Україна)",
39881         "ua",
39882         "380"
39883       ],
39884       [
39885         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39886         "ae",
39887         "971"
39888       ],
39889       [
39890         "United Kingdom",
39891         "gb",
39892         "44",
39893         0
39894       ],
39895       [
39896         "United States",
39897         "us",
39898         "1",
39899         0
39900       ],
39901       [
39902         "Uruguay",
39903         "uy",
39904         "598"
39905       ],
39906       [
39907         "Uzbekistan (Oʻzbekiston)",
39908         "uz",
39909         "998"
39910       ],
39911       [
39912         "Vanuatu",
39913         "vu",
39914         "678"
39915       ],
39916       [
39917         "Vatican City (Città del Vaticano)",
39918         "va",
39919         "39",
39920         1
39921       ],
39922       [
39923         "Venezuela",
39924         "ve",
39925         "58"
39926       ],
39927       [
39928         "Vietnam (Việt Nam)",
39929         "vn",
39930         "84"
39931       ],
39932       [
39933         "Wallis and Futuna (Wallis-et-Futuna)",
39934         "wf",
39935         "681"
39936       ],
39937       [
39938         "Western Sahara (‫الصحراء الغربية‬‎)",
39939         "eh",
39940         "212",
39941         1
39942       ],
39943       [
39944         "Yemen (‫اليمن‬‎)",
39945         "ye",
39946         "967"
39947       ],
39948       [
39949         "Zambia",
39950         "zm",
39951         "260"
39952       ],
39953       [
39954         "Zimbabwe",
39955         "zw",
39956         "263"
39957       ],
39958       [
39959         "Åland Islands",
39960         "ax",
39961         "358",
39962         1
39963       ]
39964   ];
39965   
39966   return d;
39967 }/**
39968 *    This script refer to:
39969 *    Title: International Telephone Input
39970 *    Author: Jack O'Connor
39971 *    Code version:  v12.1.12
39972 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39973 **/
39974
39975 /**
39976  * @class Roo.bootstrap.PhoneInput
39977  * @extends Roo.bootstrap.TriggerField
39978  * An input with International dial-code selection
39979  
39980  * @cfg {String} defaultDialCode default '+852'
39981  * @cfg {Array} preferedCountries default []
39982   
39983  * @constructor
39984  * Create a new PhoneInput.
39985  * @param {Object} config Configuration options
39986  */
39987
39988 Roo.bootstrap.PhoneInput = function(config) {
39989     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39990 };
39991
39992 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39993         
39994         listWidth: undefined,
39995         
39996         selectedClass: 'active',
39997         
39998         invalidClass : "has-warning",
39999         
40000         validClass: 'has-success',
40001         
40002         allowed: '0123456789',
40003         
40004         max_length: 15,
40005         
40006         /**
40007          * @cfg {String} defaultDialCode The default dial code when initializing the input
40008          */
40009         defaultDialCode: '+852',
40010         
40011         /**
40012          * @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
40013          */
40014         preferedCountries: false,
40015         
40016         getAutoCreate : function()
40017         {
40018             var data = Roo.bootstrap.PhoneInputData();
40019             var align = this.labelAlign || this.parentLabelAlign();
40020             var id = Roo.id();
40021             
40022             this.allCountries = [];
40023             this.dialCodeMapping = [];
40024             
40025             for (var i = 0; i < data.length; i++) {
40026               var c = data[i];
40027               this.allCountries[i] = {
40028                 name: c[0],
40029                 iso2: c[1],
40030                 dialCode: c[2],
40031                 priority: c[3] || 0,
40032                 areaCodes: c[4] || null
40033               };
40034               this.dialCodeMapping[c[2]] = {
40035                   name: c[0],
40036                   iso2: c[1],
40037                   priority: c[3] || 0,
40038                   areaCodes: c[4] || null
40039               };
40040             }
40041             
40042             var cfg = {
40043                 cls: 'form-group',
40044                 cn: []
40045             };
40046             
40047             var input =  {
40048                 tag: 'input',
40049                 id : id,
40050                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40051                 maxlength: this.max_length,
40052                 cls : 'form-control tel-input',
40053                 autocomplete: 'new-password'
40054             };
40055             
40056             var hiddenInput = {
40057                 tag: 'input',
40058                 type: 'hidden',
40059                 cls: 'hidden-tel-input'
40060             };
40061             
40062             if (this.name) {
40063                 hiddenInput.name = this.name;
40064             }
40065             
40066             if (this.disabled) {
40067                 input.disabled = true;
40068             }
40069             
40070             var flag_container = {
40071                 tag: 'div',
40072                 cls: 'flag-box',
40073                 cn: [
40074                     {
40075                         tag: 'div',
40076                         cls: 'flag'
40077                     },
40078                     {
40079                         tag: 'div',
40080                         cls: 'caret'
40081                     }
40082                 ]
40083             };
40084             
40085             var box = {
40086                 tag: 'div',
40087                 cls: this.hasFeedback ? 'has-feedback' : '',
40088                 cn: [
40089                     hiddenInput,
40090                     input,
40091                     {
40092                         tag: 'input',
40093                         cls: 'dial-code-holder',
40094                         disabled: true
40095                     }
40096                 ]
40097             };
40098             
40099             var container = {
40100                 cls: 'roo-select2-container input-group',
40101                 cn: [
40102                     flag_container,
40103                     box
40104                 ]
40105             };
40106             
40107             if (this.fieldLabel.length) {
40108                 var indicator = {
40109                     tag: 'i',
40110                     tooltip: 'This field is required'
40111                 };
40112                 
40113                 var label = {
40114                     tag: 'label',
40115                     'for':  id,
40116                     cls: 'control-label',
40117                     cn: []
40118                 };
40119                 
40120                 var label_text = {
40121                     tag: 'span',
40122                     html: this.fieldLabel
40123                 };
40124                 
40125                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40126                 label.cn = [
40127                     indicator,
40128                     label_text
40129                 ];
40130                 
40131                 if(this.indicatorpos == 'right') {
40132                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40133                     label.cn = [
40134                         label_text,
40135                         indicator
40136                     ];
40137                 }
40138                 
40139                 if(align == 'left') {
40140                     container = {
40141                         tag: 'div',
40142                         cn: [
40143                             container
40144                         ]
40145                     };
40146                     
40147                     if(this.labelWidth > 12){
40148                         label.style = "width: " + this.labelWidth + 'px';
40149                     }
40150                     if(this.labelWidth < 13 && this.labelmd == 0){
40151                         this.labelmd = this.labelWidth;
40152                     }
40153                     if(this.labellg > 0){
40154                         label.cls += ' col-lg-' + this.labellg;
40155                         input.cls += ' col-lg-' + (12 - this.labellg);
40156                     }
40157                     if(this.labelmd > 0){
40158                         label.cls += ' col-md-' + this.labelmd;
40159                         container.cls += ' col-md-' + (12 - this.labelmd);
40160                     }
40161                     if(this.labelsm > 0){
40162                         label.cls += ' col-sm-' + this.labelsm;
40163                         container.cls += ' col-sm-' + (12 - this.labelsm);
40164                     }
40165                     if(this.labelxs > 0){
40166                         label.cls += ' col-xs-' + this.labelxs;
40167                         container.cls += ' col-xs-' + (12 - this.labelxs);
40168                     }
40169                 }
40170             }
40171             
40172             cfg.cn = [
40173                 label,
40174                 container
40175             ];
40176             
40177             var settings = this;
40178             
40179             ['xs','sm','md','lg'].map(function(size){
40180                 if (settings[size]) {
40181                     cfg.cls += ' col-' + size + '-' + settings[size];
40182                 }
40183             });
40184             
40185             this.store = new Roo.data.Store({
40186                 proxy : new Roo.data.MemoryProxy({}),
40187                 reader : new Roo.data.JsonReader({
40188                     fields : [
40189                         {
40190                             'name' : 'name',
40191                             'type' : 'string'
40192                         },
40193                         {
40194                             'name' : 'iso2',
40195                             'type' : 'string'
40196                         },
40197                         {
40198                             'name' : 'dialCode',
40199                             'type' : 'string'
40200                         },
40201                         {
40202                             'name' : 'priority',
40203                             'type' : 'string'
40204                         },
40205                         {
40206                             'name' : 'areaCodes',
40207                             'type' : 'string'
40208                         }
40209                     ]
40210                 })
40211             });
40212             
40213             if(!this.preferedCountries) {
40214                 this.preferedCountries = [
40215                     'hk',
40216                     'gb',
40217                     'us'
40218                 ];
40219             }
40220             
40221             var p = this.preferedCountries.reverse();
40222             
40223             if(p) {
40224                 for (var i = 0; i < p.length; i++) {
40225                     for (var j = 0; j < this.allCountries.length; j++) {
40226                         if(this.allCountries[j].iso2 == p[i]) {
40227                             var t = this.allCountries[j];
40228                             this.allCountries.splice(j,1);
40229                             this.allCountries.unshift(t);
40230                         }
40231                     } 
40232                 }
40233             }
40234             
40235             this.store.proxy.data = {
40236                 success: true,
40237                 data: this.allCountries
40238             };
40239             
40240             return cfg;
40241         },
40242         
40243         initEvents : function()
40244         {
40245             this.createList();
40246             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40247             
40248             this.indicator = this.indicatorEl();
40249             this.flag = this.flagEl();
40250             this.dialCodeHolder = this.dialCodeHolderEl();
40251             
40252             this.trigger = this.el.select('div.flag-box',true).first();
40253             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40254             
40255             var _this = this;
40256             
40257             (function(){
40258                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40259                 _this.list.setWidth(lw);
40260             }).defer(100);
40261             
40262             this.list.on('mouseover', this.onViewOver, this);
40263             this.list.on('mousemove', this.onViewMove, this);
40264             this.inputEl().on("keyup", this.onKeyUp, this);
40265             this.inputEl().on("keypress", this.onKeyPress, this);
40266             
40267             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40268
40269             this.view = new Roo.View(this.list, this.tpl, {
40270                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40271             });
40272             
40273             this.view.on('click', this.onViewClick, this);
40274             this.setValue(this.defaultDialCode);
40275         },
40276         
40277         onTriggerClick : function(e)
40278         {
40279             Roo.log('trigger click');
40280             if(this.disabled){
40281                 return;
40282             }
40283             
40284             if(this.isExpanded()){
40285                 this.collapse();
40286                 this.hasFocus = false;
40287             }else {
40288                 this.store.load({});
40289                 this.hasFocus = true;
40290                 this.expand();
40291             }
40292         },
40293         
40294         isExpanded : function()
40295         {
40296             return this.list.isVisible();
40297         },
40298         
40299         collapse : function()
40300         {
40301             if(!this.isExpanded()){
40302                 return;
40303             }
40304             this.list.hide();
40305             Roo.get(document).un('mousedown', this.collapseIf, this);
40306             Roo.get(document).un('mousewheel', this.collapseIf, this);
40307             this.fireEvent('collapse', this);
40308             this.validate();
40309         },
40310         
40311         expand : function()
40312         {
40313             Roo.log('expand');
40314
40315             if(this.isExpanded() || !this.hasFocus){
40316                 return;
40317             }
40318             
40319             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40320             this.list.setWidth(lw);
40321             
40322             this.list.show();
40323             this.restrictHeight();
40324             
40325             Roo.get(document).on('mousedown', this.collapseIf, this);
40326             Roo.get(document).on('mousewheel', this.collapseIf, this);
40327             
40328             this.fireEvent('expand', this);
40329         },
40330         
40331         restrictHeight : function()
40332         {
40333             this.list.alignTo(this.inputEl(), this.listAlign);
40334             this.list.alignTo(this.inputEl(), this.listAlign);
40335         },
40336         
40337         onViewOver : function(e, t)
40338         {
40339             if(this.inKeyMode){
40340                 return;
40341             }
40342             var item = this.view.findItemFromChild(t);
40343             
40344             if(item){
40345                 var index = this.view.indexOf(item);
40346                 this.select(index, false);
40347             }
40348         },
40349
40350         // private
40351         onViewClick : function(view, doFocus, el, e)
40352         {
40353             var index = this.view.getSelectedIndexes()[0];
40354             
40355             var r = this.store.getAt(index);
40356             
40357             if(r){
40358                 this.onSelect(r, index);
40359             }
40360             if(doFocus !== false && !this.blockFocus){
40361                 this.inputEl().focus();
40362             }
40363         },
40364         
40365         onViewMove : function(e, t)
40366         {
40367             this.inKeyMode = false;
40368         },
40369         
40370         select : function(index, scrollIntoView)
40371         {
40372             this.selectedIndex = index;
40373             this.view.select(index);
40374             if(scrollIntoView !== false){
40375                 var el = this.view.getNode(index);
40376                 if(el){
40377                     this.list.scrollChildIntoView(el, false);
40378                 }
40379             }
40380         },
40381         
40382         createList : function()
40383         {
40384             this.list = Roo.get(document.body).createChild({
40385                 tag: 'ul',
40386                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40387                 style: 'display:none'
40388             });
40389             
40390             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40391         },
40392         
40393         collapseIf : function(e)
40394         {
40395             var in_combo  = e.within(this.el);
40396             var in_list =  e.within(this.list);
40397             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40398             
40399             if (in_combo || in_list || is_list) {
40400                 return;
40401             }
40402             this.collapse();
40403         },
40404         
40405         onSelect : function(record, index)
40406         {
40407             if(this.fireEvent('beforeselect', this, record, index) !== false){
40408                 
40409                 this.setFlagClass(record.data.iso2);
40410                 this.setDialCode(record.data.dialCode);
40411                 this.hasFocus = false;
40412                 this.collapse();
40413                 this.fireEvent('select', this, record, index);
40414             }
40415         },
40416         
40417         flagEl : function()
40418         {
40419             var flag = this.el.select('div.flag',true).first();
40420             if(!flag){
40421                 return false;
40422             }
40423             return flag;
40424         },
40425         
40426         dialCodeHolderEl : function()
40427         {
40428             var d = this.el.select('input.dial-code-holder',true).first();
40429             if(!d){
40430                 return false;
40431             }
40432             return d;
40433         },
40434         
40435         setDialCode : function(v)
40436         {
40437             this.dialCodeHolder.dom.value = '+'+v;
40438         },
40439         
40440         setFlagClass : function(n)
40441         {
40442             this.flag.dom.className = 'flag '+n;
40443         },
40444         
40445         getValue : function()
40446         {
40447             var v = this.inputEl().getValue();
40448             if(this.dialCodeHolder) {
40449                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40450             }
40451             return v;
40452         },
40453         
40454         setValue : function(v)
40455         {
40456             var d = this.getDialCode(v);
40457             
40458             //invalid dial code
40459             if(v.length == 0 || !d || d.length == 0) {
40460                 if(this.rendered){
40461                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40462                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40463                 }
40464                 return;
40465             }
40466             
40467             //valid dial code
40468             this.setFlagClass(this.dialCodeMapping[d].iso2);
40469             this.setDialCode(d);
40470             this.inputEl().dom.value = v.replace('+'+d,'');
40471             this.hiddenEl().dom.value = this.getValue();
40472             
40473             this.validate();
40474         },
40475         
40476         getDialCode : function(v)
40477         {
40478             v = v ||  '';
40479             
40480             if (v.length == 0) {
40481                 return this.dialCodeHolder.dom.value;
40482             }
40483             
40484             var dialCode = "";
40485             if (v.charAt(0) != "+") {
40486                 return false;
40487             }
40488             var numericChars = "";
40489             for (var i = 1; i < v.length; i++) {
40490               var c = v.charAt(i);
40491               if (!isNaN(c)) {
40492                 numericChars += c;
40493                 if (this.dialCodeMapping[numericChars]) {
40494                   dialCode = v.substr(1, i);
40495                 }
40496                 if (numericChars.length == 4) {
40497                   break;
40498                 }
40499               }
40500             }
40501             return dialCode;
40502         },
40503         
40504         reset : function()
40505         {
40506             this.setValue(this.defaultDialCode);
40507             this.validate();
40508         },
40509         
40510         hiddenEl : function()
40511         {
40512             return this.el.select('input.hidden-tel-input',true).first();
40513         },
40514         
40515         // after setting val
40516         onKeyUp : function(e){
40517             this.setValue(this.getValue());
40518         },
40519         
40520         onKeyPress : function(e){
40521             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40522                 e.stopEvent();
40523             }
40524         }
40525         
40526 });
40527 /**
40528  * @class Roo.bootstrap.MoneyField
40529  * @extends Roo.bootstrap.ComboBox
40530  * Bootstrap MoneyField class
40531  * 
40532  * @constructor
40533  * Create a new MoneyField.
40534  * @param {Object} config Configuration options
40535  */
40536
40537 Roo.bootstrap.MoneyField = function(config) {
40538     
40539     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40540     
40541 };
40542
40543 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40544     
40545     /**
40546      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40547      */
40548     allowDecimals : true,
40549     /**
40550      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40551      */
40552     decimalSeparator : ".",
40553     /**
40554      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40555      */
40556     decimalPrecision : 0,
40557     /**
40558      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40559      */
40560     allowNegative : true,
40561     /**
40562      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40563      */
40564     allowZero: true,
40565     /**
40566      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40567      */
40568     minValue : Number.NEGATIVE_INFINITY,
40569     /**
40570      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40571      */
40572     maxValue : Number.MAX_VALUE,
40573     /**
40574      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40575      */
40576     minText : "The minimum value for this field is {0}",
40577     /**
40578      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40579      */
40580     maxText : "The maximum value for this field is {0}",
40581     /**
40582      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40583      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40584      */
40585     nanText : "{0} is not a valid number",
40586     /**
40587      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40588      */
40589     castInt : true,
40590     /**
40591      * @cfg {String} defaults currency of the MoneyField
40592      * value should be in lkey
40593      */
40594     defaultCurrency : false,
40595     /**
40596      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40597      */
40598     thousandsDelimiter : false,
40599     /**
40600      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40601      */
40602     max_length: false,
40603     
40604     inputlg : 9,
40605     inputmd : 9,
40606     inputsm : 9,
40607     inputxs : 6,
40608     
40609     store : false,
40610     
40611     getAutoCreate : function()
40612     {
40613         var align = this.labelAlign || this.parentLabelAlign();
40614         
40615         var id = Roo.id();
40616
40617         var cfg = {
40618             cls: 'form-group',
40619             cn: []
40620         };
40621
40622         var input =  {
40623             tag: 'input',
40624             id : id,
40625             cls : 'form-control roo-money-amount-input',
40626             autocomplete: 'new-password'
40627         };
40628         
40629         var hiddenInput = {
40630             tag: 'input',
40631             type: 'hidden',
40632             id: Roo.id(),
40633             cls: 'hidden-number-input'
40634         };
40635         
40636         if(this.max_length) {
40637             input.maxlength = this.max_length; 
40638         }
40639         
40640         if (this.name) {
40641             hiddenInput.name = this.name;
40642         }
40643
40644         if (this.disabled) {
40645             input.disabled = true;
40646         }
40647
40648         var clg = 12 - this.inputlg;
40649         var cmd = 12 - this.inputmd;
40650         var csm = 12 - this.inputsm;
40651         var cxs = 12 - this.inputxs;
40652         
40653         var container = {
40654             tag : 'div',
40655             cls : 'row roo-money-field',
40656             cn : [
40657                 {
40658                     tag : 'div',
40659                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40660                     cn : [
40661                         {
40662                             tag : 'div',
40663                             cls: 'roo-select2-container input-group',
40664                             cn: [
40665                                 {
40666                                     tag : 'input',
40667                                     cls : 'form-control roo-money-currency-input',
40668                                     autocomplete: 'new-password',
40669                                     readOnly : 1,
40670                                     name : this.currencyName
40671                                 },
40672                                 {
40673                                     tag :'span',
40674                                     cls : 'input-group-addon',
40675                                     cn : [
40676                                         {
40677                                             tag: 'span',
40678                                             cls: 'caret'
40679                                         }
40680                                     ]
40681                                 }
40682                             ]
40683                         }
40684                     ]
40685                 },
40686                 {
40687                     tag : 'div',
40688                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40689                     cn : [
40690                         {
40691                             tag: 'div',
40692                             cls: this.hasFeedback ? 'has-feedback' : '',
40693                             cn: [
40694                                 input
40695                             ]
40696                         }
40697                     ]
40698                 }
40699             ]
40700             
40701         };
40702         
40703         if (this.fieldLabel.length) {
40704             var indicator = {
40705                 tag: 'i',
40706                 tooltip: 'This field is required'
40707             };
40708
40709             var label = {
40710                 tag: 'label',
40711                 'for':  id,
40712                 cls: 'control-label',
40713                 cn: []
40714             };
40715
40716             var label_text = {
40717                 tag: 'span',
40718                 html: this.fieldLabel
40719             };
40720
40721             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40722             label.cn = [
40723                 indicator,
40724                 label_text
40725             ];
40726
40727             if(this.indicatorpos == 'right') {
40728                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40729                 label.cn = [
40730                     label_text,
40731                     indicator
40732                 ];
40733             }
40734
40735             if(align == 'left') {
40736                 container = {
40737                     tag: 'div',
40738                     cn: [
40739                         container
40740                     ]
40741                 };
40742
40743                 if(this.labelWidth > 12){
40744                     label.style = "width: " + this.labelWidth + 'px';
40745                 }
40746                 if(this.labelWidth < 13 && this.labelmd == 0){
40747                     this.labelmd = this.labelWidth;
40748                 }
40749                 if(this.labellg > 0){
40750                     label.cls += ' col-lg-' + this.labellg;
40751                     input.cls += ' col-lg-' + (12 - this.labellg);
40752                 }
40753                 if(this.labelmd > 0){
40754                     label.cls += ' col-md-' + this.labelmd;
40755                     container.cls += ' col-md-' + (12 - this.labelmd);
40756                 }
40757                 if(this.labelsm > 0){
40758                     label.cls += ' col-sm-' + this.labelsm;
40759                     container.cls += ' col-sm-' + (12 - this.labelsm);
40760                 }
40761                 if(this.labelxs > 0){
40762                     label.cls += ' col-xs-' + this.labelxs;
40763                     container.cls += ' col-xs-' + (12 - this.labelxs);
40764                 }
40765             }
40766         }
40767
40768         cfg.cn = [
40769             label,
40770             container,
40771             hiddenInput
40772         ];
40773         
40774         var settings = this;
40775
40776         ['xs','sm','md','lg'].map(function(size){
40777             if (settings[size]) {
40778                 cfg.cls += ' col-' + size + '-' + settings[size];
40779             }
40780         });
40781         
40782         return cfg;
40783     },
40784     
40785     initEvents : function()
40786     {
40787         this.indicator = this.indicatorEl();
40788         
40789         this.initCurrencyEvent();
40790         
40791         this.initNumberEvent();
40792     },
40793     
40794     initCurrencyEvent : function()
40795     {
40796         if (!this.store) {
40797             throw "can not find store for combo";
40798         }
40799         
40800         this.store = Roo.factory(this.store, Roo.data);
40801         this.store.parent = this;
40802         
40803         this.createList();
40804         
40805         this.triggerEl = this.el.select('.input-group-addon', true).first();
40806         
40807         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40808         
40809         var _this = this;
40810         
40811         (function(){
40812             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40813             _this.list.setWidth(lw);
40814         }).defer(100);
40815         
40816         this.list.on('mouseover', this.onViewOver, this);
40817         this.list.on('mousemove', this.onViewMove, this);
40818         this.list.on('scroll', this.onViewScroll, this);
40819         
40820         if(!this.tpl){
40821             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40822         }
40823         
40824         this.view = new Roo.View(this.list, this.tpl, {
40825             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40826         });
40827         
40828         this.view.on('click', this.onViewClick, this);
40829         
40830         this.store.on('beforeload', this.onBeforeLoad, this);
40831         this.store.on('load', this.onLoad, this);
40832         this.store.on('loadexception', this.onLoadException, this);
40833         
40834         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40835             "up" : function(e){
40836                 this.inKeyMode = true;
40837                 this.selectPrev();
40838             },
40839
40840             "down" : function(e){
40841                 if(!this.isExpanded()){
40842                     this.onTriggerClick();
40843                 }else{
40844                     this.inKeyMode = true;
40845                     this.selectNext();
40846                 }
40847             },
40848
40849             "enter" : function(e){
40850                 this.collapse();
40851                 
40852                 if(this.fireEvent("specialkey", this, e)){
40853                     this.onViewClick(false);
40854                 }
40855                 
40856                 return true;
40857             },
40858
40859             "esc" : function(e){
40860                 this.collapse();
40861             },
40862
40863             "tab" : function(e){
40864                 this.collapse();
40865                 
40866                 if(this.fireEvent("specialkey", this, e)){
40867                     this.onViewClick(false);
40868                 }
40869                 
40870                 return true;
40871             },
40872
40873             scope : this,
40874
40875             doRelay : function(foo, bar, hname){
40876                 if(hname == 'down' || this.scope.isExpanded()){
40877                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40878                 }
40879                 return true;
40880             },
40881
40882             forceKeyDown: true
40883         });
40884         
40885         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40886         
40887     },
40888     
40889     initNumberEvent : function(e)
40890     {
40891         this.inputEl().on("keydown" , this.fireKey,  this);
40892         this.inputEl().on("focus", this.onFocus,  this);
40893         this.inputEl().on("blur", this.onBlur,  this);
40894         
40895         this.inputEl().relayEvent('keyup', this);
40896         
40897         if(this.indicator){
40898             this.indicator.addClass('invisible');
40899         }
40900  
40901         this.originalValue = this.getValue();
40902         
40903         if(this.validationEvent == 'keyup'){
40904             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40905             this.inputEl().on('keyup', this.filterValidation, this);
40906         }
40907         else if(this.validationEvent !== false){
40908             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40909         }
40910         
40911         if(this.selectOnFocus){
40912             this.on("focus", this.preFocus, this);
40913             
40914         }
40915         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40916             this.inputEl().on("keypress", this.filterKeys, this);
40917         } else {
40918             this.inputEl().relayEvent('keypress', this);
40919         }
40920         
40921         var allowed = "0123456789";
40922         
40923         if(this.allowDecimals){
40924             allowed += this.decimalSeparator;
40925         }
40926         
40927         if(this.allowNegative){
40928             allowed += "-";
40929         }
40930         
40931         if(this.thousandsDelimiter) {
40932             allowed += ",";
40933         }
40934         
40935         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40936         
40937         var keyPress = function(e){
40938             
40939             var k = e.getKey();
40940             
40941             var c = e.getCharCode();
40942             
40943             if(
40944                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40945                     allowed.indexOf(String.fromCharCode(c)) === -1
40946             ){
40947                 e.stopEvent();
40948                 return;
40949             }
40950             
40951             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40952                 return;
40953             }
40954             
40955             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40956                 e.stopEvent();
40957             }
40958         };
40959         
40960         this.inputEl().on("keypress", keyPress, this);
40961         
40962     },
40963     
40964     onTriggerClick : function(e)
40965     {   
40966         if(this.disabled){
40967             return;
40968         }
40969         
40970         this.page = 0;
40971         this.loadNext = false;
40972         
40973         if(this.isExpanded()){
40974             this.collapse();
40975             return;
40976         }
40977         
40978         this.hasFocus = true;
40979         
40980         if(this.triggerAction == 'all') {
40981             this.doQuery(this.allQuery, true);
40982             return;
40983         }
40984         
40985         this.doQuery(this.getRawValue());
40986     },
40987     
40988     getCurrency : function()
40989     {   
40990         var v = this.currencyEl().getValue();
40991         
40992         return v;
40993     },
40994     
40995     restrictHeight : function()
40996     {
40997         this.list.alignTo(this.currencyEl(), this.listAlign);
40998         this.list.alignTo(this.currencyEl(), this.listAlign);
40999     },
41000     
41001     onViewClick : function(view, doFocus, el, e)
41002     {
41003         var index = this.view.getSelectedIndexes()[0];
41004         
41005         var r = this.store.getAt(index);
41006         
41007         if(r){
41008             this.onSelect(r, index);
41009         }
41010     },
41011     
41012     onSelect : function(record, index){
41013         
41014         if(this.fireEvent('beforeselect', this, record, index) !== false){
41015         
41016             this.setFromCurrencyData(index > -1 ? record.data : false);
41017             
41018             this.collapse();
41019             
41020             this.fireEvent('select', this, record, index);
41021         }
41022     },
41023     
41024     setFromCurrencyData : function(o)
41025     {
41026         var currency = '';
41027         
41028         this.lastCurrency = o;
41029         
41030         if (this.currencyField) {
41031             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41032         } else {
41033             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41034         }
41035         
41036         this.lastSelectionText = currency;
41037         
41038         //setting default currency
41039         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41040             this.setCurrency(this.defaultCurrency);
41041             return;
41042         }
41043         
41044         this.setCurrency(currency);
41045     },
41046     
41047     setFromData : function(o)
41048     {
41049         var c = {};
41050         
41051         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41052         
41053         this.setFromCurrencyData(c);
41054         
41055         var value = '';
41056         
41057         if (this.name) {
41058             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41059         } else {
41060             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41061         }
41062         
41063         this.setValue(value);
41064         
41065     },
41066     
41067     setCurrency : function(v)
41068     {   
41069         this.currencyValue = v;
41070         
41071         if(this.rendered){
41072             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41073             this.validate();
41074         }
41075     },
41076     
41077     setValue : function(v)
41078     {
41079         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41080         
41081         this.value = v;
41082         
41083         if(this.rendered){
41084             
41085             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41086             
41087             this.inputEl().dom.value = (v == '') ? '' :
41088                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41089             
41090             if(!this.allowZero && v === '0') {
41091                 this.hiddenEl().dom.value = '';
41092                 this.inputEl().dom.value = '';
41093             }
41094             
41095             this.validate();
41096         }
41097     },
41098     
41099     getRawValue : function()
41100     {
41101         var v = this.inputEl().getValue();
41102         
41103         return v;
41104     },
41105     
41106     getValue : function()
41107     {
41108         return this.fixPrecision(this.parseValue(this.getRawValue()));
41109     },
41110     
41111     parseValue : function(value)
41112     {
41113         if(this.thousandsDelimiter) {
41114             value += "";
41115             r = new RegExp(",", "g");
41116             value = value.replace(r, "");
41117         }
41118         
41119         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41120         return isNaN(value) ? '' : value;
41121         
41122     },
41123     
41124     fixPrecision : function(value)
41125     {
41126         if(this.thousandsDelimiter) {
41127             value += "";
41128             r = new RegExp(",", "g");
41129             value = value.replace(r, "");
41130         }
41131         
41132         var nan = isNaN(value);
41133         
41134         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41135             return nan ? '' : value;
41136         }
41137         return parseFloat(value).toFixed(this.decimalPrecision);
41138     },
41139     
41140     decimalPrecisionFcn : function(v)
41141     {
41142         return Math.floor(v);
41143     },
41144     
41145     validateValue : function(value)
41146     {
41147         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41148             return false;
41149         }
41150         
41151         var num = this.parseValue(value);
41152         
41153         if(isNaN(num)){
41154             this.markInvalid(String.format(this.nanText, value));
41155             return false;
41156         }
41157         
41158         if(num < this.minValue){
41159             this.markInvalid(String.format(this.minText, this.minValue));
41160             return false;
41161         }
41162         
41163         if(num > this.maxValue){
41164             this.markInvalid(String.format(this.maxText, this.maxValue));
41165             return false;
41166         }
41167         
41168         return true;
41169     },
41170     
41171     validate : function()
41172     {
41173         if(this.disabled || this.allowBlank){
41174             this.markValid();
41175             return true;
41176         }
41177         
41178         var currency = this.getCurrency();
41179         
41180         if(this.validateValue(this.getRawValue()) && currency.length){
41181             this.markValid();
41182             return true;
41183         }
41184         
41185         this.markInvalid();
41186         return false;
41187     },
41188     
41189     getName: function()
41190     {
41191         return this.name;
41192     },
41193     
41194     beforeBlur : function()
41195     {
41196         if(!this.castInt){
41197             return;
41198         }
41199         
41200         var v = this.parseValue(this.getRawValue());
41201         
41202         if(v || v == 0){
41203             this.setValue(v);
41204         }
41205     },
41206     
41207     onBlur : function()
41208     {
41209         this.beforeBlur();
41210         
41211         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41212             //this.el.removeClass(this.focusClass);
41213         }
41214         
41215         this.hasFocus = false;
41216         
41217         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41218             this.validate();
41219         }
41220         
41221         var v = this.getValue();
41222         
41223         if(String(v) !== String(this.startValue)){
41224             this.fireEvent('change', this, v, this.startValue);
41225         }
41226         
41227         this.fireEvent("blur", this);
41228     },
41229     
41230     inputEl : function()
41231     {
41232         return this.el.select('.roo-money-amount-input', true).first();
41233     },
41234     
41235     currencyEl : function()
41236     {
41237         return this.el.select('.roo-money-currency-input', true).first();
41238     },
41239     
41240     hiddenEl : function()
41241     {
41242         return this.el.select('input.hidden-number-input',true).first();
41243     }
41244     
41245 });